<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://ouroboros.rocks/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Dimitri</id>
	<title>Ouroboros - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://ouroboros.rocks/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Dimitri"/>
	<link rel="alternate" type="text/html" href="https://ouroboros.rocks/wiki/Special:Contributions/Dimitri"/>
	<updated>2026-07-18T04:14:08Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.17</generator>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1945</id>
		<title>Congestion avoidance</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1945"/>
		<updated>2026-07-12T09:13:06Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: Blanked the page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1944</id>
		<title>Congestion avoidance</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1944"/>
		<updated>2026-07-11T22:23:01Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:mb-ecn - Multi-bit ECN Congestion Avoidance}}&lt;br /&gt;
&lt;br /&gt;
mb-ecn is a congestion-avoidance (CA) policy for the Ouroboros unicast&lt;br /&gt;
IPCP. Reliability, ordering and flow control are FRCP&#039;s job, end-to-end&lt;br /&gt;
and per flow; congestion avoidance is a separate concern, and it runs&lt;br /&gt;
in the IPCP, once per layer. Within a layer the IPCP controls&lt;br /&gt;
&#039;&#039;&#039;aggregates&#039;&#039;&#039;: one control loop per (destination address, QoS cube),&lt;br /&gt;
shared by every flow to that destination.&lt;br /&gt;
&lt;br /&gt;
This document is about the algorithm: what it does and why. The&lt;br /&gt;
code follows it; the [[#References|reference section]] maps the pieces&lt;br /&gt;
to source files. It doesn&#039;t oversell: the [[#Limitations|limitations]]&lt;br /&gt;
section says plainly what it can&#039;t do.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;MUST&amp;quot;, &amp;quot;SHOULD&amp;quot;, &amp;quot;MAY&amp;quot; and friends mean what BCP 14 (RFC 2119,&lt;br /&gt;
RFC 8174) says, but only where they appear in capitals.&lt;br /&gt;
&lt;br /&gt;
Note: Co-authered by Anthropic Claude Fable 5.&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Symbol !! Units !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; || B/s || Paced send rate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt; || B/s || Bottleneck capacity&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; || bytes || Packet size&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;q&amp;lt;/code&amp;gt; || packets || Standing queue occupancy at the bottleneck&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; || packets || Marking quantum: packets per mark unit (4)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; || 8-bit code || Per-packet congestion mark set by a forwarder&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; || 1/32 &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; || Receiver-side congestion estimate: 32 &amp;amp;times; time-mean &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;cap8&amp;lt;/code&amp;gt; || 8-bit code || Path capacity, quarter-log2: &amp;lt;code&amp;gt;C &amp;amp;asymp; 2^(cap8 / 4)&amp;lt;/code&amp;gt; B/s, 0 = unknown&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt; || ns || Receiver averaging window&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; || B/s^2 || Additive-increase slope&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; units || Full-congestion reference level (512)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;Delta;t&amp;lt;/code&amp;gt; || ns || Elapsed wall-clock time between two events&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; || n/a || Path round-trip time (feedback delay); the controller does not measure it&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rate is the control variable throughout.&lt;br /&gt;
&lt;br /&gt;
Every concrete figure in this document (window bounds, horizons,&lt;br /&gt;
rate thresholds) is just an instance of the parameter values in the&lt;br /&gt;
[[#Parameters|parameters table]]. What the algorithm fixes is the&lt;br /&gt;
mechanisms and the relations between parameters; retune the parameters&lt;br /&gt;
and the figures move together.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
mb-ecn is a rate-based, ECN-driven controller with three moving parts,&lt;br /&gt;
one at each point of the loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        sender                                  forwarder&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
      |  SFQ virtual-clock pacer       |       |  ecn = MAX(ecn, queue/Q) |&lt;br /&gt;
      |  rate r, paced per packet      | data  |  (deepest queue wins)    |&lt;br /&gt;
      |  AIMD + PD control on r        |------&amp;gt;|  cap8 = MIN(cap8, link)  |&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
                 ^                                        |&lt;br /&gt;
                 | ece + cap8 feedback                    | marked data&lt;br /&gt;
                 |                                        v&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
      |  reads downstream ece,         |&amp;lt;------|  receiver                |&lt;br /&gt;
      |  updates the rate on feedback, |  ece  |  time-mean of ecn over   |&lt;br /&gt;
      |  scales floor/slope to cap8    |  cap8 |  an adaptive window      |&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# The &#039;&#039;&#039;forwarder&#039;&#039;&#039; marks each packet with a &#039;&#039;multi-bit&#039;&#039; magnitude proportional to its standing queue, and MIN-stamps its measured outgoing-link capacity into the same PCI.&lt;br /&gt;
# The &#039;&#039;&#039;receiver&#039;&#039;&#039; turns the stream of marks into a time-averaged congestion estimate &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; and feeds it back to the sender, together with the window minimum of the path-capacity codes.&lt;br /&gt;
# The &#039;&#039;&#039;sender&#039;&#039;&#039; paces its rate with a start-time fair-queuing (SFQ) virtual clock, and adjusts that rate with additive-increase / multiplicative-decrease (AIMD) driven by &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;; the rate floor and the additive slope derive from the fed-back path capacity.&lt;br /&gt;
&lt;br /&gt;
There&#039;s no timer thread: the controller runs when a packet goes out or&lt;br /&gt;
feedback comes back. Every step is scaled by elapsed wall-clock time,&lt;br /&gt;
and that is what makes the &#039;&#039;allocation&#039;&#039; independent of RTT (see&lt;br /&gt;
[[#RTT behaviour|RTT behaviour]]).&lt;br /&gt;
&lt;br /&gt;
The unit of control is the &#039;&#039;&#039;aggregate&#039;&#039;&#039;: one per&lt;br /&gt;
&amp;lt;code&amp;gt;(destination address, QoS cube)&amp;lt;/code&amp;gt;, shared by every flow to&lt;br /&gt;
that destination at that QoS. They share one controller and one rate.&lt;br /&gt;
A new flow joins at whatever rate the aggregate is already running; when&lt;br /&gt;
a flow leaves, its share goes to the others. The pacer (SFQ) splits&lt;br /&gt;
that rate fairly across the member flows, so per-flow fairness is the&lt;br /&gt;
scheduler&#039;s doing, not the rate law&#039;s.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Forwarder marking ==&lt;br /&gt;
&lt;br /&gt;
A forwarder marks a packet based on how many packets are already queued&lt;br /&gt;
on the outgoing link:&lt;br /&gt;
&lt;br /&gt;
  mark = queued / Q                         (Q = 4 packets)&lt;br /&gt;
  ecn  = MAX(ecn, mark)                     (saturating, 8-bit field)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; is fixed for the whole layer, not tuned per hop: the marks&lt;br /&gt;
get MAX-combined across hops, so every forwarder MUST use the same&lt;br /&gt;
quantum or the numbers don&#039;t compare.&lt;br /&gt;
&lt;br /&gt;
Two things worth noting:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;It&#039;s a magnitude, not a bit.&#039;&#039;&#039; The mark is an integer that tracks queue depth, in steps of &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; packets: a coarse read of the standing queue.&lt;br /&gt;
* &#039;&#039;&#039;MAX along the path.&#039;&#039;&#039; Cross several forwarders and the packet carries the largest mark, i.e. the deepest queue it passed. Taking the MAX (not a sum) keeps the number bounded and monotone across hops.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a &#039;&#039;&#039;dead zone&#039;&#039;&#039;: below &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; queued packets the mark is 0.&lt;br /&gt;
&lt;br /&gt;
=== Capacity stamping ===&lt;br /&gt;
&lt;br /&gt;
Each forwarder also measures its outgoing link&#039;s capacity and stamps it&lt;br /&gt;
into the packet. The measurement is a busy-period drain rate: look at&lt;br /&gt;
the queue at most once a millisecond, and keep a window open until 16&lt;br /&gt;
packets have drained. So the window &#039;&#039;&#039;sizes itself to the link&#039;&#039;&#039;:&lt;br /&gt;
the once-a-millisecond cadence caps it on fast links, the 16-packet&lt;br /&gt;
drain time on slow ones (~19 ms at 10 Mbit/s). If more than 1/8 of the&lt;br /&gt;
arrivals hit an empty queue, throw the window out: the link&lt;br /&gt;
wasn&#039;t saturated. The odd empty sample (a token-bucket shaper grazing&lt;br /&gt;
zero) is fine. A saturated queue drains at the link rate, so a max&lt;br /&gt;
filter that decays slowly (1/16 per window) creeps up to it from below.&lt;br /&gt;
A window that started or ended on an empty queue might have drained&lt;br /&gt;
into buffers downstream faster than the wire, so it may pull the&lt;br /&gt;
estimate down but never up. On the wire the code is quarter-log2&lt;br /&gt;
(&amp;lt;code&amp;gt;cap8 = 4 log2 C&amp;lt;/code&amp;gt;, ~19% a step, 0 = unknown), and each hop&lt;br /&gt;
MIN-combines its own into the byte, so the packet arrives carrying the&lt;br /&gt;
slowest hop&#039;s rate. A hop that has never backed up stamps nothing,&lt;br /&gt;
which is fine, since only backed-up hops matter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Receiver estimate ==&lt;br /&gt;
&lt;br /&gt;
The receiver turns the marks into a smoothed estimate &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;,&lt;br /&gt;
averaged over a window &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt; that &#039;&#039;&#039;tracks the incoming byte rate&#039;&#039;&#039;.&lt;br /&gt;
It&#039;s a plain time-average over that window (a box-car, not an EWMA).&lt;br /&gt;
Per packet, with &amp;lt;code&amp;gt;&amp;amp;Delta;t&amp;lt;/code&amp;gt; the gap since the last one:&lt;br /&gt;
&lt;br /&gt;
  A += ecn * MIN(&amp;amp;Delta;t, W)              dwell-weighted; one packet&lt;br /&gt;
                                          weighs at most one window&lt;br /&gt;
  B += S                                  accumulate bytes this window&lt;br /&gt;
  ...&lt;br /&gt;
  at window close (elapsed &amp;gt;= W, or B &amp;gt;= 2 * B_target with at&lt;br /&gt;
                   least the window floor elapsed):&lt;br /&gt;
      ece = 32 * A / elapsed              mean over the actual window&lt;br /&gt;
      W  += (B_target * elapsed / B - W) / 4    nudge toward the size&lt;br /&gt;
                                                that holds the target&lt;br /&gt;
      W   = clamp(W, ~1.05 ms, ~4.3 s)&lt;br /&gt;
      A   = 0 ; B = 0                     hard reset&lt;br /&gt;
&lt;br /&gt;
When a window closes, the next one is nudged (a quarter-weight&lt;br /&gt;
moving average) toward the size that would hold about 16 packets&lt;br /&gt;
at the current byte rate, starting from ~67 ms. The target is in bytes&lt;br /&gt;
(&amp;lt;code&amp;gt;B_target&amp;lt;/code&amp;gt; = 16,000), so &amp;quot;16 packets&amp;quot; is only exact at&lt;br /&gt;
~1000-byte packets, and it takes a few closes to settle. The mean&lt;br /&gt;
always divides by the window that &#039;&#039;&#039;actually&#039;&#039;&#039; elapsed, so a change&lt;br /&gt;
in rate leaves the current estimate correct and only resizes the&lt;br /&gt;
&#039;&#039;next&#039;&#039; one. Net effect: a roughly constant ~16 samples a window from&lt;br /&gt;
about 30 kbit/s to 122 Mbit/s. Above that the window bottoms out at&lt;br /&gt;
~1 ms (still thousands of samples a window at 10&amp;amp;ndash;100 GbE); below&lt;br /&gt;
~30 kbit/s it hits the ~4.3 s ceiling. The averaging clock stretches&lt;br /&gt;
with the flow, the way TCP&#039;s ACK clock stretches with the RTT. A&lt;br /&gt;
sudden speed-up doesn&#039;t wait out a stretched window: once twice the&lt;br /&gt;
target bytes have arrived it closes anyway (the ~1 ms floor keeps that&lt;br /&gt;
from running away at high rate). So the &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; the sender sees&lt;br /&gt;
is a staircase: one step per window, steps getting shorter as&lt;br /&gt;
the rate climbs. Two edge cases:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Onset.&#039;&#039;&#039; The first mark after an idle stretch is passed through as-is, so a queue that&#039;s just starting to build shows up without waiting out a whole window.&lt;br /&gt;
* &#039;&#039;&#039;Gap.&#039;&#039;&#039; A silence longer than 4 windows resets and sends the raw sample. &amp;quot;Long&amp;quot; is &#039;&#039;&#039;measured in the flow&#039;s own windows&#039;&#039;&#039;, so a slow flow&#039;s normal spacing never reads as idle; and the dwell clamp above limits what a real pause can add to the mean before the reset fires.&lt;br /&gt;
&lt;br /&gt;
Alongside the marks, the receiver keeps the smallest non-zero capacity&lt;br /&gt;
code it saw this window and ships it with each &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; (on an&lt;br /&gt;
onset/gap restart, just the current packet&#039;s code). Then it resets, so&lt;br /&gt;
if the path reroutes onto something faster the fed-back capacity can&lt;br /&gt;
climb within one window.&lt;br /&gt;
&lt;br /&gt;
The estimate rides back to the sender on the reverse flow.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Sender pacer ==&lt;br /&gt;
&lt;br /&gt;
The sender paces off an SFQ virtual clock. The aggregate keeps a&lt;br /&gt;
virtual time &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;; each flow keeps a finish tag &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
  V    += r * &amp;amp;Delta;t                     virtual time advances by bytes served&lt;br /&gt;
  s     = MAX(F, V)                  a packet behind the clock starts now&lt;br /&gt;
  F     = s + S                      the one after it queues behind&lt;br /&gt;
  wait  = (s - V) / r                time until this packet may go&lt;br /&gt;
&lt;br /&gt;
That &amp;lt;code&amp;gt;wait&amp;lt;/code&amp;gt; is the flow&#039;s scheduling deadline. A packet&lt;br /&gt;
that&#039;s behind the virtual clock goes out now (&amp;lt;code&amp;gt;wait = 0&amp;lt;/code&amp;gt;);&lt;br /&gt;
one that&#039;s ahead waits, and only that flow waits, nobody else.&lt;br /&gt;
Across an idle gap longer than 50 ms the clock credits at most the&lt;br /&gt;
flow&#039;s own owed lead plus one burst (50 ms of service, at least one&lt;br /&gt;
packet). So a flow paced slower than a packet per 50 ms still gets its&lt;br /&gt;
&#039;&#039;&#039;real&#039;&#039;&#039; elapsed service (capping the credit by time would&lt;br /&gt;
starve it) while an idle flow still can&#039;t bank an unbounded&lt;br /&gt;
burst.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; is shared by every flow in the aggregate; the finish&lt;br /&gt;
tags are not. Since every flow measures its start tag &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt;&lt;br /&gt;
against the same &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;, a flow that just sent is now ahead of&lt;br /&gt;
&amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; and has to wait, while an idle one (tag at or behind&lt;br /&gt;
&amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;) goes right away. That is what splits the aggregate rate&lt;br /&gt;
&amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; fairly among them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Rate control (AIMD + PD) ==&lt;br /&gt;
&lt;br /&gt;
The rate law runs on packet sends, at most once a millisecond, and&lt;br /&gt;
also when feedback arrives, so a sender with no traffic of its&lt;br /&gt;
own still reacts. The increase terms and the proportional decrease are&lt;br /&gt;
each scaled by elapsed time, but not by the same clock: the increases&lt;br /&gt;
use banked time &#039;&#039;&#039;capped&#039;&#039;&#039; at 50 ms (an idle gap shouldn&#039;t buy an&lt;br /&gt;
unbounded ramp), while the decrease uses the &#039;&#039;&#039;uncapped&#039;&#039;&#039; elapsed&lt;br /&gt;
time (a starved sender still cuts by the right amount). The one-sided&lt;br /&gt;
derivative (see the&lt;br /&gt;
[[#Multiplicative decrease (proportional + derivative)|decrease]]) is&lt;br /&gt;
not scaled by time at all; it is per-step.&lt;br /&gt;
&lt;br /&gt;
=== Slow start ===&lt;br /&gt;
&lt;br /&gt;
Before the first congestion signal, the rate ramps up exponentially&lt;br /&gt;
with a 20 ms time constant:&lt;br /&gt;
&lt;br /&gt;
  r += r * &amp;amp;Delta;t / T_ss                  (T_ss = 20 ms)&lt;br /&gt;
&lt;br /&gt;
Slow start ends the first time a congestion signal shows up, whether&lt;br /&gt;
fed back from the receiver or seen at the sender&#039;s own first hop, and&lt;br /&gt;
it never comes back.&lt;br /&gt;
&lt;br /&gt;
Slow start belongs to the aggregate, so it happens&lt;br /&gt;
&#039;&#039;&#039;once per (destination, QoS cube)&#039;&#039;&#039;, on the first flow. A flow that&lt;br /&gt;
shows up later, at an aggregate that&#039;s already running, rides the&lt;br /&gt;
existing&lt;br /&gt;
estimate: it starts at the current rate and takes its share through&lt;br /&gt;
the [[#Sender pacer|pacer]], without probing of its own.&lt;br /&gt;
&lt;br /&gt;
=== Increase: additive plus proportional probe (always on) ===&lt;br /&gt;
&lt;br /&gt;
Past slow start, two increase terms run every step, congested or not:&lt;br /&gt;
&lt;br /&gt;
  r += a * &amp;amp;Delta;t                         additive; a defaults to 2^16 B/s^2&lt;br /&gt;
  r += r * &amp;amp;Delta;t / T_probe               proportional; T_probe = 8 s&lt;br /&gt;
&lt;br /&gt;
The additive term is a fixed slope (bytes/s per second), sized to the&lt;br /&gt;
path capacity (see&lt;br /&gt;
[[#Capacity-derived floor and slope|the capacity floor]]). The&lt;br /&gt;
proportional probe adds a fixed fraction of the rate per unit time (it&lt;br /&gt;
e-folds over &amp;lt;code&amp;gt;T_probe&amp;lt;/code&amp;gt;): that is what lets a flow recover in&lt;br /&gt;
the same number of steps whatever the link rate; a fixed additive step&lt;br /&gt;
is negligible next to a fast link. Together they are the&lt;br /&gt;
upward pressure the multiplicative decrease balances at equilibrium.&lt;br /&gt;
The probe is not free: it costs a standing queue that does not shrink&lt;br /&gt;
with rate (see [[#Fairness|fairness]]).&lt;br /&gt;
&lt;br /&gt;
=== Multiplicative decrease (proportional + derivative) ===&lt;br /&gt;
&lt;br /&gt;
With congestion level &amp;lt;code&amp;gt;m&amp;lt;/code&amp;gt; (the fed-back &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;, or&lt;br /&gt;
the local first-hop mark if there&#039;s no feedback yet), the cut is a&lt;br /&gt;
proportional term plus a one-sided derivative term, a PD controller on&lt;br /&gt;
the congestion signal:&lt;br /&gt;
&lt;br /&gt;
  mark = MIN(m, M)                                   M = 512&lt;br /&gt;
  rise = MAX(m - m_prev, 0), capped at M             one-sided derivative&lt;br /&gt;
&lt;br /&gt;
  cut  = r * rise / (4 * M)                          derivative, gain 1/4&lt;br /&gt;
  cut += r * mark * &amp;amp;Delta;t_ms / (1000 * M)              proportional&lt;br /&gt;
  cut  = MIN(cut, r / 2)                             at most a halving&lt;br /&gt;
&lt;br /&gt;
  r     -= cut&lt;br /&gt;
  m_prev = m&lt;br /&gt;
&lt;br /&gt;
* The &#039;&#039;&#039;proportional&#039;&#039;&#039; term uses the real elapsed milliseconds, so a starved sender that hasn&#039;t run the controller in a while still cuts by the right amount.&lt;br /&gt;
* The &#039;&#039;&#039;derivative&#039;&#039;&#039; term (&amp;lt;code&amp;gt;rise&amp;lt;/code&amp;gt;) only fires when the congestion level &#039;&#039;goes up&#039;&#039; between samples: one-sided, per-step. It sharpens the response right as congestion starts.&lt;br /&gt;
* The cut is capped at &amp;lt;code&amp;gt;r/2&amp;lt;/code&amp;gt;, so one step can at most halve the rate.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;M = 512 = 32 &amp;amp;times; 16&amp;lt;/code&amp;gt; pins &amp;quot;full congestion&amp;quot; at a mean&lt;br /&gt;
&amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; of 16, i.e. a mean queue of &amp;lt;code&amp;gt;16 &amp;amp;times; Q = 64&amp;lt;/code&amp;gt;&lt;br /&gt;
packets.&lt;br /&gt;
&lt;br /&gt;
=== Staleness ===&lt;br /&gt;
&lt;br /&gt;
Feedback comes once per receiver window, and that window stretches with&lt;br /&gt;
the flow&#039;s byte rate (see&lt;br /&gt;
[[#Receiver estimate|the receiver estimate]]), so the staleness horizon&lt;br /&gt;
has to stretch with it too, or a slow flow&#039;s mark would expire&lt;br /&gt;
&#039;&#039;between&#039;&#039; feedbacks and the rate would ramp straight into a congested&lt;br /&gt;
path. The sender mirrors the receiver: the horizon is four target&lt;br /&gt;
windows&#039; worth of bytes at the current rate, floored at ~268 ms so fast&lt;br /&gt;
flows keep a fixed one; at the default rate floor it&#039;s ~8 s. Once a&lt;br /&gt;
signal is older than that it&#039;s dropped (both the fed-back estimate and&lt;br /&gt;
the local mark), which frees the rate to climb again after congestion&lt;br /&gt;
clears. Every step clamps the rate to &amp;lt;code&amp;gt;[r_min, r_max]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Capacity-derived floor and slope ===&lt;br /&gt;
&lt;br /&gt;
The fed-back capacity code sets the controller&#039;s two rate-scale&lt;br /&gt;
constants, per aggregate:&lt;br /&gt;
&lt;br /&gt;
  target = decode(cap8) / 32              clamped to [2^13, 2^32] B/s&lt;br /&gt;
  r_min += (target - r_min) / 2           per feedback (moving average)&lt;br /&gt;
  a      = r_min&lt;br /&gt;
&lt;br /&gt;
On a path that never reports a capacity, both fall back to fixed&lt;br /&gt;
defaults (&amp;lt;code&amp;gt;r_min = 2^13 B/s&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;a = 2^16 B/s^2&amp;lt;/code&amp;gt;).&lt;br /&gt;
When the estimator is live, the fairness floor and the post-cut&lt;br /&gt;
recovery slope scale with the bottleneck instead, and capacity kicks in&lt;br /&gt;
wherever &amp;lt;code&amp;gt;C/32&amp;lt;/code&amp;gt; beats the default floor, roughly above&lt;br /&gt;
2 Mbit/s. A capacity older than 16 staleness horizons (~4.3 s for fast&lt;br /&gt;
flows) reverts both to the defaults. That horizon is deliberately&lt;br /&gt;
longer than the feedback horizon: feedback stops the instant the marks&lt;br /&gt;
clear, which is exactly when you need the recovery slope, and the&lt;br /&gt;
onset-fresh capacity (see&lt;br /&gt;
[[#Receiver estimate|the receiver estimate]]) re-seeds it on the first&lt;br /&gt;
mark of the next episode anyway.&lt;br /&gt;
&lt;br /&gt;
=== Ramp and recovery ===&lt;br /&gt;
&lt;br /&gt;
Three mechanisms set how fast an aggregate reaches a fast link&#039;s&lt;br /&gt;
rate, each owning one regime:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;From scratch: slow start.&#039;&#039;&#039; The exponential ramp hits any real link rate in well under a second (~200 ms from the seed to 10 Gbit/s; every further doubling of link speed adds one ~14 ms doubling). Nothing marks on an uncongested path, so slow start just runs until the aggregate reaches its bottleneck.&lt;br /&gt;
* &#039;&#039;&#039;After a cut: the probe.&#039;&#039;&#039; A cut is at most a halving, and the mark expires on the rate-relative horizon once congestion clears (see [[#Staleness|staleness]]); the probe then climbs back from a halving in &amp;lt;code&amp;gt;T_probe &amp;amp;middot; ln 2 &amp;amp;asymp; 5.5 s&amp;lt;/code&amp;gt;, whatever the link rate.&lt;br /&gt;
* &#039;&#039;&#039;At the bottom: the capacity floor.&#039;&#039;&#039; The rate never sits more than 32&amp;amp;times; below a measured bottleneck, and the additive slope refills &amp;lt;code&amp;gt;C/32&amp;lt;/code&amp;gt; a second, so the worst hole is bounded: floor to full capacity in tens of seconds, a halving in seconds.&lt;br /&gt;
&lt;br /&gt;
These fit together because a deep cut needs sustained congestion,&lt;br /&gt;
sustained congestion backs up the bottleneck queue, and a backed-up&lt;br /&gt;
queue is exactly when its capacity gets measured and stamped (see&lt;br /&gt;
[[#Forwarder marking|forwarder marking]]). So whenever the controller&lt;br /&gt;
has been cut deep, the scaled floor is already live. The default floor&lt;br /&gt;
only ever applies to paths that never congested the sender in the first&lt;br /&gt;
place, where there&#039;s nothing to recover from. That&#039;s why slow start can&lt;br /&gt;
quit for good: the floor and the probe handle every recovery after it.&lt;br /&gt;
&lt;br /&gt;
=== Regimes ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   +-------------+   first mark seen    +----------------+&lt;br /&gt;
   | slow start  |---------------------&amp;gt;|  congestion    |&lt;br /&gt;
   | r *= e^(t/T)|                      |  avoidance     |&lt;br /&gt;
   +-------------+                      |                |&lt;br /&gt;
                                        | m == 0 ---&amp;gt; additive increase&lt;br /&gt;
                                        | m  &amp;gt; 0 ---&amp;gt; AI + PD decrease&lt;br /&gt;
                                        +----------------+&lt;br /&gt;
    m = fed-back ece, or the local first-hop mark when ece == 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== RTT behaviour ==&lt;br /&gt;
&lt;br /&gt;
Because every increase and decrease is scaled by wall-clock time, two&lt;br /&gt;
flows with different RTTs sharing a bottleneck follow the &#039;&#039;same&#039;&#039; rate&lt;br /&gt;
law and settle at the &#039;&#039;same&#039;&#039; rate. So the steady-state&lt;br /&gt;
&#039;&#039;&#039;allocation is RTT-independent&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
That is a property of the equilibrium, not the dynamics. The feedback&lt;br /&gt;
delay in the loop &#039;&#039;is&#039;&#039; the path RTT &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;, and the&lt;br /&gt;
controller neither measures nor compensates for it. At low rate the&lt;br /&gt;
receiver window (up to its ~4.3 s ceiling) is the biggest lag: the loop&lt;br /&gt;
is slow there in proportion to how slow the flow is, just like a&lt;br /&gt;
long-RTT TCP. As the rate climbs the window shrinks toward its ~1 ms&lt;br /&gt;
floor, so its share of the loop delay &#039;&#039;falls&#039;&#039; with capacity and the&lt;br /&gt;
path RTT &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; becomes the irreducible part. Two things keep&lt;br /&gt;
the loop damped: the adaptive window takes out the delay-dominated&lt;br /&gt;
corner at low &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;, and a bucketed&lt;br /&gt;
&#039;&#039;&#039;rate-velocity damping&#039;&#039;&#039; (the washout: each ~31 ms bucket pulls the&lt;br /&gt;
rate a quarter of the way back toward its value at the previous bucket)&lt;br /&gt;
supplies the damping this otherwise near-double-integrator loop lacks,&lt;br /&gt;
which is what holds it together at high capacity and moderate&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;. What is left is the loop gain&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;prop; &amp;amp;radic;C&amp;lt;/code&amp;gt;: at very high capacity &#039;&#039;and&#039;&#039;&lt;br /&gt;
large &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; the &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt; phase&lt;br /&gt;
budget is the binding constraint, and no amount of windowing or damping&lt;br /&gt;
removes it (see [[#Limitations|Limitations]]). So, precisely:&lt;br /&gt;
&#039;&#039;&#039;RTT-fair in equilibrium, and dynamically stable up to a&#039;&#039;&#039;&lt;br /&gt;
&#039;&#039;&#039;capacity-dependent RTT limit set by &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Fairness ==&lt;br /&gt;
&lt;br /&gt;
At equilibrium the two increase terms balance the proportional&lt;br /&gt;
decrease, &amp;lt;code&amp;gt;a + r/T_probe = r &amp;amp;middot; m*/M&amp;lt;/code&amp;gt;, which pins the&lt;br /&gt;
congestion level at &amp;lt;code&amp;gt;m* = M &amp;amp;middot; (a/r + 1/T_probe)&amp;lt;/code&amp;gt;. Since&lt;br /&gt;
&amp;lt;code&amp;gt;ece = 8q&amp;lt;/code&amp;gt; (one mark unit per &amp;lt;code&amp;gt;Q = 4&amp;lt;/code&amp;gt; packets, 32&lt;br /&gt;
fixed-point steps per unit), that is a standing queue of&lt;br /&gt;
&lt;br /&gt;
  q* = 64 * a / r  +  64 / T_probe        (packets, r in bytes/s)&lt;br /&gt;
&lt;br /&gt;
The additive part (&amp;lt;code&amp;gt;64 &amp;amp;middot; a / r&amp;lt;/code&amp;gt;) fades to nothing with&lt;br /&gt;
rate under the default slope; with the capacity-derived slope&lt;br /&gt;
(&amp;lt;code&amp;gt;a = C/32&amp;lt;/code&amp;gt;; see&lt;br /&gt;
[[#Capacity-derived floor and slope|the capacity floor]]) it settles at&lt;br /&gt;
&amp;lt;code&amp;gt;2 C / r&amp;lt;/code&amp;gt;, about two packets per competing flow, whatever the&lt;br /&gt;
rate. The probe part (&amp;lt;code&amp;gt;64 / T_probe&amp;lt;/code&amp;gt;) is a floor that does&lt;br /&gt;
not move with rate (~8 packets at 8 s). That floor is what&lt;br /&gt;
rate-independent convergence costs you, and it is what keeps&lt;br /&gt;
&amp;lt;code&amp;gt;q*&amp;lt;/code&amp;gt; above the marking threshold at high rate.&lt;br /&gt;
&lt;br /&gt;
Two flows at the same bottleneck see the same &amp;lt;code&amp;gt;m*&amp;lt;/code&amp;gt;, so each&lt;br /&gt;
solves &amp;lt;code&amp;gt;r_i = a / (m*/M - 1/T_probe)&amp;lt;/code&amp;gt; to the same rate. In&lt;br /&gt;
network-utility-maximization terms that is &#039;&#039;&#039;proportional fairness&#039;&#039;&#039;&lt;br /&gt;
with equal weights, which at a single bottleneck is just max-min fair.&lt;br /&gt;
&lt;br /&gt;
Getting a good measurement at low rate is the estimator&#039;s job, not the&lt;br /&gt;
floor&#039;s: the averaging window stretches with the flow (see&lt;br /&gt;
[[#Receiver estimate|the receiver estimate]]), so a CA-limited flow&lt;br /&gt;
still keeps ~16 packets a window down to ~30 kbit/s, and the&lt;br /&gt;
rate-relative gap threshold keeps its normal spacing from reading as&lt;br /&gt;
idle. The default floor (&amp;lt;code&amp;gt;2^13 B/s&amp;lt;/code&amp;gt;) only bounds the extremes&lt;br /&gt;
(window ceiling, staleness horizon, pacer arithmetic). The floor itself&lt;br /&gt;
scales with the fed-back path capacity (&amp;lt;code&amp;gt;r_min = C/32&amp;lt;/code&amp;gt;; see&lt;br /&gt;
[[#Capacity-derived floor and slope|the capacity floor]]), so about 32&lt;br /&gt;
CA-limited flows fit above it on any class of link. Flows whose&lt;br /&gt;
bottlenecks are in different classes get different slopes, so fairness&lt;br /&gt;
between them is capacity-weighted, not equal; see&lt;br /&gt;
[[#Limitations|Limitations]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Limitations ==&lt;br /&gt;
&lt;br /&gt;
Things the algorithm &#039;&#039;&#039;doesn&#039;t&#039;&#039;&#039; do. Listed here so nobody credits it&lt;br /&gt;
with more than it manages.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Queue-only signal, no rate term.&#039;&#039;&#039; &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; is a queue level, so mb-ecn is a standing-queue controller: it has to build a queue (&amp;lt;code&amp;gt;q* = 64 &amp;amp;middot; a / r + 64 / T_probe&amp;lt;/code&amp;gt;) to get any signal at all, and can&#039;t sit at full utilisation on an empty queue the way a controller with a rate estimate can. The probe floor (~8 packets) keeps &amp;lt;code&amp;gt;q*&amp;lt;/code&amp;gt; above the marking threshold at every rate, but you pay for it with a permanent standing queue.&lt;br /&gt;
* &#039;&#039;&#039;Aggregation is per destination, per QoS cube.&#039;&#039;&#039; The aggregate is a stand-in for the path: flows to the same destination are assumed to share a bottleneck, and flows to different destinations run separate loops even when they pile up at the same hop. Each loop still converges to its own fair share (see [[#Fairness|fairness]]), so the cost is duplicated state and probing at that shared queue, not unfairness. One day we could tag the queue in the packet: if a forwarder stamped a queue id next to its mark, senders could key their aggregates on the real bottleneck instead of the destination, and pool the estimate and the probing across everything sharing it.&lt;br /&gt;
* &#039;&#039;&#039;Capacity is learned, and there are gaps.&#039;&#039;&#039; The floor and slope ride on an in-band path-capacity estimate (see [[#Forwarder marking|forwarder marking]] and [[#Capacity-derived floor and slope|the capacity floor]]), and that estimate has its own limits. A hop only learns its rate while its queue is backed up, so a rerouted or brand-new bottleneck reads &#039;&#039;unknown&#039;&#039; at first and the path MIN can briefly come from faster hops (the sender&#039;s smoothing and the next feedback bound the overshoot). The measurement is order-of-magnitude on purpose: quarter-log2 code (~19% a step), burst noise held down by a 16-packet minimum window and a max filter, and nothing learned below ~120 packets a second of busy drain (16 packets inside the ~134 ms staleness cap; at MTU that&#039;s ~1.4 Mbit/s, below which capacity/32 sits under the default floor anyway). The derived floor clamps to &amp;lt;code&amp;gt;[2^13, 2^32]&amp;lt;/code&amp;gt; B/s, and flows with bottlenecks in different classes get capacity-weighted, not equal, shares at a common queue (see [[#Fairness|fairness]]).&lt;br /&gt;
* &#039;&#039;&#039;The low end trades speed, not correctness.&#039;&#039;&#039; Below ~30 kbit/s the averaging window hits its ~4.3 s ceiling and holds fewer samples; below ~64 kbit/s of offered load an &#039;&#039;app-limited&#039;&#039; flow spaces its packets past the gap threshold and rides the raw onset branch, as it always did. At the floor, feedback and the staleness horizon are both measured in seconds, so a slow flow converges slowly, TCP-style. Nothing blows up; nothing&#039;s quick either.&lt;br /&gt;
* &#039;&#039;&#039;No loss response.&#039;&#039;&#039; The controller only reacts to ECN marks. Loss is FRCP&#039;s problem, handled by retransmission, out of the CA&#039;s sight. At a bottleneck that drops instead of marking (say a plain drop-tail queue at the Ethernet or UDP shim), mb-ecn sees nothing and additive increase just keeps ramping. It&#039;s built for Ouroboros-native bottlenecks where the forwarder marks; there&#039;s no classic-bottleneck fallback.&lt;br /&gt;
* &#039;&#039;&#039;Dynamic stability has a capacity-dependent RTT limit.&#039;&#039;&#039; As covered under [[#RTT behaviour|RTT behaviour]], the allocation is RTT-independent but the dynamics aren&#039;t: the binding constraint is &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;prop; &amp;amp;radic;C&amp;lt;/code&amp;gt;. The adaptive window and the washout push that limit out a long way (a single flow at 10 GbE stays stable to ~50 ms RTT in the fluid model), but the far corner (100 GbE at &amp;amp;ge; 50 ms) is out of reach for a window/damping controller and would need explicit-rate signalling.&lt;br /&gt;
&lt;br /&gt;
== Architectural fit ==&lt;br /&gt;
&lt;br /&gt;
Each of these properties falls out of something structural in the&lt;br /&gt;
recursive architecture. Here&#039;s the mapping.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CA is fully separate from ARQ and flow control.&#039;&#039;&#039; Three concerns, three mechanisms, two scopes: FRCP does retransmission (ARQ) and flow control (the peer pacing the sender), end-to-end and per flow; CA runs in the IPCP, per aggregate. Each signal says one thing. A loss just means retransmit: a lossy link reads as lossy, and the congestion call is left to the marks (the [[#Limitations|limitations]] section&#039;s &amp;quot;no loss response&amp;quot; is this same separation from the other side). The peer&#039;s flow-control window just paces the endpoint: back-pressure from a slow receiver stays separate from congestion in the network. A retransmitted packet is ordinary traffic as far as the pacer cares. TCP jams all three into one window machine, where the receive window caps the congestion window and loss is both the reliability trigger and the congestion signal; here you can reason about, test, and change each one on its own.&lt;br /&gt;
* &#039;&#039;&#039;CA sits below what the application picks.&#039;&#039;&#039; Every flow in the layer runs under the same rate law, whatever its QoS: a greedy raw sender shares a bottleneck fairly with a reliable stream because the control belongs to the layer, on the aggregate, not to the endpoint transport&#039;s good manners.&lt;br /&gt;
* &#039;&#039;&#039;A layer owns its PCI, so the signal can be rich.&#039;&#039;&#039; Everyone in a layer enrolled into it, so the layer is one administrative domain by construction, at whatever scope it covers, and the header is the layer&#039;s own. That&#039;s why forwarders can write a multi-bit queue magnitude and a capacity byte straight into the packet. The datacenter schemes in [[#Heritage and positioning|heritage and positioning]] need exactly that kind of domain and only get it inside one operator&#039;s fabric; a recursive layer has it everywhere.&lt;br /&gt;
* &#039;&#039;&#039;Layer-internal addressing makes the aggregate well-defined.&#039;&#039;&#039; Flows in a layer run between layer addresses, so the (destination, QoS cube) aggregate, which is RFC 3124&#039;s macroflow, drops straight out of the naming. Controller state scales with the number of destinations, and each QoS cube keeps its own loop, so service classes don&#039;t share a fate.&lt;br /&gt;
* &#039;&#039;&#039;Flows are allocated, so feedback already has a channel.&#039;&#039;&#039; Every flow has state at both ends and a reverse direction, so the receiver&#039;s &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; and the path capacity just ride the flow allocator&#039;s existing exchange.&lt;br /&gt;
* &#039;&#039;&#039;There&#039;s no transport ack clock, and nothing leans on one.&#039;&#039;&#039; The layer has to handle raw flows, which carry no acknowledgements at all, so the controller paces on wall-clock time, and that constraint is exactly what buys the RTT-independent allocation under [[#RTT behaviour|RTT behaviour]]. A restriction turned into the feature.&lt;br /&gt;
* &#039;&#039;&#039;Recursion scales it.&#039;&#039;&#039; Each layer handles congestion over its own scope and timescale; many N-flows ride one N-1 flow, so aggregation compounds down the stack. The capacity a layer sees at its egress queue is whatever the layer below is actually giving it (a paced, shared lower flow, not some nominal wire speed), so the signal means something at every level and back-pressure cascades down layer by layer.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a boundary: at a shim over legacy media the layer below neither&lt;br /&gt;
enrolls nor marks, which is the deployment edge behind the&lt;br /&gt;
[[#Limitations|limitations]] section&#039;s no-loss-response point.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Heritage and positioning ==&lt;br /&gt;
&lt;br /&gt;
mb-ecn is basically a rate-based ECN controller in the DECbit / QCN&lt;br /&gt;
line, adapted to a recursive layer with no acknowledgements.&lt;br /&gt;
&lt;br /&gt;
The core goes back to the first Ouroboros CA policy (2020): a multi-bit&lt;br /&gt;
queue-depth mark relayed by forwarders to the receiver, a smoothed&lt;br /&gt;
multi-bit &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; fed back on the reverse flow at the same 1/32&lt;br /&gt;
fixed point used today, and a sender that paced packets from a time&lt;br /&gt;
budget and ran slow start, additive increase and multiplicative&lt;br /&gt;
decrease on wall-clock time slots. Rate-based and acknowledgement-free&lt;br /&gt;
from the start, with RTT-independent allocation already the goal. The&lt;br /&gt;
present algorithm keeps that skeleton and reworks every estimator&lt;br /&gt;
around it: per-path aggregation with SFQ pacing, the rate-adaptive&lt;br /&gt;
box-car receiver, elapsed-time-scaled AIMD with a derivative term and&lt;br /&gt;
rate-velocity damping, and the in-band capacity signal.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;DECbit&#039;&#039;&#039; (Ramakrishnan &amp;amp;amp; Jain, 1988; ToCS 1990) established binary congestion feedback set by the switch on queue occupancy. mb-ecn keeps the switch-sets-on-queue idea and carries a &#039;&#039;magnitude&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;The Congestion Manager&#039;&#039;&#039; (Balakrishnan et al., SIGCOMM 1999; RFC 3124, 2001) aggregates congestion state at the end host: one controller per macroflow, a scheduler apportioning its rate across the member streams, and new streams joining at the ensemble&#039;s current state instead of probing from scratch. mb-ecn&#039;s (destination, QoS cube) aggregate is the same idea placed inside the IPCP: every layer manages its own macroflows, recursively, the SFQ pacer plays the CM scheduler&#039;s role (see [[#Sender pacer|the pacer]]), and a joining flow rides the aggregate&#039;s estimates (see [[#Slow start|slow start]]). The CM gathers its signal from transport feedback at the edge; mb-ecn reads it off the wire from the forwarders.&lt;br /&gt;
* &#039;&#039;&#039;DCTCP&#039;&#039;&#039; (Alizadeh et al., SIGCOMM 2010) recovers a queue magnitude at the endpoint by averaging a single threshold bit. mb-ecn instead reads the magnitude off the wire, then time-averages it at the receiver. The additive-increase / proportional-decrease structure is shared.&lt;br /&gt;
* &#039;&#039;&#039;QCN&#039;&#039;&#039; (IEEE 802.1Qau, 2010) and &#039;&#039;&#039;DCQCN&#039;&#039;&#039; (Zhu et al., SIGCOMM 2015) contribute the rate-based (not window-based) ECN control skeleton. mb-ecn&#039;s proportional decrease plus a probing increase mirrors that skeleton; the increase here is wall-clock scaled rather than byte-counter/timer based.&lt;br /&gt;
* &#039;&#039;&#039;HPCC&#039;&#039;&#039; (Li et al., SIGCOMM 2019) and &#039;&#039;&#039;PowerTCP&#039;&#039;&#039; (Addanki et al., NSDI 2022) combine a queue term &#039;&#039;and&#039;&#039; a rate term (via in-band telemetry or delay). mb-ecn now carries a coarse in-band rate (the quarter-log2 path MIN; see [[#Forwarder marking|forwarder marking]]) but uses it only to scale the controller&#039;s gains; the congestion &#039;&#039;signal&#039;&#039; remains queue-only, which is still the main functional difference (see [[#Limitations|Limitations]]).&lt;br /&gt;
* &#039;&#039;&#039;L4S / TCP Prague&#039;&#039;&#039; (RFC 9330&amp;amp;ndash;9332, 2023) make RTT-independence a requirement and use a high-frequency single-bit signal. mb-ecn shares the RTT-independent &#039;&#039;allocation&#039;&#039; goal but reaches it by wall-clock-scaled rate control, with a low-frequency multi-bit signal.&lt;br /&gt;
&lt;br /&gt;
What&#039;s new is the pairing: a multi-bit magnitude on the wire with a&lt;br /&gt;
wall-clock-scaled rate pacer, in a setting with no per-flow ack clock.&lt;br /&gt;
The trade-off, spelled out above, is a queue-only signal and a&lt;br /&gt;
reactivity capped by the averaging window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Parameters ==&lt;br /&gt;
&lt;br /&gt;
These are the values behind every figure in this document. They&#039;re&lt;br /&gt;
&#039;&#039;&#039;coupled&#039;&#039;&#039;: the mechanisms only fit together while a few relations&lt;br /&gt;
hold. The gap-restart horizon at the rate floor has to exceed a&lt;br /&gt;
packet&#039;s service time (or a floor-rate flow loops at onset); a&lt;br /&gt;
floor-rate flow&#039;s target window has to fit under the window ceiling;&lt;br /&gt;
the control interval has to sit under the damping bucket, which sits&lt;br /&gt;
under the idle credit cap; the rate floor mustn&#039;t exceed the slow-start&lt;br /&gt;
seed; and the derived-floor ceiling has to stay under the rate ceiling.&lt;br /&gt;
Retune them as a set, against those constraints.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| Marking quantum &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; || 4 packets || Mark onset and step; layer-wide&lt;br /&gt;
|-&lt;br /&gt;
| Feedback resolution || 1/32 &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; unit || Fixed-point LSB of &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Full-congestion reference &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; || 512 || Mean queue of 64 packets&lt;br /&gt;
|-&lt;br /&gt;
| Receiver window target || 16 packets (16,000 bytes) || Samples per window&lt;br /&gt;
|-&lt;br /&gt;
| Initial receiver window || ~67 ms || Before rate is known&lt;br /&gt;
|-&lt;br /&gt;
| Receiver window bounds || ~1.05 ms &amp;amp;ndash; ~4.3 s || Clamp on &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Window resize weight || 1/4 || Moving average toward target&lt;br /&gt;
|-&lt;br /&gt;
| Early window close || 2 &amp;amp;times; target bytes || Speed-up escape&lt;br /&gt;
|-&lt;br /&gt;
| Gap restart || 4 windows || Idle horizon (rate-relative)&lt;br /&gt;
|-&lt;br /&gt;
| Additive slope &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; (default) || 2^16 B/s^2 || Lower clamp of derived slope&lt;br /&gt;
|-&lt;br /&gt;
| Probe time constant &amp;lt;code&amp;gt;T_probe&amp;lt;/code&amp;gt; || 8 s || Rate-proportional recovery&lt;br /&gt;
|-&lt;br /&gt;
| Derivative gain || 1/4 || Onset sharpening&lt;br /&gt;
|-&lt;br /&gt;
| Rate floor &amp;lt;code&amp;gt;r_min&amp;lt;/code&amp;gt; (default) || 2^13 B/s || Lower clamp of derived floor&lt;br /&gt;
|-&lt;br /&gt;
| Slow-start seed rate || 2^16 B/s || Initial rate&lt;br /&gt;
|-&lt;br /&gt;
| Slow-start time constant || 20 ms || Exponential ramp&lt;br /&gt;
|-&lt;br /&gt;
| Rate ceiling &amp;lt;code&amp;gt;r_max&amp;lt;/code&amp;gt; || 2^37 B/s || Upper clamp&lt;br /&gt;
|-&lt;br /&gt;
| Capacity scale || &amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt; / 32 || Floor and slope from bottleneck&lt;br /&gt;
|-&lt;br /&gt;
| Capacity smoothing || 1/2 || Moving average per feedback&lt;br /&gt;
|-&lt;br /&gt;
| Capacity staleness || 16 &amp;amp;times; feedback horizon || Revert to defaults&lt;br /&gt;
|-&lt;br /&gt;
| Derived floor ceiling || 2^32 B/s || Upper clamp on derived floor&lt;br /&gt;
|-&lt;br /&gt;
| Control interval || 1 ms || Minimum spacing of rate updates&lt;br /&gt;
|-&lt;br /&gt;
| Idle credit cap || 50 ms || Burst allowance / increase banking&lt;br /&gt;
|-&lt;br /&gt;
| Feedback staleness floor || ~268 ms || Floor of rate-relative horizon&lt;br /&gt;
|-&lt;br /&gt;
| Damping bucket || ~31 ms || Rate-velocity washout cadence&lt;br /&gt;
|-&lt;br /&gt;
| Damping fraction || 1/4 || Pullback per bucket&lt;br /&gt;
|-&lt;br /&gt;
| Estimator fold spacing || 1 ms || Minimum measurement cadence&lt;br /&gt;
|-&lt;br /&gt;
| Estimator window closure || 16 packets drained || Self-scaling window&lt;br /&gt;
|-&lt;br /&gt;
| Estimator window staleness || ~134 ms || Discard across traffic gaps&lt;br /&gt;
|-&lt;br /&gt;
| Estimator idle tolerance || 1/8 of arrivals || Unsaturated-window rejection&lt;br /&gt;
|-&lt;br /&gt;
| Estimator decay || 1/16 per window || Max-filter convergence from below&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
=== Source ===&lt;br /&gt;
&lt;br /&gt;
The implementation follows the algorithm above:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/ca/mb-ecn.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mb-ecn.h&amp;lt;/code&amp;gt;: receiver estimator, pacer and rate control.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/ca/ops.h&amp;lt;/code&amp;gt;: CA policy interface.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/cap.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cap.h&amp;lt;/code&amp;gt;: link-capacity estimator and quarter-log2 codec.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/dt.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fa.c&amp;lt;/code&amp;gt;: per-hop marking and stamping, and the feedback carrying &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;cap8&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/psched.c&amp;lt;/code&amp;gt;: consumes the pacing deadline.&lt;br /&gt;
&lt;br /&gt;
=== Literature ===&lt;br /&gt;
&lt;br /&gt;
* K. K. Ramakrishnan, R. Jain, &amp;quot;A Binary Feedback Scheme for Congestion Avoidance in Computer Networks&amp;quot; (DECbit), ACM ToCS, 1990.&lt;br /&gt;
* H. Balakrishnan, H. S. Rahul, S. Seshan, &amp;quot;An Integrated Congestion Management Architecture for Internet Hosts&amp;quot;, SIGCOMM 1999; H. Balakrishnan, S. Seshan, &amp;quot;The Congestion Manager&amp;quot;, RFC 3124, 2001.&lt;br /&gt;
* M. Alizadeh et al., &amp;quot;Data Center TCP (DCTCP)&amp;quot;, SIGCOMM 2010.&lt;br /&gt;
* Y. Zhu et al., &amp;quot;Congestion Control for Large-Scale RDMA Deployments&amp;quot; (DCQCN), SIGCOMM 2015.&lt;br /&gt;
* R. Mittal et al., &amp;quot;TIMELY: RTT-based Congestion Control&amp;quot;, SIGCOMM 2015.&lt;br /&gt;
* Y. Li et al., &amp;quot;HPCC: High Precision Congestion Control&amp;quot;, SIGCOMM 2019.&lt;br /&gt;
* V. Addanki et al., &amp;quot;PowerTCP&amp;quot;, NSDI 2022.&lt;br /&gt;
* K. De Schepper, B. Briscoe (eds.), &amp;quot;Low Latency, Low Loss, and Scalable Throughput (L4S)&amp;quot;, RFC 9330/9331/9332.&lt;br /&gt;
* F. Kelly, &amp;quot;Charging and Rate Control for Elastic Traffic&amp;quot;, 1997; R. Srikant, &amp;quot;The Mathematics of Internet Congestion Control&amp;quot;, 2004.&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1943</id>
		<title>Congestion avoidance</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1943"/>
		<updated>2026-07-11T22:22:48Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:mb-ecn - Multi-bit ECN Congestion Avoidance}}&lt;br /&gt;
&lt;br /&gt;
mb-ecn is a congestion-avoidance (CA) policy for the Ouroboros unicast&lt;br /&gt;
IPCP. Reliability, ordering and flow control are FRCP&#039;s job, end-to-end&lt;br /&gt;
and per flow; congestion avoidance is a separate concern, and it runs&lt;br /&gt;
in the IPCP, once per layer. Within a layer the IPCP controls&lt;br /&gt;
&#039;&#039;&#039;aggregates&#039;&#039;&#039;: one control loop per (destination address, QoS cube),&lt;br /&gt;
shared by every flow to that destination.&lt;br /&gt;
&lt;br /&gt;
This document is about the algorithm: what it does and why. The&lt;br /&gt;
code follows it; the [[#References|reference section]] maps the pieces&lt;br /&gt;
to source files. It doesn&#039;t oversell: the [[#Limitations|limitations]]&lt;br /&gt;
section says plainly what it can&#039;t do.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;MUST&amp;quot;, &amp;quot;SHOULD&amp;quot;, &amp;quot;MAY&amp;quot; and friends mean what BCP 14 (RFC 2119,&lt;br /&gt;
RFC 8174) says, but only where they appear in capitals.&lt;br /&gt;
&lt;br /&gt;
Note: Co-authered by Claude Fable 5.&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Symbol !! Units !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; || B/s || Paced send rate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt; || B/s || Bottleneck capacity&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; || bytes || Packet size&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;q&amp;lt;/code&amp;gt; || packets || Standing queue occupancy at the bottleneck&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; || packets || Marking quantum: packets per mark unit (4)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; || 8-bit code || Per-packet congestion mark set by a forwarder&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; || 1/32 &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; || Receiver-side congestion estimate: 32 &amp;amp;times; time-mean &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;cap8&amp;lt;/code&amp;gt; || 8-bit code || Path capacity, quarter-log2: &amp;lt;code&amp;gt;C &amp;amp;asymp; 2^(cap8 / 4)&amp;lt;/code&amp;gt; B/s, 0 = unknown&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt; || ns || Receiver averaging window&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; || B/s^2 || Additive-increase slope&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; units || Full-congestion reference level (512)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;Delta;t&amp;lt;/code&amp;gt; || ns || Elapsed wall-clock time between two events&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; || n/a || Path round-trip time (feedback delay); the controller does not measure it&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rate is the control variable throughout.&lt;br /&gt;
&lt;br /&gt;
Every concrete figure in this document (window bounds, horizons,&lt;br /&gt;
rate thresholds) is just an instance of the parameter values in the&lt;br /&gt;
[[#Parameters|parameters table]]. What the algorithm fixes is the&lt;br /&gt;
mechanisms and the relations between parameters; retune the parameters&lt;br /&gt;
and the figures move together.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
mb-ecn is a rate-based, ECN-driven controller with three moving parts,&lt;br /&gt;
one at each point of the loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        sender                                  forwarder&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
      |  SFQ virtual-clock pacer       |       |  ecn = MAX(ecn, queue/Q) |&lt;br /&gt;
      |  rate r, paced per packet      | data  |  (deepest queue wins)    |&lt;br /&gt;
      |  AIMD + PD control on r        |------&amp;gt;|  cap8 = MIN(cap8, link)  |&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
                 ^                                        |&lt;br /&gt;
                 | ece + cap8 feedback                    | marked data&lt;br /&gt;
                 |                                        v&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
      |  reads downstream ece,         |&amp;lt;------|  receiver                |&lt;br /&gt;
      |  updates the rate on feedback, |  ece  |  time-mean of ecn over   |&lt;br /&gt;
      |  scales floor/slope to cap8    |  cap8 |  an adaptive window      |&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# The &#039;&#039;&#039;forwarder&#039;&#039;&#039; marks each packet with a &#039;&#039;multi-bit&#039;&#039; magnitude proportional to its standing queue, and MIN-stamps its measured outgoing-link capacity into the same PCI.&lt;br /&gt;
# The &#039;&#039;&#039;receiver&#039;&#039;&#039; turns the stream of marks into a time-averaged congestion estimate &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; and feeds it back to the sender, together with the window minimum of the path-capacity codes.&lt;br /&gt;
# The &#039;&#039;&#039;sender&#039;&#039;&#039; paces its rate with a start-time fair-queuing (SFQ) virtual clock, and adjusts that rate with additive-increase / multiplicative-decrease (AIMD) driven by &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;; the rate floor and the additive slope derive from the fed-back path capacity.&lt;br /&gt;
&lt;br /&gt;
There&#039;s no timer thread: the controller runs when a packet goes out or&lt;br /&gt;
feedback comes back. Every step is scaled by elapsed wall-clock time,&lt;br /&gt;
and that is what makes the &#039;&#039;allocation&#039;&#039; independent of RTT (see&lt;br /&gt;
[[#RTT behaviour|RTT behaviour]]).&lt;br /&gt;
&lt;br /&gt;
The unit of control is the &#039;&#039;&#039;aggregate&#039;&#039;&#039;: one per&lt;br /&gt;
&amp;lt;code&amp;gt;(destination address, QoS cube)&amp;lt;/code&amp;gt;, shared by every flow to&lt;br /&gt;
that destination at that QoS. They share one controller and one rate.&lt;br /&gt;
A new flow joins at whatever rate the aggregate is already running; when&lt;br /&gt;
a flow leaves, its share goes to the others. The pacer (SFQ) splits&lt;br /&gt;
that rate fairly across the member flows, so per-flow fairness is the&lt;br /&gt;
scheduler&#039;s doing, not the rate law&#039;s.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Forwarder marking ==&lt;br /&gt;
&lt;br /&gt;
A forwarder marks a packet based on how many packets are already queued&lt;br /&gt;
on the outgoing link:&lt;br /&gt;
&lt;br /&gt;
  mark = queued / Q                         (Q = 4 packets)&lt;br /&gt;
  ecn  = MAX(ecn, mark)                     (saturating, 8-bit field)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; is fixed for the whole layer, not tuned per hop: the marks&lt;br /&gt;
get MAX-combined across hops, so every forwarder MUST use the same&lt;br /&gt;
quantum or the numbers don&#039;t compare.&lt;br /&gt;
&lt;br /&gt;
Two things worth noting:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;It&#039;s a magnitude, not a bit.&#039;&#039;&#039; The mark is an integer that tracks queue depth, in steps of &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; packets: a coarse read of the standing queue.&lt;br /&gt;
* &#039;&#039;&#039;MAX along the path.&#039;&#039;&#039; Cross several forwarders and the packet carries the largest mark, i.e. the deepest queue it passed. Taking the MAX (not a sum) keeps the number bounded and monotone across hops.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a &#039;&#039;&#039;dead zone&#039;&#039;&#039;: below &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; queued packets the mark is 0.&lt;br /&gt;
&lt;br /&gt;
=== Capacity stamping ===&lt;br /&gt;
&lt;br /&gt;
Each forwarder also measures its outgoing link&#039;s capacity and stamps it&lt;br /&gt;
into the packet. The measurement is a busy-period drain rate: look at&lt;br /&gt;
the queue at most once a millisecond, and keep a window open until 16&lt;br /&gt;
packets have drained. So the window &#039;&#039;&#039;sizes itself to the link&#039;&#039;&#039;:&lt;br /&gt;
the once-a-millisecond cadence caps it on fast links, the 16-packet&lt;br /&gt;
drain time on slow ones (~19 ms at 10 Mbit/s). If more than 1/8 of the&lt;br /&gt;
arrivals hit an empty queue, throw the window out: the link&lt;br /&gt;
wasn&#039;t saturated. The odd empty sample (a token-bucket shaper grazing&lt;br /&gt;
zero) is fine. A saturated queue drains at the link rate, so a max&lt;br /&gt;
filter that decays slowly (1/16 per window) creeps up to it from below.&lt;br /&gt;
A window that started or ended on an empty queue might have drained&lt;br /&gt;
into buffers downstream faster than the wire, so it may pull the&lt;br /&gt;
estimate down but never up. On the wire the code is quarter-log2&lt;br /&gt;
(&amp;lt;code&amp;gt;cap8 = 4 log2 C&amp;lt;/code&amp;gt;, ~19% a step, 0 = unknown), and each hop&lt;br /&gt;
MIN-combines its own into the byte, so the packet arrives carrying the&lt;br /&gt;
slowest hop&#039;s rate. A hop that has never backed up stamps nothing,&lt;br /&gt;
which is fine, since only backed-up hops matter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Receiver estimate ==&lt;br /&gt;
&lt;br /&gt;
The receiver turns the marks into a smoothed estimate &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;,&lt;br /&gt;
averaged over a window &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt; that &#039;&#039;&#039;tracks the incoming byte rate&#039;&#039;&#039;.&lt;br /&gt;
It&#039;s a plain time-average over that window (a box-car, not an EWMA).&lt;br /&gt;
Per packet, with &amp;lt;code&amp;gt;&amp;amp;Delta;t&amp;lt;/code&amp;gt; the gap since the last one:&lt;br /&gt;
&lt;br /&gt;
  A += ecn * MIN(&amp;amp;Delta;t, W)              dwell-weighted; one packet&lt;br /&gt;
                                          weighs at most one window&lt;br /&gt;
  B += S                                  accumulate bytes this window&lt;br /&gt;
  ...&lt;br /&gt;
  at window close (elapsed &amp;gt;= W, or B &amp;gt;= 2 * B_target with at&lt;br /&gt;
                   least the window floor elapsed):&lt;br /&gt;
      ece = 32 * A / elapsed              mean over the actual window&lt;br /&gt;
      W  += (B_target * elapsed / B - W) / 4    nudge toward the size&lt;br /&gt;
                                                that holds the target&lt;br /&gt;
      W   = clamp(W, ~1.05 ms, ~4.3 s)&lt;br /&gt;
      A   = 0 ; B = 0                     hard reset&lt;br /&gt;
&lt;br /&gt;
When a window closes, the next one is nudged (a quarter-weight&lt;br /&gt;
moving average) toward the size that would hold about 16 packets&lt;br /&gt;
at the current byte rate, starting from ~67 ms. The target is in bytes&lt;br /&gt;
(&amp;lt;code&amp;gt;B_target&amp;lt;/code&amp;gt; = 16,000), so &amp;quot;16 packets&amp;quot; is only exact at&lt;br /&gt;
~1000-byte packets, and it takes a few closes to settle. The mean&lt;br /&gt;
always divides by the window that &#039;&#039;&#039;actually&#039;&#039;&#039; elapsed, so a change&lt;br /&gt;
in rate leaves the current estimate correct and only resizes the&lt;br /&gt;
&#039;&#039;next&#039;&#039; one. Net effect: a roughly constant ~16 samples a window from&lt;br /&gt;
about 30 kbit/s to 122 Mbit/s. Above that the window bottoms out at&lt;br /&gt;
~1 ms (still thousands of samples a window at 10&amp;amp;ndash;100 GbE); below&lt;br /&gt;
~30 kbit/s it hits the ~4.3 s ceiling. The averaging clock stretches&lt;br /&gt;
with the flow, the way TCP&#039;s ACK clock stretches with the RTT. A&lt;br /&gt;
sudden speed-up doesn&#039;t wait out a stretched window: once twice the&lt;br /&gt;
target bytes have arrived it closes anyway (the ~1 ms floor keeps that&lt;br /&gt;
from running away at high rate). So the &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; the sender sees&lt;br /&gt;
is a staircase: one step per window, steps getting shorter as&lt;br /&gt;
the rate climbs. Two edge cases:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Onset.&#039;&#039;&#039; The first mark after an idle stretch is passed through as-is, so a queue that&#039;s just starting to build shows up without waiting out a whole window.&lt;br /&gt;
* &#039;&#039;&#039;Gap.&#039;&#039;&#039; A silence longer than 4 windows resets and sends the raw sample. &amp;quot;Long&amp;quot; is &#039;&#039;&#039;measured in the flow&#039;s own windows&#039;&#039;&#039;, so a slow flow&#039;s normal spacing never reads as idle; and the dwell clamp above limits what a real pause can add to the mean before the reset fires.&lt;br /&gt;
&lt;br /&gt;
Alongside the marks, the receiver keeps the smallest non-zero capacity&lt;br /&gt;
code it saw this window and ships it with each &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; (on an&lt;br /&gt;
onset/gap restart, just the current packet&#039;s code). Then it resets, so&lt;br /&gt;
if the path reroutes onto something faster the fed-back capacity can&lt;br /&gt;
climb within one window.&lt;br /&gt;
&lt;br /&gt;
The estimate rides back to the sender on the reverse flow.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Sender pacer ==&lt;br /&gt;
&lt;br /&gt;
The sender paces off an SFQ virtual clock. The aggregate keeps a&lt;br /&gt;
virtual time &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;; each flow keeps a finish tag &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
  V    += r * &amp;amp;Delta;t                     virtual time advances by bytes served&lt;br /&gt;
  s     = MAX(F, V)                  a packet behind the clock starts now&lt;br /&gt;
  F     = s + S                      the one after it queues behind&lt;br /&gt;
  wait  = (s - V) / r                time until this packet may go&lt;br /&gt;
&lt;br /&gt;
That &amp;lt;code&amp;gt;wait&amp;lt;/code&amp;gt; is the flow&#039;s scheduling deadline. A packet&lt;br /&gt;
that&#039;s behind the virtual clock goes out now (&amp;lt;code&amp;gt;wait = 0&amp;lt;/code&amp;gt;);&lt;br /&gt;
one that&#039;s ahead waits, and only that flow waits, nobody else.&lt;br /&gt;
Across an idle gap longer than 50 ms the clock credits at most the&lt;br /&gt;
flow&#039;s own owed lead plus one burst (50 ms of service, at least one&lt;br /&gt;
packet). So a flow paced slower than a packet per 50 ms still gets its&lt;br /&gt;
&#039;&#039;&#039;real&#039;&#039;&#039; elapsed service (capping the credit by time would&lt;br /&gt;
starve it) while an idle flow still can&#039;t bank an unbounded&lt;br /&gt;
burst.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; is shared by every flow in the aggregate; the finish&lt;br /&gt;
tags are not. Since every flow measures its start tag &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt;&lt;br /&gt;
against the same &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;, a flow that just sent is now ahead of&lt;br /&gt;
&amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; and has to wait, while an idle one (tag at or behind&lt;br /&gt;
&amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;) goes right away. That is what splits the aggregate rate&lt;br /&gt;
&amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; fairly among them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Rate control (AIMD + PD) ==&lt;br /&gt;
&lt;br /&gt;
The rate law runs on packet sends, at most once a millisecond, and&lt;br /&gt;
also when feedback arrives, so a sender with no traffic of its&lt;br /&gt;
own still reacts. The increase terms and the proportional decrease are&lt;br /&gt;
each scaled by elapsed time, but not by the same clock: the increases&lt;br /&gt;
use banked time &#039;&#039;&#039;capped&#039;&#039;&#039; at 50 ms (an idle gap shouldn&#039;t buy an&lt;br /&gt;
unbounded ramp), while the decrease uses the &#039;&#039;&#039;uncapped&#039;&#039;&#039; elapsed&lt;br /&gt;
time (a starved sender still cuts by the right amount). The one-sided&lt;br /&gt;
derivative (see the&lt;br /&gt;
[[#Multiplicative decrease (proportional + derivative)|decrease]]) is&lt;br /&gt;
not scaled by time at all; it is per-step.&lt;br /&gt;
&lt;br /&gt;
=== Slow start ===&lt;br /&gt;
&lt;br /&gt;
Before the first congestion signal, the rate ramps up exponentially&lt;br /&gt;
with a 20 ms time constant:&lt;br /&gt;
&lt;br /&gt;
  r += r * &amp;amp;Delta;t / T_ss                  (T_ss = 20 ms)&lt;br /&gt;
&lt;br /&gt;
Slow start ends the first time a congestion signal shows up, whether&lt;br /&gt;
fed back from the receiver or seen at the sender&#039;s own first hop, and&lt;br /&gt;
it never comes back.&lt;br /&gt;
&lt;br /&gt;
Slow start belongs to the aggregate, so it happens&lt;br /&gt;
&#039;&#039;&#039;once per (destination, QoS cube)&#039;&#039;&#039;, on the first flow. A flow that&lt;br /&gt;
shows up later, at an aggregate that&#039;s already running, rides the&lt;br /&gt;
existing&lt;br /&gt;
estimate: it starts at the current rate and takes its share through&lt;br /&gt;
the [[#Sender pacer|pacer]], without probing of its own.&lt;br /&gt;
&lt;br /&gt;
=== Increase: additive plus proportional probe (always on) ===&lt;br /&gt;
&lt;br /&gt;
Past slow start, two increase terms run every step, congested or not:&lt;br /&gt;
&lt;br /&gt;
  r += a * &amp;amp;Delta;t                         additive; a defaults to 2^16 B/s^2&lt;br /&gt;
  r += r * &amp;amp;Delta;t / T_probe               proportional; T_probe = 8 s&lt;br /&gt;
&lt;br /&gt;
The additive term is a fixed slope (bytes/s per second), sized to the&lt;br /&gt;
path capacity (see&lt;br /&gt;
[[#Capacity-derived floor and slope|the capacity floor]]). The&lt;br /&gt;
proportional probe adds a fixed fraction of the rate per unit time (it&lt;br /&gt;
e-folds over &amp;lt;code&amp;gt;T_probe&amp;lt;/code&amp;gt;): that is what lets a flow recover in&lt;br /&gt;
the same number of steps whatever the link rate; a fixed additive step&lt;br /&gt;
is negligible next to a fast link. Together they are the&lt;br /&gt;
upward pressure the multiplicative decrease balances at equilibrium.&lt;br /&gt;
The probe is not free: it costs a standing queue that does not shrink&lt;br /&gt;
with rate (see [[#Fairness|fairness]]).&lt;br /&gt;
&lt;br /&gt;
=== Multiplicative decrease (proportional + derivative) ===&lt;br /&gt;
&lt;br /&gt;
With congestion level &amp;lt;code&amp;gt;m&amp;lt;/code&amp;gt; (the fed-back &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;, or&lt;br /&gt;
the local first-hop mark if there&#039;s no feedback yet), the cut is a&lt;br /&gt;
proportional term plus a one-sided derivative term, a PD controller on&lt;br /&gt;
the congestion signal:&lt;br /&gt;
&lt;br /&gt;
  mark = MIN(m, M)                                   M = 512&lt;br /&gt;
  rise = MAX(m - m_prev, 0), capped at M             one-sided derivative&lt;br /&gt;
&lt;br /&gt;
  cut  = r * rise / (4 * M)                          derivative, gain 1/4&lt;br /&gt;
  cut += r * mark * &amp;amp;Delta;t_ms / (1000 * M)              proportional&lt;br /&gt;
  cut  = MIN(cut, r / 2)                             at most a halving&lt;br /&gt;
&lt;br /&gt;
  r     -= cut&lt;br /&gt;
  m_prev = m&lt;br /&gt;
&lt;br /&gt;
* The &#039;&#039;&#039;proportional&#039;&#039;&#039; term uses the real elapsed milliseconds, so a starved sender that hasn&#039;t run the controller in a while still cuts by the right amount.&lt;br /&gt;
* The &#039;&#039;&#039;derivative&#039;&#039;&#039; term (&amp;lt;code&amp;gt;rise&amp;lt;/code&amp;gt;) only fires when the congestion level &#039;&#039;goes up&#039;&#039; between samples: one-sided, per-step. It sharpens the response right as congestion starts.&lt;br /&gt;
* The cut is capped at &amp;lt;code&amp;gt;r/2&amp;lt;/code&amp;gt;, so one step can at most halve the rate.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;M = 512 = 32 &amp;amp;times; 16&amp;lt;/code&amp;gt; pins &amp;quot;full congestion&amp;quot; at a mean&lt;br /&gt;
&amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; of 16, i.e. a mean queue of &amp;lt;code&amp;gt;16 &amp;amp;times; Q = 64&amp;lt;/code&amp;gt;&lt;br /&gt;
packets.&lt;br /&gt;
&lt;br /&gt;
=== Staleness ===&lt;br /&gt;
&lt;br /&gt;
Feedback comes once per receiver window, and that window stretches with&lt;br /&gt;
the flow&#039;s byte rate (see&lt;br /&gt;
[[#Receiver estimate|the receiver estimate]]), so the staleness horizon&lt;br /&gt;
has to stretch with it too, or a slow flow&#039;s mark would expire&lt;br /&gt;
&#039;&#039;between&#039;&#039; feedbacks and the rate would ramp straight into a congested&lt;br /&gt;
path. The sender mirrors the receiver: the horizon is four target&lt;br /&gt;
windows&#039; worth of bytes at the current rate, floored at ~268 ms so fast&lt;br /&gt;
flows keep a fixed one; at the default rate floor it&#039;s ~8 s. Once a&lt;br /&gt;
signal is older than that it&#039;s dropped (both the fed-back estimate and&lt;br /&gt;
the local mark), which frees the rate to climb again after congestion&lt;br /&gt;
clears. Every step clamps the rate to &amp;lt;code&amp;gt;[r_min, r_max]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Capacity-derived floor and slope ===&lt;br /&gt;
&lt;br /&gt;
The fed-back capacity code sets the controller&#039;s two rate-scale&lt;br /&gt;
constants, per aggregate:&lt;br /&gt;
&lt;br /&gt;
  target = decode(cap8) / 32              clamped to [2^13, 2^32] B/s&lt;br /&gt;
  r_min += (target - r_min) / 2           per feedback (moving average)&lt;br /&gt;
  a      = r_min&lt;br /&gt;
&lt;br /&gt;
On a path that never reports a capacity, both fall back to fixed&lt;br /&gt;
defaults (&amp;lt;code&amp;gt;r_min = 2^13 B/s&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;a = 2^16 B/s^2&amp;lt;/code&amp;gt;).&lt;br /&gt;
When the estimator is live, the fairness floor and the post-cut&lt;br /&gt;
recovery slope scale with the bottleneck instead, and capacity kicks in&lt;br /&gt;
wherever &amp;lt;code&amp;gt;C/32&amp;lt;/code&amp;gt; beats the default floor, roughly above&lt;br /&gt;
2 Mbit/s. A capacity older than 16 staleness horizons (~4.3 s for fast&lt;br /&gt;
flows) reverts both to the defaults. That horizon is deliberately&lt;br /&gt;
longer than the feedback horizon: feedback stops the instant the marks&lt;br /&gt;
clear, which is exactly when you need the recovery slope, and the&lt;br /&gt;
onset-fresh capacity (see&lt;br /&gt;
[[#Receiver estimate|the receiver estimate]]) re-seeds it on the first&lt;br /&gt;
mark of the next episode anyway.&lt;br /&gt;
&lt;br /&gt;
=== Ramp and recovery ===&lt;br /&gt;
&lt;br /&gt;
Three mechanisms set how fast an aggregate reaches a fast link&#039;s&lt;br /&gt;
rate, each owning one regime:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;From scratch: slow start.&#039;&#039;&#039; The exponential ramp hits any real link rate in well under a second (~200 ms from the seed to 10 Gbit/s; every further doubling of link speed adds one ~14 ms doubling). Nothing marks on an uncongested path, so slow start just runs until the aggregate reaches its bottleneck.&lt;br /&gt;
* &#039;&#039;&#039;After a cut: the probe.&#039;&#039;&#039; A cut is at most a halving, and the mark expires on the rate-relative horizon once congestion clears (see [[#Staleness|staleness]]); the probe then climbs back from a halving in &amp;lt;code&amp;gt;T_probe &amp;amp;middot; ln 2 &amp;amp;asymp; 5.5 s&amp;lt;/code&amp;gt;, whatever the link rate.&lt;br /&gt;
* &#039;&#039;&#039;At the bottom: the capacity floor.&#039;&#039;&#039; The rate never sits more than 32&amp;amp;times; below a measured bottleneck, and the additive slope refills &amp;lt;code&amp;gt;C/32&amp;lt;/code&amp;gt; a second, so the worst hole is bounded: floor to full capacity in tens of seconds, a halving in seconds.&lt;br /&gt;
&lt;br /&gt;
These fit together because a deep cut needs sustained congestion,&lt;br /&gt;
sustained congestion backs up the bottleneck queue, and a backed-up&lt;br /&gt;
queue is exactly when its capacity gets measured and stamped (see&lt;br /&gt;
[[#Forwarder marking|forwarder marking]]). So whenever the controller&lt;br /&gt;
has been cut deep, the scaled floor is already live. The default floor&lt;br /&gt;
only ever applies to paths that never congested the sender in the first&lt;br /&gt;
place, where there&#039;s nothing to recover from. That&#039;s why slow start can&lt;br /&gt;
quit for good: the floor and the probe handle every recovery after it.&lt;br /&gt;
&lt;br /&gt;
=== Regimes ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   +-------------+   first mark seen    +----------------+&lt;br /&gt;
   | slow start  |---------------------&amp;gt;|  congestion    |&lt;br /&gt;
   | r *= e^(t/T)|                      |  avoidance     |&lt;br /&gt;
   +-------------+                      |                |&lt;br /&gt;
                                        | m == 0 ---&amp;gt; additive increase&lt;br /&gt;
                                        | m  &amp;gt; 0 ---&amp;gt; AI + PD decrease&lt;br /&gt;
                                        +----------------+&lt;br /&gt;
    m = fed-back ece, or the local first-hop mark when ece == 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== RTT behaviour ==&lt;br /&gt;
&lt;br /&gt;
Because every increase and decrease is scaled by wall-clock time, two&lt;br /&gt;
flows with different RTTs sharing a bottleneck follow the &#039;&#039;same&#039;&#039; rate&lt;br /&gt;
law and settle at the &#039;&#039;same&#039;&#039; rate. So the steady-state&lt;br /&gt;
&#039;&#039;&#039;allocation is RTT-independent&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
That is a property of the equilibrium, not the dynamics. The feedback&lt;br /&gt;
delay in the loop &#039;&#039;is&#039;&#039; the path RTT &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;, and the&lt;br /&gt;
controller neither measures nor compensates for it. At low rate the&lt;br /&gt;
receiver window (up to its ~4.3 s ceiling) is the biggest lag: the loop&lt;br /&gt;
is slow there in proportion to how slow the flow is, just like a&lt;br /&gt;
long-RTT TCP. As the rate climbs the window shrinks toward its ~1 ms&lt;br /&gt;
floor, so its share of the loop delay &#039;&#039;falls&#039;&#039; with capacity and the&lt;br /&gt;
path RTT &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; becomes the irreducible part. Two things keep&lt;br /&gt;
the loop damped: the adaptive window takes out the delay-dominated&lt;br /&gt;
corner at low &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;, and a bucketed&lt;br /&gt;
&#039;&#039;&#039;rate-velocity damping&#039;&#039;&#039; (the washout: each ~31 ms bucket pulls the&lt;br /&gt;
rate a quarter of the way back toward its value at the previous bucket)&lt;br /&gt;
supplies the damping this otherwise near-double-integrator loop lacks,&lt;br /&gt;
which is what holds it together at high capacity and moderate&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;. What is left is the loop gain&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;prop; &amp;amp;radic;C&amp;lt;/code&amp;gt;: at very high capacity &#039;&#039;and&#039;&#039;&lt;br /&gt;
large &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; the &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt; phase&lt;br /&gt;
budget is the binding constraint, and no amount of windowing or damping&lt;br /&gt;
removes it (see [[#Limitations|Limitations]]). So, precisely:&lt;br /&gt;
&#039;&#039;&#039;RTT-fair in equilibrium, and dynamically stable up to a&#039;&#039;&#039;&lt;br /&gt;
&#039;&#039;&#039;capacity-dependent RTT limit set by &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Fairness ==&lt;br /&gt;
&lt;br /&gt;
At equilibrium the two increase terms balance the proportional&lt;br /&gt;
decrease, &amp;lt;code&amp;gt;a + r/T_probe = r &amp;amp;middot; m*/M&amp;lt;/code&amp;gt;, which pins the&lt;br /&gt;
congestion level at &amp;lt;code&amp;gt;m* = M &amp;amp;middot; (a/r + 1/T_probe)&amp;lt;/code&amp;gt;. Since&lt;br /&gt;
&amp;lt;code&amp;gt;ece = 8q&amp;lt;/code&amp;gt; (one mark unit per &amp;lt;code&amp;gt;Q = 4&amp;lt;/code&amp;gt; packets, 32&lt;br /&gt;
fixed-point steps per unit), that is a standing queue of&lt;br /&gt;
&lt;br /&gt;
  q* = 64 * a / r  +  64 / T_probe        (packets, r in bytes/s)&lt;br /&gt;
&lt;br /&gt;
The additive part (&amp;lt;code&amp;gt;64 &amp;amp;middot; a / r&amp;lt;/code&amp;gt;) fades to nothing with&lt;br /&gt;
rate under the default slope; with the capacity-derived slope&lt;br /&gt;
(&amp;lt;code&amp;gt;a = C/32&amp;lt;/code&amp;gt;; see&lt;br /&gt;
[[#Capacity-derived floor and slope|the capacity floor]]) it settles at&lt;br /&gt;
&amp;lt;code&amp;gt;2 C / r&amp;lt;/code&amp;gt;, about two packets per competing flow, whatever the&lt;br /&gt;
rate. The probe part (&amp;lt;code&amp;gt;64 / T_probe&amp;lt;/code&amp;gt;) is a floor that does&lt;br /&gt;
not move with rate (~8 packets at 8 s). That floor is what&lt;br /&gt;
rate-independent convergence costs you, and it is what keeps&lt;br /&gt;
&amp;lt;code&amp;gt;q*&amp;lt;/code&amp;gt; above the marking threshold at high rate.&lt;br /&gt;
&lt;br /&gt;
Two flows at the same bottleneck see the same &amp;lt;code&amp;gt;m*&amp;lt;/code&amp;gt;, so each&lt;br /&gt;
solves &amp;lt;code&amp;gt;r_i = a / (m*/M - 1/T_probe)&amp;lt;/code&amp;gt; to the same rate. In&lt;br /&gt;
network-utility-maximization terms that is &#039;&#039;&#039;proportional fairness&#039;&#039;&#039;&lt;br /&gt;
with equal weights, which at a single bottleneck is just max-min fair.&lt;br /&gt;
&lt;br /&gt;
Getting a good measurement at low rate is the estimator&#039;s job, not the&lt;br /&gt;
floor&#039;s: the averaging window stretches with the flow (see&lt;br /&gt;
[[#Receiver estimate|the receiver estimate]]), so a CA-limited flow&lt;br /&gt;
still keeps ~16 packets a window down to ~30 kbit/s, and the&lt;br /&gt;
rate-relative gap threshold keeps its normal spacing from reading as&lt;br /&gt;
idle. The default floor (&amp;lt;code&amp;gt;2^13 B/s&amp;lt;/code&amp;gt;) only bounds the extremes&lt;br /&gt;
(window ceiling, staleness horizon, pacer arithmetic). The floor itself&lt;br /&gt;
scales with the fed-back path capacity (&amp;lt;code&amp;gt;r_min = C/32&amp;lt;/code&amp;gt;; see&lt;br /&gt;
[[#Capacity-derived floor and slope|the capacity floor]]), so about 32&lt;br /&gt;
CA-limited flows fit above it on any class of link. Flows whose&lt;br /&gt;
bottlenecks are in different classes get different slopes, so fairness&lt;br /&gt;
between them is capacity-weighted, not equal; see&lt;br /&gt;
[[#Limitations|Limitations]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Limitations ==&lt;br /&gt;
&lt;br /&gt;
Things the algorithm &#039;&#039;&#039;doesn&#039;t&#039;&#039;&#039; do. Listed here so nobody credits it&lt;br /&gt;
with more than it manages.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Queue-only signal, no rate term.&#039;&#039;&#039; &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; is a queue level, so mb-ecn is a standing-queue controller: it has to build a queue (&amp;lt;code&amp;gt;q* = 64 &amp;amp;middot; a / r + 64 / T_probe&amp;lt;/code&amp;gt;) to get any signal at all, and can&#039;t sit at full utilisation on an empty queue the way a controller with a rate estimate can. The probe floor (~8 packets) keeps &amp;lt;code&amp;gt;q*&amp;lt;/code&amp;gt; above the marking threshold at every rate, but you pay for it with a permanent standing queue.&lt;br /&gt;
* &#039;&#039;&#039;Aggregation is per destination, per QoS cube.&#039;&#039;&#039; The aggregate is a stand-in for the path: flows to the same destination are assumed to share a bottleneck, and flows to different destinations run separate loops even when they pile up at the same hop. Each loop still converges to its own fair share (see [[#Fairness|fairness]]), so the cost is duplicated state and probing at that shared queue, not unfairness. One day we could tag the queue in the packet: if a forwarder stamped a queue id next to its mark, senders could key their aggregates on the real bottleneck instead of the destination, and pool the estimate and the probing across everything sharing it.&lt;br /&gt;
* &#039;&#039;&#039;Capacity is learned, and there are gaps.&#039;&#039;&#039; The floor and slope ride on an in-band path-capacity estimate (see [[#Forwarder marking|forwarder marking]] and [[#Capacity-derived floor and slope|the capacity floor]]), and that estimate has its own limits. A hop only learns its rate while its queue is backed up, so a rerouted or brand-new bottleneck reads &#039;&#039;unknown&#039;&#039; at first and the path MIN can briefly come from faster hops (the sender&#039;s smoothing and the next feedback bound the overshoot). The measurement is order-of-magnitude on purpose: quarter-log2 code (~19% a step), burst noise held down by a 16-packet minimum window and a max filter, and nothing learned below ~120 packets a second of busy drain (16 packets inside the ~134 ms staleness cap; at MTU that&#039;s ~1.4 Mbit/s, below which capacity/32 sits under the default floor anyway). The derived floor clamps to &amp;lt;code&amp;gt;[2^13, 2^32]&amp;lt;/code&amp;gt; B/s, and flows with bottlenecks in different classes get capacity-weighted, not equal, shares at a common queue (see [[#Fairness|fairness]]).&lt;br /&gt;
* &#039;&#039;&#039;The low end trades speed, not correctness.&#039;&#039;&#039; Below ~30 kbit/s the averaging window hits its ~4.3 s ceiling and holds fewer samples; below ~64 kbit/s of offered load an &#039;&#039;app-limited&#039;&#039; flow spaces its packets past the gap threshold and rides the raw onset branch, as it always did. At the floor, feedback and the staleness horizon are both measured in seconds, so a slow flow converges slowly, TCP-style. Nothing blows up; nothing&#039;s quick either.&lt;br /&gt;
* &#039;&#039;&#039;No loss response.&#039;&#039;&#039; The controller only reacts to ECN marks. Loss is FRCP&#039;s problem, handled by retransmission, out of the CA&#039;s sight. At a bottleneck that drops instead of marking (say a plain drop-tail queue at the Ethernet or UDP shim), mb-ecn sees nothing and additive increase just keeps ramping. It&#039;s built for Ouroboros-native bottlenecks where the forwarder marks; there&#039;s no classic-bottleneck fallback.&lt;br /&gt;
* &#039;&#039;&#039;Dynamic stability has a capacity-dependent RTT limit.&#039;&#039;&#039; As covered under [[#RTT behaviour|RTT behaviour]], the allocation is RTT-independent but the dynamics aren&#039;t: the binding constraint is &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;prop; &amp;amp;radic;C&amp;lt;/code&amp;gt;. The adaptive window and the washout push that limit out a long way (a single flow at 10 GbE stays stable to ~50 ms RTT in the fluid model), but the far corner (100 GbE at &amp;amp;ge; 50 ms) is out of reach for a window/damping controller and would need explicit-rate signalling.&lt;br /&gt;
&lt;br /&gt;
== Architectural fit ==&lt;br /&gt;
&lt;br /&gt;
Each of these properties falls out of something structural in the&lt;br /&gt;
recursive architecture. Here&#039;s the mapping.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CA is fully separate from ARQ and flow control.&#039;&#039;&#039; Three concerns, three mechanisms, two scopes: FRCP does retransmission (ARQ) and flow control (the peer pacing the sender), end-to-end and per flow; CA runs in the IPCP, per aggregate. Each signal says one thing. A loss just means retransmit: a lossy link reads as lossy, and the congestion call is left to the marks (the [[#Limitations|limitations]] section&#039;s &amp;quot;no loss response&amp;quot; is this same separation from the other side). The peer&#039;s flow-control window just paces the endpoint: back-pressure from a slow receiver stays separate from congestion in the network. A retransmitted packet is ordinary traffic as far as the pacer cares. TCP jams all three into one window machine, where the receive window caps the congestion window and loss is both the reliability trigger and the congestion signal; here you can reason about, test, and change each one on its own.&lt;br /&gt;
* &#039;&#039;&#039;CA sits below what the application picks.&#039;&#039;&#039; Every flow in the layer runs under the same rate law, whatever its QoS: a greedy raw sender shares a bottleneck fairly with a reliable stream because the control belongs to the layer, on the aggregate, not to the endpoint transport&#039;s good manners.&lt;br /&gt;
* &#039;&#039;&#039;A layer owns its PCI, so the signal can be rich.&#039;&#039;&#039; Everyone in a layer enrolled into it, so the layer is one administrative domain by construction, at whatever scope it covers, and the header is the layer&#039;s own. That&#039;s why forwarders can write a multi-bit queue magnitude and a capacity byte straight into the packet. The datacenter schemes in [[#Heritage and positioning|heritage and positioning]] need exactly that kind of domain and only get it inside one operator&#039;s fabric; a recursive layer has it everywhere.&lt;br /&gt;
* &#039;&#039;&#039;Layer-internal addressing makes the aggregate well-defined.&#039;&#039;&#039; Flows in a layer run between layer addresses, so the (destination, QoS cube) aggregate, which is RFC 3124&#039;s macroflow, drops straight out of the naming. Controller state scales with the number of destinations, and each QoS cube keeps its own loop, so service classes don&#039;t share a fate.&lt;br /&gt;
* &#039;&#039;&#039;Flows are allocated, so feedback already has a channel.&#039;&#039;&#039; Every flow has state at both ends and a reverse direction, so the receiver&#039;s &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; and the path capacity just ride the flow allocator&#039;s existing exchange.&lt;br /&gt;
* &#039;&#039;&#039;There&#039;s no transport ack clock, and nothing leans on one.&#039;&#039;&#039; The layer has to handle raw flows, which carry no acknowledgements at all, so the controller paces on wall-clock time, and that constraint is exactly what buys the RTT-independent allocation under [[#RTT behaviour|RTT behaviour]]. A restriction turned into the feature.&lt;br /&gt;
* &#039;&#039;&#039;Recursion scales it.&#039;&#039;&#039; Each layer handles congestion over its own scope and timescale; many N-flows ride one N-1 flow, so aggregation compounds down the stack. The capacity a layer sees at its egress queue is whatever the layer below is actually giving it (a paced, shared lower flow, not some nominal wire speed), so the signal means something at every level and back-pressure cascades down layer by layer.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a boundary: at a shim over legacy media the layer below neither&lt;br /&gt;
enrolls nor marks, which is the deployment edge behind the&lt;br /&gt;
[[#Limitations|limitations]] section&#039;s no-loss-response point.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Heritage and positioning ==&lt;br /&gt;
&lt;br /&gt;
mb-ecn is basically a rate-based ECN controller in the DECbit / QCN&lt;br /&gt;
line, adapted to a recursive layer with no acknowledgements.&lt;br /&gt;
&lt;br /&gt;
The core goes back to the first Ouroboros CA policy (2020): a multi-bit&lt;br /&gt;
queue-depth mark relayed by forwarders to the receiver, a smoothed&lt;br /&gt;
multi-bit &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; fed back on the reverse flow at the same 1/32&lt;br /&gt;
fixed point used today, and a sender that paced packets from a time&lt;br /&gt;
budget and ran slow start, additive increase and multiplicative&lt;br /&gt;
decrease on wall-clock time slots. Rate-based and acknowledgement-free&lt;br /&gt;
from the start, with RTT-independent allocation already the goal. The&lt;br /&gt;
present algorithm keeps that skeleton and reworks every estimator&lt;br /&gt;
around it: per-path aggregation with SFQ pacing, the rate-adaptive&lt;br /&gt;
box-car receiver, elapsed-time-scaled AIMD with a derivative term and&lt;br /&gt;
rate-velocity damping, and the in-band capacity signal.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;DECbit&#039;&#039;&#039; (Ramakrishnan &amp;amp;amp; Jain, 1988; ToCS 1990) established binary congestion feedback set by the switch on queue occupancy. mb-ecn keeps the switch-sets-on-queue idea and carries a &#039;&#039;magnitude&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;The Congestion Manager&#039;&#039;&#039; (Balakrishnan et al., SIGCOMM 1999; RFC 3124, 2001) aggregates congestion state at the end host: one controller per macroflow, a scheduler apportioning its rate across the member streams, and new streams joining at the ensemble&#039;s current state instead of probing from scratch. mb-ecn&#039;s (destination, QoS cube) aggregate is the same idea placed inside the IPCP: every layer manages its own macroflows, recursively, the SFQ pacer plays the CM scheduler&#039;s role (see [[#Sender pacer|the pacer]]), and a joining flow rides the aggregate&#039;s estimates (see [[#Slow start|slow start]]). The CM gathers its signal from transport feedback at the edge; mb-ecn reads it off the wire from the forwarders.&lt;br /&gt;
* &#039;&#039;&#039;DCTCP&#039;&#039;&#039; (Alizadeh et al., SIGCOMM 2010) recovers a queue magnitude at the endpoint by averaging a single threshold bit. mb-ecn instead reads the magnitude off the wire, then time-averages it at the receiver. The additive-increase / proportional-decrease structure is shared.&lt;br /&gt;
* &#039;&#039;&#039;QCN&#039;&#039;&#039; (IEEE 802.1Qau, 2010) and &#039;&#039;&#039;DCQCN&#039;&#039;&#039; (Zhu et al., SIGCOMM 2015) contribute the rate-based (not window-based) ECN control skeleton. mb-ecn&#039;s proportional decrease plus a probing increase mirrors that skeleton; the increase here is wall-clock scaled rather than byte-counter/timer based.&lt;br /&gt;
* &#039;&#039;&#039;HPCC&#039;&#039;&#039; (Li et al., SIGCOMM 2019) and &#039;&#039;&#039;PowerTCP&#039;&#039;&#039; (Addanki et al., NSDI 2022) combine a queue term &#039;&#039;and&#039;&#039; a rate term (via in-band telemetry or delay). mb-ecn now carries a coarse in-band rate (the quarter-log2 path MIN; see [[#Forwarder marking|forwarder marking]]) but uses it only to scale the controller&#039;s gains; the congestion &#039;&#039;signal&#039;&#039; remains queue-only, which is still the main functional difference (see [[#Limitations|Limitations]]).&lt;br /&gt;
* &#039;&#039;&#039;L4S / TCP Prague&#039;&#039;&#039; (RFC 9330&amp;amp;ndash;9332, 2023) make RTT-independence a requirement and use a high-frequency single-bit signal. mb-ecn shares the RTT-independent &#039;&#039;allocation&#039;&#039; goal but reaches it by wall-clock-scaled rate control, with a low-frequency multi-bit signal.&lt;br /&gt;
&lt;br /&gt;
What&#039;s new is the pairing: a multi-bit magnitude on the wire with a&lt;br /&gt;
wall-clock-scaled rate pacer, in a setting with no per-flow ack clock.&lt;br /&gt;
The trade-off, spelled out above, is a queue-only signal and a&lt;br /&gt;
reactivity capped by the averaging window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Parameters ==&lt;br /&gt;
&lt;br /&gt;
These are the values behind every figure in this document. They&#039;re&lt;br /&gt;
&#039;&#039;&#039;coupled&#039;&#039;&#039;: the mechanisms only fit together while a few relations&lt;br /&gt;
hold. The gap-restart horizon at the rate floor has to exceed a&lt;br /&gt;
packet&#039;s service time (or a floor-rate flow loops at onset); a&lt;br /&gt;
floor-rate flow&#039;s target window has to fit under the window ceiling;&lt;br /&gt;
the control interval has to sit under the damping bucket, which sits&lt;br /&gt;
under the idle credit cap; the rate floor mustn&#039;t exceed the slow-start&lt;br /&gt;
seed; and the derived-floor ceiling has to stay under the rate ceiling.&lt;br /&gt;
Retune them as a set, against those constraints.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| Marking quantum &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; || 4 packets || Mark onset and step; layer-wide&lt;br /&gt;
|-&lt;br /&gt;
| Feedback resolution || 1/32 &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; unit || Fixed-point LSB of &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Full-congestion reference &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; || 512 || Mean queue of 64 packets&lt;br /&gt;
|-&lt;br /&gt;
| Receiver window target || 16 packets (16,000 bytes) || Samples per window&lt;br /&gt;
|-&lt;br /&gt;
| Initial receiver window || ~67 ms || Before rate is known&lt;br /&gt;
|-&lt;br /&gt;
| Receiver window bounds || ~1.05 ms &amp;amp;ndash; ~4.3 s || Clamp on &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Window resize weight || 1/4 || Moving average toward target&lt;br /&gt;
|-&lt;br /&gt;
| Early window close || 2 &amp;amp;times; target bytes || Speed-up escape&lt;br /&gt;
|-&lt;br /&gt;
| Gap restart || 4 windows || Idle horizon (rate-relative)&lt;br /&gt;
|-&lt;br /&gt;
| Additive slope &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; (default) || 2^16 B/s^2 || Lower clamp of derived slope&lt;br /&gt;
|-&lt;br /&gt;
| Probe time constant &amp;lt;code&amp;gt;T_probe&amp;lt;/code&amp;gt; || 8 s || Rate-proportional recovery&lt;br /&gt;
|-&lt;br /&gt;
| Derivative gain || 1/4 || Onset sharpening&lt;br /&gt;
|-&lt;br /&gt;
| Rate floor &amp;lt;code&amp;gt;r_min&amp;lt;/code&amp;gt; (default) || 2^13 B/s || Lower clamp of derived floor&lt;br /&gt;
|-&lt;br /&gt;
| Slow-start seed rate || 2^16 B/s || Initial rate&lt;br /&gt;
|-&lt;br /&gt;
| Slow-start time constant || 20 ms || Exponential ramp&lt;br /&gt;
|-&lt;br /&gt;
| Rate ceiling &amp;lt;code&amp;gt;r_max&amp;lt;/code&amp;gt; || 2^37 B/s || Upper clamp&lt;br /&gt;
|-&lt;br /&gt;
| Capacity scale || &amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt; / 32 || Floor and slope from bottleneck&lt;br /&gt;
|-&lt;br /&gt;
| Capacity smoothing || 1/2 || Moving average per feedback&lt;br /&gt;
|-&lt;br /&gt;
| Capacity staleness || 16 &amp;amp;times; feedback horizon || Revert to defaults&lt;br /&gt;
|-&lt;br /&gt;
| Derived floor ceiling || 2^32 B/s || Upper clamp on derived floor&lt;br /&gt;
|-&lt;br /&gt;
| Control interval || 1 ms || Minimum spacing of rate updates&lt;br /&gt;
|-&lt;br /&gt;
| Idle credit cap || 50 ms || Burst allowance / increase banking&lt;br /&gt;
|-&lt;br /&gt;
| Feedback staleness floor || ~268 ms || Floor of rate-relative horizon&lt;br /&gt;
|-&lt;br /&gt;
| Damping bucket || ~31 ms || Rate-velocity washout cadence&lt;br /&gt;
|-&lt;br /&gt;
| Damping fraction || 1/4 || Pullback per bucket&lt;br /&gt;
|-&lt;br /&gt;
| Estimator fold spacing || 1 ms || Minimum measurement cadence&lt;br /&gt;
|-&lt;br /&gt;
| Estimator window closure || 16 packets drained || Self-scaling window&lt;br /&gt;
|-&lt;br /&gt;
| Estimator window staleness || ~134 ms || Discard across traffic gaps&lt;br /&gt;
|-&lt;br /&gt;
| Estimator idle tolerance || 1/8 of arrivals || Unsaturated-window rejection&lt;br /&gt;
|-&lt;br /&gt;
| Estimator decay || 1/16 per window || Max-filter convergence from below&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
=== Source ===&lt;br /&gt;
&lt;br /&gt;
The implementation follows the algorithm above:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/ca/mb-ecn.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mb-ecn.h&amp;lt;/code&amp;gt;: receiver estimator, pacer and rate control.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/ca/ops.h&amp;lt;/code&amp;gt;: CA policy interface.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/cap.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cap.h&amp;lt;/code&amp;gt;: link-capacity estimator and quarter-log2 codec.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/dt.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fa.c&amp;lt;/code&amp;gt;: per-hop marking and stamping, and the feedback carrying &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;cap8&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/psched.c&amp;lt;/code&amp;gt;: consumes the pacing deadline.&lt;br /&gt;
&lt;br /&gt;
=== Literature ===&lt;br /&gt;
&lt;br /&gt;
* K. K. Ramakrishnan, R. Jain, &amp;quot;A Binary Feedback Scheme for Congestion Avoidance in Computer Networks&amp;quot; (DECbit), ACM ToCS, 1990.&lt;br /&gt;
* H. Balakrishnan, H. S. Rahul, S. Seshan, &amp;quot;An Integrated Congestion Management Architecture for Internet Hosts&amp;quot;, SIGCOMM 1999; H. Balakrishnan, S. Seshan, &amp;quot;The Congestion Manager&amp;quot;, RFC 3124, 2001.&lt;br /&gt;
* M. Alizadeh et al., &amp;quot;Data Center TCP (DCTCP)&amp;quot;, SIGCOMM 2010.&lt;br /&gt;
* Y. Zhu et al., &amp;quot;Congestion Control for Large-Scale RDMA Deployments&amp;quot; (DCQCN), SIGCOMM 2015.&lt;br /&gt;
* R. Mittal et al., &amp;quot;TIMELY: RTT-based Congestion Control&amp;quot;, SIGCOMM 2015.&lt;br /&gt;
* Y. Li et al., &amp;quot;HPCC: High Precision Congestion Control&amp;quot;, SIGCOMM 2019.&lt;br /&gt;
* V. Addanki et al., &amp;quot;PowerTCP&amp;quot;, NSDI 2022.&lt;br /&gt;
* K. De Schepper, B. Briscoe (eds.), &amp;quot;Low Latency, Low Loss, and Scalable Throughput (L4S)&amp;quot;, RFC 9330/9331/9332.&lt;br /&gt;
* F. Kelly, &amp;quot;Charging and Rate Control for Elastic Traffic&amp;quot;, 1997; R. Srikant, &amp;quot;The Mathematics of Internet Congestion Control&amp;quot;, 2004.&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1942</id>
		<title>Congestion avoidance</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1942"/>
		<updated>2026-07-11T22:14:31Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:mb-ecn - Multi-bit ECN Congestion Avoidance}}&lt;br /&gt;
&lt;br /&gt;
mb-ecn is a congestion-avoidance (CA) policy for the Ouroboros unicast&lt;br /&gt;
IPCP. Reliability, ordering and flow control are FRCP&#039;s job, end-to-end&lt;br /&gt;
and per flow; congestion avoidance is a separate concern, and it runs&lt;br /&gt;
in the IPCP, once per layer. Within a layer the IPCP controls&lt;br /&gt;
&#039;&#039;&#039;aggregates&#039;&#039;&#039;: one control loop per (destination address, QoS cube),&lt;br /&gt;
shared by every flow to that destination.&lt;br /&gt;
&lt;br /&gt;
This document is about the algorithm: what it does and why. The&lt;br /&gt;
code follows it; the [[#References|reference section]] maps the pieces&lt;br /&gt;
to source files. It doesn&#039;t oversell: the [[#Limitations|limitations]]&lt;br /&gt;
section says plainly what it can&#039;t do.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;MUST&amp;quot;, &amp;quot;SHOULD&amp;quot;, &amp;quot;MAY&amp;quot; and friends mean what BCP 14 (RFC 2119,&lt;br /&gt;
RFC 8174) says, but only where they appear in capitals.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Symbol !! Units !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; || B/s || Paced send rate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt; || B/s || Bottleneck capacity&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; || bytes || Packet size&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;q&amp;lt;/code&amp;gt; || packets || Standing queue occupancy at the bottleneck&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; || packets || Marking quantum: packets per mark unit (4)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; || 8-bit code || Per-packet congestion mark set by a forwarder&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; || 1/32 &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; || Receiver-side congestion estimate: 32 &amp;amp;times; time-mean &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;cap8&amp;lt;/code&amp;gt; || 8-bit code || Path capacity, quarter-log2: &amp;lt;code&amp;gt;C &amp;amp;asymp; 2^(cap8 / 4)&amp;lt;/code&amp;gt; B/s, 0 = unknown&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt; || ns || Receiver averaging window&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; || B/s^2 || Additive-increase slope&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; units || Full-congestion reference level (512)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;Delta;t&amp;lt;/code&amp;gt; || ns || Elapsed wall-clock time between two events&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; || n/a || Path round-trip time (feedback delay); the controller does not measure it&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rate is the control variable throughout.&lt;br /&gt;
&lt;br /&gt;
Every concrete figure in this document (window bounds, horizons,&lt;br /&gt;
rate thresholds) is just an instance of the parameter values in the&lt;br /&gt;
[[#Parameters|parameters table]]. What the algorithm fixes is the&lt;br /&gt;
mechanisms and the relations between parameters; retune the parameters&lt;br /&gt;
and the figures move together.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
mb-ecn is a rate-based, ECN-driven controller with three moving parts,&lt;br /&gt;
one at each point of the loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        sender                                  forwarder&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
      |  SFQ virtual-clock pacer       |       |  ecn = MAX(ecn, queue/Q) |&lt;br /&gt;
      |  rate r, paced per packet      | data  |  (deepest queue wins)    |&lt;br /&gt;
      |  AIMD + PD control on r        |------&amp;gt;|  cap8 = MIN(cap8, link)  |&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
                 ^                                        |&lt;br /&gt;
                 | ece + cap8 feedback                    | marked data&lt;br /&gt;
                 |                                        v&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
      |  reads downstream ece,         |&amp;lt;------|  receiver                |&lt;br /&gt;
      |  updates the rate on feedback, |  ece  |  time-mean of ecn over   |&lt;br /&gt;
      |  scales floor/slope to cap8    |  cap8 |  an adaptive window      |&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# The &#039;&#039;&#039;forwarder&#039;&#039;&#039; marks each packet with a &#039;&#039;multi-bit&#039;&#039; magnitude proportional to its standing queue, and MIN-stamps its measured outgoing-link capacity into the same PCI.&lt;br /&gt;
# The &#039;&#039;&#039;receiver&#039;&#039;&#039; turns the stream of marks into a time-averaged congestion estimate &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; and feeds it back to the sender, together with the window minimum of the path-capacity codes.&lt;br /&gt;
# The &#039;&#039;&#039;sender&#039;&#039;&#039; paces its rate with a start-time fair-queuing (SFQ) virtual clock, and adjusts that rate with additive-increase / multiplicative-decrease (AIMD) driven by &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;; the rate floor and the additive slope derive from the fed-back path capacity.&lt;br /&gt;
&lt;br /&gt;
There&#039;s no timer thread: the controller runs when a packet goes out or&lt;br /&gt;
feedback comes back. Every step is scaled by elapsed wall-clock time,&lt;br /&gt;
and that is what makes the &#039;&#039;allocation&#039;&#039; independent of RTT (see&lt;br /&gt;
[[#RTT behaviour|RTT behaviour]]).&lt;br /&gt;
&lt;br /&gt;
The unit of control is the &#039;&#039;&#039;aggregate&#039;&#039;&#039;: one per&lt;br /&gt;
&amp;lt;code&amp;gt;(destination address, QoS cube)&amp;lt;/code&amp;gt;, shared by every flow to&lt;br /&gt;
that destination at that QoS. They share one controller and one rate.&lt;br /&gt;
A new flow joins at whatever rate the aggregate is already running; when&lt;br /&gt;
a flow leaves, its share goes to the others. The pacer (SFQ) splits&lt;br /&gt;
that rate fairly across the member flows, so per-flow fairness is the&lt;br /&gt;
scheduler&#039;s doing, not the rate law&#039;s.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Forwarder marking ==&lt;br /&gt;
&lt;br /&gt;
A forwarder marks a packet based on how many packets are already queued&lt;br /&gt;
on the outgoing link:&lt;br /&gt;
&lt;br /&gt;
  mark = queued / Q                         (Q = 4 packets)&lt;br /&gt;
  ecn  = MAX(ecn, mark)                     (saturating, 8-bit field)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; is fixed for the whole layer, not tuned per hop: the marks&lt;br /&gt;
get MAX-combined across hops, so every forwarder MUST use the same&lt;br /&gt;
quantum or the numbers don&#039;t compare.&lt;br /&gt;
&lt;br /&gt;
Two things worth noting:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;It&#039;s a magnitude, not a bit.&#039;&#039;&#039; The mark is an integer that tracks queue depth, in steps of &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; packets: a coarse read of the standing queue.&lt;br /&gt;
* &#039;&#039;&#039;MAX along the path.&#039;&#039;&#039; Cross several forwarders and the packet carries the largest mark, i.e. the deepest queue it passed. Taking the MAX (not a sum) keeps the number bounded and monotone across hops.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a &#039;&#039;&#039;dead zone&#039;&#039;&#039;: below &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; queued packets the mark is 0.&lt;br /&gt;
&lt;br /&gt;
=== Capacity stamping ===&lt;br /&gt;
&lt;br /&gt;
Each forwarder also measures its outgoing link&#039;s capacity and stamps it&lt;br /&gt;
into the packet. The measurement is a busy-period drain rate: look at&lt;br /&gt;
the queue at most once a millisecond, and keep a window open until 16&lt;br /&gt;
packets have drained. So the window &#039;&#039;&#039;sizes itself to the link&#039;&#039;&#039;:&lt;br /&gt;
the once-a-millisecond cadence caps it on fast links, the 16-packet&lt;br /&gt;
drain time on slow ones (~19 ms at 10 Mbit/s). If more than 1/8 of the&lt;br /&gt;
arrivals hit an empty queue, throw the window out: the link&lt;br /&gt;
wasn&#039;t saturated. The odd empty sample (a token-bucket shaper grazing&lt;br /&gt;
zero) is fine. A saturated queue drains at the link rate, so a max&lt;br /&gt;
filter that decays slowly (1/16 per window) creeps up to it from below.&lt;br /&gt;
A window that started or ended on an empty queue might have drained&lt;br /&gt;
into buffers downstream faster than the wire, so it may pull the&lt;br /&gt;
estimate down but never up. On the wire the code is quarter-log2&lt;br /&gt;
(&amp;lt;code&amp;gt;cap8 = 4 log2 C&amp;lt;/code&amp;gt;, ~19% a step, 0 = unknown), and each hop&lt;br /&gt;
MIN-combines its own into the byte, so the packet arrives carrying the&lt;br /&gt;
slowest hop&#039;s rate. A hop that has never backed up stamps nothing,&lt;br /&gt;
which is fine, since only backed-up hops matter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Receiver estimate ==&lt;br /&gt;
&lt;br /&gt;
The receiver turns the marks into a smoothed estimate &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;,&lt;br /&gt;
averaged over a window &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt; that &#039;&#039;&#039;tracks the incoming byte rate&#039;&#039;&#039;.&lt;br /&gt;
It&#039;s a plain time-average over that window (a box-car, not an EWMA).&lt;br /&gt;
Per packet, with &amp;lt;code&amp;gt;&amp;amp;Delta;t&amp;lt;/code&amp;gt; the gap since the last one:&lt;br /&gt;
&lt;br /&gt;
  A += ecn * MIN(&amp;amp;Delta;t, W)              dwell-weighted; one packet&lt;br /&gt;
                                          weighs at most one window&lt;br /&gt;
  B += S                                  accumulate bytes this window&lt;br /&gt;
  ...&lt;br /&gt;
  at window close (elapsed &amp;gt;= W, or B &amp;gt;= 2 * B_target with at&lt;br /&gt;
                   least the window floor elapsed):&lt;br /&gt;
      ece = 32 * A / elapsed              mean over the actual window&lt;br /&gt;
      W  += (B_target * elapsed / B - W) / 4    nudge toward the size&lt;br /&gt;
                                                that holds the target&lt;br /&gt;
      W   = clamp(W, ~1.05 ms, ~4.3 s)&lt;br /&gt;
      A   = 0 ; B = 0                     hard reset&lt;br /&gt;
&lt;br /&gt;
When a window closes, the next one is nudged (a quarter-weight&lt;br /&gt;
moving average) toward the size that would hold about 16 packets&lt;br /&gt;
at the current byte rate, starting from ~67 ms. The target is in bytes&lt;br /&gt;
(&amp;lt;code&amp;gt;B_target&amp;lt;/code&amp;gt; = 16,000), so &amp;quot;16 packets&amp;quot; is only exact at&lt;br /&gt;
~1000-byte packets, and it takes a few closes to settle. The mean&lt;br /&gt;
always divides by the window that &#039;&#039;&#039;actually&#039;&#039;&#039; elapsed, so a change&lt;br /&gt;
in rate leaves the current estimate correct and only resizes the&lt;br /&gt;
&#039;&#039;next&#039;&#039; one. Net effect: a roughly constant ~16 samples a window from&lt;br /&gt;
about 30 kbit/s to 122 Mbit/s. Above that the window bottoms out at&lt;br /&gt;
~1 ms (still thousands of samples a window at 10&amp;amp;ndash;100 GbE); below&lt;br /&gt;
~30 kbit/s it hits the ~4.3 s ceiling. The averaging clock stretches&lt;br /&gt;
with the flow, the way TCP&#039;s ACK clock stretches with the RTT. A&lt;br /&gt;
sudden speed-up doesn&#039;t wait out a stretched window: once twice the&lt;br /&gt;
target bytes have arrived it closes anyway (the ~1 ms floor keeps that&lt;br /&gt;
from running away at high rate). So the &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; the sender sees&lt;br /&gt;
is a staircase: one step per window, steps getting shorter as&lt;br /&gt;
the rate climbs. Two edge cases:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Onset.&#039;&#039;&#039; The first mark after an idle stretch is passed through as-is, so a queue that&#039;s just starting to build shows up without waiting out a whole window.&lt;br /&gt;
* &#039;&#039;&#039;Gap.&#039;&#039;&#039; A silence longer than 4 windows resets and sends the raw sample. &amp;quot;Long&amp;quot; is &#039;&#039;&#039;measured in the flow&#039;s own windows&#039;&#039;&#039;, so a slow flow&#039;s normal spacing never reads as idle; and the dwell clamp above limits what a real pause can add to the mean before the reset fires.&lt;br /&gt;
&lt;br /&gt;
Alongside the marks, the receiver keeps the smallest non-zero capacity&lt;br /&gt;
code it saw this window and ships it with each &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; (on an&lt;br /&gt;
onset/gap restart, just the current packet&#039;s code). Then it resets, so&lt;br /&gt;
if the path reroutes onto something faster the fed-back capacity can&lt;br /&gt;
climb within one window.&lt;br /&gt;
&lt;br /&gt;
The estimate rides back to the sender on the reverse flow.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Sender pacer ==&lt;br /&gt;
&lt;br /&gt;
The sender paces off an SFQ virtual clock. The aggregate keeps a&lt;br /&gt;
virtual time &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;; each flow keeps a finish tag &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
  V    += r * &amp;amp;Delta;t                     virtual time advances by bytes served&lt;br /&gt;
  s     = MAX(F, V)                  a packet behind the clock starts now&lt;br /&gt;
  F     = s + S                      the one after it queues behind&lt;br /&gt;
  wait  = (s - V) / r                time until this packet may go&lt;br /&gt;
&lt;br /&gt;
That &amp;lt;code&amp;gt;wait&amp;lt;/code&amp;gt; is the flow&#039;s scheduling deadline. A packet&lt;br /&gt;
that&#039;s behind the virtual clock goes out now (&amp;lt;code&amp;gt;wait = 0&amp;lt;/code&amp;gt;);&lt;br /&gt;
one that&#039;s ahead waits, and only that flow waits, nobody else.&lt;br /&gt;
Across an idle gap longer than 50 ms the clock credits at most the&lt;br /&gt;
flow&#039;s own owed lead plus one burst (50 ms of service, at least one&lt;br /&gt;
packet). So a flow paced slower than a packet per 50 ms still gets its&lt;br /&gt;
&#039;&#039;&#039;real&#039;&#039;&#039; elapsed service (capping the credit by time would&lt;br /&gt;
starve it) while an idle flow still can&#039;t bank an unbounded&lt;br /&gt;
burst.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; is shared by every flow in the aggregate; the finish&lt;br /&gt;
tags are not. Since every flow measures its start tag &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt;&lt;br /&gt;
against the same &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;, a flow that just sent is now ahead of&lt;br /&gt;
&amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; and has to wait, while an idle one (tag at or behind&lt;br /&gt;
&amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;) goes right away. That is what splits the aggregate rate&lt;br /&gt;
&amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; fairly among them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Rate control (AIMD + PD) ==&lt;br /&gt;
&lt;br /&gt;
The rate law runs on packet sends, at most once a millisecond, and&lt;br /&gt;
also when feedback arrives, so a sender with no traffic of its&lt;br /&gt;
own still reacts. The increase terms and the proportional decrease are&lt;br /&gt;
each scaled by elapsed time, but not by the same clock: the increases&lt;br /&gt;
use banked time &#039;&#039;&#039;capped&#039;&#039;&#039; at 50 ms (an idle gap shouldn&#039;t buy an&lt;br /&gt;
unbounded ramp), while the decrease uses the &#039;&#039;&#039;uncapped&#039;&#039;&#039; elapsed&lt;br /&gt;
time (a starved sender still cuts by the right amount). The one-sided&lt;br /&gt;
derivative (see the&lt;br /&gt;
[[#Multiplicative decrease (proportional + derivative)|decrease]]) is&lt;br /&gt;
not scaled by time at all; it is per-step.&lt;br /&gt;
&lt;br /&gt;
=== Slow start ===&lt;br /&gt;
&lt;br /&gt;
Before the first congestion signal, the rate ramps up exponentially&lt;br /&gt;
with a 20 ms time constant:&lt;br /&gt;
&lt;br /&gt;
  r += r * &amp;amp;Delta;t / T_ss                  (T_ss = 20 ms)&lt;br /&gt;
&lt;br /&gt;
Slow start ends the first time a congestion signal shows up, whether&lt;br /&gt;
fed back from the receiver or seen at the sender&#039;s own first hop, and&lt;br /&gt;
it never comes back.&lt;br /&gt;
&lt;br /&gt;
Slow start belongs to the aggregate, so it happens&lt;br /&gt;
&#039;&#039;&#039;once per (destination, QoS cube)&#039;&#039;&#039;, on the first flow. A flow that&lt;br /&gt;
shows up later, at an aggregate that&#039;s already running, rides the&lt;br /&gt;
existing&lt;br /&gt;
estimate: it starts at the current rate and takes its share through&lt;br /&gt;
the [[#Sender pacer|pacer]], without probing of its own.&lt;br /&gt;
&lt;br /&gt;
=== Increase: additive plus proportional probe (always on) ===&lt;br /&gt;
&lt;br /&gt;
Past slow start, two increase terms run every step, congested or not:&lt;br /&gt;
&lt;br /&gt;
  r += a * &amp;amp;Delta;t                         additive; a defaults to 2^16 B/s^2&lt;br /&gt;
  r += r * &amp;amp;Delta;t / T_probe               proportional; T_probe = 8 s&lt;br /&gt;
&lt;br /&gt;
The additive term is a fixed slope (bytes/s per second), sized to the&lt;br /&gt;
path capacity (see&lt;br /&gt;
[[#Capacity-derived floor and slope|the capacity floor]]). The&lt;br /&gt;
proportional probe adds a fixed fraction of the rate per unit time (it&lt;br /&gt;
e-folds over &amp;lt;code&amp;gt;T_probe&amp;lt;/code&amp;gt;): that is what lets a flow recover in&lt;br /&gt;
the same number of steps whatever the link rate; a fixed additive step&lt;br /&gt;
is negligible next to a fast link. Together they are the&lt;br /&gt;
upward pressure the multiplicative decrease balances at equilibrium.&lt;br /&gt;
The probe is not free: it costs a standing queue that does not shrink&lt;br /&gt;
with rate (see [[#Fairness|fairness]]).&lt;br /&gt;
&lt;br /&gt;
=== Multiplicative decrease (proportional + derivative) ===&lt;br /&gt;
&lt;br /&gt;
With congestion level &amp;lt;code&amp;gt;m&amp;lt;/code&amp;gt; (the fed-back &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;, or&lt;br /&gt;
the local first-hop mark if there&#039;s no feedback yet), the cut is a&lt;br /&gt;
proportional term plus a one-sided derivative term, a PD controller on&lt;br /&gt;
the congestion signal:&lt;br /&gt;
&lt;br /&gt;
  mark = MIN(m, M)                                   M = 512&lt;br /&gt;
  rise = MAX(m - m_prev, 0), capped at M             one-sided derivative&lt;br /&gt;
&lt;br /&gt;
  cut  = r * rise / (4 * M)                          derivative, gain 1/4&lt;br /&gt;
  cut += r * mark * &amp;amp;Delta;t_ms / (1000 * M)              proportional&lt;br /&gt;
  cut  = MIN(cut, r / 2)                             at most a halving&lt;br /&gt;
&lt;br /&gt;
  r     -= cut&lt;br /&gt;
  m_prev = m&lt;br /&gt;
&lt;br /&gt;
* The &#039;&#039;&#039;proportional&#039;&#039;&#039; term uses the real elapsed milliseconds, so a starved sender that hasn&#039;t run the controller in a while still cuts by the right amount.&lt;br /&gt;
* The &#039;&#039;&#039;derivative&#039;&#039;&#039; term (&amp;lt;code&amp;gt;rise&amp;lt;/code&amp;gt;) only fires when the congestion level &#039;&#039;goes up&#039;&#039; between samples: one-sided, per-step. It sharpens the response right as congestion starts.&lt;br /&gt;
* The cut is capped at &amp;lt;code&amp;gt;r/2&amp;lt;/code&amp;gt;, so one step can at most halve the rate.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;M = 512 = 32 &amp;amp;times; 16&amp;lt;/code&amp;gt; pins &amp;quot;full congestion&amp;quot; at a mean&lt;br /&gt;
&amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; of 16, i.e. a mean queue of &amp;lt;code&amp;gt;16 &amp;amp;times; Q = 64&amp;lt;/code&amp;gt;&lt;br /&gt;
packets.&lt;br /&gt;
&lt;br /&gt;
=== Staleness ===&lt;br /&gt;
&lt;br /&gt;
Feedback comes once per receiver window, and that window stretches with&lt;br /&gt;
the flow&#039;s byte rate (see&lt;br /&gt;
[[#Receiver estimate|the receiver estimate]]), so the staleness horizon&lt;br /&gt;
has to stretch with it too, or a slow flow&#039;s mark would expire&lt;br /&gt;
&#039;&#039;between&#039;&#039; feedbacks and the rate would ramp straight into a congested&lt;br /&gt;
path. The sender mirrors the receiver: the horizon is four target&lt;br /&gt;
windows&#039; worth of bytes at the current rate, floored at ~268 ms so fast&lt;br /&gt;
flows keep a fixed one; at the default rate floor it&#039;s ~8 s. Once a&lt;br /&gt;
signal is older than that it&#039;s dropped (both the fed-back estimate and&lt;br /&gt;
the local mark), which frees the rate to climb again after congestion&lt;br /&gt;
clears. Every step clamps the rate to &amp;lt;code&amp;gt;[r_min, r_max]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Capacity-derived floor and slope ===&lt;br /&gt;
&lt;br /&gt;
The fed-back capacity code sets the controller&#039;s two rate-scale&lt;br /&gt;
constants, per aggregate:&lt;br /&gt;
&lt;br /&gt;
  target = decode(cap8) / 32              clamped to [2^13, 2^32] B/s&lt;br /&gt;
  r_min += (target - r_min) / 2           per feedback (moving average)&lt;br /&gt;
  a      = r_min&lt;br /&gt;
&lt;br /&gt;
On a path that never reports a capacity, both fall back to fixed&lt;br /&gt;
defaults (&amp;lt;code&amp;gt;r_min = 2^13 B/s&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;a = 2^16 B/s^2&amp;lt;/code&amp;gt;).&lt;br /&gt;
When the estimator is live, the fairness floor and the post-cut&lt;br /&gt;
recovery slope scale with the bottleneck instead, and capacity kicks in&lt;br /&gt;
wherever &amp;lt;code&amp;gt;C/32&amp;lt;/code&amp;gt; beats the default floor, roughly above&lt;br /&gt;
2 Mbit/s. A capacity older than 16 staleness horizons (~4.3 s for fast&lt;br /&gt;
flows) reverts both to the defaults. That horizon is deliberately&lt;br /&gt;
longer than the feedback horizon: feedback stops the instant the marks&lt;br /&gt;
clear, which is exactly when you need the recovery slope, and the&lt;br /&gt;
onset-fresh capacity (see&lt;br /&gt;
[[#Receiver estimate|the receiver estimate]]) re-seeds it on the first&lt;br /&gt;
mark of the next episode anyway.&lt;br /&gt;
&lt;br /&gt;
=== Ramp and recovery ===&lt;br /&gt;
&lt;br /&gt;
Three mechanisms set how fast an aggregate reaches a fast link&#039;s&lt;br /&gt;
rate, each owning one regime:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;From scratch: slow start.&#039;&#039;&#039; The exponential ramp hits any real link rate in well under a second (~200 ms from the seed to 10 Gbit/s; every further doubling of link speed adds one ~14 ms doubling). Nothing marks on an uncongested path, so slow start just runs until the aggregate reaches its bottleneck.&lt;br /&gt;
* &#039;&#039;&#039;After a cut: the probe.&#039;&#039;&#039; A cut is at most a halving, and the mark expires on the rate-relative horizon once congestion clears (see [[#Staleness|staleness]]); the probe then climbs back from a halving in &amp;lt;code&amp;gt;T_probe &amp;amp;middot; ln 2 &amp;amp;asymp; 5.5 s&amp;lt;/code&amp;gt;, whatever the link rate.&lt;br /&gt;
* &#039;&#039;&#039;At the bottom: the capacity floor.&#039;&#039;&#039; The rate never sits more than 32&amp;amp;times; below a measured bottleneck, and the additive slope refills &amp;lt;code&amp;gt;C/32&amp;lt;/code&amp;gt; a second, so the worst hole is bounded: floor to full capacity in tens of seconds, a halving in seconds.&lt;br /&gt;
&lt;br /&gt;
These fit together because a deep cut needs sustained congestion,&lt;br /&gt;
sustained congestion backs up the bottleneck queue, and a backed-up&lt;br /&gt;
queue is exactly when its capacity gets measured and stamped (see&lt;br /&gt;
[[#Forwarder marking|forwarder marking]]). So whenever the controller&lt;br /&gt;
has been cut deep, the scaled floor is already live. The default floor&lt;br /&gt;
only ever applies to paths that never congested the sender in the first&lt;br /&gt;
place, where there&#039;s nothing to recover from. That&#039;s why slow start can&lt;br /&gt;
quit for good: the floor and the probe handle every recovery after it.&lt;br /&gt;
&lt;br /&gt;
=== Regimes ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   +-------------+   first mark seen    +----------------+&lt;br /&gt;
   | slow start  |---------------------&amp;gt;|  congestion    |&lt;br /&gt;
   | r *= e^(t/T)|                      |  avoidance     |&lt;br /&gt;
   +-------------+                      |                |&lt;br /&gt;
                                        | m == 0 ---&amp;gt; additive increase&lt;br /&gt;
                                        | m  &amp;gt; 0 ---&amp;gt; AI + PD decrease&lt;br /&gt;
                                        +----------------+&lt;br /&gt;
    m = fed-back ece, or the local first-hop mark when ece == 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== RTT behaviour ==&lt;br /&gt;
&lt;br /&gt;
Because every increase and decrease is scaled by wall-clock time, two&lt;br /&gt;
flows with different RTTs sharing a bottleneck follow the &#039;&#039;same&#039;&#039; rate&lt;br /&gt;
law and settle at the &#039;&#039;same&#039;&#039; rate. So the steady-state&lt;br /&gt;
&#039;&#039;&#039;allocation is RTT-independent&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
That is a property of the equilibrium, not the dynamics. The feedback&lt;br /&gt;
delay in the loop &#039;&#039;is&#039;&#039; the path RTT &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;, and the&lt;br /&gt;
controller neither measures nor compensates for it. At low rate the&lt;br /&gt;
receiver window (up to its ~4.3 s ceiling) is the biggest lag: the loop&lt;br /&gt;
is slow there in proportion to how slow the flow is, just like a&lt;br /&gt;
long-RTT TCP. As the rate climbs the window shrinks toward its ~1 ms&lt;br /&gt;
floor, so its share of the loop delay &#039;&#039;falls&#039;&#039; with capacity and the&lt;br /&gt;
path RTT &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; becomes the irreducible part. Two things keep&lt;br /&gt;
the loop damped: the adaptive window takes out the delay-dominated&lt;br /&gt;
corner at low &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;, and a bucketed&lt;br /&gt;
&#039;&#039;&#039;rate-velocity damping&#039;&#039;&#039; (the washout: each ~31 ms bucket pulls the&lt;br /&gt;
rate a quarter of the way back toward its value at the previous bucket)&lt;br /&gt;
supplies the damping this otherwise near-double-integrator loop lacks,&lt;br /&gt;
which is what holds it together at high capacity and moderate&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;. What is left is the loop gain&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;prop; &amp;amp;radic;C&amp;lt;/code&amp;gt;: at very high capacity &#039;&#039;and&#039;&#039;&lt;br /&gt;
large &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; the &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt; phase&lt;br /&gt;
budget is the binding constraint, and no amount of windowing or damping&lt;br /&gt;
removes it (see [[#Limitations|Limitations]]). So, precisely:&lt;br /&gt;
&#039;&#039;&#039;RTT-fair in equilibrium, and dynamically stable up to a&#039;&#039;&#039;&lt;br /&gt;
&#039;&#039;&#039;capacity-dependent RTT limit set by &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Fairness ==&lt;br /&gt;
&lt;br /&gt;
At equilibrium the two increase terms balance the proportional&lt;br /&gt;
decrease, &amp;lt;code&amp;gt;a + r/T_probe = r &amp;amp;middot; m*/M&amp;lt;/code&amp;gt;, which pins the&lt;br /&gt;
congestion level at &amp;lt;code&amp;gt;m* = M &amp;amp;middot; (a/r + 1/T_probe)&amp;lt;/code&amp;gt;. Since&lt;br /&gt;
&amp;lt;code&amp;gt;ece = 8q&amp;lt;/code&amp;gt; (one mark unit per &amp;lt;code&amp;gt;Q = 4&amp;lt;/code&amp;gt; packets, 32&lt;br /&gt;
fixed-point steps per unit), that is a standing queue of&lt;br /&gt;
&lt;br /&gt;
  q* = 64 * a / r  +  64 / T_probe        (packets, r in bytes/s)&lt;br /&gt;
&lt;br /&gt;
The additive part (&amp;lt;code&amp;gt;64 &amp;amp;middot; a / r&amp;lt;/code&amp;gt;) fades to nothing with&lt;br /&gt;
rate under the default slope; with the capacity-derived slope&lt;br /&gt;
(&amp;lt;code&amp;gt;a = C/32&amp;lt;/code&amp;gt;; see&lt;br /&gt;
[[#Capacity-derived floor and slope|the capacity floor]]) it settles at&lt;br /&gt;
&amp;lt;code&amp;gt;2 C / r&amp;lt;/code&amp;gt;, about two packets per competing flow, whatever the&lt;br /&gt;
rate. The probe part (&amp;lt;code&amp;gt;64 / T_probe&amp;lt;/code&amp;gt;) is a floor that does&lt;br /&gt;
not move with rate (~8 packets at 8 s). That floor is what&lt;br /&gt;
rate-independent convergence costs you, and it is what keeps&lt;br /&gt;
&amp;lt;code&amp;gt;q*&amp;lt;/code&amp;gt; above the marking threshold at high rate.&lt;br /&gt;
&lt;br /&gt;
Two flows at the same bottleneck see the same &amp;lt;code&amp;gt;m*&amp;lt;/code&amp;gt;, so each&lt;br /&gt;
solves &amp;lt;code&amp;gt;r_i = a / (m*/M - 1/T_probe)&amp;lt;/code&amp;gt; to the same rate. In&lt;br /&gt;
network-utility-maximization terms that is &#039;&#039;&#039;proportional fairness&#039;&#039;&#039;&lt;br /&gt;
with equal weights, which at a single bottleneck is just max-min fair.&lt;br /&gt;
&lt;br /&gt;
Getting a good measurement at low rate is the estimator&#039;s job, not the&lt;br /&gt;
floor&#039;s: the averaging window stretches with the flow (see&lt;br /&gt;
[[#Receiver estimate|the receiver estimate]]), so a CA-limited flow&lt;br /&gt;
still keeps ~16 packets a window down to ~30 kbit/s, and the&lt;br /&gt;
rate-relative gap threshold keeps its normal spacing from reading as&lt;br /&gt;
idle. The default floor (&amp;lt;code&amp;gt;2^13 B/s&amp;lt;/code&amp;gt;) only bounds the extremes&lt;br /&gt;
(window ceiling, staleness horizon, pacer arithmetic). The floor itself&lt;br /&gt;
scales with the fed-back path capacity (&amp;lt;code&amp;gt;r_min = C/32&amp;lt;/code&amp;gt;; see&lt;br /&gt;
[[#Capacity-derived floor and slope|the capacity floor]]), so about 32&lt;br /&gt;
CA-limited flows fit above it on any class of link. Flows whose&lt;br /&gt;
bottlenecks are in different classes get different slopes, so fairness&lt;br /&gt;
between them is capacity-weighted, not equal; see&lt;br /&gt;
[[#Limitations|Limitations]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Limitations ==&lt;br /&gt;
&lt;br /&gt;
Things the algorithm &#039;&#039;&#039;doesn&#039;t&#039;&#039;&#039; do. Listed here so nobody credits it&lt;br /&gt;
with more than it manages.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Queue-only signal, no rate term.&#039;&#039;&#039; &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; is a queue level, so mb-ecn is a standing-queue controller: it has to build a queue (&amp;lt;code&amp;gt;q* = 64 &amp;amp;middot; a / r + 64 / T_probe&amp;lt;/code&amp;gt;) to get any signal at all, and can&#039;t sit at full utilisation on an empty queue the way a controller with a rate estimate can. The probe floor (~8 packets) keeps &amp;lt;code&amp;gt;q*&amp;lt;/code&amp;gt; above the marking threshold at every rate, but you pay for it with a permanent standing queue.&lt;br /&gt;
* &#039;&#039;&#039;Aggregation is per destination, per QoS cube.&#039;&#039;&#039; The aggregate is a stand-in for the path: flows to the same destination are assumed to share a bottleneck, and flows to different destinations run separate loops even when they pile up at the same hop. Each loop still converges to its own fair share (see [[#Fairness|fairness]]), so the cost is duplicated state and probing at that shared queue, not unfairness. One day we could tag the queue in the packet: if a forwarder stamped a queue id next to its mark, senders could key their aggregates on the real bottleneck instead of the destination, and pool the estimate and the probing across everything sharing it.&lt;br /&gt;
* &#039;&#039;&#039;Capacity is learned, and there are gaps.&#039;&#039;&#039; The floor and slope ride on an in-band path-capacity estimate (see [[#Forwarder marking|forwarder marking]] and [[#Capacity-derived floor and slope|the capacity floor]]), and that estimate has its own limits. A hop only learns its rate while its queue is backed up, so a rerouted or brand-new bottleneck reads &#039;&#039;unknown&#039;&#039; at first and the path MIN can briefly come from faster hops (the sender&#039;s smoothing and the next feedback bound the overshoot). The measurement is order-of-magnitude on purpose: quarter-log2 code (~19% a step), burst noise held down by a 16-packet minimum window and a max filter, and nothing learned below ~120 packets a second of busy drain (16 packets inside the ~134 ms staleness cap; at MTU that&#039;s ~1.4 Mbit/s, below which capacity/32 sits under the default floor anyway). The derived floor clamps to &amp;lt;code&amp;gt;[2^13, 2^32]&amp;lt;/code&amp;gt; B/s, and flows with bottlenecks in different classes get capacity-weighted, not equal, shares at a common queue (see [[#Fairness|fairness]]).&lt;br /&gt;
* &#039;&#039;&#039;The low end trades speed, not correctness.&#039;&#039;&#039; Below ~30 kbit/s the averaging window hits its ~4.3 s ceiling and holds fewer samples; below ~64 kbit/s of offered load an &#039;&#039;app-limited&#039;&#039; flow spaces its packets past the gap threshold and rides the raw onset branch, as it always did. At the floor, feedback and the staleness horizon are both measured in seconds, so a slow flow converges slowly, TCP-style. Nothing blows up; nothing&#039;s quick either.&lt;br /&gt;
* &#039;&#039;&#039;No loss response.&#039;&#039;&#039; The controller only reacts to ECN marks. Loss is FRCP&#039;s problem, handled by retransmission, out of the CA&#039;s sight. At a bottleneck that drops instead of marking (say a plain drop-tail queue at the Ethernet or UDP shim), mb-ecn sees nothing and additive increase just keeps ramping. It&#039;s built for Ouroboros-native bottlenecks where the forwarder marks; there&#039;s no classic-bottleneck fallback.&lt;br /&gt;
* &#039;&#039;&#039;Dynamic stability has a capacity-dependent RTT limit.&#039;&#039;&#039; As covered under [[#RTT behaviour|RTT behaviour]], the allocation is RTT-independent but the dynamics aren&#039;t: the binding constraint is &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;prop; &amp;amp;radic;C&amp;lt;/code&amp;gt;. The adaptive window and the washout push that limit out a long way (a single flow at 10 GbE stays stable to ~50 ms RTT in the fluid model), but the far corner (100 GbE at &amp;amp;ge; 50 ms) is out of reach for a window/damping controller and would need explicit-rate signalling.&lt;br /&gt;
&lt;br /&gt;
== Architectural fit ==&lt;br /&gt;
&lt;br /&gt;
Each of these properties falls out of something structural in the&lt;br /&gt;
recursive architecture. Here&#039;s the mapping.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CA is fully separate from ARQ and flow control.&#039;&#039;&#039; Three concerns, three mechanisms, two scopes: FRCP does retransmission (ARQ) and flow control (the peer pacing the sender), end-to-end and per flow; CA runs in the IPCP, per aggregate. Each signal says one thing. A loss just means retransmit: a lossy link reads as lossy, and the congestion call is left to the marks (the [[#Limitations|limitations]] section&#039;s &amp;quot;no loss response&amp;quot; is this same separation from the other side). The peer&#039;s flow-control window just paces the endpoint: back-pressure from a slow receiver stays separate from congestion in the network. A retransmitted packet is ordinary traffic as far as the pacer cares. TCP jams all three into one window machine, where the receive window caps the congestion window and loss is both the reliability trigger and the congestion signal; here you can reason about, test, and change each one on its own.&lt;br /&gt;
* &#039;&#039;&#039;CA sits below what the application picks.&#039;&#039;&#039; Every flow in the layer runs under the same rate law, whatever its QoS: a greedy raw sender shares a bottleneck fairly with a reliable stream because the control belongs to the layer, on the aggregate, not to the endpoint transport&#039;s good manners.&lt;br /&gt;
* &#039;&#039;&#039;A layer owns its PCI, so the signal can be rich.&#039;&#039;&#039; Everyone in a layer enrolled into it, so the layer is one administrative domain by construction, at whatever scope it covers, and the header is the layer&#039;s own. That&#039;s why forwarders can write a multi-bit queue magnitude and a capacity byte straight into the packet. The datacenter schemes in [[#Heritage and positioning|heritage and positioning]] need exactly that kind of domain and only get it inside one operator&#039;s fabric; a recursive layer has it everywhere.&lt;br /&gt;
* &#039;&#039;&#039;Layer-internal addressing makes the aggregate well-defined.&#039;&#039;&#039; Flows in a layer run between layer addresses, so the (destination, QoS cube) aggregate, which is RFC 3124&#039;s macroflow, drops straight out of the naming. Controller state scales with the number of destinations, and each QoS cube keeps its own loop, so service classes don&#039;t share a fate.&lt;br /&gt;
* &#039;&#039;&#039;Flows are allocated, so feedback already has a channel.&#039;&#039;&#039; Every flow has state at both ends and a reverse direction, so the receiver&#039;s &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; and the path capacity just ride the flow allocator&#039;s existing exchange.&lt;br /&gt;
* &#039;&#039;&#039;There&#039;s no transport ack clock, and nothing leans on one.&#039;&#039;&#039; The layer has to handle raw flows, which carry no acknowledgements at all, so the controller paces on wall-clock time, and that constraint is exactly what buys the RTT-independent allocation under [[#RTT behaviour|RTT behaviour]]. A restriction turned into the feature.&lt;br /&gt;
* &#039;&#039;&#039;Recursion scales it.&#039;&#039;&#039; Each layer handles congestion over its own scope and timescale; many N-flows ride one N-1 flow, so aggregation compounds down the stack. The capacity a layer sees at its egress queue is whatever the layer below is actually giving it (a paced, shared lower flow, not some nominal wire speed), so the signal means something at every level and back-pressure cascades down layer by layer.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a boundary: at a shim over legacy media the layer below neither&lt;br /&gt;
enrolls nor marks, which is the deployment edge behind the&lt;br /&gt;
[[#Limitations|limitations]] section&#039;s no-loss-response point.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Heritage and positioning ==&lt;br /&gt;
&lt;br /&gt;
mb-ecn is basically a rate-based ECN controller in the DECbit / QCN&lt;br /&gt;
line, adapted to a recursive layer with no acknowledgements.&lt;br /&gt;
&lt;br /&gt;
The core goes back to the first Ouroboros CA policy (2020): a multi-bit&lt;br /&gt;
queue-depth mark relayed by forwarders to the receiver, a smoothed&lt;br /&gt;
multi-bit &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; fed back on the reverse flow at the same 1/32&lt;br /&gt;
fixed point used today, and a sender that paced packets from a time&lt;br /&gt;
budget and ran slow start, additive increase and multiplicative&lt;br /&gt;
decrease on wall-clock time slots. Rate-based and acknowledgement-free&lt;br /&gt;
from the start, with RTT-independent allocation already the goal. The&lt;br /&gt;
present algorithm keeps that skeleton and reworks every estimator&lt;br /&gt;
around it: per-path aggregation with SFQ pacing, the rate-adaptive&lt;br /&gt;
box-car receiver, elapsed-time-scaled AIMD with a derivative term and&lt;br /&gt;
rate-velocity damping, and the in-band capacity signal.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;DECbit&#039;&#039;&#039; (Ramakrishnan &amp;amp;amp; Jain, 1988; ToCS 1990) established binary congestion feedback set by the switch on queue occupancy. mb-ecn keeps the switch-sets-on-queue idea and carries a &#039;&#039;magnitude&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;The Congestion Manager&#039;&#039;&#039; (Balakrishnan et al., SIGCOMM 1999; RFC 3124, 2001) aggregates congestion state at the end host: one controller per macroflow, a scheduler apportioning its rate across the member streams, and new streams joining at the ensemble&#039;s current state instead of probing from scratch. mb-ecn&#039;s (destination, QoS cube) aggregate is the same idea placed inside the IPCP: every layer manages its own macroflows, recursively, the SFQ pacer plays the CM scheduler&#039;s role (see [[#Sender pacer|the pacer]]), and a joining flow rides the aggregate&#039;s estimates (see [[#Slow start|slow start]]). The CM gathers its signal from transport feedback at the edge; mb-ecn reads it off the wire from the forwarders.&lt;br /&gt;
* &#039;&#039;&#039;DCTCP&#039;&#039;&#039; (Alizadeh et al., SIGCOMM 2010) recovers a queue magnitude at the endpoint by averaging a single threshold bit. mb-ecn instead reads the magnitude off the wire, then time-averages it at the receiver. The additive-increase / proportional-decrease structure is shared.&lt;br /&gt;
* &#039;&#039;&#039;QCN&#039;&#039;&#039; (IEEE 802.1Qau, 2010) and &#039;&#039;&#039;DCQCN&#039;&#039;&#039; (Zhu et al., SIGCOMM 2015) contribute the rate-based (not window-based) ECN control skeleton. mb-ecn&#039;s proportional decrease plus a probing increase mirrors that skeleton; the increase here is wall-clock scaled rather than byte-counter/timer based.&lt;br /&gt;
* &#039;&#039;&#039;HPCC&#039;&#039;&#039; (Li et al., SIGCOMM 2019) and &#039;&#039;&#039;PowerTCP&#039;&#039;&#039; (Addanki et al., NSDI 2022) combine a queue term &#039;&#039;and&#039;&#039; a rate term (via in-band telemetry or delay). mb-ecn now carries a coarse in-band rate (the quarter-log2 path MIN; see [[#Forwarder marking|forwarder marking]]) but uses it only to scale the controller&#039;s gains; the congestion &#039;&#039;signal&#039;&#039; remains queue-only, which is still the main functional difference (see [[#Limitations|Limitations]]).&lt;br /&gt;
* &#039;&#039;&#039;L4S / TCP Prague&#039;&#039;&#039; (RFC 9330&amp;amp;ndash;9332, 2023) make RTT-independence a requirement and use a high-frequency single-bit signal. mb-ecn shares the RTT-independent &#039;&#039;allocation&#039;&#039; goal but reaches it by wall-clock-scaled rate control, with a low-frequency multi-bit signal.&lt;br /&gt;
&lt;br /&gt;
What&#039;s new is the pairing: a multi-bit magnitude on the wire with a&lt;br /&gt;
wall-clock-scaled rate pacer, in a setting with no per-flow ack clock.&lt;br /&gt;
The trade-off, spelled out above, is a queue-only signal and a&lt;br /&gt;
reactivity capped by the averaging window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Parameters ==&lt;br /&gt;
&lt;br /&gt;
These are the values behind every figure in this document. They&#039;re&lt;br /&gt;
&#039;&#039;&#039;coupled&#039;&#039;&#039;: the mechanisms only fit together while a few relations&lt;br /&gt;
hold. The gap-restart horizon at the rate floor has to exceed a&lt;br /&gt;
packet&#039;s service time (or a floor-rate flow loops at onset); a&lt;br /&gt;
floor-rate flow&#039;s target window has to fit under the window ceiling;&lt;br /&gt;
the control interval has to sit under the damping bucket, which sits&lt;br /&gt;
under the idle credit cap; the rate floor mustn&#039;t exceed the slow-start&lt;br /&gt;
seed; and the derived-floor ceiling has to stay under the rate ceiling.&lt;br /&gt;
Retune them as a set, against those constraints.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| Marking quantum &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; || 4 packets || Mark onset and step; layer-wide&lt;br /&gt;
|-&lt;br /&gt;
| Feedback resolution || 1/32 &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; unit || Fixed-point LSB of &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Full-congestion reference &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; || 512 || Mean queue of 64 packets&lt;br /&gt;
|-&lt;br /&gt;
| Receiver window target || 16 packets (16,000 bytes) || Samples per window&lt;br /&gt;
|-&lt;br /&gt;
| Initial receiver window || ~67 ms || Before rate is known&lt;br /&gt;
|-&lt;br /&gt;
| Receiver window bounds || ~1.05 ms &amp;amp;ndash; ~4.3 s || Clamp on &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Window resize weight || 1/4 || Moving average toward target&lt;br /&gt;
|-&lt;br /&gt;
| Early window close || 2 &amp;amp;times; target bytes || Speed-up escape&lt;br /&gt;
|-&lt;br /&gt;
| Gap restart || 4 windows || Idle horizon (rate-relative)&lt;br /&gt;
|-&lt;br /&gt;
| Additive slope &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; (default) || 2^16 B/s^2 || Lower clamp of derived slope&lt;br /&gt;
|-&lt;br /&gt;
| Probe time constant &amp;lt;code&amp;gt;T_probe&amp;lt;/code&amp;gt; || 8 s || Rate-proportional recovery&lt;br /&gt;
|-&lt;br /&gt;
| Derivative gain || 1/4 || Onset sharpening&lt;br /&gt;
|-&lt;br /&gt;
| Rate floor &amp;lt;code&amp;gt;r_min&amp;lt;/code&amp;gt; (default) || 2^13 B/s || Lower clamp of derived floor&lt;br /&gt;
|-&lt;br /&gt;
| Slow-start seed rate || 2^16 B/s || Initial rate&lt;br /&gt;
|-&lt;br /&gt;
| Slow-start time constant || 20 ms || Exponential ramp&lt;br /&gt;
|-&lt;br /&gt;
| Rate ceiling &amp;lt;code&amp;gt;r_max&amp;lt;/code&amp;gt; || 2^37 B/s || Upper clamp&lt;br /&gt;
|-&lt;br /&gt;
| Capacity scale || &amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt; / 32 || Floor and slope from bottleneck&lt;br /&gt;
|-&lt;br /&gt;
| Capacity smoothing || 1/2 || Moving average per feedback&lt;br /&gt;
|-&lt;br /&gt;
| Capacity staleness || 16 &amp;amp;times; feedback horizon || Revert to defaults&lt;br /&gt;
|-&lt;br /&gt;
| Derived floor ceiling || 2^32 B/s || Upper clamp on derived floor&lt;br /&gt;
|-&lt;br /&gt;
| Control interval || 1 ms || Minimum spacing of rate updates&lt;br /&gt;
|-&lt;br /&gt;
| Idle credit cap || 50 ms || Burst allowance / increase banking&lt;br /&gt;
|-&lt;br /&gt;
| Feedback staleness floor || ~268 ms || Floor of rate-relative horizon&lt;br /&gt;
|-&lt;br /&gt;
| Damping bucket || ~31 ms || Rate-velocity washout cadence&lt;br /&gt;
|-&lt;br /&gt;
| Damping fraction || 1/4 || Pullback per bucket&lt;br /&gt;
|-&lt;br /&gt;
| Estimator fold spacing || 1 ms || Minimum measurement cadence&lt;br /&gt;
|-&lt;br /&gt;
| Estimator window closure || 16 packets drained || Self-scaling window&lt;br /&gt;
|-&lt;br /&gt;
| Estimator window staleness || ~134 ms || Discard across traffic gaps&lt;br /&gt;
|-&lt;br /&gt;
| Estimator idle tolerance || 1/8 of arrivals || Unsaturated-window rejection&lt;br /&gt;
|-&lt;br /&gt;
| Estimator decay || 1/16 per window || Max-filter convergence from below&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
=== Source ===&lt;br /&gt;
&lt;br /&gt;
The implementation follows the algorithm above:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/ca/mb-ecn.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mb-ecn.h&amp;lt;/code&amp;gt;: receiver estimator, pacer and rate control.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/ca/ops.h&amp;lt;/code&amp;gt;: CA policy interface.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/cap.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cap.h&amp;lt;/code&amp;gt;: link-capacity estimator and quarter-log2 codec.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/dt.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fa.c&amp;lt;/code&amp;gt;: per-hop marking and stamping, and the feedback carrying &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;cap8&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/psched.c&amp;lt;/code&amp;gt;: consumes the pacing deadline.&lt;br /&gt;
&lt;br /&gt;
=== Literature ===&lt;br /&gt;
&lt;br /&gt;
* K. K. Ramakrishnan, R. Jain, &amp;quot;A Binary Feedback Scheme for Congestion Avoidance in Computer Networks&amp;quot; (DECbit), ACM ToCS, 1990.&lt;br /&gt;
* H. Balakrishnan, H. S. Rahul, S. Seshan, &amp;quot;An Integrated Congestion Management Architecture for Internet Hosts&amp;quot;, SIGCOMM 1999; H. Balakrishnan, S. Seshan, &amp;quot;The Congestion Manager&amp;quot;, RFC 3124, 2001.&lt;br /&gt;
* M. Alizadeh et al., &amp;quot;Data Center TCP (DCTCP)&amp;quot;, SIGCOMM 2010.&lt;br /&gt;
* Y. Zhu et al., &amp;quot;Congestion Control for Large-Scale RDMA Deployments&amp;quot; (DCQCN), SIGCOMM 2015.&lt;br /&gt;
* R. Mittal et al., &amp;quot;TIMELY: RTT-based Congestion Control&amp;quot;, SIGCOMM 2015.&lt;br /&gt;
* Y. Li et al., &amp;quot;HPCC: High Precision Congestion Control&amp;quot;, SIGCOMM 2019.&lt;br /&gt;
* V. Addanki et al., &amp;quot;PowerTCP&amp;quot;, NSDI 2022.&lt;br /&gt;
* K. De Schepper, B. Briscoe (eds.), &amp;quot;Low Latency, Low Loss, and Scalable Throughput (L4S)&amp;quot;, RFC 9330/9331/9332.&lt;br /&gt;
* F. Kelly, &amp;quot;Charging and Rate Control for Elastic Traffic&amp;quot;, 1997; R. Srikant, &amp;quot;The Mathematics of Internet Congestion Control&amp;quot;, 2004.&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1941</id>
		<title>Congestion avoidance</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1941"/>
		<updated>2026-07-11T22:13:43Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:mb-ecn - Multi-bit ECN Congestion Avoidance}}&lt;br /&gt;
&lt;br /&gt;
mb-ecn is a congestion-avoidance (CA) policy for the Ouroboros unicast&lt;br /&gt;
IPCP. Reliability, ordering and flow control are FRCP&#039;s job, end-to-end&lt;br /&gt;
and per flow; congestion avoidance is a separate concern, and it runs&lt;br /&gt;
in the IPCP, once per layer. Within a layer the IPCP controls&lt;br /&gt;
&#039;&#039;&#039;aggregates&#039;&#039;&#039;: one control loop per (destination address, QoS cube),&lt;br /&gt;
shared by every flow to that destination. The other policy is&lt;br /&gt;
&amp;lt;code&amp;gt;nop&amp;lt;/code&amp;gt;, which does nothing.&lt;br /&gt;
&lt;br /&gt;
This document is about the algorithm: what it does and why. The&lt;br /&gt;
code follows it; the [[#References|reference section]] maps the pieces&lt;br /&gt;
to source files. It doesn&#039;t oversell: the [[#Limitations|limitations]]&lt;br /&gt;
section says plainly what it can&#039;t do.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;MUST&amp;quot;, &amp;quot;SHOULD&amp;quot;, &amp;quot;MAY&amp;quot; and friends mean what BCP 14 (RFC 2119,&lt;br /&gt;
RFC 8174) says, but only where they appear in capitals.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Symbol !! Units !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; || B/s || Paced send rate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt; || B/s || Bottleneck capacity&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; || bytes || Packet size&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;q&amp;lt;/code&amp;gt; || packets || Standing queue occupancy at the bottleneck&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; || packets || Marking quantum: packets per mark unit (4)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; || 8-bit code || Per-packet congestion mark set by a forwarder&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; || 1/32 &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; || Receiver-side congestion estimate: 32 &amp;amp;times; time-mean &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;cap8&amp;lt;/code&amp;gt; || 8-bit code || Path capacity, quarter-log2: &amp;lt;code&amp;gt;C &amp;amp;asymp; 2^(cap8 / 4)&amp;lt;/code&amp;gt; B/s, 0 = unknown&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt; || ns || Receiver averaging window&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; || B/s^2 || Additive-increase slope&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; units || Full-congestion reference level (512)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;Delta;t&amp;lt;/code&amp;gt; || ns || Elapsed wall-clock time between two events&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; || n/a || Path round-trip time (feedback delay); the controller does not measure it&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rate is the control variable throughout.&lt;br /&gt;
&lt;br /&gt;
Every concrete figure in this document (window bounds, horizons,&lt;br /&gt;
rate thresholds) is just an instance of the parameter values in the&lt;br /&gt;
[[#Parameters|parameters table]]. What the algorithm fixes is the&lt;br /&gt;
mechanisms and the relations between parameters; retune the parameters&lt;br /&gt;
and the figures move together.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
mb-ecn is a rate-based, ECN-driven controller with three moving parts,&lt;br /&gt;
one at each point of the loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        sender                                  forwarder&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
      |  SFQ virtual-clock pacer       |       |  ecn = MAX(ecn, queue/Q) |&lt;br /&gt;
      |  rate r, paced per packet      | data  |  (deepest queue wins)    |&lt;br /&gt;
      |  AIMD + PD control on r        |------&amp;gt;|  cap8 = MIN(cap8, link)  |&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
                 ^                                        |&lt;br /&gt;
                 | ece + cap8 feedback                    | marked data&lt;br /&gt;
                 |                                        v&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
      |  reads downstream ece,         |&amp;lt;------|  receiver                |&lt;br /&gt;
      |  updates the rate on feedback, |  ece  |  time-mean of ecn over   |&lt;br /&gt;
      |  scales floor/slope to cap8    |  cap8 |  an adaptive window      |&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# The &#039;&#039;&#039;forwarder&#039;&#039;&#039; marks each packet with a &#039;&#039;multi-bit&#039;&#039; magnitude proportional to its standing queue, and MIN-stamps its measured outgoing-link capacity into the same PCI.&lt;br /&gt;
# The &#039;&#039;&#039;receiver&#039;&#039;&#039; turns the stream of marks into a time-averaged congestion estimate &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; and feeds it back to the sender, together with the window minimum of the path-capacity codes.&lt;br /&gt;
# The &#039;&#039;&#039;sender&#039;&#039;&#039; paces its rate with a start-time fair-queuing (SFQ) virtual clock, and adjusts that rate with additive-increase / multiplicative-decrease (AIMD) driven by &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;; the rate floor and the additive slope derive from the fed-back path capacity.&lt;br /&gt;
&lt;br /&gt;
There&#039;s no timer thread: the controller runs when a packet goes out or&lt;br /&gt;
feedback comes back. Every step is scaled by elapsed wall-clock time,&lt;br /&gt;
and that is what makes the &#039;&#039;allocation&#039;&#039; independent of RTT (see&lt;br /&gt;
[[#RTT behaviour|RTT behaviour]]).&lt;br /&gt;
&lt;br /&gt;
The unit of control is the &#039;&#039;&#039;aggregate&#039;&#039;&#039;: one per&lt;br /&gt;
&amp;lt;code&amp;gt;(destination address, QoS cube)&amp;lt;/code&amp;gt;, shared by every flow to&lt;br /&gt;
that destination at that QoS. They share one controller and one rate.&lt;br /&gt;
A new flow joins at whatever rate the aggregate is already running; when&lt;br /&gt;
a flow leaves, its share goes to the others. The pacer (SFQ) splits&lt;br /&gt;
that rate fairly across the member flows, so per-flow fairness is the&lt;br /&gt;
scheduler&#039;s doing, not the rate law&#039;s.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Forwarder marking ==&lt;br /&gt;
&lt;br /&gt;
A forwarder marks a packet based on how many packets are already queued&lt;br /&gt;
on the outgoing link:&lt;br /&gt;
&lt;br /&gt;
  mark = queued / Q                         (Q = 4 packets)&lt;br /&gt;
  ecn  = MAX(ecn, mark)                     (saturating, 8-bit field)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; is fixed for the whole layer, not tuned per hop: the marks&lt;br /&gt;
get MAX-combined across hops, so every forwarder MUST use the same&lt;br /&gt;
quantum or the numbers don&#039;t compare.&lt;br /&gt;
&lt;br /&gt;
Two things worth noting:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;It&#039;s a magnitude, not a bit.&#039;&#039;&#039; The mark is an integer that tracks queue depth, in steps of &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; packets: a coarse read of the standing queue.&lt;br /&gt;
* &#039;&#039;&#039;MAX along the path.&#039;&#039;&#039; Cross several forwarders and the packet carries the largest mark, i.e. the deepest queue it passed. Taking the MAX (not a sum) keeps the number bounded and monotone across hops.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a &#039;&#039;&#039;dead zone&#039;&#039;&#039;: below &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; queued packets the mark is 0.&lt;br /&gt;
&lt;br /&gt;
=== Capacity stamping ===&lt;br /&gt;
&lt;br /&gt;
Each forwarder also measures its outgoing link&#039;s capacity and stamps it&lt;br /&gt;
into the packet. The measurement is a busy-period drain rate: look at&lt;br /&gt;
the queue at most once a millisecond, and keep a window open until 16&lt;br /&gt;
packets have drained. So the window &#039;&#039;&#039;sizes itself to the link&#039;&#039;&#039;:&lt;br /&gt;
the once-a-millisecond cadence caps it on fast links, the 16-packet&lt;br /&gt;
drain time on slow ones (~19 ms at 10 Mbit/s). If more than 1/8 of the&lt;br /&gt;
arrivals hit an empty queue, throw the window out: the link&lt;br /&gt;
wasn&#039;t saturated. The odd empty sample (a token-bucket shaper grazing&lt;br /&gt;
zero) is fine. A saturated queue drains at the link rate, so a max&lt;br /&gt;
filter that decays slowly (1/16 per window) creeps up to it from below.&lt;br /&gt;
A window that started or ended on an empty queue might have drained&lt;br /&gt;
into buffers downstream faster than the wire, so it may pull the&lt;br /&gt;
estimate down but never up. On the wire the code is quarter-log2&lt;br /&gt;
(&amp;lt;code&amp;gt;cap8 = 4 log2 C&amp;lt;/code&amp;gt;, ~19% a step, 0 = unknown), and each hop&lt;br /&gt;
MIN-combines its own into the byte, so the packet arrives carrying the&lt;br /&gt;
slowest hop&#039;s rate. A hop that has never backed up stamps nothing,&lt;br /&gt;
which is fine, since only backed-up hops matter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Receiver estimate ==&lt;br /&gt;
&lt;br /&gt;
The receiver turns the marks into a smoothed estimate &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;,&lt;br /&gt;
averaged over a window &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt; that &#039;&#039;&#039;tracks the incoming byte rate&#039;&#039;&#039;.&lt;br /&gt;
It&#039;s a plain time-average over that window (a box-car, not an EWMA).&lt;br /&gt;
Per packet, with &amp;lt;code&amp;gt;&amp;amp;Delta;t&amp;lt;/code&amp;gt; the gap since the last one:&lt;br /&gt;
&lt;br /&gt;
  A += ecn * MIN(&amp;amp;Delta;t, W)              dwell-weighted; one packet&lt;br /&gt;
                                          weighs at most one window&lt;br /&gt;
  B += S                                  accumulate bytes this window&lt;br /&gt;
  ...&lt;br /&gt;
  at window close (elapsed &amp;gt;= W, or B &amp;gt;= 2 * B_target with at&lt;br /&gt;
                   least the window floor elapsed):&lt;br /&gt;
      ece = 32 * A / elapsed              mean over the actual window&lt;br /&gt;
      W  += (B_target * elapsed / B - W) / 4    nudge toward the size&lt;br /&gt;
                                                that holds the target&lt;br /&gt;
      W   = clamp(W, ~1.05 ms, ~4.3 s)&lt;br /&gt;
      A   = 0 ; B = 0                     hard reset&lt;br /&gt;
&lt;br /&gt;
When a window closes, the next one is nudged (a quarter-weight&lt;br /&gt;
moving average) toward the size that would hold about 16 packets&lt;br /&gt;
at the current byte rate, starting from ~67 ms. The target is in bytes&lt;br /&gt;
(&amp;lt;code&amp;gt;B_target&amp;lt;/code&amp;gt; = 16,000), so &amp;quot;16 packets&amp;quot; is only exact at&lt;br /&gt;
~1000-byte packets, and it takes a few closes to settle. The mean&lt;br /&gt;
always divides by the window that &#039;&#039;&#039;actually&#039;&#039;&#039; elapsed, so a change&lt;br /&gt;
in rate leaves the current estimate correct and only resizes the&lt;br /&gt;
&#039;&#039;next&#039;&#039; one. Net effect: a roughly constant ~16 samples a window from&lt;br /&gt;
about 30 kbit/s to 122 Mbit/s. Above that the window bottoms out at&lt;br /&gt;
~1 ms (still thousands of samples a window at 10&amp;amp;ndash;100 GbE); below&lt;br /&gt;
~30 kbit/s it hits the ~4.3 s ceiling. The averaging clock stretches&lt;br /&gt;
with the flow, the way TCP&#039;s ACK clock stretches with the RTT. A&lt;br /&gt;
sudden speed-up doesn&#039;t wait out a stretched window: once twice the&lt;br /&gt;
target bytes have arrived it closes anyway (the ~1 ms floor keeps that&lt;br /&gt;
from running away at high rate). So the &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; the sender sees&lt;br /&gt;
is a staircase: one step per window, steps getting shorter as&lt;br /&gt;
the rate climbs. Two edge cases:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Onset.&#039;&#039;&#039; The first mark after an idle stretch is passed through as-is, so a queue that&#039;s just starting to build shows up without waiting out a whole window.&lt;br /&gt;
* &#039;&#039;&#039;Gap.&#039;&#039;&#039; A silence longer than 4 windows resets and sends the raw sample. &amp;quot;Long&amp;quot; is &#039;&#039;&#039;measured in the flow&#039;s own windows&#039;&#039;&#039;, so a slow flow&#039;s normal spacing never reads as idle; and the dwell clamp above limits what a real pause can add to the mean before the reset fires.&lt;br /&gt;
&lt;br /&gt;
Alongside the marks, the receiver keeps the smallest non-zero capacity&lt;br /&gt;
code it saw this window and ships it with each &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; (on an&lt;br /&gt;
onset/gap restart, just the current packet&#039;s code). Then it resets, so&lt;br /&gt;
if the path reroutes onto something faster the fed-back capacity can&lt;br /&gt;
climb within one window.&lt;br /&gt;
&lt;br /&gt;
The estimate rides back to the sender on the reverse flow.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Sender pacer ==&lt;br /&gt;
&lt;br /&gt;
The sender paces off an SFQ virtual clock. The aggregate keeps a&lt;br /&gt;
virtual time &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;; each flow keeps a finish tag &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
  V    += r * &amp;amp;Delta;t                     virtual time advances by bytes served&lt;br /&gt;
  s     = MAX(F, V)                  a packet behind the clock starts now&lt;br /&gt;
  F     = s + S                      the one after it queues behind&lt;br /&gt;
  wait  = (s - V) / r                time until this packet may go&lt;br /&gt;
&lt;br /&gt;
That &amp;lt;code&amp;gt;wait&amp;lt;/code&amp;gt; is the flow&#039;s scheduling deadline. A packet&lt;br /&gt;
that&#039;s behind the virtual clock goes out now (&amp;lt;code&amp;gt;wait = 0&amp;lt;/code&amp;gt;);&lt;br /&gt;
one that&#039;s ahead waits, and only that flow waits, nobody else.&lt;br /&gt;
Across an idle gap longer than 50 ms the clock credits at most the&lt;br /&gt;
flow&#039;s own owed lead plus one burst (50 ms of service, at least one&lt;br /&gt;
packet). So a flow paced slower than a packet per 50 ms still gets its&lt;br /&gt;
&#039;&#039;&#039;real&#039;&#039;&#039; elapsed service (capping the credit by time would&lt;br /&gt;
starve it) while an idle flow still can&#039;t bank an unbounded&lt;br /&gt;
burst.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; is shared by every flow in the aggregate; the finish&lt;br /&gt;
tags are not. Since every flow measures its start tag &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt;&lt;br /&gt;
against the same &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;, a flow that just sent is now ahead of&lt;br /&gt;
&amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; and has to wait, while an idle one (tag at or behind&lt;br /&gt;
&amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;) goes right away. That is what splits the aggregate rate&lt;br /&gt;
&amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; fairly among them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Rate control (AIMD + PD) ==&lt;br /&gt;
&lt;br /&gt;
The rate law runs on packet sends, at most once a millisecond, and&lt;br /&gt;
also when feedback arrives, so a sender with no traffic of its&lt;br /&gt;
own still reacts. The increase terms and the proportional decrease are&lt;br /&gt;
each scaled by elapsed time, but not by the same clock: the increases&lt;br /&gt;
use banked time &#039;&#039;&#039;capped&#039;&#039;&#039; at 50 ms (an idle gap shouldn&#039;t buy an&lt;br /&gt;
unbounded ramp), while the decrease uses the &#039;&#039;&#039;uncapped&#039;&#039;&#039; elapsed&lt;br /&gt;
time (a starved sender still cuts by the right amount). The one-sided&lt;br /&gt;
derivative (see the&lt;br /&gt;
[[#Multiplicative decrease (proportional + derivative)|decrease]]) is&lt;br /&gt;
not scaled by time at all; it is per-step.&lt;br /&gt;
&lt;br /&gt;
=== Slow start ===&lt;br /&gt;
&lt;br /&gt;
Before the first congestion signal, the rate ramps up exponentially&lt;br /&gt;
with a 20 ms time constant:&lt;br /&gt;
&lt;br /&gt;
  r += r * &amp;amp;Delta;t / T_ss                  (T_ss = 20 ms)&lt;br /&gt;
&lt;br /&gt;
Slow start ends the first time a congestion signal shows up, whether&lt;br /&gt;
fed back from the receiver or seen at the sender&#039;s own first hop, and&lt;br /&gt;
it never comes back.&lt;br /&gt;
&lt;br /&gt;
Slow start belongs to the aggregate, so it happens&lt;br /&gt;
&#039;&#039;&#039;once per (destination, QoS cube)&#039;&#039;&#039;, on the first flow. A flow that&lt;br /&gt;
shows up later, at an aggregate that&#039;s already running, rides the&lt;br /&gt;
existing&lt;br /&gt;
estimate: it starts at the current rate and takes its share through&lt;br /&gt;
the [[#Sender pacer|pacer]], without probing of its own.&lt;br /&gt;
&lt;br /&gt;
=== Increase: additive plus proportional probe (always on) ===&lt;br /&gt;
&lt;br /&gt;
Past slow start, two increase terms run every step, congested or not:&lt;br /&gt;
&lt;br /&gt;
  r += a * &amp;amp;Delta;t                         additive; a defaults to 2^16 B/s^2&lt;br /&gt;
  r += r * &amp;amp;Delta;t / T_probe               proportional; T_probe = 8 s&lt;br /&gt;
&lt;br /&gt;
The additive term is a fixed slope (bytes/s per second), sized to the&lt;br /&gt;
path capacity (see&lt;br /&gt;
[[#Capacity-derived floor and slope|the capacity floor]]). The&lt;br /&gt;
proportional probe adds a fixed fraction of the rate per unit time (it&lt;br /&gt;
e-folds over &amp;lt;code&amp;gt;T_probe&amp;lt;/code&amp;gt;): that is what lets a flow recover in&lt;br /&gt;
the same number of steps whatever the link rate; a fixed additive step&lt;br /&gt;
is negligible next to a fast link. Together they are the&lt;br /&gt;
upward pressure the multiplicative decrease balances at equilibrium.&lt;br /&gt;
The probe is not free: it costs a standing queue that does not shrink&lt;br /&gt;
with rate (see [[#Fairness|fairness]]).&lt;br /&gt;
&lt;br /&gt;
=== Multiplicative decrease (proportional + derivative) ===&lt;br /&gt;
&lt;br /&gt;
With congestion level &amp;lt;code&amp;gt;m&amp;lt;/code&amp;gt; (the fed-back &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;, or&lt;br /&gt;
the local first-hop mark if there&#039;s no feedback yet), the cut is a&lt;br /&gt;
proportional term plus a one-sided derivative term, a PD controller on&lt;br /&gt;
the congestion signal:&lt;br /&gt;
&lt;br /&gt;
  mark = MIN(m, M)                                   M = 512&lt;br /&gt;
  rise = MAX(m - m_prev, 0), capped at M             one-sided derivative&lt;br /&gt;
&lt;br /&gt;
  cut  = r * rise / (4 * M)                          derivative, gain 1/4&lt;br /&gt;
  cut += r * mark * &amp;amp;Delta;t_ms / (1000 * M)              proportional&lt;br /&gt;
  cut  = MIN(cut, r / 2)                             at most a halving&lt;br /&gt;
&lt;br /&gt;
  r     -= cut&lt;br /&gt;
  m_prev = m&lt;br /&gt;
&lt;br /&gt;
* The &#039;&#039;&#039;proportional&#039;&#039;&#039; term uses the real elapsed milliseconds, so a starved sender that hasn&#039;t run the controller in a while still cuts by the right amount.&lt;br /&gt;
* The &#039;&#039;&#039;derivative&#039;&#039;&#039; term (&amp;lt;code&amp;gt;rise&amp;lt;/code&amp;gt;) only fires when the congestion level &#039;&#039;goes up&#039;&#039; between samples: one-sided, per-step. It sharpens the response right as congestion starts.&lt;br /&gt;
* The cut is capped at &amp;lt;code&amp;gt;r/2&amp;lt;/code&amp;gt;, so one step can at most halve the rate.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;M = 512 = 32 &amp;amp;times; 16&amp;lt;/code&amp;gt; pins &amp;quot;full congestion&amp;quot; at a mean&lt;br /&gt;
&amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; of 16, i.e. a mean queue of &amp;lt;code&amp;gt;16 &amp;amp;times; Q = 64&amp;lt;/code&amp;gt;&lt;br /&gt;
packets.&lt;br /&gt;
&lt;br /&gt;
=== Staleness ===&lt;br /&gt;
&lt;br /&gt;
Feedback comes once per receiver window, and that window stretches with&lt;br /&gt;
the flow&#039;s byte rate (see&lt;br /&gt;
[[#Receiver estimate|the receiver estimate]]), so the staleness horizon&lt;br /&gt;
has to stretch with it too, or a slow flow&#039;s mark would expire&lt;br /&gt;
&#039;&#039;between&#039;&#039; feedbacks and the rate would ramp straight into a congested&lt;br /&gt;
path. The sender mirrors the receiver: the horizon is four target&lt;br /&gt;
windows&#039; worth of bytes at the current rate, floored at ~268 ms so fast&lt;br /&gt;
flows keep a fixed one; at the default rate floor it&#039;s ~8 s. Once a&lt;br /&gt;
signal is older than that it&#039;s dropped (both the fed-back estimate and&lt;br /&gt;
the local mark), which frees the rate to climb again after congestion&lt;br /&gt;
clears. Every step clamps the rate to &amp;lt;code&amp;gt;[r_min, r_max]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Capacity-derived floor and slope ===&lt;br /&gt;
&lt;br /&gt;
The fed-back capacity code sets the controller&#039;s two rate-scale&lt;br /&gt;
constants, per aggregate:&lt;br /&gt;
&lt;br /&gt;
  target = decode(cap8) / 32              clamped to [2^13, 2^32] B/s&lt;br /&gt;
  r_min += (target - r_min) / 2           per feedback (moving average)&lt;br /&gt;
  a      = r_min&lt;br /&gt;
&lt;br /&gt;
On a path that never reports a capacity, both fall back to fixed&lt;br /&gt;
defaults (&amp;lt;code&amp;gt;r_min = 2^13 B/s&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;a = 2^16 B/s^2&amp;lt;/code&amp;gt;).&lt;br /&gt;
When the estimator is live, the fairness floor and the post-cut&lt;br /&gt;
recovery slope scale with the bottleneck instead, and capacity kicks in&lt;br /&gt;
wherever &amp;lt;code&amp;gt;C/32&amp;lt;/code&amp;gt; beats the default floor, roughly above&lt;br /&gt;
2 Mbit/s. A capacity older than 16 staleness horizons (~4.3 s for fast&lt;br /&gt;
flows) reverts both to the defaults. That horizon is deliberately&lt;br /&gt;
longer than the feedback horizon: feedback stops the instant the marks&lt;br /&gt;
clear, which is exactly when you need the recovery slope, and the&lt;br /&gt;
onset-fresh capacity (see&lt;br /&gt;
[[#Receiver estimate|the receiver estimate]]) re-seeds it on the first&lt;br /&gt;
mark of the next episode anyway.&lt;br /&gt;
&lt;br /&gt;
=== Ramp and recovery ===&lt;br /&gt;
&lt;br /&gt;
Three mechanisms set how fast an aggregate reaches a fast link&#039;s&lt;br /&gt;
rate, each owning one regime:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;From scratch: slow start.&#039;&#039;&#039; The exponential ramp hits any real link rate in well under a second (~200 ms from the seed to 10 Gbit/s; every further doubling of link speed adds one ~14 ms doubling). Nothing marks on an uncongested path, so slow start just runs until the aggregate reaches its bottleneck.&lt;br /&gt;
* &#039;&#039;&#039;After a cut: the probe.&#039;&#039;&#039; A cut is at most a halving, and the mark expires on the rate-relative horizon once congestion clears (see [[#Staleness|staleness]]); the probe then climbs back from a halving in &amp;lt;code&amp;gt;T_probe &amp;amp;middot; ln 2 &amp;amp;asymp; 5.5 s&amp;lt;/code&amp;gt;, whatever the link rate.&lt;br /&gt;
* &#039;&#039;&#039;At the bottom: the capacity floor.&#039;&#039;&#039; The rate never sits more than 32&amp;amp;times; below a measured bottleneck, and the additive slope refills &amp;lt;code&amp;gt;C/32&amp;lt;/code&amp;gt; a second, so the worst hole is bounded: floor to full capacity in tens of seconds, a halving in seconds.&lt;br /&gt;
&lt;br /&gt;
These fit together because a deep cut needs sustained congestion,&lt;br /&gt;
sustained congestion backs up the bottleneck queue, and a backed-up&lt;br /&gt;
queue is exactly when its capacity gets measured and stamped (see&lt;br /&gt;
[[#Forwarder marking|forwarder marking]]). So whenever the controller&lt;br /&gt;
has been cut deep, the scaled floor is already live. The default floor&lt;br /&gt;
only ever applies to paths that never congested the sender in the first&lt;br /&gt;
place, where there&#039;s nothing to recover from. That&#039;s why slow start can&lt;br /&gt;
quit for good: the floor and the probe handle every recovery after it.&lt;br /&gt;
&lt;br /&gt;
=== Regimes ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   +-------------+   first mark seen    +----------------+&lt;br /&gt;
   | slow start  |---------------------&amp;gt;|  congestion    |&lt;br /&gt;
   | r *= e^(t/T)|                      |  avoidance     |&lt;br /&gt;
   +-------------+                      |                |&lt;br /&gt;
                                        | m == 0 ---&amp;gt; additive increase&lt;br /&gt;
                                        | m  &amp;gt; 0 ---&amp;gt; AI + PD decrease&lt;br /&gt;
                                        +----------------+&lt;br /&gt;
    m = fed-back ece, or the local first-hop mark when ece == 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== RTT behaviour ==&lt;br /&gt;
&lt;br /&gt;
Because every increase and decrease is scaled by wall-clock time, two&lt;br /&gt;
flows with different RTTs sharing a bottleneck follow the &#039;&#039;same&#039;&#039; rate&lt;br /&gt;
law and settle at the &#039;&#039;same&#039;&#039; rate. So the steady-state&lt;br /&gt;
&#039;&#039;&#039;allocation is RTT-independent&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
That is a property of the equilibrium, not the dynamics. The feedback&lt;br /&gt;
delay in the loop &#039;&#039;is&#039;&#039; the path RTT &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;, and the&lt;br /&gt;
controller neither measures nor compensates for it. At low rate the&lt;br /&gt;
receiver window (up to its ~4.3 s ceiling) is the biggest lag: the loop&lt;br /&gt;
is slow there in proportion to how slow the flow is, just like a&lt;br /&gt;
long-RTT TCP. As the rate climbs the window shrinks toward its ~1 ms&lt;br /&gt;
floor, so its share of the loop delay &#039;&#039;falls&#039;&#039; with capacity and the&lt;br /&gt;
path RTT &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; becomes the irreducible part. Two things keep&lt;br /&gt;
the loop damped: the adaptive window takes out the delay-dominated&lt;br /&gt;
corner at low &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;, and a bucketed&lt;br /&gt;
&#039;&#039;&#039;rate-velocity damping&#039;&#039;&#039; (the washout: each ~31 ms bucket pulls the&lt;br /&gt;
rate a quarter of the way back toward its value at the previous bucket)&lt;br /&gt;
supplies the damping this otherwise near-double-integrator loop lacks,&lt;br /&gt;
which is what holds it together at high capacity and moderate&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;. What is left is the loop gain&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;prop; &amp;amp;radic;C&amp;lt;/code&amp;gt;: at very high capacity &#039;&#039;and&#039;&#039;&lt;br /&gt;
large &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; the &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt; phase&lt;br /&gt;
budget is the binding constraint, and no amount of windowing or damping&lt;br /&gt;
removes it (see [[#Limitations|Limitations]]). So, precisely:&lt;br /&gt;
&#039;&#039;&#039;RTT-fair in equilibrium, and dynamically stable up to a&#039;&#039;&#039;&lt;br /&gt;
&#039;&#039;&#039;capacity-dependent RTT limit set by &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Fairness ==&lt;br /&gt;
&lt;br /&gt;
At equilibrium the two increase terms balance the proportional&lt;br /&gt;
decrease, &amp;lt;code&amp;gt;a + r/T_probe = r &amp;amp;middot; m*/M&amp;lt;/code&amp;gt;, which pins the&lt;br /&gt;
congestion level at &amp;lt;code&amp;gt;m* = M &amp;amp;middot; (a/r + 1/T_probe)&amp;lt;/code&amp;gt;. Since&lt;br /&gt;
&amp;lt;code&amp;gt;ece = 8q&amp;lt;/code&amp;gt; (one mark unit per &amp;lt;code&amp;gt;Q = 4&amp;lt;/code&amp;gt; packets, 32&lt;br /&gt;
fixed-point steps per unit), that is a standing queue of&lt;br /&gt;
&lt;br /&gt;
  q* = 64 * a / r  +  64 / T_probe        (packets, r in bytes/s)&lt;br /&gt;
&lt;br /&gt;
The additive part (&amp;lt;code&amp;gt;64 &amp;amp;middot; a / r&amp;lt;/code&amp;gt;) fades to nothing with&lt;br /&gt;
rate under the default slope; with the capacity-derived slope&lt;br /&gt;
(&amp;lt;code&amp;gt;a = C/32&amp;lt;/code&amp;gt;; see&lt;br /&gt;
[[#Capacity-derived floor and slope|the capacity floor]]) it settles at&lt;br /&gt;
&amp;lt;code&amp;gt;2 C / r&amp;lt;/code&amp;gt;, about two packets per competing flow, whatever the&lt;br /&gt;
rate. The probe part (&amp;lt;code&amp;gt;64 / T_probe&amp;lt;/code&amp;gt;) is a floor that does&lt;br /&gt;
not move with rate (~8 packets at 8 s). That floor is what&lt;br /&gt;
rate-independent convergence costs you, and it is what keeps&lt;br /&gt;
&amp;lt;code&amp;gt;q*&amp;lt;/code&amp;gt; above the marking threshold at high rate.&lt;br /&gt;
&lt;br /&gt;
Two flows at the same bottleneck see the same &amp;lt;code&amp;gt;m*&amp;lt;/code&amp;gt;, so each&lt;br /&gt;
solves &amp;lt;code&amp;gt;r_i = a / (m*/M - 1/T_probe)&amp;lt;/code&amp;gt; to the same rate. In&lt;br /&gt;
network-utility-maximization terms that is &#039;&#039;&#039;proportional fairness&#039;&#039;&#039;&lt;br /&gt;
with equal weights, which at a single bottleneck is just max-min fair.&lt;br /&gt;
&lt;br /&gt;
Getting a good measurement at low rate is the estimator&#039;s job, not the&lt;br /&gt;
floor&#039;s: the averaging window stretches with the flow (see&lt;br /&gt;
[[#Receiver estimate|the receiver estimate]]), so a CA-limited flow&lt;br /&gt;
still keeps ~16 packets a window down to ~30 kbit/s, and the&lt;br /&gt;
rate-relative gap threshold keeps its normal spacing from reading as&lt;br /&gt;
idle. The default floor (&amp;lt;code&amp;gt;2^13 B/s&amp;lt;/code&amp;gt;) only bounds the extremes&lt;br /&gt;
(window ceiling, staleness horizon, pacer arithmetic). The floor itself&lt;br /&gt;
scales with the fed-back path capacity (&amp;lt;code&amp;gt;r_min = C/32&amp;lt;/code&amp;gt;; see&lt;br /&gt;
[[#Capacity-derived floor and slope|the capacity floor]]), so about 32&lt;br /&gt;
CA-limited flows fit above it on any class of link. Flows whose&lt;br /&gt;
bottlenecks are in different classes get different slopes, so fairness&lt;br /&gt;
between them is capacity-weighted, not equal; see&lt;br /&gt;
[[#Limitations|Limitations]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Limitations ==&lt;br /&gt;
&lt;br /&gt;
Things the algorithm &#039;&#039;&#039;doesn&#039;t&#039;&#039;&#039; do. Listed here so nobody credits it&lt;br /&gt;
with more than it manages.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Queue-only signal, no rate term.&#039;&#039;&#039; &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; is a queue level, so mb-ecn is a standing-queue controller: it has to build a queue (&amp;lt;code&amp;gt;q* = 64 &amp;amp;middot; a / r + 64 / T_probe&amp;lt;/code&amp;gt;) to get any signal at all, and can&#039;t sit at full utilisation on an empty queue the way a controller with a rate estimate can. The probe floor (~8 packets) keeps &amp;lt;code&amp;gt;q*&amp;lt;/code&amp;gt; above the marking threshold at every rate, but you pay for it with a permanent standing queue.&lt;br /&gt;
* &#039;&#039;&#039;Aggregation is per destination, per QoS cube.&#039;&#039;&#039; The aggregate is a stand-in for the path: flows to the same destination are assumed to share a bottleneck, and flows to different destinations run separate loops even when they pile up at the same hop. Each loop still converges to its own fair share (see [[#Fairness|fairness]]), so the cost is duplicated state and probing at that shared queue, not unfairness. One day we could tag the queue in the packet: if a forwarder stamped a queue id next to its mark, senders could key their aggregates on the real bottleneck instead of the destination, and pool the estimate and the probing across everything sharing it.&lt;br /&gt;
* &#039;&#039;&#039;Capacity is learned, and there are gaps.&#039;&#039;&#039; The floor and slope ride on an in-band path-capacity estimate (see [[#Forwarder marking|forwarder marking]] and [[#Capacity-derived floor and slope|the capacity floor]]), and that estimate has its own limits. A hop only learns its rate while its queue is backed up, so a rerouted or brand-new bottleneck reads &#039;&#039;unknown&#039;&#039; at first and the path MIN can briefly come from faster hops (the sender&#039;s smoothing and the next feedback bound the overshoot). The measurement is order-of-magnitude on purpose: quarter-log2 code (~19% a step), burst noise held down by a 16-packet minimum window and a max filter, and nothing learned below ~120 packets a second of busy drain (16 packets inside the ~134 ms staleness cap; at MTU that&#039;s ~1.4 Mbit/s, below which capacity/32 sits under the default floor anyway). The derived floor clamps to &amp;lt;code&amp;gt;[2^13, 2^32]&amp;lt;/code&amp;gt; B/s, and flows with bottlenecks in different classes get capacity-weighted, not equal, shares at a common queue (see [[#Fairness|fairness]]).&lt;br /&gt;
* &#039;&#039;&#039;The low end trades speed, not correctness.&#039;&#039;&#039; Below ~30 kbit/s the averaging window hits its ~4.3 s ceiling and holds fewer samples; below ~64 kbit/s of offered load an &#039;&#039;app-limited&#039;&#039; flow spaces its packets past the gap threshold and rides the raw onset branch, as it always did. At the floor, feedback and the staleness horizon are both measured in seconds, so a slow flow converges slowly, TCP-style. Nothing blows up; nothing&#039;s quick either.&lt;br /&gt;
* &#039;&#039;&#039;No loss response.&#039;&#039;&#039; The controller only reacts to ECN marks. Loss is FRCP&#039;s problem, handled by retransmission, out of the CA&#039;s sight. At a bottleneck that drops instead of marking (say a plain drop-tail queue at the Ethernet or UDP shim), mb-ecn sees nothing and additive increase just keeps ramping. It&#039;s built for Ouroboros-native bottlenecks where the forwarder marks; there&#039;s no classic-bottleneck fallback.&lt;br /&gt;
* &#039;&#039;&#039;Dynamic stability has a capacity-dependent RTT limit.&#039;&#039;&#039; As covered under [[#RTT behaviour|RTT behaviour]], the allocation is RTT-independent but the dynamics aren&#039;t: the binding constraint is &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;prop; &amp;amp;radic;C&amp;lt;/code&amp;gt;. The adaptive window and the washout push that limit out a long way (a single flow at 10 GbE stays stable to ~50 ms RTT in the fluid model), but the far corner (100 GbE at &amp;amp;ge; 50 ms) is out of reach for a window/damping controller and would need explicit-rate signalling.&lt;br /&gt;
&lt;br /&gt;
== Architectural fit ==&lt;br /&gt;
&lt;br /&gt;
Each of these properties falls out of something structural in the&lt;br /&gt;
recursive architecture. Here&#039;s the mapping.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;CA is fully separate from ARQ and flow control.&#039;&#039;&#039; Three concerns, three mechanisms, two scopes: FRCP does retransmission (ARQ) and flow control (the peer pacing the sender), end-to-end and per flow; CA runs in the IPCP, per aggregate. Each signal says one thing. A loss just means retransmit: a lossy link reads as lossy, and the congestion call is left to the marks (the [[#Limitations|limitations]] section&#039;s &amp;quot;no loss response&amp;quot; is this same separation from the other side). The peer&#039;s flow-control window just paces the endpoint: back-pressure from a slow receiver stays separate from congestion in the network. A retransmitted packet is ordinary traffic as far as the pacer cares. TCP jams all three into one window machine, where the receive window caps the congestion window and loss is both the reliability trigger and the congestion signal; here you can reason about, test, and change each one on its own.&lt;br /&gt;
* &#039;&#039;&#039;CA sits below what the application picks.&#039;&#039;&#039; Every flow in the layer runs under the same rate law, whatever its QoS: a greedy raw sender shares a bottleneck fairly with a reliable stream because the control belongs to the layer, on the aggregate, not to the endpoint transport&#039;s good manners.&lt;br /&gt;
* &#039;&#039;&#039;A layer owns its PCI, so the signal can be rich.&#039;&#039;&#039; Everyone in a layer enrolled into it, so the layer is one administrative domain by construction, at whatever scope it covers, and the header is the layer&#039;s own. That&#039;s why forwarders can write a multi-bit queue magnitude and a capacity byte straight into the packet. The datacenter schemes in [[#Heritage and positioning|heritage and positioning]] need exactly that kind of domain and only get it inside one operator&#039;s fabric; a recursive layer has it everywhere.&lt;br /&gt;
* &#039;&#039;&#039;Layer-internal addressing makes the aggregate well-defined.&#039;&#039;&#039; Flows in a layer run between layer addresses, so the (destination, QoS cube) aggregate, which is RFC 3124&#039;s macroflow, drops straight out of the naming. Controller state scales with the number of destinations, and each QoS cube keeps its own loop, so service classes don&#039;t share a fate.&lt;br /&gt;
* &#039;&#039;&#039;Flows are allocated, so feedback already has a channel.&#039;&#039;&#039; Every flow has state at both ends and a reverse direction, so the receiver&#039;s &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; and the path capacity just ride the flow allocator&#039;s existing exchange.&lt;br /&gt;
* &#039;&#039;&#039;There&#039;s no transport ack clock, and nothing leans on one.&#039;&#039;&#039; The layer has to handle raw flows, which carry no acknowledgements at all, so the controller paces on wall-clock time, and that constraint is exactly what buys the RTT-independent allocation under [[#RTT behaviour|RTT behaviour]]. A restriction turned into the feature.&lt;br /&gt;
* &#039;&#039;&#039;Recursion scales it.&#039;&#039;&#039; Each layer handles congestion over its own scope and timescale; many N-flows ride one N-1 flow, so aggregation compounds down the stack. The capacity a layer sees at its egress queue is whatever the layer below is actually giving it (a paced, shared lower flow, not some nominal wire speed), so the signal means something at every level and back-pressure cascades down layer by layer.&lt;br /&gt;
&lt;br /&gt;
There&#039;s a boundary: at a shim over legacy media the layer below neither&lt;br /&gt;
enrolls nor marks, which is the deployment edge behind the&lt;br /&gt;
[[#Limitations|limitations]] section&#039;s no-loss-response point.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Heritage and positioning ==&lt;br /&gt;
&lt;br /&gt;
mb-ecn is basically a rate-based ECN controller in the DECbit / QCN&lt;br /&gt;
line, adapted to a recursive layer with no acknowledgements.&lt;br /&gt;
&lt;br /&gt;
The core goes back to the first Ouroboros CA policy (2020): a multi-bit&lt;br /&gt;
queue-depth mark relayed by forwarders to the receiver, a smoothed&lt;br /&gt;
multi-bit &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; fed back on the reverse flow at the same 1/32&lt;br /&gt;
fixed point used today, and a sender that paced packets from a time&lt;br /&gt;
budget and ran slow start, additive increase and multiplicative&lt;br /&gt;
decrease on wall-clock time slots. Rate-based and acknowledgement-free&lt;br /&gt;
from the start, with RTT-independent allocation already the goal. The&lt;br /&gt;
present algorithm keeps that skeleton and reworks every estimator&lt;br /&gt;
around it: per-path aggregation with SFQ pacing, the rate-adaptive&lt;br /&gt;
box-car receiver, elapsed-time-scaled AIMD with a derivative term and&lt;br /&gt;
rate-velocity damping, and the in-band capacity signal.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;DECbit&#039;&#039;&#039; (Ramakrishnan &amp;amp;amp; Jain, 1988; ToCS 1990) established binary congestion feedback set by the switch on queue occupancy. mb-ecn keeps the switch-sets-on-queue idea and carries a &#039;&#039;magnitude&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;The Congestion Manager&#039;&#039;&#039; (Balakrishnan et al., SIGCOMM 1999; RFC 3124, 2001) aggregates congestion state at the end host: one controller per macroflow, a scheduler apportioning its rate across the member streams, and new streams joining at the ensemble&#039;s current state instead of probing from scratch. mb-ecn&#039;s (destination, QoS cube) aggregate is the same idea placed inside the IPCP: every layer manages its own macroflows, recursively, the SFQ pacer plays the CM scheduler&#039;s role (see [[#Sender pacer|the pacer]]), and a joining flow rides the aggregate&#039;s estimates (see [[#Slow start|slow start]]). The CM gathers its signal from transport feedback at the edge; mb-ecn reads it off the wire from the forwarders.&lt;br /&gt;
* &#039;&#039;&#039;DCTCP&#039;&#039;&#039; (Alizadeh et al., SIGCOMM 2010) recovers a queue magnitude at the endpoint by averaging a single threshold bit. mb-ecn instead reads the magnitude off the wire, then time-averages it at the receiver. The additive-increase / proportional-decrease structure is shared.&lt;br /&gt;
* &#039;&#039;&#039;QCN&#039;&#039;&#039; (IEEE 802.1Qau, 2010) and &#039;&#039;&#039;DCQCN&#039;&#039;&#039; (Zhu et al., SIGCOMM 2015) contribute the rate-based (not window-based) ECN control skeleton. mb-ecn&#039;s proportional decrease plus a probing increase mirrors that skeleton; the increase here is wall-clock scaled rather than byte-counter/timer based.&lt;br /&gt;
* &#039;&#039;&#039;HPCC&#039;&#039;&#039; (Li et al., SIGCOMM 2019) and &#039;&#039;&#039;PowerTCP&#039;&#039;&#039; (Addanki et al., NSDI 2022) combine a queue term &#039;&#039;and&#039;&#039; a rate term (via in-band telemetry or delay). mb-ecn now carries a coarse in-band rate (the quarter-log2 path MIN; see [[#Forwarder marking|forwarder marking]]) but uses it only to scale the controller&#039;s gains; the congestion &#039;&#039;signal&#039;&#039; remains queue-only, which is still the main functional difference (see [[#Limitations|Limitations]]).&lt;br /&gt;
* &#039;&#039;&#039;L4S / TCP Prague&#039;&#039;&#039; (RFC 9330&amp;amp;ndash;9332, 2023) make RTT-independence a requirement and use a high-frequency single-bit signal. mb-ecn shares the RTT-independent &#039;&#039;allocation&#039;&#039; goal but reaches it by wall-clock-scaled rate control, with a low-frequency multi-bit signal.&lt;br /&gt;
&lt;br /&gt;
What&#039;s new is the pairing: a multi-bit magnitude on the wire with a&lt;br /&gt;
wall-clock-scaled rate pacer, in a setting with no per-flow ack clock.&lt;br /&gt;
The trade-off, spelled out above, is a queue-only signal and a&lt;br /&gt;
reactivity capped by the averaging window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Parameters ==&lt;br /&gt;
&lt;br /&gt;
These are the values behind every figure in this document. They&#039;re&lt;br /&gt;
&#039;&#039;&#039;coupled&#039;&#039;&#039;: the mechanisms only fit together while a few relations&lt;br /&gt;
hold. The gap-restart horizon at the rate floor has to exceed a&lt;br /&gt;
packet&#039;s service time (or a floor-rate flow loops at onset); a&lt;br /&gt;
floor-rate flow&#039;s target window has to fit under the window ceiling;&lt;br /&gt;
the control interval has to sit under the damping bucket, which sits&lt;br /&gt;
under the idle credit cap; the rate floor mustn&#039;t exceed the slow-start&lt;br /&gt;
seed; and the derived-floor ceiling has to stay under the rate ceiling.&lt;br /&gt;
Retune them as a set, against those constraints.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| Marking quantum &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; || 4 packets || Mark onset and step; layer-wide&lt;br /&gt;
|-&lt;br /&gt;
| Feedback resolution || 1/32 &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; unit || Fixed-point LSB of &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Full-congestion reference &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; || 512 || Mean queue of 64 packets&lt;br /&gt;
|-&lt;br /&gt;
| Receiver window target || 16 packets (16,000 bytes) || Samples per window&lt;br /&gt;
|-&lt;br /&gt;
| Initial receiver window || ~67 ms || Before rate is known&lt;br /&gt;
|-&lt;br /&gt;
| Receiver window bounds || ~1.05 ms &amp;amp;ndash; ~4.3 s || Clamp on &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Window resize weight || 1/4 || Moving average toward target&lt;br /&gt;
|-&lt;br /&gt;
| Early window close || 2 &amp;amp;times; target bytes || Speed-up escape&lt;br /&gt;
|-&lt;br /&gt;
| Gap restart || 4 windows || Idle horizon (rate-relative)&lt;br /&gt;
|-&lt;br /&gt;
| Additive slope &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; (default) || 2^16 B/s^2 || Lower clamp of derived slope&lt;br /&gt;
|-&lt;br /&gt;
| Probe time constant &amp;lt;code&amp;gt;T_probe&amp;lt;/code&amp;gt; || 8 s || Rate-proportional recovery&lt;br /&gt;
|-&lt;br /&gt;
| Derivative gain || 1/4 || Onset sharpening&lt;br /&gt;
|-&lt;br /&gt;
| Rate floor &amp;lt;code&amp;gt;r_min&amp;lt;/code&amp;gt; (default) || 2^13 B/s || Lower clamp of derived floor&lt;br /&gt;
|-&lt;br /&gt;
| Slow-start seed rate || 2^16 B/s || Initial rate&lt;br /&gt;
|-&lt;br /&gt;
| Slow-start time constant || 20 ms || Exponential ramp&lt;br /&gt;
|-&lt;br /&gt;
| Rate ceiling &amp;lt;code&amp;gt;r_max&amp;lt;/code&amp;gt; || 2^37 B/s || Upper clamp&lt;br /&gt;
|-&lt;br /&gt;
| Capacity scale || &amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt; / 32 || Floor and slope from bottleneck&lt;br /&gt;
|-&lt;br /&gt;
| Capacity smoothing || 1/2 || Moving average per feedback&lt;br /&gt;
|-&lt;br /&gt;
| Capacity staleness || 16 &amp;amp;times; feedback horizon || Revert to defaults&lt;br /&gt;
|-&lt;br /&gt;
| Derived floor ceiling || 2^32 B/s || Upper clamp on derived floor&lt;br /&gt;
|-&lt;br /&gt;
| Control interval || 1 ms || Minimum spacing of rate updates&lt;br /&gt;
|-&lt;br /&gt;
| Idle credit cap || 50 ms || Burst allowance / increase banking&lt;br /&gt;
|-&lt;br /&gt;
| Feedback staleness floor || ~268 ms || Floor of rate-relative horizon&lt;br /&gt;
|-&lt;br /&gt;
| Damping bucket || ~31 ms || Rate-velocity washout cadence&lt;br /&gt;
|-&lt;br /&gt;
| Damping fraction || 1/4 || Pullback per bucket&lt;br /&gt;
|-&lt;br /&gt;
| Estimator fold spacing || 1 ms || Minimum measurement cadence&lt;br /&gt;
|-&lt;br /&gt;
| Estimator window closure || 16 packets drained || Self-scaling window&lt;br /&gt;
|-&lt;br /&gt;
| Estimator window staleness || ~134 ms || Discard across traffic gaps&lt;br /&gt;
|-&lt;br /&gt;
| Estimator idle tolerance || 1/8 of arrivals || Unsaturated-window rejection&lt;br /&gt;
|-&lt;br /&gt;
| Estimator decay || 1/16 per window || Max-filter convergence from below&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&lt;br /&gt;
=== Source ===&lt;br /&gt;
&lt;br /&gt;
The implementation follows the algorithm above:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/ca/mb-ecn.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mb-ecn.h&amp;lt;/code&amp;gt;: receiver estimator, pacer and rate control.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/ca/ops.h&amp;lt;/code&amp;gt;: CA policy interface.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/cap.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cap.h&amp;lt;/code&amp;gt;: link-capacity estimator and quarter-log2 codec.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/dt.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fa.c&amp;lt;/code&amp;gt;: per-hop marking and stamping, and the feedback carrying &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;cap8&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/psched.c&amp;lt;/code&amp;gt;: consumes the pacing deadline.&lt;br /&gt;
&lt;br /&gt;
=== Literature ===&lt;br /&gt;
&lt;br /&gt;
* K. K. Ramakrishnan, R. Jain, &amp;quot;A Binary Feedback Scheme for Congestion Avoidance in Computer Networks&amp;quot; (DECbit), ACM ToCS, 1990.&lt;br /&gt;
* H. Balakrishnan, H. S. Rahul, S. Seshan, &amp;quot;An Integrated Congestion Management Architecture for Internet Hosts&amp;quot;, SIGCOMM 1999; H. Balakrishnan, S. Seshan, &amp;quot;The Congestion Manager&amp;quot;, RFC 3124, 2001.&lt;br /&gt;
* M. Alizadeh et al., &amp;quot;Data Center TCP (DCTCP)&amp;quot;, SIGCOMM 2010.&lt;br /&gt;
* Y. Zhu et al., &amp;quot;Congestion Control for Large-Scale RDMA Deployments&amp;quot; (DCQCN), SIGCOMM 2015.&lt;br /&gt;
* R. Mittal et al., &amp;quot;TIMELY: RTT-based Congestion Control&amp;quot;, SIGCOMM 2015.&lt;br /&gt;
* Y. Li et al., &amp;quot;HPCC: High Precision Congestion Control&amp;quot;, SIGCOMM 2019.&lt;br /&gt;
* V. Addanki et al., &amp;quot;PowerTCP&amp;quot;, NSDI 2022.&lt;br /&gt;
* K. De Schepper, B. Briscoe (eds.), &amp;quot;Low Latency, Low Loss, and Scalable Throughput (L4S)&amp;quot;, RFC 9330/9331/9332.&lt;br /&gt;
* F. Kelly, &amp;quot;Charging and Rate Control for Elastic Traffic&amp;quot;, 1997; R. Srikant, &amp;quot;The Mathematics of Internet Congestion Control&amp;quot;, 2004.&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1940</id>
		<title>Congestion avoidance</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1940"/>
		<updated>2026-07-11T21:25:12Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:mb-ecn - Multi-bit ECN Congestion Avoidance}}&lt;br /&gt;
&lt;br /&gt;
mb-ecn is a congestion-avoidance (CA) policy for the Ouroboros unicast&lt;br /&gt;
IPCP. It lives in the IPC Process: the flow-and-retransmission task&lt;br /&gt;
(FRCP) provides reliability, in-order delivery and flow control&lt;br /&gt;
end-to-end; congestion avoidance is orthogonal and runs per IPCP&lt;br /&gt;
layer. Within a layer, the IPCP controls &#039;&#039;&#039;aggregates&#039;&#039;&#039;, one loop&lt;br /&gt;
per (destination address, QoS cube), shared by every flow toward that&lt;br /&gt;
destination. The alternative policy is &amp;lt;code&amp;gt;nop&amp;lt;/code&amp;gt; (no congestion&lt;br /&gt;
avoidance).&lt;br /&gt;
&lt;br /&gt;
This document describes the algorithm: what the policy does and why.&lt;br /&gt;
The implementation follows it ([[#12. References|Section 12]] maps the&lt;br /&gt;
pieces to source files). It does not claim properties the algorithm&lt;br /&gt;
does not have; [[#8. Limitations|Section 8]] states the known gaps&lt;br /&gt;
explicitly.&lt;br /&gt;
&lt;br /&gt;
The keywords &amp;quot;MUST&amp;quot;, &amp;quot;SHOULD&amp;quot;, &amp;quot;MAY&amp;quot; etc. are used only where they&lt;br /&gt;
appear in all capitals, per BCP 14 (RFC 2119, RFC 8174).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Symbol !! Units !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; || B/s || Paced send rate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt; || B/s || Bottleneck capacity&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt; || bytes || Packet size&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;q&amp;lt;/code&amp;gt; || packets || Standing queue occupancy at the bottleneck&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; || packets || Marking quantum: packets per mark unit (4)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; || 8-bit code || Per-packet congestion mark set by a forwarder&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; || 32 &amp;amp;times; ecn || Receiver-side congestion estimate (time-mean of &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;cap8&amp;lt;/code&amp;gt; || 8-bit code || Path capacity, quarter-log2: &amp;lt;code&amp;gt;C &amp;amp;asymp; 2^(cap8 / 4)&amp;lt;/code&amp;gt; B/s, 0 = unknown&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt; || ns || Receiver averaging window&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; || B/s^2 || Additive-increase slope&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; || Full-congestion reference level (512)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;Delta;t&amp;lt;/code&amp;gt; || ns || Elapsed wall-clock time between two events&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; || &amp;amp;mdash; || Path round-trip time (feedback delay); the controller does not measure it&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rate is the control variable throughout.&lt;br /&gt;
&lt;br /&gt;
All concrete figures in this document &amp;amp;mdash; window bounds, horizons,&lt;br /&gt;
rate thresholds &amp;amp;mdash; are instances of the parameter values in&lt;br /&gt;
[[#11. Parameters|Section 11]]. The mechanisms and the relations&lt;br /&gt;
between parameters are what the algorithm fixes; retuning the&lt;br /&gt;
parameters moves the figures together.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
mb-ecn is a rate-based, ECN-driven controller with three moving parts,&lt;br /&gt;
one at each point of the loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        sender                                  forwarder&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
      |  SFQ virtual-clock pacer       |       |  ecn = MAX(ecn, queue/Q) |&lt;br /&gt;
      |  rate r, paced per packet      | data  |  (deepest queue wins)    |&lt;br /&gt;
      |  AIMD + PD control on r        |------&amp;gt;|  cap8 = MIN(cap8, link)  |&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
                 ^                                        |&lt;br /&gt;
                 | ece + cap8 feedback                    | marked data&lt;br /&gt;
                 |                                        v&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
      |  reads downstream ece,         |&amp;lt;------|  receiver                |&lt;br /&gt;
      |  updates the rate on feedback, |  ece  |  time-mean of ecn over   |&lt;br /&gt;
      |  scales floor/slope to cap8    |  cap8 |  an adaptive window      |&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# The &#039;&#039;&#039;forwarder&#039;&#039;&#039; marks each packet with a &#039;&#039;multi-bit&#039;&#039; magnitude&lt;br /&gt;
  proportional to its standing queue, and MIN-stamps its measured&lt;br /&gt;
  outgoing-link capacity into the same PCI.&lt;br /&gt;
# The &#039;&#039;&#039;receiver&#039;&#039;&#039; turns the stream of marks into a time-averaged&lt;br /&gt;
  congestion estimate &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; and feeds it back to the sender,&lt;br /&gt;
  together with the window minimum of the path-capacity codes.&lt;br /&gt;
# The &#039;&#039;&#039;sender&#039;&#039;&#039; paces its rate with a start-time fair-queuing (SFQ)&lt;br /&gt;
  virtual clock, and adjusts that rate with additive-increase /&lt;br /&gt;
  multiplicative-decrease (AIMD) driven by &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;; the rate floor&lt;br /&gt;
  and the additive slope derive from the fed-back path capacity.&lt;br /&gt;
&lt;br /&gt;
The controller runs on packet sends and on feedback arrivals. All&lt;br /&gt;
increase/decrease steps are scaled by elapsed&lt;br /&gt;
wall-clock time, which is what makes the &#039;&#039;allocation&#039;&#039; independent of&lt;br /&gt;
RTT (see [[#6. RTT behaviour|Section 6]]).&lt;br /&gt;
&lt;br /&gt;
The unit of control is the &#039;&#039;&#039;aggregate&#039;&#039;&#039;, kept in the IPCP per&lt;br /&gt;
&amp;lt;code&amp;gt;(destination address, QoS cube)&amp;lt;/code&amp;gt;. Every flow toward that&lt;br /&gt;
destination at that QoS shares one controller and one rate: a new&lt;br /&gt;
flow joins the aggregate at its current rate, and a departing flow&lt;br /&gt;
leaves the rate to the others. The pacer divides the aggregate rate&lt;br /&gt;
fairly across the member flows (SFQ), so per-flow fairness within an&lt;br /&gt;
aggregate is a scheduler property, distinct from the rate law&lt;br /&gt;
described here.&lt;br /&gt;
&lt;br /&gt;
== Forwarder marking ==&lt;br /&gt;
&lt;br /&gt;
A forwarder marks a packet as a function of the packets already queued&lt;br /&gt;
on the outgoing link:&lt;br /&gt;
&lt;br /&gt;
  mark = queued / Q                         (Q = 4 packets)&lt;br /&gt;
  ecn  = MAX(ecn, mark)                     (saturating, 8-bit field)&lt;br /&gt;
&lt;br /&gt;
The quantum &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; is a layer-wide constant: the marks it&lt;br /&gt;
quantizes are combined by MAX across hops, so all forwarders in a&lt;br /&gt;
layer MUST use the same quantum for the signal to be comparable.&lt;br /&gt;
&lt;br /&gt;
Two properties:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-bit magnitude.&#039;&#039;&#039; The mark is an integer proportional to&lt;br /&gt;
  queue depth, quantized in steps of &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; packets: a coarse&lt;br /&gt;
  readout of the standing queue.&lt;br /&gt;
* &#039;&#039;&#039;MAX along the path.&#039;&#039;&#039; A packet crossing several forwarders carries&lt;br /&gt;
  the maximum mark, i.e. the deepest queue on its path. MAX keeps the&lt;br /&gt;
  signal range-stable and monotone across hops.&lt;br /&gt;
&lt;br /&gt;
There is a &#039;&#039;&#039;dead zone&#039;&#039;&#039;: for &amp;lt;code&amp;gt;queued &amp;lt; Q&amp;lt;/code&amp;gt; the mark is 0.&lt;br /&gt;
&lt;br /&gt;
=== Capacity stamping ===&lt;br /&gt;
&lt;br /&gt;
Each forwarder also measures the capacity of its outgoing link and&lt;br /&gt;
stamps it into the packet. The estimate is a busy-period drain rate:&lt;br /&gt;
the queue is observed at most once per millisecond, and a measurement&lt;br /&gt;
window stays open until 16 packets have drained, so its length&lt;br /&gt;
&#039;&#039;&#039;self-scales with the link rate&#039;&#039;&#039; &amp;amp;mdash; the millisecond cadence&lt;br /&gt;
bounds it on fast links, the 16-packet drain time on slow ones&lt;br /&gt;
(~19 ms at 10 Mbit/s). A window in which more than 1/8 of&lt;br /&gt;
the arrivals found the queue empty is discarded as unsaturated;&lt;br /&gt;
occasional empty observations (token-bucket shapers grazing zero) are&lt;br /&gt;
tolerated. The drain rate of a saturated queue is the link rate; a max&lt;br /&gt;
filter with slow decay (1/16 per window) converges on it from below. A&lt;br /&gt;
window that opened or closed on an empty queue may have drained into&lt;br /&gt;
buffers below at above-wire rate, so it may lower the estimate but&lt;br /&gt;
never raise it. The code on the wire is quarter-log2&lt;br /&gt;
(&amp;lt;code&amp;gt;cap8 = 4 log2 C&amp;lt;/code&amp;gt;, ~19% per step, 0 = unknown) and each hop&lt;br /&gt;
MIN-combines its own code into the byte, so a packet arrives carrying&lt;br /&gt;
the bottleneck&#039;s rate. A hop that has never been backlogged stamps&lt;br /&gt;
nothing; since only backlogged hops matter, the signal exists exactly&lt;br /&gt;
when it is needed.&lt;br /&gt;
&lt;br /&gt;
== 3. Receiver estimate ==&lt;br /&gt;
&lt;br /&gt;
The receiver converts marks into a smoothed estimate &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; over a&lt;br /&gt;
time window &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt; that &#039;&#039;&#039;adapts to the incoming byte rate&#039;&#039;&#039;. It is&lt;br /&gt;
a &#039;&#039;&#039;boxcar (rectangular) time-integral mean&#039;&#039;&#039;. Per packet, with&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;Delta;t&amp;lt;/code&amp;gt; the gap since the previous packet:&lt;br /&gt;
&lt;br /&gt;
  A += ecn * MIN(&amp;amp;Delta;t, W)              dwell-weighted; one packet&lt;br /&gt;
                                          weighs at most one window&lt;br /&gt;
  B += S                                  accumulate bytes this window&lt;br /&gt;
  ...&lt;br /&gt;
  at window close (elapsed &amp;gt;= W, or B &amp;gt;= 2 * B_target with at&lt;br /&gt;
                   least the window floor elapsed):&lt;br /&gt;
      ece = 32 * A / elapsed              mean over the actual window&lt;br /&gt;
      W  += (B_target * elapsed / B - W) / 4    nudge toward the size&lt;br /&gt;
                                                that holds the target&lt;br /&gt;
      W   = clamp(W, ~1.05 ms, ~4.3 s)&lt;br /&gt;
      A   = 0 ; B = 0                     hard reset&lt;br /&gt;
&lt;br /&gt;
At each close the next window is nudged by a quarter-weight moving&lt;br /&gt;
average toward the size that would hold about 16 packets at the&lt;br /&gt;
measured byte rate, from an initial ~67 ms. The target is byte-based (&amp;lt;code&amp;gt;B_target&amp;lt;/code&amp;gt; = 16,000 bytes), so&lt;br /&gt;
&amp;quot;16 packets&amp;quot; is exact only at ~1000-byte packets, and the window&lt;br /&gt;
converges to it over several closes. The mean always divides by the&lt;br /&gt;
&#039;&#039;&#039;actual&#039;&#039;&#039; elapsed window, so a rate step leaves the current&lt;br /&gt;
estimate exact and only re-sizes the &#039;&#039;next&#039;&#039; window. The effect is a roughly constant sample&lt;br /&gt;
count &amp;amp;mdash; ~16 packets &amp;amp;mdash; from about 30 kbit/s to 122 Mbit/s;&lt;br /&gt;
above that the window floors at ~1 ms (a cadence still carrying&lt;br /&gt;
thousands of samples at 10&amp;amp;ndash;100 GbE), and below ~30 kbit/s it&lt;br /&gt;
saturates at the ~4.3 s ceiling. The averaging clock stretches with&lt;br /&gt;
the flow the way TCP&#039;s ACK clock stretches with the RTT. A speed-up&lt;br /&gt;
escapes a stretched window early: once twice the target bytes arrive&lt;br /&gt;
the window closes anyway (the window floor keeps that cadence bounded&lt;br /&gt;
at high rate). The fed-back &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; is thus a piecewise-constant&lt;br /&gt;
staircase whose step period tracks the rate. Two edge cases:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Onset&#039;&#039;&#039; (first mark after an idle estimate) emits the&lt;br /&gt;
  instantaneous mark undiluted, so a starting queue is reported without&lt;br /&gt;
  a full window of delay.&lt;br /&gt;
* &#039;&#039;&#039;Gap&#039;&#039;&#039; longer than 4 windows resets the window and emits the raw&lt;br /&gt;
  sample. The threshold is &#039;&#039;&#039;rate-relative&#039;&#039;&#039;: idle means a few&lt;br /&gt;
  &#039;&#039;current&#039;&#039; windows of silence, so a slow flow&#039;s normal inter-packet&lt;br /&gt;
  gap never reads as idle, while the in-window dwell clamp above bounds&lt;br /&gt;
  what a genuine pause can add to the mean before the restart fires.&lt;br /&gt;
&lt;br /&gt;
Alongside the mark integral, the receiver keeps the MIN of the nonzero&lt;br /&gt;
capacity codes seen in the current window; each emitted &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
carries that minimum (the raw packet&#039;s code on onset/gap restarts) and&lt;br /&gt;
the fold then resets, so a reroute to a faster path can raise the&lt;br /&gt;
fed-back capacity within one window.&lt;br /&gt;
&lt;br /&gt;
The estimate is fed back to the sender on the reverse flow.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 4. Sender pacer ==&lt;br /&gt;
&lt;br /&gt;
The sender paces with an SFQ virtual clock. The context keeps a&lt;br /&gt;
virtual time &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;; each flow keeps a finish tag &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
  V    += r * &amp;amp;Delta;t                     virtual time advances by bytes served&lt;br /&gt;
  s     = MAX(F, V)                  a packet behind the clock starts now&lt;br /&gt;
  F     = s + S                      the one after it queues behind&lt;br /&gt;
  wait  = (s - V) / r                time until this packet may go&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;wait&amp;lt;/code&amp;gt; becomes the flow&#039;s scheduling deadline: a packet&lt;br /&gt;
already behind the virtual clock is sent immediately&lt;br /&gt;
(&amp;lt;code&amp;gt;wait = 0&amp;lt;/code&amp;gt;); one ahead waits, deferring only its own flow.&lt;br /&gt;
A clock advance across an idle gap longer than 50 ms credits at most&lt;br /&gt;
the calling flow&#039;s owed lead plus one burst (50 ms of service, at&lt;br /&gt;
least one packet): a paced flow slower than one packet per 50 ms&lt;br /&gt;
receives its &#039;&#039;&#039;true&#039;&#039;&#039; elapsed service &amp;amp;mdash; a time-capped credit&lt;br /&gt;
would decay such a flow without bound &amp;amp;mdash; while an idle flow still&lt;br /&gt;
cannot bank an unbounded burst.&lt;br /&gt;
&lt;br /&gt;
The virtual time &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; is shared by every flow in the context;&lt;br /&gt;
the finish tags are per flow. Because every flow computes its start&lt;br /&gt;
tag &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; against the same &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;, a flow that has just sent&lt;br /&gt;
carries a finish tag ahead of &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; and waits, while an idle flow&lt;br /&gt;
(finish tag at or behind &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;) starts immediately &amp;amp;mdash; this is&lt;br /&gt;
what fairly divides the aggregate rate &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; across them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 5. Rate control (AIMD + PD) ==&lt;br /&gt;
&lt;br /&gt;
The rate law runs on packet sends, at most once per millisecond, and&lt;br /&gt;
also on feedback arrivals so that a sender with no traffic of its own&lt;br /&gt;
still reacts. Each increase term and the proportional decrease are&lt;br /&gt;
scaled by elapsed wall-clock time, each by its own clock: the&lt;br /&gt;
increase terms use time banked and &#039;&#039;&#039;capped&#039;&#039;&#039; at 50 ms (an idle gap&lt;br /&gt;
cannot bank an unbounded ramp), while the proportional decrease uses&lt;br /&gt;
the &#039;&#039;&#039;uncapped&#039;&#039;&#039; elapsed time (a starved sender still cuts by the&lt;br /&gt;
right amount). The one-sided derivative (Section 5.3) is a per-delta&lt;br /&gt;
term.&lt;br /&gt;
&lt;br /&gt;
=== 5.1. Slow start ===&lt;br /&gt;
&lt;br /&gt;
Before the first congestion signal the rate ramps exponentially with a&lt;br /&gt;
20 ms time constant:&lt;br /&gt;
&lt;br /&gt;
  r += r * &amp;amp;Delta;t / T_ss                  (T_ss = 20 ms)&lt;br /&gt;
&lt;br /&gt;
Slow start ends the first time a congestion signal arrives &amp;amp;mdash;&lt;br /&gt;
fed back from the receiver or observed at the sender&#039;s own first hop&lt;br /&gt;
&amp;amp;mdash; and never resumes.&lt;br /&gt;
&lt;br /&gt;
Slow start belongs to the aggregate, so it runs &#039;&#039;&#039;once per&lt;br /&gt;
(destination, QoS cube)&#039;&#039;&#039;, on the first flow. A flow arriving at an&lt;br /&gt;
established aggregate rides the existing estimates: it enters at the&lt;br /&gt;
aggregate&#039;s current rate and receives its fair share of it through&lt;br /&gt;
the pacer (Section 4), with no probing of its own.&lt;br /&gt;
&lt;br /&gt;
=== 5.2. Increase: additive plus proportional probe (always on) ===&lt;br /&gt;
&lt;br /&gt;
Past slow start, two increase terms are applied every step, both&lt;br /&gt;
regardless of congestion:&lt;br /&gt;
&lt;br /&gt;
  r += a * &amp;amp;Delta;t                         additive; a defaults to 2^16 B/s^2&lt;br /&gt;
  r += r * &amp;amp;Delta;t / T_probe               proportional; T_probe = 8 s&lt;br /&gt;
&lt;br /&gt;
The additive term is a fixed per-context slope (bytes/s per second),&lt;br /&gt;
scaled to the path capacity (Section 5.5). The proportional probe&lt;br /&gt;
grows the rate by a fixed fraction per unit time (e-folds over&lt;br /&gt;
&amp;lt;code&amp;gt;T_probe&amp;lt;/code&amp;gt;): a flow recovers in the same number of steps at any&lt;br /&gt;
link rate; the fixed additive term&#039;s effect vanishes relative to a&lt;br /&gt;
fast link. Both are the &amp;quot;probing&amp;quot;&lt;br /&gt;
pressure the multiplicative decrease balances at equilibrium; the&lt;br /&gt;
probe&#039;s cost is a rate-independent standing-queue floor (Section 7).&lt;br /&gt;
&lt;br /&gt;
=== 5.3. Multiplicative decrease (proportional + derivative) ===&lt;br /&gt;
&lt;br /&gt;
With congestion level &amp;lt;code&amp;gt;m&amp;lt;/code&amp;gt; (the fed-back &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;, or a local&lt;br /&gt;
first-hop mark as fallback), the decrease is the sum of a proportional&lt;br /&gt;
term and a one-sided derivative term &amp;amp;mdash; a PD controller on the&lt;br /&gt;
congestion signal:&lt;br /&gt;
&lt;br /&gt;
  mark = MIN(m, M)                                   M = 512&lt;br /&gt;
  rise = MAX(m - m_prev, 0), capped at M             one-sided derivative&lt;br /&gt;
&lt;br /&gt;
  cut  = r * rise / (4 * M)                          derivative, gain 1/4&lt;br /&gt;
  cut += r * mark * &amp;amp;Delta;t_ms / (1000 * M)              proportional&lt;br /&gt;
  cut  = MIN(cut, r / 2)                             at most a halving&lt;br /&gt;
&lt;br /&gt;
  r     -= cut&lt;br /&gt;
  m_prev = m&lt;br /&gt;
&lt;br /&gt;
* The &#039;&#039;&#039;proportional&#039;&#039;&#039; term is scaled by honest elapsed time in&lt;br /&gt;
  milliseconds, so a starved sender that has not run the controller&lt;br /&gt;
  for a while still cuts by the right amount.&lt;br /&gt;
* The &#039;&#039;&#039;derivative&#039;&#039;&#039; term (&amp;lt;code&amp;gt;rise&amp;lt;/code&amp;gt;) reacts to an &#039;&#039;increase&#039;&#039; in&lt;br /&gt;
  the congestion level between samples; it is one-sided and per-delta.&lt;br /&gt;
  It sharpens the reaction at the onset of congestion.&lt;br /&gt;
* The cut is clamped to &amp;lt;code&amp;gt;r/2&amp;lt;/code&amp;gt; per step, bounding the decrease to&lt;br /&gt;
  a halving.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;M = 512 = 32 &amp;amp;times; 16&amp;lt;/code&amp;gt; defines &amp;quot;full congestion&amp;quot; as a mean&lt;br /&gt;
&amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; of 16, i.e. a mean queue of &amp;lt;code&amp;gt;16 &amp;amp;times; Q = 64&amp;lt;/code&amp;gt; packets.&lt;br /&gt;
&lt;br /&gt;
=== 5.4. Staleness ===&lt;br /&gt;
&lt;br /&gt;
Feedback arrives once per receiver window, and the window stretches&lt;br /&gt;
with the flow&#039;s byte rate (Section 3) &amp;amp;mdash; so the staleness horizon&lt;br /&gt;
must stretch with it, or a slow flow&#039;s mark would age out &#039;&#039;between&#039;&#039;&lt;br /&gt;
feedbacks and the rate would ramp against a congested path. The&lt;br /&gt;
sender mirrors the receiver: the horizon is four target windows of&lt;br /&gt;
bytes at the current rate, floored at ~268 ms so fast flows keep a&lt;br /&gt;
fixed horizon; at the default rate floor it is ~8 s. A signal older&lt;br /&gt;
than the horizon is aged out (both the fed-back estimate and the&lt;br /&gt;
local mark), freeing the rate to ramp once congestion clears. The&lt;br /&gt;
rate is clamped to&lt;br /&gt;
&amp;lt;code&amp;gt;[r_min, r_max]&amp;lt;/code&amp;gt; on every step.&lt;br /&gt;
&lt;br /&gt;
=== 5.5. Capacity-derived floor and slope ===&lt;br /&gt;
&lt;br /&gt;
The fed-back capacity code sets the two rate-scale constants of the&lt;br /&gt;
controller, per context:&lt;br /&gt;
&lt;br /&gt;
  target = decode(cap8) / 32              clamped to [2^13, 2^32] B/s&lt;br /&gt;
  r_min += (target - r_min) / 2           per feedback (moving average)&lt;br /&gt;
  a      = r_min&lt;br /&gt;
&lt;br /&gt;
The defaults are fixed constants (&amp;lt;code&amp;gt;r_min = 2^13 B/s&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;a = 2^16 B/s^2&amp;lt;/code&amp;gt;), used on paths that never report a capacity;&lt;br /&gt;
with the estimator live the fairness floor and the post-cut recovery&lt;br /&gt;
slope scale with the bottleneck, and capacity engages wherever&lt;br /&gt;
&amp;lt;code&amp;gt;C/32&amp;lt;/code&amp;gt; clears the default floor, i.e. above ~2 Mbit/s. A&lt;br /&gt;
capacity older than 16 staleness horizons (~4.3 s for fast flows)&lt;br /&gt;
reverts both to the defaults. That horizon deliberately outlives the&lt;br /&gt;
feedback horizon: feedback stops the moment the marks clear, which is&lt;br /&gt;
exactly when the recovery slope is needed; the onset-fresh capacity&lt;br /&gt;
(Section 3) re-seeds it on the first mark of the next episode either&lt;br /&gt;
way.&lt;br /&gt;
&lt;br /&gt;
=== 5.6. Ramp and recovery ===&lt;br /&gt;
&lt;br /&gt;
Three mechanisms set how fast an aggregate reaches a fast link&#039;s&lt;br /&gt;
rate, each owning one regime:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;From scratch: slow start.&#039;&#039;&#039; The exponential ramp reaches any&lt;br /&gt;
  physical link rate in well under a second (~200 ms from the seed to&lt;br /&gt;
  10 Gbit/s; each further doubling of link speed costs one more&lt;br /&gt;
  ~14 ms doubling time). On an uncongested path nothing marks, so&lt;br /&gt;
  slow start runs until the aggregate arrives at its bottleneck.&lt;br /&gt;
* &#039;&#039;&#039;After a cut: the probe.&#039;&#039;&#039; A decrease is at most a halving per&lt;br /&gt;
  step, and the mark ages out on the rate-relative horizon once&lt;br /&gt;
  congestion clears (Section 5.4); the proportional probe then heals&lt;br /&gt;
  a halving in &amp;lt;code&amp;gt;T_probe &amp;amp;middot; ln 2 &amp;amp;asymp; 5.5 s&amp;lt;/code&amp;gt; at any&lt;br /&gt;
  link rate.&lt;br /&gt;
* &#039;&#039;&#039;At the bottom: the capacity floor.&#039;&#039;&#039; The rate sits at most a&lt;br /&gt;
  factor 32 below a measured bottleneck, and the additive slope&lt;br /&gt;
  refills &amp;lt;code&amp;gt;C/32&amp;lt;/code&amp;gt; per second, so the deepest hole is bounded:&lt;br /&gt;
  floor to full capacity in tens of seconds, a halving in seconds.&lt;br /&gt;
&lt;br /&gt;
These compose because a deep cut requires sustained congestion,&lt;br /&gt;
sustained congestion backlogs the bottleneck queue, and a backlogged&lt;br /&gt;
queue is exactly when its capacity is measured and stamped&lt;br /&gt;
(Section 2): whenever the controller is cut deep, the scaled floor is&lt;br /&gt;
live. The default floor only governs paths that never congested the&lt;br /&gt;
sender &amp;amp;mdash; where there is nothing to recover from. This is why&lt;br /&gt;
slow start can end for good: the floor and the probe cover every&lt;br /&gt;
later recovery.&lt;br /&gt;
&lt;br /&gt;
=== 5.7. Regimes ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   +-------------+   first mark seen    +----------------+&lt;br /&gt;
   | slow start  |---------------------&amp;gt;|  congestion    |&lt;br /&gt;
   | r *= e^(t/T)|                      |  avoidance     |&lt;br /&gt;
   +-------------+                      |                |&lt;br /&gt;
                                        | m == 0 -&amp;gt; AI   |&lt;br /&gt;
                                        | m  &amp;gt; 0 -&amp;gt; AIPD |&lt;br /&gt;
                                        +----------------+&lt;br /&gt;
    m = fed-back ece, or the local first-hop mark when ece == 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 6. RTT behaviour ==&lt;br /&gt;
&lt;br /&gt;
Because every increase and decrease is scaled by wall-clock time,&lt;br /&gt;
two flows of different RTT that share a bottleneck obey the &#039;&#039;same&#039;&#039;&lt;br /&gt;
rate law and converge to the &#039;&#039;same&#039;&#039; rate. The steady-state&lt;br /&gt;
&#039;&#039;&#039;allocation is RTT-independent&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
This is a property of the equilibrium, not of the dynamics. The&lt;br /&gt;
feedback delay in the loop &#039;&#039;is&#039;&#039; the path RTT &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;, and the&lt;br /&gt;
controller does not measure or compensate for it. At low rate the&lt;br /&gt;
receiver window (up to its ~4.3 s ceiling) is the dominant lag &amp;amp;mdash;&lt;br /&gt;
the loop is slow there in proportion to how slow the flow is, exactly&lt;br /&gt;
as a long-RTT TCP is &amp;amp;mdash; while as the rate rises the window shrinks&lt;br /&gt;
toward its ~1 ms floor, so the window&#039;s contribution to the loop delay&lt;br /&gt;
&#039;&#039;falls&#039;&#039; with capacity and the path RTT &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; becomes the&lt;br /&gt;
irreducible term. Two mechanisms keep the loop damped: the adaptive&lt;br /&gt;
window removes the delay-dominated corner at low &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;, and a&lt;br /&gt;
bucketed &#039;&#039;&#039;rate-velocity damping&#039;&#039;&#039; (a washout: each ~31 ms bucket&lt;br /&gt;
pulls the rate a quarter of the way back toward its value at the&lt;br /&gt;
previous bucket) supplies the damping the otherwise&lt;br /&gt;
near-double-integrator lacks, which is what holds the loop together at&lt;br /&gt;
high capacity and moderate &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;. What remains is the loop gain&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;prop; &amp;amp;radic;C&amp;lt;/code&amp;gt;: at very high capacity &#039;&#039;and&#039;&#039; large&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; the &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt; phase budget is the&lt;br /&gt;
binding constraint, and no window or damping trick removes it (see&lt;br /&gt;
[[#8. Limitations|Section 8]]). A precise statement is therefore:&lt;br /&gt;
&#039;&#039;&#039;RTT-fair in equilibrium, and dynamically stable up to a&lt;br /&gt;
capacity-dependent RTT limit set by &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 7. Fairness ==&lt;br /&gt;
&lt;br /&gt;
At equilibrium the two increase terms balance the proportional&lt;br /&gt;
decrease, &amp;lt;code&amp;gt;a + r/T_probe = r &amp;amp;middot; m*/M&amp;lt;/code&amp;gt;, which pins the&lt;br /&gt;
congestion level to &amp;lt;code&amp;gt;m* = M &amp;amp;middot; (a/r + 1/T_probe)&amp;lt;/code&amp;gt; and, since&lt;br /&gt;
&amp;lt;code&amp;gt;ece = 8q&amp;lt;/code&amp;gt; (one mark unit per &amp;lt;code&amp;gt;Q = 4&amp;lt;/code&amp;gt; packets, 32 fixed-point&lt;br /&gt;
steps per unit), a standing queue of&lt;br /&gt;
&lt;br /&gt;
  q* = 64 * a / r  +  64 / T_probe        (packets, r in bytes/s)&lt;br /&gt;
&lt;br /&gt;
The additive part (&amp;lt;code&amp;gt;64 &amp;amp;middot; a / r&amp;lt;/code&amp;gt;) vanishes with rate under&lt;br /&gt;
the default slope; with the capacity-derived slope (&amp;lt;code&amp;gt;a = C/32&amp;lt;/code&amp;gt;,&lt;br /&gt;
Section 5.5) it becomes &amp;lt;code&amp;gt;2 C / r&amp;lt;/code&amp;gt; &amp;amp;mdash; about two packets per&lt;br /&gt;
competing flow, rate-independent. The proportional-probe part&lt;br /&gt;
(&amp;lt;code&amp;gt;64 / T_probe&amp;lt;/code&amp;gt;) is a rate-independent floor (~8 packets at&lt;br /&gt;
8 s). That floor is the cost of rate-independent convergence, and it&lt;br /&gt;
keeps &amp;lt;code&amp;gt;q*&amp;lt;/code&amp;gt; above the marking threshold at high rate.&lt;br /&gt;
&lt;br /&gt;
Two flows at the same bottleneck see the same &amp;lt;code&amp;gt;m*&amp;lt;/code&amp;gt;, so each&lt;br /&gt;
solves &amp;lt;code&amp;gt;r_i = a / (m*/M - 1/T_probe)&amp;lt;/code&amp;gt; to the same rate. Under&lt;br /&gt;
the network-utility-maximization view this corresponds to&lt;br /&gt;
&#039;&#039;&#039;proportional fairness&#039;&#039;&#039; (equal weights), which is max-min fair at&lt;br /&gt;
a single bottleneck.&lt;br /&gt;
&lt;br /&gt;
Measurement fidelity at low rate comes from the estimator itself:&lt;br /&gt;
the averaging window stretches with the flow&lt;br /&gt;
(Section 3), so a CA-limited flow keeps ~16 packets per window at any&lt;br /&gt;
rate down to ~30 kbit/s, and the rate-relative gap threshold keeps its&lt;br /&gt;
inter-packet spacing from reading as idle. The default floor&lt;br /&gt;
(&amp;lt;code&amp;gt;2^13&amp;lt;/code&amp;gt; B/s) only bounds the extremes (window ceiling,&lt;br /&gt;
staleness horizon, pacer arithmetic). The floor scales with the&lt;br /&gt;
fed-back path capacity (&amp;lt;code&amp;gt;r_min = C/32&amp;lt;/code&amp;gt;, Section 5.5), so&lt;br /&gt;
roughly 32 CA-limited flows fit above it at any link class. Flows&lt;br /&gt;
whose paths differ in bottleneck class derive different slopes, so&lt;br /&gt;
fairness across them is capacity-weighted; see&lt;br /&gt;
[[#8. Limitations|Section 8]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 9. Architectural fit ==&lt;br /&gt;
&lt;br /&gt;
Each property the preceding sections build toward attaches to a&lt;br /&gt;
structural feature of the recursive architecture. This section makes&lt;br /&gt;
the mapping explicit.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Congestion avoidance is fully orthogonal to ARQ and to flow&lt;br /&gt;
  control.&#039;&#039;&#039; Three concerns, three mechanisms, two scopes: FRCP&lt;br /&gt;
  provides retransmission (ARQ) and flow control (the peer pacing the&lt;br /&gt;
  sender) end-to-end, per flow; congestion avoidance runs in the&lt;br /&gt;
  IPCP, per aggregate. Each signal means one thing. A loss triggers&lt;br /&gt;
  a retransmission and nothing else &amp;amp;mdash; a lossy link reads as&lt;br /&gt;
  lossy, with the congestion verdict left to the marks (Section 8&#039;s&lt;br /&gt;
  &amp;quot;no loss response&amp;quot; is this orthogonality stated from the other&lt;br /&gt;
  side). The peer&#039;s flow-control window paces the endpoint and&lt;br /&gt;
  nothing else &amp;amp;mdash; back-pressure from a slow receiver stays&lt;br /&gt;
  distinct from congestion in the network. A retransmitted packet is&lt;br /&gt;
  ordinary traffic to the pacer. TCP folds all three into one window&lt;br /&gt;
  machine, where the receive window bounds the congestion window and&lt;br /&gt;
  loss serves as both reliability trigger and congestion signal;&lt;br /&gt;
  here each mechanism can be reasoned about, tested and evolved&lt;br /&gt;
  alone.&lt;br /&gt;
* &#039;&#039;&#039;Congestion avoidance sits below application choice.&#039;&#039;&#039; Every&lt;br /&gt;
  flow in the layer is paced by the same rate law, whatever its QoS:&lt;br /&gt;
  a greedy raw sender shares a bottleneck fairly with a reliable&lt;br /&gt;
  stream because the control is a property of the layer, on the&lt;br /&gt;
  aggregate, rather than a courtesy of the endpoint transport.&lt;br /&gt;
* &#039;&#039;&#039;A layer defines its own PCI, so the signal can be rich.&#039;&#039;&#039;&lt;br /&gt;
  Every member of a layer is enrolled into it: the layer is a single&lt;br /&gt;
  administrative domain by construction, at whatever scope it spans,&lt;br /&gt;
  and its header is layer-internal. Forwarders therefore write a&lt;br /&gt;
  multi-bit queue magnitude and a capacity byte directly into the&lt;br /&gt;
  packet. The datacenter schemes of&lt;br /&gt;
  [[#10. Heritage and positioning|Section 10]] require exactly such a&lt;br /&gt;
  domain and find it only inside one operator&#039;s fabric; a recursive&lt;br /&gt;
  layer supplies it everywhere.&lt;br /&gt;
* &#039;&#039;&#039;Layer-internal addressing makes the aggregate well-defined.&#039;&#039;&#039;&lt;br /&gt;
  Flows in a layer run between layer addresses, so the&lt;br /&gt;
  (destination, QoS cube) aggregate &amp;amp;mdash; RFC 3124&#039;s macroflow&lt;br /&gt;
  &amp;amp;mdash; falls out of the naming structure. Controller state scales&lt;br /&gt;
  with destinations, and each QoS cube keeps its own loop, so&lt;br /&gt;
  service classes keep separate fates.&lt;br /&gt;
* &#039;&#039;&#039;Flows are allocated, so feedback has a channel.&#039;&#039;&#039; Every flow&lt;br /&gt;
  has state at both ends and a reverse direction; the receiver&#039;s&lt;br /&gt;
  &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; and the path capacity ride the flow allocator&#039;s&lt;br /&gt;
  existing exchange.&lt;br /&gt;
* &#039;&#039;&#039;There is no transport ack clock, and none is relied on.&#039;&#039;&#039; The&lt;br /&gt;
  layer must control raw flows, which carry no acknowledgements at&lt;br /&gt;
  all, so the controller paces on wall-clock time &amp;amp;mdash; and that&lt;br /&gt;
  constraint yields the RTT-independent allocation of Section 6. An&lt;br /&gt;
  architectural restriction becomes the controller&#039;s distinguishing&lt;br /&gt;
  property.&lt;br /&gt;
* &#039;&#039;&#039;Recursion scales the scheme.&#039;&#039;&#039; Each layer controls congestion&lt;br /&gt;
  over its own scope and timescale; many N-flows ride one N-1 flow,&lt;br /&gt;
  so aggregation compounds down the stack. The capacity a layer&lt;br /&gt;
  measures at its egress queue is the rate the layer below actually&lt;br /&gt;
  provides &amp;amp;mdash; a paced, shared lower flow rather than a nominal&lt;br /&gt;
  wire speed &amp;amp;mdash; so the signal stays meaningful at every level,&lt;br /&gt;
  and pushback cascades layer by layer.&lt;br /&gt;
&lt;br /&gt;
The fit has a boundary: at a shim over legacy media the layer below&lt;br /&gt;
neither enrolls nor marks, which is the deployment edge behind&lt;br /&gt;
Section 8&#039;s no-loss-response limitation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 10. Heritage and positioning ==&lt;br /&gt;
&lt;br /&gt;
mb-ecn is best understood as a rate-based ECN controller in the DECbit /&lt;br /&gt;
QCN line, adapted to an acknowledgement-less recursive layer.&lt;br /&gt;
&lt;br /&gt;
The core of the scheme dates to the first Ouroboros&lt;br /&gt;
congestion-avoidance policy (2020): a multi-bit queue-depth mark&lt;br /&gt;
relayed by forwarders to the receiver, a smoothed multi-bit&lt;br /&gt;
&amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; fed back on the reverse flow at the same 1/32 fixed&lt;br /&gt;
point used today, and a sender that paced packets from a time budget&lt;br /&gt;
and ran slow start, additive increase and multiplicative decrease on&lt;br /&gt;
wall-clock time slots &amp;amp;mdash; rate-based and acknowledgement-free from&lt;br /&gt;
the start, with RTT-independent allocation already an objective. The&lt;br /&gt;
present algorithm keeps that skeleton and reworks every estimator&lt;br /&gt;
around it: per-path aggregation with SFQ pacing, the rate-adaptive&lt;br /&gt;
boxcar receiver, elapsed-time-scaled AIMD with a derivative term and&lt;br /&gt;
rate-velocity damping, and the in-band capacity signal.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;DECbit&#039;&#039;&#039; (Ramakrishnan &amp;amp;amp; Jain, 1988; ToCS 1990) established&lt;br /&gt;
  binary congestion feedback set by the switch on queue occupancy.&lt;br /&gt;
  mb-ecn keeps the switch-sets-on-queue idea and carries a&lt;br /&gt;
  &#039;&#039;magnitude&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;The Congestion Manager&#039;&#039;&#039; (Balakrishnan et al., SIGCOMM 1999;&lt;br /&gt;
  RFC 3124, 2001) aggregates congestion state at the end host: one&lt;br /&gt;
  controller per macroflow, a scheduler apportioning its rate across&lt;br /&gt;
  the member streams, and new streams joining at the ensemble&#039;s&lt;br /&gt;
  current state instead of probing from scratch. mb-ecn&#039;s&lt;br /&gt;
  (destination, QoS cube) aggregate is the same idea placed inside&lt;br /&gt;
  the IPCP: every layer manages its own macroflows, recursively, the&lt;br /&gt;
  SFQ pacer plays the CM scheduler&#039;s role (Section 4), and a joining&lt;br /&gt;
  flow rides the aggregate&#039;s estimates (Section 5.1). The CM gathers&lt;br /&gt;
  its signal from transport feedback at the edge; mb-ecn reads it&lt;br /&gt;
  off the wire from the forwarders.&lt;br /&gt;
* &#039;&#039;&#039;DCTCP&#039;&#039;&#039; (Alizadeh et al., SIGCOMM 2010) recovers a queue&lt;br /&gt;
  magnitude at the endpoint by averaging a single threshold bit. mb-ecn&lt;br /&gt;
  instead reads the magnitude off the wire, then time-averages it at&lt;br /&gt;
  the receiver. The additive-increase / proportional-decrease structure&lt;br /&gt;
  is shared.&lt;br /&gt;
* &#039;&#039;&#039;QCN&#039;&#039;&#039; (IEEE 802.1Qau, 2010) and &#039;&#039;&#039;DCQCN&#039;&#039;&#039; (Zhu et al., SIGCOMM&lt;br /&gt;
  2015) contribute the rate-based (not window-based) ECN control&lt;br /&gt;
  skeleton. mb-ecn&#039;s proportional decrease plus a probing increase&lt;br /&gt;
  mirrors that skeleton; the increase here is wall-clock scaled rather&lt;br /&gt;
  than byte-counter/timer based.&lt;br /&gt;
* &#039;&#039;&#039;HPCC&#039;&#039;&#039; (Li et al., SIGCOMM 2019) and &#039;&#039;&#039;PowerTCP&#039;&#039;&#039; (Addanki et&lt;br /&gt;
  al., NSDI 2022) combine a queue term &#039;&#039;and&#039;&#039; a rate term (via in-band&lt;br /&gt;
  telemetry or delay). mb-ecn now carries a coarse in-band rate (the&lt;br /&gt;
  quarter-log2 path MIN, Section 2) but uses it only to scale the&lt;br /&gt;
  controller&#039;s gains; the congestion &#039;&#039;signal&#039;&#039; remains queue-only,&lt;br /&gt;
  which is still the main functional difference (Section 8).&lt;br /&gt;
* &#039;&#039;&#039;L4S / TCP Prague&#039;&#039;&#039; (RFC 9330&amp;amp;ndash;9332, 2023) make&lt;br /&gt;
  RTT-independence a requirement and use a high-frequency single-bit&lt;br /&gt;
  signal. mb-ecn shares the RTT-independent &#039;&#039;allocation&#039;&#039; goal but&lt;br /&gt;
  reaches it by wall-clock-scaled rate control, with a low-frequency&lt;br /&gt;
  multi-bit signal.&lt;br /&gt;
&lt;br /&gt;
The novel combination is the multi-bit magnitude on the wire fused with&lt;br /&gt;
a wall-clock-scaled rate pacer in a setting with no per-flow ack clock.&lt;br /&gt;
The trade-off, made explicit above, is a queue-only signal and a&lt;br /&gt;
reactivity capped by the averaging window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 11. Parameters ==&lt;br /&gt;
&lt;br /&gt;
These are the values every figure in this document instantiates. They&lt;br /&gt;
are &#039;&#039;&#039;coupled&#039;&#039;&#039;: the mechanisms compose only while certain relations&lt;br /&gt;
hold &amp;amp;mdash; the gap-restart horizon at the rate floor must exceed a&lt;br /&gt;
packet&#039;s service time (or a floor-rate flow restart-loops at onset),&lt;br /&gt;
a floor-rate flow&#039;s target window must fit under the window ceiling,&lt;br /&gt;
the control interval must sit under the damping bucket under the idle&lt;br /&gt;
credit cap, the rate floor must not exceed the slow-start seed, and&lt;br /&gt;
the derived-floor ceiling must stay under the rate ceiling. Retune&lt;br /&gt;
them as a set, against those invariants.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| Marking quantum &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; || 4 packets || Mark onset and step; layer-wide&lt;br /&gt;
|-&lt;br /&gt;
| Feedback resolution || 1/32 &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; unit || Fixed-point LSB of &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Full-congestion reference &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; || 512 || Mean queue of 64 packets&lt;br /&gt;
|-&lt;br /&gt;
| Receiver window target || 16 packets (16,000 bytes) || Samples per window&lt;br /&gt;
|-&lt;br /&gt;
| Initial receiver window || ~67 ms || Before rate is known&lt;br /&gt;
|-&lt;br /&gt;
| Receiver window bounds || ~1.05 ms &amp;amp;ndash; ~4.3 s || Clamp on &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Window resize weight || 1/4 || Moving average toward target&lt;br /&gt;
|-&lt;br /&gt;
| Early window close || 2 &amp;amp;times; target bytes || Speed-up escape&lt;br /&gt;
|-&lt;br /&gt;
| Gap restart || 4 windows || Idle horizon (rate-relative)&lt;br /&gt;
|-&lt;br /&gt;
| Additive slope &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; (default) || 2^16 B/s^2 || Lower clamp of derived slope&lt;br /&gt;
|-&lt;br /&gt;
| Probe time constant &amp;lt;code&amp;gt;T_probe&amp;lt;/code&amp;gt; || 8 s || Rate-proportional recovery&lt;br /&gt;
|-&lt;br /&gt;
| Derivative gain || 1/4 || Onset sharpening&lt;br /&gt;
|-&lt;br /&gt;
| Rate floor &amp;lt;code&amp;gt;r_min&amp;lt;/code&amp;gt; (default) || 2^13 B/s || Lower clamp of derived floor&lt;br /&gt;
|-&lt;br /&gt;
| Slow-start seed rate || 2^16 B/s || Initial rate&lt;br /&gt;
|-&lt;br /&gt;
| Slow-start time constant || 20 ms || Exponential ramp&lt;br /&gt;
|-&lt;br /&gt;
| Rate ceiling &amp;lt;code&amp;gt;r_max&amp;lt;/code&amp;gt; || 2^37 B/s || Upper clamp&lt;br /&gt;
|-&lt;br /&gt;
| Capacity scale || &amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt; / 32 || Floor and slope from bottleneck&lt;br /&gt;
|-&lt;br /&gt;
| Capacity smoothing || 1/2 || Moving average per feedback&lt;br /&gt;
|-&lt;br /&gt;
| Capacity staleness || 16 &amp;amp;times; feedback horizon || Revert to defaults&lt;br /&gt;
|-&lt;br /&gt;
| Derived floor ceiling || 2^32 B/s || Upper clamp on derived floor&lt;br /&gt;
|-&lt;br /&gt;
| Control interval || 1 ms || Minimum spacing of rate updates&lt;br /&gt;
|-&lt;br /&gt;
| Idle credit cap || 50 ms || Burst allowance / increase banking&lt;br /&gt;
|-&lt;br /&gt;
| Feedback staleness floor || ~268 ms || Floor of rate-relative horizon&lt;br /&gt;
|-&lt;br /&gt;
| Damping bucket || ~31 ms || Rate-velocity washout cadence&lt;br /&gt;
|-&lt;br /&gt;
| Damping fraction || 1/4 || Pullback per bucket&lt;br /&gt;
|-&lt;br /&gt;
| Estimator fold spacing || 1 ms || Minimum measurement cadence&lt;br /&gt;
|-&lt;br /&gt;
| Estimator window closure || 16 packets drained || Self-scaling window&lt;br /&gt;
|-&lt;br /&gt;
| Estimator window staleness || ~134 ms || Discard across traffic gaps&lt;br /&gt;
|-&lt;br /&gt;
| Estimator idle tolerance || 1/8 of arrivals || Unsaturated-window rejection&lt;br /&gt;
|-&lt;br /&gt;
| Estimator decay || 1/16 per window || Max-filter convergence from below&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 12. References ==&lt;br /&gt;
&lt;br /&gt;
=== 12.1. Source ===&lt;br /&gt;
&lt;br /&gt;
The implementation follows the algorithm above:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/ca/mb-ecn.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mb-ecn.h&amp;lt;/code&amp;gt; &amp;amp;mdash;&lt;br /&gt;
  receiver estimator, pacer and rate control.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/ca/ops.h&amp;lt;/code&amp;gt; &amp;amp;mdash; CA policy interface.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/cap.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cap.h&amp;lt;/code&amp;gt; &amp;amp;mdash; link-capacity&lt;br /&gt;
  estimator and quarter-log2 codec.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/dt.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fa.c&amp;lt;/code&amp;gt; &amp;amp;mdash; per-hop marking&lt;br /&gt;
  and stamping, and the feedback carrying &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;cap8&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/psched.c&amp;lt;/code&amp;gt; &amp;amp;mdash; consumes the pacing deadline.&lt;br /&gt;
&lt;br /&gt;
=== 12.2. Literature ===&lt;br /&gt;
&lt;br /&gt;
* K. K. Ramakrishnan, R. Jain, &amp;quot;A Binary Feedback Scheme for Congestion&lt;br /&gt;
  Avoidance in Computer Networks&amp;quot; (DECbit), ACM ToCS, 1990.&lt;br /&gt;
* H. Balakrishnan, H. S. Rahul, S. Seshan, &amp;quot;An Integrated Congestion&lt;br /&gt;
  Management Architecture for Internet Hosts&amp;quot;, SIGCOMM 1999;&lt;br /&gt;
  H. Balakrishnan, S. Seshan, &amp;quot;The Congestion Manager&amp;quot;, RFC 3124, 2001.&lt;br /&gt;
* M. Alizadeh et al., &amp;quot;Data Center TCP (DCTCP)&amp;quot;, SIGCOMM 2010.&lt;br /&gt;
* Y. Zhu et al., &amp;quot;Congestion Control for Large-Scale RDMA Deployments&amp;quot;&lt;br /&gt;
  (DCQCN), SIGCOMM 2015.&lt;br /&gt;
* R. Mittal et al., &amp;quot;TIMELY: RTT-based Congestion Control&amp;quot;, SIGCOMM 2015.&lt;br /&gt;
* Y. Li et al., &amp;quot;HPCC: High Precision Congestion Control&amp;quot;, SIGCOMM 2019.&lt;br /&gt;
* V. Addanki et al., &amp;quot;PowerTCP&amp;quot;, NSDI 2022.&lt;br /&gt;
* K. De Schepper, B. Briscoe (eds.), &amp;quot;Low Latency, Low Loss, and&lt;br /&gt;
  Scalable Throughput (L4S)&amp;quot;, RFC 9330/9331/9332.&lt;br /&gt;
* F. Kelly, &amp;quot;Charging and Rate Control for Elastic Traffic&amp;quot;, 1997;&lt;br /&gt;
  R. Srikant, &amp;quot;The Mathematics of Internet Congestion Control&amp;quot;, 2004.&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1939</id>
		<title>Congestion avoidance</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1939"/>
		<updated>2026-07-11T20:46:46Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 2. Forwarder marking */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:mb-ecn - Multi-bit ECN Congestion Avoidance}}&lt;br /&gt;
&lt;br /&gt;
mb-ecn is a congestion-avoidance (CA) policy for the Ouroboros unicast&lt;br /&gt;
IPCP. It lives in the IPC Process: the flow-and-retransmission task&lt;br /&gt;
(FRCP) provides reliability, in-order delivery and flow control&lt;br /&gt;
end-to-end; congestion avoidance is orthogonal and runs per IPCP&lt;br /&gt;
layer. Within a layer, the IPCP controls &#039;&#039;&#039;aggregates&#039;&#039;&#039;, one loop&lt;br /&gt;
per (destination address, QoS cube), shared by every flow toward that&lt;br /&gt;
destination. The alternative policy is &amp;lt;code&amp;gt;nop&amp;lt;/code&amp;gt; (no congestion&lt;br /&gt;
avoidance).&lt;br /&gt;
&lt;br /&gt;
This document describes the algorithm: what the policy does and why.&lt;br /&gt;
The implementation follows it ([[#12. References|Section 12]] maps the&lt;br /&gt;
pieces to source files). It does not claim properties the algorithm&lt;br /&gt;
does not have; [[#8. Limitations|Section 8]] states the known gaps&lt;br /&gt;
explicitly.&lt;br /&gt;
&lt;br /&gt;
The keywords &amp;quot;MUST&amp;quot;, &amp;quot;SHOULD&amp;quot;, &amp;quot;MAY&amp;quot; etc. are used only where they&lt;br /&gt;
appear in all capitals, per BCP 14 (RFC 2119, RFC 8174).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt;&lt;br /&gt;
:Paced send rate, bytes per second.&lt;br /&gt;
;&amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bottleneck capacity, bytes per second.&lt;br /&gt;
;&amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt;&lt;br /&gt;
:Packet size in bytes.&lt;br /&gt;
;&amp;lt;code&amp;gt;q&amp;lt;/code&amp;gt;&lt;br /&gt;
:Standing queue occupancy at the bottleneck, in packets.&lt;br /&gt;
;&amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt;&lt;br /&gt;
:Marking quantum, packets per mark unit (4).&lt;br /&gt;
;&amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt;&lt;br /&gt;
:Per-packet congestion mark set by a forwarder, an unsigned integer&lt;br /&gt;
 (on-wire field is 8-bit).&lt;br /&gt;
;&amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
:Receiver-side congestion estimate, fixed point with LSB = 1/32 of an&lt;br /&gt;
 &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; unit, i.e. &amp;lt;code&amp;gt;ece = 32 &amp;amp;times; (time-mean ecn)&amp;lt;/code&amp;gt;.&lt;br /&gt;
;&amp;lt;code&amp;gt;cap8&amp;lt;/code&amp;gt;&lt;br /&gt;
:Path-capacity code carried per packet, quarter-log2:&lt;br /&gt;
 &amp;lt;code&amp;gt;C &amp;amp;asymp; 2^(cap8 / 4)&amp;lt;/code&amp;gt; bytes/s, 0 = unknown.&lt;br /&gt;
;&amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt;&lt;br /&gt;
:Receiver averaging window, nanoseconds.&lt;br /&gt;
;&amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt;&lt;br /&gt;
:Additive-increase slope, bytes/s per second.&lt;br /&gt;
;&amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt;&lt;br /&gt;
:Full-congestion reference level of &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; (512).&lt;br /&gt;
;&amp;lt;code&amp;gt;&amp;amp;Delta;t&amp;lt;/code&amp;gt;&lt;br /&gt;
:Elapsed wall-clock time between two events, nanoseconds.&lt;br /&gt;
;&amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;&lt;br /&gt;
:Path round-trip time (feedback delay). The controller does not&lt;br /&gt;
 measure it.&lt;br /&gt;
&lt;br /&gt;
Rate is the control variable throughout.&lt;br /&gt;
&lt;br /&gt;
All concrete figures in this document &amp;amp;mdash; window bounds, horizons,&lt;br /&gt;
rate thresholds &amp;amp;mdash; are instances of the parameter values in&lt;br /&gt;
[[#11. Parameters|Section 11]]. The mechanisms and the relations&lt;br /&gt;
between parameters are what the algorithm fixes; retuning the&lt;br /&gt;
parameters moves the figures together.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
mb-ecn is a rate-based, ECN-driven controller with three moving parts,&lt;br /&gt;
one at each point of the loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        sender                                  forwarder&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
      |  SFQ virtual-clock pacer       |       |  ecn = MAX(ecn, queue/Q) |&lt;br /&gt;
      |  rate r, paced per packet      | data  |  (deepest queue wins)    |&lt;br /&gt;
      |  AIMD + PD control on r        |------&amp;gt;|  cap8 = MIN(cap8, link)  |&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
                 ^                                        |&lt;br /&gt;
                 | ece + cap8 feedback                    | marked data&lt;br /&gt;
                 |                                        v&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
      |  reads downstream ece,         |&amp;lt;------|  receiver                |&lt;br /&gt;
      |  updates the rate on feedback, |  ece  |  time-mean of ecn over   |&lt;br /&gt;
      |  scales floor/slope to cap8    |  cap8 |  an adaptive window      |&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# The &#039;&#039;&#039;forwarder&#039;&#039;&#039; marks each packet with a &#039;&#039;multi-bit&#039;&#039; magnitude&lt;br /&gt;
  proportional to its standing queue, and MIN-stamps its measured&lt;br /&gt;
  outgoing-link capacity into the same PCI.&lt;br /&gt;
# The &#039;&#039;&#039;receiver&#039;&#039;&#039; turns the stream of marks into a time-averaged&lt;br /&gt;
  congestion estimate &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; and feeds it back to the sender,&lt;br /&gt;
  together with the window minimum of the path-capacity codes.&lt;br /&gt;
# The &#039;&#039;&#039;sender&#039;&#039;&#039; paces its rate with a start-time fair-queuing (SFQ)&lt;br /&gt;
  virtual clock, and adjusts that rate with additive-increase /&lt;br /&gt;
  multiplicative-decrease (AIMD) driven by &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;; the rate floor&lt;br /&gt;
  and the additive slope derive from the fed-back path capacity.&lt;br /&gt;
&lt;br /&gt;
The controller runs on packet sends and on feedback arrivals. All&lt;br /&gt;
increase/decrease steps are scaled by elapsed&lt;br /&gt;
wall-clock time, which is what makes the &#039;&#039;allocation&#039;&#039; independent of&lt;br /&gt;
RTT (see [[#6. RTT behaviour|Section 6]]).&lt;br /&gt;
&lt;br /&gt;
The unit of control is the &#039;&#039;&#039;aggregate&#039;&#039;&#039;, kept in the IPCP per&lt;br /&gt;
&amp;lt;code&amp;gt;(destination address, QoS cube)&amp;lt;/code&amp;gt;. Every flow toward that&lt;br /&gt;
destination at that QoS shares one controller and one rate: a new&lt;br /&gt;
flow joins the aggregate at its current rate, and a departing flow&lt;br /&gt;
leaves the rate to the others. The pacer divides the aggregate rate&lt;br /&gt;
fairly across the member flows (SFQ), so per-flow fairness within an&lt;br /&gt;
aggregate is a scheduler property, distinct from the rate law&lt;br /&gt;
described here.&lt;br /&gt;
&lt;br /&gt;
== Forwarder marking ==&lt;br /&gt;
&lt;br /&gt;
A forwarder marks a packet as a function of the packets already queued&lt;br /&gt;
on the outgoing link:&lt;br /&gt;
&lt;br /&gt;
  mark = queued / Q                         (Q = 4 packets)&lt;br /&gt;
  ecn  = MAX(ecn, mark)                     (saturating, 8-bit field)&lt;br /&gt;
&lt;br /&gt;
The quantum &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; is a layer-wide constant: the marks it&lt;br /&gt;
quantizes are combined by MAX across hops, so all forwarders in a&lt;br /&gt;
layer MUST use the same quantum for the signal to be comparable.&lt;br /&gt;
&lt;br /&gt;
Two properties:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-bit magnitude.&#039;&#039;&#039; The mark is an integer proportional to&lt;br /&gt;
  queue depth, quantized in steps of &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; packets: a coarse&lt;br /&gt;
  readout of the standing queue.&lt;br /&gt;
* &#039;&#039;&#039;MAX along the path.&#039;&#039;&#039; A packet crossing several forwarders carries&lt;br /&gt;
  the maximum mark, i.e. the deepest queue on its path. MAX keeps the&lt;br /&gt;
  signal range-stable and monotone across hops.&lt;br /&gt;
&lt;br /&gt;
There is a &#039;&#039;&#039;dead zone&#039;&#039;&#039;: for &amp;lt;code&amp;gt;queued &amp;lt; Q&amp;lt;/code&amp;gt; the mark is 0.&lt;br /&gt;
&lt;br /&gt;
=== Capacity stamping ===&lt;br /&gt;
&lt;br /&gt;
Each forwarder also measures the capacity of its outgoing link and&lt;br /&gt;
stamps it into the packet. The estimate is a busy-period drain rate:&lt;br /&gt;
the queue is observed at most once per millisecond, and a measurement&lt;br /&gt;
window stays open until 16 packets have drained, so its length&lt;br /&gt;
&#039;&#039;&#039;self-scales with the link rate&#039;&#039;&#039; &amp;amp;mdash; the millisecond cadence&lt;br /&gt;
bounds it on fast links, the 16-packet drain time on slow ones&lt;br /&gt;
(~19 ms at 10 Mbit/s). A window in which more than 1/8 of&lt;br /&gt;
the arrivals found the queue empty is discarded as unsaturated;&lt;br /&gt;
occasional empty observations (token-bucket shapers grazing zero) are&lt;br /&gt;
tolerated. The drain rate of a saturated queue is the link rate; a max&lt;br /&gt;
filter with slow decay (1/16 per window) converges on it from below. A&lt;br /&gt;
window that opened or closed on an empty queue may have drained into&lt;br /&gt;
buffers below at above-wire rate, so it may lower the estimate but&lt;br /&gt;
never raise it. The code on the wire is quarter-log2&lt;br /&gt;
(&amp;lt;code&amp;gt;cap8 = 4 log2 C&amp;lt;/code&amp;gt;, ~19% per step, 0 = unknown) and each hop&lt;br /&gt;
MIN-combines its own code into the byte, so a packet arrives carrying&lt;br /&gt;
the bottleneck&#039;s rate. A hop that has never been backlogged stamps&lt;br /&gt;
nothing; since only backlogged hops matter, the signal exists exactly&lt;br /&gt;
when it is needed.&lt;br /&gt;
&lt;br /&gt;
== 3. Receiver estimate ==&lt;br /&gt;
&lt;br /&gt;
The receiver converts marks into a smoothed estimate &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; over a&lt;br /&gt;
time window &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt; that &#039;&#039;&#039;adapts to the incoming byte rate&#039;&#039;&#039;. It is&lt;br /&gt;
a &#039;&#039;&#039;boxcar (rectangular) time-integral mean&#039;&#039;&#039;. Per packet, with&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;Delta;t&amp;lt;/code&amp;gt; the gap since the previous packet:&lt;br /&gt;
&lt;br /&gt;
  A += ecn * MIN(&amp;amp;Delta;t, W)              dwell-weighted; one packet&lt;br /&gt;
                                          weighs at most one window&lt;br /&gt;
  B += S                                  accumulate bytes this window&lt;br /&gt;
  ...&lt;br /&gt;
  at window close (elapsed &amp;gt;= W, or B &amp;gt;= 2 * B_target with at&lt;br /&gt;
                   least the window floor elapsed):&lt;br /&gt;
      ece = 32 * A / elapsed              mean over the actual window&lt;br /&gt;
      W  += (B_target * elapsed / B - W) / 4    nudge toward the size&lt;br /&gt;
                                                that holds the target&lt;br /&gt;
      W   = clamp(W, ~1.05 ms, ~4.3 s)&lt;br /&gt;
      A   = 0 ; B = 0                     hard reset&lt;br /&gt;
&lt;br /&gt;
At each close the next window is nudged by a quarter-weight moving&lt;br /&gt;
average toward the size that would hold about 16 packets at the&lt;br /&gt;
measured byte rate, from an initial ~67 ms. The target is byte-based (&amp;lt;code&amp;gt;B_target&amp;lt;/code&amp;gt; = 16,000 bytes), so&lt;br /&gt;
&amp;quot;16 packets&amp;quot; is exact only at ~1000-byte packets, and the window&lt;br /&gt;
converges to it over several closes. The mean always divides by the&lt;br /&gt;
&#039;&#039;&#039;actual&#039;&#039;&#039; elapsed window, so a rate step leaves the current&lt;br /&gt;
estimate exact and only re-sizes the &#039;&#039;next&#039;&#039; window. The effect is a roughly constant sample&lt;br /&gt;
count &amp;amp;mdash; ~16 packets &amp;amp;mdash; from about 30 kbit/s to 122 Mbit/s;&lt;br /&gt;
above that the window floors at ~1 ms (a cadence still carrying&lt;br /&gt;
thousands of samples at 10&amp;amp;ndash;100 GbE), and below ~30 kbit/s it&lt;br /&gt;
saturates at the ~4.3 s ceiling. The averaging clock stretches with&lt;br /&gt;
the flow the way TCP&#039;s ACK clock stretches with the RTT. A speed-up&lt;br /&gt;
escapes a stretched window early: once twice the target bytes arrive&lt;br /&gt;
the window closes anyway (the window floor keeps that cadence bounded&lt;br /&gt;
at high rate). The fed-back &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; is thus a piecewise-constant&lt;br /&gt;
staircase whose step period tracks the rate. Two edge cases:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Onset&#039;&#039;&#039; (first mark after an idle estimate) emits the&lt;br /&gt;
  instantaneous mark undiluted, so a starting queue is reported without&lt;br /&gt;
  a full window of delay.&lt;br /&gt;
* &#039;&#039;&#039;Gap&#039;&#039;&#039; longer than 4 windows resets the window and emits the raw&lt;br /&gt;
  sample. The threshold is &#039;&#039;&#039;rate-relative&#039;&#039;&#039;: idle means a few&lt;br /&gt;
  &#039;&#039;current&#039;&#039; windows of silence, so a slow flow&#039;s normal inter-packet&lt;br /&gt;
  gap never reads as idle, while the in-window dwell clamp above bounds&lt;br /&gt;
  what a genuine pause can add to the mean before the restart fires.&lt;br /&gt;
&lt;br /&gt;
Alongside the mark integral, the receiver keeps the MIN of the nonzero&lt;br /&gt;
capacity codes seen in the current window; each emitted &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
carries that minimum (the raw packet&#039;s code on onset/gap restarts) and&lt;br /&gt;
the fold then resets, so a reroute to a faster path can raise the&lt;br /&gt;
fed-back capacity within one window.&lt;br /&gt;
&lt;br /&gt;
The estimate is fed back to the sender on the reverse flow.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 4. Sender pacer ==&lt;br /&gt;
&lt;br /&gt;
The sender paces with an SFQ virtual clock. The context keeps a&lt;br /&gt;
virtual time &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;; each flow keeps a finish tag &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
  V    += r * &amp;amp;Delta;t                     virtual time advances by bytes served&lt;br /&gt;
  s     = MAX(F, V)                  a packet behind the clock starts now&lt;br /&gt;
  F     = s + S                      the one after it queues behind&lt;br /&gt;
  wait  = (s - V) / r                time until this packet may go&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;wait&amp;lt;/code&amp;gt; becomes the flow&#039;s scheduling deadline: a packet&lt;br /&gt;
already behind the virtual clock is sent immediately&lt;br /&gt;
(&amp;lt;code&amp;gt;wait = 0&amp;lt;/code&amp;gt;); one ahead waits, deferring only its own flow.&lt;br /&gt;
A clock advance across an idle gap longer than 50 ms credits at most&lt;br /&gt;
the calling flow&#039;s owed lead plus one burst (50 ms of service, at&lt;br /&gt;
least one packet): a paced flow slower than one packet per 50 ms&lt;br /&gt;
receives its &#039;&#039;&#039;true&#039;&#039;&#039; elapsed service &amp;amp;mdash; a time-capped credit&lt;br /&gt;
would decay such a flow without bound &amp;amp;mdash; while an idle flow still&lt;br /&gt;
cannot bank an unbounded burst.&lt;br /&gt;
&lt;br /&gt;
The virtual time &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; is shared by every flow in the context;&lt;br /&gt;
the finish tags are per flow. Because every flow computes its start&lt;br /&gt;
tag &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; against the same &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;, a flow that has just sent&lt;br /&gt;
carries a finish tag ahead of &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; and waits, while an idle flow&lt;br /&gt;
(finish tag at or behind &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;) starts immediately &amp;amp;mdash; this is&lt;br /&gt;
what fairly divides the aggregate rate &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; across them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 5. Rate control (AIMD + PD) ==&lt;br /&gt;
&lt;br /&gt;
The rate law runs on packet sends, at most once per millisecond, and&lt;br /&gt;
also on feedback arrivals so that a sender with no traffic of its own&lt;br /&gt;
still reacts. Each increase term and the proportional decrease are&lt;br /&gt;
scaled by elapsed wall-clock time, each by its own clock: the&lt;br /&gt;
increase terms use time banked and &#039;&#039;&#039;capped&#039;&#039;&#039; at 50 ms (an idle gap&lt;br /&gt;
cannot bank an unbounded ramp), while the proportional decrease uses&lt;br /&gt;
the &#039;&#039;&#039;uncapped&#039;&#039;&#039; elapsed time (a starved sender still cuts by the&lt;br /&gt;
right amount). The one-sided derivative (Section 5.3) is a per-delta&lt;br /&gt;
term.&lt;br /&gt;
&lt;br /&gt;
=== 5.1. Slow start ===&lt;br /&gt;
&lt;br /&gt;
Before the first congestion signal the rate ramps exponentially with a&lt;br /&gt;
20 ms time constant:&lt;br /&gt;
&lt;br /&gt;
  r += r * &amp;amp;Delta;t / T_ss                  (T_ss = 20 ms)&lt;br /&gt;
&lt;br /&gt;
Slow start ends the first time a congestion signal arrives &amp;amp;mdash;&lt;br /&gt;
fed back from the receiver or observed at the sender&#039;s own first hop&lt;br /&gt;
&amp;amp;mdash; and never resumes.&lt;br /&gt;
&lt;br /&gt;
Slow start belongs to the aggregate, so it runs &#039;&#039;&#039;once per&lt;br /&gt;
(destination, QoS cube)&#039;&#039;&#039;, on the first flow. A flow arriving at an&lt;br /&gt;
established aggregate rides the existing estimates: it enters at the&lt;br /&gt;
aggregate&#039;s current rate and receives its fair share of it through&lt;br /&gt;
the pacer (Section 4), with no probing of its own.&lt;br /&gt;
&lt;br /&gt;
=== 5.2. Increase: additive plus proportional probe (always on) ===&lt;br /&gt;
&lt;br /&gt;
Past slow start, two increase terms are applied every step, both&lt;br /&gt;
regardless of congestion:&lt;br /&gt;
&lt;br /&gt;
  r += a * &amp;amp;Delta;t                         additive; a defaults to 2^16 B/s^2&lt;br /&gt;
  r += r * &amp;amp;Delta;t / T_probe               proportional; T_probe = 8 s&lt;br /&gt;
&lt;br /&gt;
The additive term is a fixed per-context slope (bytes/s per second),&lt;br /&gt;
scaled to the path capacity (Section 5.5). The proportional probe&lt;br /&gt;
grows the rate by a fixed fraction per unit time (e-folds over&lt;br /&gt;
&amp;lt;code&amp;gt;T_probe&amp;lt;/code&amp;gt;): a flow recovers in the same number of steps at any&lt;br /&gt;
link rate; the fixed additive term&#039;s effect vanishes relative to a&lt;br /&gt;
fast link. Both are the &amp;quot;probing&amp;quot;&lt;br /&gt;
pressure the multiplicative decrease balances at equilibrium; the&lt;br /&gt;
probe&#039;s cost is a rate-independent standing-queue floor (Section 7).&lt;br /&gt;
&lt;br /&gt;
=== 5.3. Multiplicative decrease (proportional + derivative) ===&lt;br /&gt;
&lt;br /&gt;
With congestion level &amp;lt;code&amp;gt;m&amp;lt;/code&amp;gt; (the fed-back &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;, or a local&lt;br /&gt;
first-hop mark as fallback), the decrease is the sum of a proportional&lt;br /&gt;
term and a one-sided derivative term &amp;amp;mdash; a PD controller on the&lt;br /&gt;
congestion signal:&lt;br /&gt;
&lt;br /&gt;
  mark = MIN(m, M)                                   M = 512&lt;br /&gt;
  rise = MAX(m - m_prev, 0), capped at M             one-sided derivative&lt;br /&gt;
&lt;br /&gt;
  cut  = r * rise / (4 * M)                          derivative, gain 1/4&lt;br /&gt;
  cut += r * mark * &amp;amp;Delta;t_ms / (1000 * M)              proportional&lt;br /&gt;
  cut  = MIN(cut, r / 2)                             at most a halving&lt;br /&gt;
&lt;br /&gt;
  r     -= cut&lt;br /&gt;
  m_prev = m&lt;br /&gt;
&lt;br /&gt;
* The &#039;&#039;&#039;proportional&#039;&#039;&#039; term is scaled by honest elapsed time in&lt;br /&gt;
  milliseconds, so a starved sender that has not run the controller&lt;br /&gt;
  for a while still cuts by the right amount.&lt;br /&gt;
* The &#039;&#039;&#039;derivative&#039;&#039;&#039; term (&amp;lt;code&amp;gt;rise&amp;lt;/code&amp;gt;) reacts to an &#039;&#039;increase&#039;&#039; in&lt;br /&gt;
  the congestion level between samples; it is one-sided and per-delta.&lt;br /&gt;
  It sharpens the reaction at the onset of congestion.&lt;br /&gt;
* The cut is clamped to &amp;lt;code&amp;gt;r/2&amp;lt;/code&amp;gt; per step, bounding the decrease to&lt;br /&gt;
  a halving.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;M = 512 = 32 &amp;amp;times; 16&amp;lt;/code&amp;gt; defines &amp;quot;full congestion&amp;quot; as a mean&lt;br /&gt;
&amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; of 16, i.e. a mean queue of &amp;lt;code&amp;gt;16 &amp;amp;times; Q = 64&amp;lt;/code&amp;gt; packets.&lt;br /&gt;
&lt;br /&gt;
=== 5.4. Staleness ===&lt;br /&gt;
&lt;br /&gt;
Feedback arrives once per receiver window, and the window stretches&lt;br /&gt;
with the flow&#039;s byte rate (Section 3) &amp;amp;mdash; so the staleness horizon&lt;br /&gt;
must stretch with it, or a slow flow&#039;s mark would age out &#039;&#039;between&#039;&#039;&lt;br /&gt;
feedbacks and the rate would ramp against a congested path. The&lt;br /&gt;
sender mirrors the receiver: the horizon is four target windows of&lt;br /&gt;
bytes at the current rate, floored at ~268 ms so fast flows keep a&lt;br /&gt;
fixed horizon; at the default rate floor it is ~8 s. A signal older&lt;br /&gt;
than the horizon is aged out (both the fed-back estimate and the&lt;br /&gt;
local mark), freeing the rate to ramp once congestion clears. The&lt;br /&gt;
rate is clamped to&lt;br /&gt;
&amp;lt;code&amp;gt;[r_min, r_max]&amp;lt;/code&amp;gt; on every step.&lt;br /&gt;
&lt;br /&gt;
=== 5.5. Capacity-derived floor and slope ===&lt;br /&gt;
&lt;br /&gt;
The fed-back capacity code sets the two rate-scale constants of the&lt;br /&gt;
controller, per context:&lt;br /&gt;
&lt;br /&gt;
  target = decode(cap8) / 32              clamped to [2^13, 2^32] B/s&lt;br /&gt;
  r_min += (target - r_min) / 2           per feedback (moving average)&lt;br /&gt;
  a      = r_min&lt;br /&gt;
&lt;br /&gt;
The defaults are fixed constants (&amp;lt;code&amp;gt;r_min = 2^13 B/s&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;a = 2^16 B/s^2&amp;lt;/code&amp;gt;), used on paths that never report a capacity;&lt;br /&gt;
with the estimator live the fairness floor and the post-cut recovery&lt;br /&gt;
slope scale with the bottleneck, and capacity engages wherever&lt;br /&gt;
&amp;lt;code&amp;gt;C/32&amp;lt;/code&amp;gt; clears the default floor, i.e. above ~2 Mbit/s. A&lt;br /&gt;
capacity older than 16 staleness horizons (~4.3 s for fast flows)&lt;br /&gt;
reverts both to the defaults. That horizon deliberately outlives the&lt;br /&gt;
feedback horizon: feedback stops the moment the marks clear, which is&lt;br /&gt;
exactly when the recovery slope is needed; the onset-fresh capacity&lt;br /&gt;
(Section 3) re-seeds it on the first mark of the next episode either&lt;br /&gt;
way.&lt;br /&gt;
&lt;br /&gt;
=== 5.6. Ramp and recovery ===&lt;br /&gt;
&lt;br /&gt;
Three mechanisms set how fast an aggregate reaches a fast link&#039;s&lt;br /&gt;
rate, each owning one regime:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;From scratch: slow start.&#039;&#039;&#039; The exponential ramp reaches any&lt;br /&gt;
  physical link rate in well under a second (~200 ms from the seed to&lt;br /&gt;
  10 Gbit/s; each further doubling of link speed costs one more&lt;br /&gt;
  ~14 ms doubling time). On an uncongested path nothing marks, so&lt;br /&gt;
  slow start runs until the aggregate arrives at its bottleneck.&lt;br /&gt;
* &#039;&#039;&#039;After a cut: the probe.&#039;&#039;&#039; A decrease is at most a halving per&lt;br /&gt;
  step, and the mark ages out on the rate-relative horizon once&lt;br /&gt;
  congestion clears (Section 5.4); the proportional probe then heals&lt;br /&gt;
  a halving in &amp;lt;code&amp;gt;T_probe &amp;amp;middot; ln 2 &amp;amp;asymp; 5.5 s&amp;lt;/code&amp;gt; at any&lt;br /&gt;
  link rate.&lt;br /&gt;
* &#039;&#039;&#039;At the bottom: the capacity floor.&#039;&#039;&#039; The rate sits at most a&lt;br /&gt;
  factor 32 below a measured bottleneck, and the additive slope&lt;br /&gt;
  refills &amp;lt;code&amp;gt;C/32&amp;lt;/code&amp;gt; per second, so the deepest hole is bounded:&lt;br /&gt;
  floor to full capacity in tens of seconds, a halving in seconds.&lt;br /&gt;
&lt;br /&gt;
These compose because a deep cut requires sustained congestion,&lt;br /&gt;
sustained congestion backlogs the bottleneck queue, and a backlogged&lt;br /&gt;
queue is exactly when its capacity is measured and stamped&lt;br /&gt;
(Section 2): whenever the controller is cut deep, the scaled floor is&lt;br /&gt;
live. The default floor only governs paths that never congested the&lt;br /&gt;
sender &amp;amp;mdash; where there is nothing to recover from. This is why&lt;br /&gt;
slow start can end for good: the floor and the probe cover every&lt;br /&gt;
later recovery.&lt;br /&gt;
&lt;br /&gt;
=== 5.7. Regimes ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   +-------------+   first mark seen    +----------------+&lt;br /&gt;
   | slow start  |---------------------&amp;gt;|  congestion    |&lt;br /&gt;
   | r *= e^(t/T)|                      |  avoidance     |&lt;br /&gt;
   +-------------+                      |                |&lt;br /&gt;
                                        | m == 0 -&amp;gt; AI   |&lt;br /&gt;
                                        | m  &amp;gt; 0 -&amp;gt; AIPD |&lt;br /&gt;
                                        +----------------+&lt;br /&gt;
    m = fed-back ece, or the local first-hop mark when ece == 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 6. RTT behaviour ==&lt;br /&gt;
&lt;br /&gt;
Because every increase and decrease is scaled by wall-clock time,&lt;br /&gt;
two flows of different RTT that share a bottleneck obey the &#039;&#039;same&#039;&#039;&lt;br /&gt;
rate law and converge to the &#039;&#039;same&#039;&#039; rate. The steady-state&lt;br /&gt;
&#039;&#039;&#039;allocation is RTT-independent&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
This is a property of the equilibrium, not of the dynamics. The&lt;br /&gt;
feedback delay in the loop &#039;&#039;is&#039;&#039; the path RTT &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;, and the&lt;br /&gt;
controller does not measure or compensate for it. At low rate the&lt;br /&gt;
receiver window (up to its ~4.3 s ceiling) is the dominant lag &amp;amp;mdash;&lt;br /&gt;
the loop is slow there in proportion to how slow the flow is, exactly&lt;br /&gt;
as a long-RTT TCP is &amp;amp;mdash; while as the rate rises the window shrinks&lt;br /&gt;
toward its ~1 ms floor, so the window&#039;s contribution to the loop delay&lt;br /&gt;
&#039;&#039;falls&#039;&#039; with capacity and the path RTT &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; becomes the&lt;br /&gt;
irreducible term. Two mechanisms keep the loop damped: the adaptive&lt;br /&gt;
window removes the delay-dominated corner at low &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;, and a&lt;br /&gt;
bucketed &#039;&#039;&#039;rate-velocity damping&#039;&#039;&#039; (a washout: each ~31 ms bucket&lt;br /&gt;
pulls the rate a quarter of the way back toward its value at the&lt;br /&gt;
previous bucket) supplies the damping the otherwise&lt;br /&gt;
near-double-integrator lacks, which is what holds the loop together at&lt;br /&gt;
high capacity and moderate &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;. What remains is the loop gain&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;prop; &amp;amp;radic;C&amp;lt;/code&amp;gt;: at very high capacity &#039;&#039;and&#039;&#039; large&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; the &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt; phase budget is the&lt;br /&gt;
binding constraint, and no window or damping trick removes it (see&lt;br /&gt;
[[#8. Limitations|Section 8]]). A precise statement is therefore:&lt;br /&gt;
&#039;&#039;&#039;RTT-fair in equilibrium, and dynamically stable up to a&lt;br /&gt;
capacity-dependent RTT limit set by &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 7. Fairness ==&lt;br /&gt;
&lt;br /&gt;
At equilibrium the two increase terms balance the proportional&lt;br /&gt;
decrease, &amp;lt;code&amp;gt;a + r/T_probe = r &amp;amp;middot; m*/M&amp;lt;/code&amp;gt;, which pins the&lt;br /&gt;
congestion level to &amp;lt;code&amp;gt;m* = M &amp;amp;middot; (a/r + 1/T_probe)&amp;lt;/code&amp;gt; and, since&lt;br /&gt;
&amp;lt;code&amp;gt;ece = 8q&amp;lt;/code&amp;gt; (one mark unit per &amp;lt;code&amp;gt;Q = 4&amp;lt;/code&amp;gt; packets, 32 fixed-point&lt;br /&gt;
steps per unit), a standing queue of&lt;br /&gt;
&lt;br /&gt;
  q* = 64 * a / r  +  64 / T_probe        (packets, r in bytes/s)&lt;br /&gt;
&lt;br /&gt;
The additive part (&amp;lt;code&amp;gt;64 &amp;amp;middot; a / r&amp;lt;/code&amp;gt;) vanishes with rate under&lt;br /&gt;
the default slope; with the capacity-derived slope (&amp;lt;code&amp;gt;a = C/32&amp;lt;/code&amp;gt;,&lt;br /&gt;
Section 5.5) it becomes &amp;lt;code&amp;gt;2 C / r&amp;lt;/code&amp;gt; &amp;amp;mdash; about two packets per&lt;br /&gt;
competing flow, rate-independent. The proportional-probe part&lt;br /&gt;
(&amp;lt;code&amp;gt;64 / T_probe&amp;lt;/code&amp;gt;) is a rate-independent floor (~8 packets at&lt;br /&gt;
8 s). That floor is the cost of rate-independent convergence, and it&lt;br /&gt;
keeps &amp;lt;code&amp;gt;q*&amp;lt;/code&amp;gt; above the marking threshold at high rate.&lt;br /&gt;
&lt;br /&gt;
Two flows at the same bottleneck see the same &amp;lt;code&amp;gt;m*&amp;lt;/code&amp;gt;, so each&lt;br /&gt;
solves &amp;lt;code&amp;gt;r_i = a / (m*/M - 1/T_probe)&amp;lt;/code&amp;gt; to the same rate. Under&lt;br /&gt;
the network-utility-maximization view this corresponds to&lt;br /&gt;
&#039;&#039;&#039;proportional fairness&#039;&#039;&#039; (equal weights), which is max-min fair at&lt;br /&gt;
a single bottleneck.&lt;br /&gt;
&lt;br /&gt;
Measurement fidelity at low rate comes from the estimator itself:&lt;br /&gt;
the averaging window stretches with the flow&lt;br /&gt;
(Section 3), so a CA-limited flow keeps ~16 packets per window at any&lt;br /&gt;
rate down to ~30 kbit/s, and the rate-relative gap threshold keeps its&lt;br /&gt;
inter-packet spacing from reading as idle. The default floor&lt;br /&gt;
(&amp;lt;code&amp;gt;2^13&amp;lt;/code&amp;gt; B/s) only bounds the extremes (window ceiling,&lt;br /&gt;
staleness horizon, pacer arithmetic). The floor scales with the&lt;br /&gt;
fed-back path capacity (&amp;lt;code&amp;gt;r_min = C/32&amp;lt;/code&amp;gt;, Section 5.5), so&lt;br /&gt;
roughly 32 CA-limited flows fit above it at any link class. Flows&lt;br /&gt;
whose paths differ in bottleneck class derive different slopes, so&lt;br /&gt;
fairness across them is capacity-weighted; see&lt;br /&gt;
[[#8. Limitations|Section 8]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 9. Architectural fit ==&lt;br /&gt;
&lt;br /&gt;
Each property the preceding sections build toward attaches to a&lt;br /&gt;
structural feature of the recursive architecture. This section makes&lt;br /&gt;
the mapping explicit.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Congestion avoidance is fully orthogonal to ARQ and to flow&lt;br /&gt;
  control.&#039;&#039;&#039; Three concerns, three mechanisms, two scopes: FRCP&lt;br /&gt;
  provides retransmission (ARQ) and flow control (the peer pacing the&lt;br /&gt;
  sender) end-to-end, per flow; congestion avoidance runs in the&lt;br /&gt;
  IPCP, per aggregate. Each signal means one thing. A loss triggers&lt;br /&gt;
  a retransmission and nothing else &amp;amp;mdash; a lossy link reads as&lt;br /&gt;
  lossy, with the congestion verdict left to the marks (Section 8&#039;s&lt;br /&gt;
  &amp;quot;no loss response&amp;quot; is this orthogonality stated from the other&lt;br /&gt;
  side). The peer&#039;s flow-control window paces the endpoint and&lt;br /&gt;
  nothing else &amp;amp;mdash; back-pressure from a slow receiver stays&lt;br /&gt;
  distinct from congestion in the network. A retransmitted packet is&lt;br /&gt;
  ordinary traffic to the pacer. TCP folds all three into one window&lt;br /&gt;
  machine, where the receive window bounds the congestion window and&lt;br /&gt;
  loss serves as both reliability trigger and congestion signal;&lt;br /&gt;
  here each mechanism can be reasoned about, tested and evolved&lt;br /&gt;
  alone.&lt;br /&gt;
* &#039;&#039;&#039;Congestion avoidance sits below application choice.&#039;&#039;&#039; Every&lt;br /&gt;
  flow in the layer is paced by the same rate law, whatever its QoS:&lt;br /&gt;
  a greedy raw sender shares a bottleneck fairly with a reliable&lt;br /&gt;
  stream because the control is a property of the layer, on the&lt;br /&gt;
  aggregate, rather than a courtesy of the endpoint transport.&lt;br /&gt;
* &#039;&#039;&#039;A layer defines its own PCI, so the signal can be rich.&#039;&#039;&#039;&lt;br /&gt;
  Every member of a layer is enrolled into it: the layer is a single&lt;br /&gt;
  administrative domain by construction, at whatever scope it spans,&lt;br /&gt;
  and its header is layer-internal. Forwarders therefore write a&lt;br /&gt;
  multi-bit queue magnitude and a capacity byte directly into the&lt;br /&gt;
  packet. The datacenter schemes of&lt;br /&gt;
  [[#10. Heritage and positioning|Section 10]] require exactly such a&lt;br /&gt;
  domain and find it only inside one operator&#039;s fabric; a recursive&lt;br /&gt;
  layer supplies it everywhere.&lt;br /&gt;
* &#039;&#039;&#039;Layer-internal addressing makes the aggregate well-defined.&#039;&#039;&#039;&lt;br /&gt;
  Flows in a layer run between layer addresses, so the&lt;br /&gt;
  (destination, QoS cube) aggregate &amp;amp;mdash; RFC 3124&#039;s macroflow&lt;br /&gt;
  &amp;amp;mdash; falls out of the naming structure. Controller state scales&lt;br /&gt;
  with destinations, and each QoS cube keeps its own loop, so&lt;br /&gt;
  service classes keep separate fates.&lt;br /&gt;
* &#039;&#039;&#039;Flows are allocated, so feedback has a channel.&#039;&#039;&#039; Every flow&lt;br /&gt;
  has state at both ends and a reverse direction; the receiver&#039;s&lt;br /&gt;
  &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; and the path capacity ride the flow allocator&#039;s&lt;br /&gt;
  existing exchange.&lt;br /&gt;
* &#039;&#039;&#039;There is no transport ack clock, and none is relied on.&#039;&#039;&#039; The&lt;br /&gt;
  layer must control raw flows, which carry no acknowledgements at&lt;br /&gt;
  all, so the controller paces on wall-clock time &amp;amp;mdash; and that&lt;br /&gt;
  constraint yields the RTT-independent allocation of Section 6. An&lt;br /&gt;
  architectural restriction becomes the controller&#039;s distinguishing&lt;br /&gt;
  property.&lt;br /&gt;
* &#039;&#039;&#039;Recursion scales the scheme.&#039;&#039;&#039; Each layer controls congestion&lt;br /&gt;
  over its own scope and timescale; many N-flows ride one N-1 flow,&lt;br /&gt;
  so aggregation compounds down the stack. The capacity a layer&lt;br /&gt;
  measures at its egress queue is the rate the layer below actually&lt;br /&gt;
  provides &amp;amp;mdash; a paced, shared lower flow rather than a nominal&lt;br /&gt;
  wire speed &amp;amp;mdash; so the signal stays meaningful at every level,&lt;br /&gt;
  and pushback cascades layer by layer.&lt;br /&gt;
&lt;br /&gt;
The fit has a boundary: at a shim over legacy media the layer below&lt;br /&gt;
neither enrolls nor marks, which is the deployment edge behind&lt;br /&gt;
Section 8&#039;s no-loss-response limitation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 10. Heritage and positioning ==&lt;br /&gt;
&lt;br /&gt;
mb-ecn is best understood as a rate-based ECN controller in the DECbit /&lt;br /&gt;
QCN line, adapted to an acknowledgement-less recursive layer.&lt;br /&gt;
&lt;br /&gt;
The core of the scheme dates to the first Ouroboros&lt;br /&gt;
congestion-avoidance policy (2020): a multi-bit queue-depth mark&lt;br /&gt;
relayed by forwarders to the receiver, a smoothed multi-bit&lt;br /&gt;
&amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; fed back on the reverse flow at the same 1/32 fixed&lt;br /&gt;
point used today, and a sender that paced packets from a time budget&lt;br /&gt;
and ran slow start, additive increase and multiplicative decrease on&lt;br /&gt;
wall-clock time slots &amp;amp;mdash; rate-based and acknowledgement-free from&lt;br /&gt;
the start, with RTT-independent allocation already an objective. The&lt;br /&gt;
present algorithm keeps that skeleton and reworks every estimator&lt;br /&gt;
around it: per-path aggregation with SFQ pacing, the rate-adaptive&lt;br /&gt;
boxcar receiver, elapsed-time-scaled AIMD with a derivative term and&lt;br /&gt;
rate-velocity damping, and the in-band capacity signal.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;DECbit&#039;&#039;&#039; (Ramakrishnan &amp;amp;amp; Jain, 1988; ToCS 1990) established&lt;br /&gt;
  binary congestion feedback set by the switch on queue occupancy.&lt;br /&gt;
  mb-ecn keeps the switch-sets-on-queue idea and carries a&lt;br /&gt;
  &#039;&#039;magnitude&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;The Congestion Manager&#039;&#039;&#039; (Balakrishnan et al., SIGCOMM 1999;&lt;br /&gt;
  RFC 3124, 2001) aggregates congestion state at the end host: one&lt;br /&gt;
  controller per macroflow, a scheduler apportioning its rate across&lt;br /&gt;
  the member streams, and new streams joining at the ensemble&#039;s&lt;br /&gt;
  current state instead of probing from scratch. mb-ecn&#039;s&lt;br /&gt;
  (destination, QoS cube) aggregate is the same idea placed inside&lt;br /&gt;
  the IPCP: every layer manages its own macroflows, recursively, the&lt;br /&gt;
  SFQ pacer plays the CM scheduler&#039;s role (Section 4), and a joining&lt;br /&gt;
  flow rides the aggregate&#039;s estimates (Section 5.1). The CM gathers&lt;br /&gt;
  its signal from transport feedback at the edge; mb-ecn reads it&lt;br /&gt;
  off the wire from the forwarders.&lt;br /&gt;
* &#039;&#039;&#039;DCTCP&#039;&#039;&#039; (Alizadeh et al., SIGCOMM 2010) recovers a queue&lt;br /&gt;
  magnitude at the endpoint by averaging a single threshold bit. mb-ecn&lt;br /&gt;
  instead reads the magnitude off the wire, then time-averages it at&lt;br /&gt;
  the receiver. The additive-increase / proportional-decrease structure&lt;br /&gt;
  is shared.&lt;br /&gt;
* &#039;&#039;&#039;QCN&#039;&#039;&#039; (IEEE 802.1Qau, 2010) and &#039;&#039;&#039;DCQCN&#039;&#039;&#039; (Zhu et al., SIGCOMM&lt;br /&gt;
  2015) contribute the rate-based (not window-based) ECN control&lt;br /&gt;
  skeleton. mb-ecn&#039;s proportional decrease plus a probing increase&lt;br /&gt;
  mirrors that skeleton; the increase here is wall-clock scaled rather&lt;br /&gt;
  than byte-counter/timer based.&lt;br /&gt;
* &#039;&#039;&#039;HPCC&#039;&#039;&#039; (Li et al., SIGCOMM 2019) and &#039;&#039;&#039;PowerTCP&#039;&#039;&#039; (Addanki et&lt;br /&gt;
  al., NSDI 2022) combine a queue term &#039;&#039;and&#039;&#039; a rate term (via in-band&lt;br /&gt;
  telemetry or delay). mb-ecn now carries a coarse in-band rate (the&lt;br /&gt;
  quarter-log2 path MIN, Section 2) but uses it only to scale the&lt;br /&gt;
  controller&#039;s gains; the congestion &#039;&#039;signal&#039;&#039; remains queue-only,&lt;br /&gt;
  which is still the main functional difference (Section 8).&lt;br /&gt;
* &#039;&#039;&#039;L4S / TCP Prague&#039;&#039;&#039; (RFC 9330&amp;amp;ndash;9332, 2023) make&lt;br /&gt;
  RTT-independence a requirement and use a high-frequency single-bit&lt;br /&gt;
  signal. mb-ecn shares the RTT-independent &#039;&#039;allocation&#039;&#039; goal but&lt;br /&gt;
  reaches it by wall-clock-scaled rate control, with a low-frequency&lt;br /&gt;
  multi-bit signal.&lt;br /&gt;
&lt;br /&gt;
The novel combination is the multi-bit magnitude on the wire fused with&lt;br /&gt;
a wall-clock-scaled rate pacer in a setting with no per-flow ack clock.&lt;br /&gt;
The trade-off, made explicit above, is a queue-only signal and a&lt;br /&gt;
reactivity capped by the averaging window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 11. Parameters ==&lt;br /&gt;
&lt;br /&gt;
These are the values every figure in this document instantiates. They&lt;br /&gt;
are &#039;&#039;&#039;coupled&#039;&#039;&#039;: the mechanisms compose only while certain relations&lt;br /&gt;
hold &amp;amp;mdash; the gap-restart horizon at the rate floor must exceed a&lt;br /&gt;
packet&#039;s service time (or a floor-rate flow restart-loops at onset),&lt;br /&gt;
a floor-rate flow&#039;s target window must fit under the window ceiling,&lt;br /&gt;
the control interval must sit under the damping bucket under the idle&lt;br /&gt;
credit cap, the rate floor must not exceed the slow-start seed, and&lt;br /&gt;
the derived-floor ceiling must stay under the rate ceiling. Retune&lt;br /&gt;
them as a set, against those invariants.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| Marking quantum &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; || 4 packets || Mark onset and step; layer-wide&lt;br /&gt;
|-&lt;br /&gt;
| Feedback resolution || 1/32 &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; unit || Fixed-point LSB of &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Full-congestion reference &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; || 512 || Mean queue of 64 packets&lt;br /&gt;
|-&lt;br /&gt;
| Receiver window target || 16 packets (16,000 bytes) || Samples per window&lt;br /&gt;
|-&lt;br /&gt;
| Initial receiver window || ~67 ms || Before rate is known&lt;br /&gt;
|-&lt;br /&gt;
| Receiver window bounds || ~1.05 ms &amp;amp;ndash; ~4.3 s || Clamp on &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Window resize weight || 1/4 || Moving average toward target&lt;br /&gt;
|-&lt;br /&gt;
| Early window close || 2 &amp;amp;times; target bytes || Speed-up escape&lt;br /&gt;
|-&lt;br /&gt;
| Gap restart || 4 windows || Idle horizon (rate-relative)&lt;br /&gt;
|-&lt;br /&gt;
| Additive slope &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; (default) || 2^16 B/s^2 || Lower clamp of derived slope&lt;br /&gt;
|-&lt;br /&gt;
| Probe time constant &amp;lt;code&amp;gt;T_probe&amp;lt;/code&amp;gt; || 8 s || Rate-proportional recovery&lt;br /&gt;
|-&lt;br /&gt;
| Derivative gain || 1/4 || Onset sharpening&lt;br /&gt;
|-&lt;br /&gt;
| Rate floor &amp;lt;code&amp;gt;r_min&amp;lt;/code&amp;gt; (default) || 2^13 B/s || Lower clamp of derived floor&lt;br /&gt;
|-&lt;br /&gt;
| Slow-start seed rate || 2^16 B/s || Initial rate&lt;br /&gt;
|-&lt;br /&gt;
| Slow-start time constant || 20 ms || Exponential ramp&lt;br /&gt;
|-&lt;br /&gt;
| Rate ceiling &amp;lt;code&amp;gt;r_max&amp;lt;/code&amp;gt; || 2^37 B/s || Upper clamp&lt;br /&gt;
|-&lt;br /&gt;
| Capacity scale || &amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt; / 32 || Floor and slope from bottleneck&lt;br /&gt;
|-&lt;br /&gt;
| Capacity smoothing || 1/2 || Moving average per feedback&lt;br /&gt;
|-&lt;br /&gt;
| Capacity staleness || 16 &amp;amp;times; feedback horizon || Revert to defaults&lt;br /&gt;
|-&lt;br /&gt;
| Derived floor ceiling || 2^32 B/s || Upper clamp on derived floor&lt;br /&gt;
|-&lt;br /&gt;
| Control interval || 1 ms || Minimum spacing of rate updates&lt;br /&gt;
|-&lt;br /&gt;
| Idle credit cap || 50 ms || Burst allowance / increase banking&lt;br /&gt;
|-&lt;br /&gt;
| Feedback staleness floor || ~268 ms || Floor of rate-relative horizon&lt;br /&gt;
|-&lt;br /&gt;
| Damping bucket || ~31 ms || Rate-velocity washout cadence&lt;br /&gt;
|-&lt;br /&gt;
| Damping fraction || 1/4 || Pullback per bucket&lt;br /&gt;
|-&lt;br /&gt;
| Estimator fold spacing || 1 ms || Minimum measurement cadence&lt;br /&gt;
|-&lt;br /&gt;
| Estimator window closure || 16 packets drained || Self-scaling window&lt;br /&gt;
|-&lt;br /&gt;
| Estimator window staleness || ~134 ms || Discard across traffic gaps&lt;br /&gt;
|-&lt;br /&gt;
| Estimator idle tolerance || 1/8 of arrivals || Unsaturated-window rejection&lt;br /&gt;
|-&lt;br /&gt;
| Estimator decay || 1/16 per window || Max-filter convergence from below&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 12. References ==&lt;br /&gt;
&lt;br /&gt;
=== 12.1. Source ===&lt;br /&gt;
&lt;br /&gt;
The implementation follows the algorithm above:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/ca/mb-ecn.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mb-ecn.h&amp;lt;/code&amp;gt; &amp;amp;mdash;&lt;br /&gt;
  receiver estimator, pacer and rate control.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/ca/ops.h&amp;lt;/code&amp;gt; &amp;amp;mdash; CA policy interface.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/cap.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cap.h&amp;lt;/code&amp;gt; &amp;amp;mdash; link-capacity&lt;br /&gt;
  estimator and quarter-log2 codec.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/dt.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fa.c&amp;lt;/code&amp;gt; &amp;amp;mdash; per-hop marking&lt;br /&gt;
  and stamping, and the feedback carrying &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;cap8&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/psched.c&amp;lt;/code&amp;gt; &amp;amp;mdash; consumes the pacing deadline.&lt;br /&gt;
&lt;br /&gt;
=== 12.2. Literature ===&lt;br /&gt;
&lt;br /&gt;
* K. K. Ramakrishnan, R. Jain, &amp;quot;A Binary Feedback Scheme for Congestion&lt;br /&gt;
  Avoidance in Computer Networks&amp;quot; (DECbit), ACM ToCS, 1990.&lt;br /&gt;
* H. Balakrishnan, H. S. Rahul, S. Seshan, &amp;quot;An Integrated Congestion&lt;br /&gt;
  Management Architecture for Internet Hosts&amp;quot;, SIGCOMM 1999;&lt;br /&gt;
  H. Balakrishnan, S. Seshan, &amp;quot;The Congestion Manager&amp;quot;, RFC 3124, 2001.&lt;br /&gt;
* M. Alizadeh et al., &amp;quot;Data Center TCP (DCTCP)&amp;quot;, SIGCOMM 2010.&lt;br /&gt;
* Y. Zhu et al., &amp;quot;Congestion Control for Large-Scale RDMA Deployments&amp;quot;&lt;br /&gt;
  (DCQCN), SIGCOMM 2015.&lt;br /&gt;
* R. Mittal et al., &amp;quot;TIMELY: RTT-based Congestion Control&amp;quot;, SIGCOMM 2015.&lt;br /&gt;
* Y. Li et al., &amp;quot;HPCC: High Precision Congestion Control&amp;quot;, SIGCOMM 2019.&lt;br /&gt;
* V. Addanki et al., &amp;quot;PowerTCP&amp;quot;, NSDI 2022.&lt;br /&gt;
* K. De Schepper, B. Briscoe (eds.), &amp;quot;Low Latency, Low Loss, and&lt;br /&gt;
  Scalable Throughput (L4S)&amp;quot;, RFC 9330/9331/9332.&lt;br /&gt;
* F. Kelly, &amp;quot;Charging and Rate Control for Elastic Traffic&amp;quot;, 1997;&lt;br /&gt;
  R. Srikant, &amp;quot;The Mathematics of Internet Congestion Control&amp;quot;, 2004.&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1938</id>
		<title>Congestion avoidance</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1938"/>
		<updated>2026-07-11T20:46:34Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 1. Overview */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:mb-ecn - Multi-bit ECN Congestion Avoidance}}&lt;br /&gt;
&lt;br /&gt;
mb-ecn is a congestion-avoidance (CA) policy for the Ouroboros unicast&lt;br /&gt;
IPCP. It lives in the IPC Process: the flow-and-retransmission task&lt;br /&gt;
(FRCP) provides reliability, in-order delivery and flow control&lt;br /&gt;
end-to-end; congestion avoidance is orthogonal and runs per IPCP&lt;br /&gt;
layer. Within a layer, the IPCP controls &#039;&#039;&#039;aggregates&#039;&#039;&#039;, one loop&lt;br /&gt;
per (destination address, QoS cube), shared by every flow toward that&lt;br /&gt;
destination. The alternative policy is &amp;lt;code&amp;gt;nop&amp;lt;/code&amp;gt; (no congestion&lt;br /&gt;
avoidance).&lt;br /&gt;
&lt;br /&gt;
This document describes the algorithm: what the policy does and why.&lt;br /&gt;
The implementation follows it ([[#12. References|Section 12]] maps the&lt;br /&gt;
pieces to source files). It does not claim properties the algorithm&lt;br /&gt;
does not have; [[#8. Limitations|Section 8]] states the known gaps&lt;br /&gt;
explicitly.&lt;br /&gt;
&lt;br /&gt;
The keywords &amp;quot;MUST&amp;quot;, &amp;quot;SHOULD&amp;quot;, &amp;quot;MAY&amp;quot; etc. are used only where they&lt;br /&gt;
appear in all capitals, per BCP 14 (RFC 2119, RFC 8174).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt;&lt;br /&gt;
:Paced send rate, bytes per second.&lt;br /&gt;
;&amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bottleneck capacity, bytes per second.&lt;br /&gt;
;&amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt;&lt;br /&gt;
:Packet size in bytes.&lt;br /&gt;
;&amp;lt;code&amp;gt;q&amp;lt;/code&amp;gt;&lt;br /&gt;
:Standing queue occupancy at the bottleneck, in packets.&lt;br /&gt;
;&amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt;&lt;br /&gt;
:Marking quantum, packets per mark unit (4).&lt;br /&gt;
;&amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt;&lt;br /&gt;
:Per-packet congestion mark set by a forwarder, an unsigned integer&lt;br /&gt;
 (on-wire field is 8-bit).&lt;br /&gt;
;&amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
:Receiver-side congestion estimate, fixed point with LSB = 1/32 of an&lt;br /&gt;
 &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; unit, i.e. &amp;lt;code&amp;gt;ece = 32 &amp;amp;times; (time-mean ecn)&amp;lt;/code&amp;gt;.&lt;br /&gt;
;&amp;lt;code&amp;gt;cap8&amp;lt;/code&amp;gt;&lt;br /&gt;
:Path-capacity code carried per packet, quarter-log2:&lt;br /&gt;
 &amp;lt;code&amp;gt;C &amp;amp;asymp; 2^(cap8 / 4)&amp;lt;/code&amp;gt; bytes/s, 0 = unknown.&lt;br /&gt;
;&amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt;&lt;br /&gt;
:Receiver averaging window, nanoseconds.&lt;br /&gt;
;&amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt;&lt;br /&gt;
:Additive-increase slope, bytes/s per second.&lt;br /&gt;
;&amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt;&lt;br /&gt;
:Full-congestion reference level of &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; (512).&lt;br /&gt;
;&amp;lt;code&amp;gt;&amp;amp;Delta;t&amp;lt;/code&amp;gt;&lt;br /&gt;
:Elapsed wall-clock time between two events, nanoseconds.&lt;br /&gt;
;&amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;&lt;br /&gt;
:Path round-trip time (feedback delay). The controller does not&lt;br /&gt;
 measure it.&lt;br /&gt;
&lt;br /&gt;
Rate is the control variable throughout.&lt;br /&gt;
&lt;br /&gt;
All concrete figures in this document &amp;amp;mdash; window bounds, horizons,&lt;br /&gt;
rate thresholds &amp;amp;mdash; are instances of the parameter values in&lt;br /&gt;
[[#11. Parameters|Section 11]]. The mechanisms and the relations&lt;br /&gt;
between parameters are what the algorithm fixes; retuning the&lt;br /&gt;
parameters moves the figures together.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
mb-ecn is a rate-based, ECN-driven controller with three moving parts,&lt;br /&gt;
one at each point of the loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        sender                                  forwarder&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
      |  SFQ virtual-clock pacer       |       |  ecn = MAX(ecn, queue/Q) |&lt;br /&gt;
      |  rate r, paced per packet      | data  |  (deepest queue wins)    |&lt;br /&gt;
      |  AIMD + PD control on r        |------&amp;gt;|  cap8 = MIN(cap8, link)  |&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
                 ^                                        |&lt;br /&gt;
                 | ece + cap8 feedback                    | marked data&lt;br /&gt;
                 |                                        v&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
      |  reads downstream ece,         |&amp;lt;------|  receiver                |&lt;br /&gt;
      |  updates the rate on feedback, |  ece  |  time-mean of ecn over   |&lt;br /&gt;
      |  scales floor/slope to cap8    |  cap8 |  an adaptive window      |&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# The &#039;&#039;&#039;forwarder&#039;&#039;&#039; marks each packet with a &#039;&#039;multi-bit&#039;&#039; magnitude&lt;br /&gt;
  proportional to its standing queue, and MIN-stamps its measured&lt;br /&gt;
  outgoing-link capacity into the same PCI.&lt;br /&gt;
# The &#039;&#039;&#039;receiver&#039;&#039;&#039; turns the stream of marks into a time-averaged&lt;br /&gt;
  congestion estimate &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; and feeds it back to the sender,&lt;br /&gt;
  together with the window minimum of the path-capacity codes.&lt;br /&gt;
# The &#039;&#039;&#039;sender&#039;&#039;&#039; paces its rate with a start-time fair-queuing (SFQ)&lt;br /&gt;
  virtual clock, and adjusts that rate with additive-increase /&lt;br /&gt;
  multiplicative-decrease (AIMD) driven by &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;; the rate floor&lt;br /&gt;
  and the additive slope derive from the fed-back path capacity.&lt;br /&gt;
&lt;br /&gt;
The controller runs on packet sends and on feedback arrivals. All&lt;br /&gt;
increase/decrease steps are scaled by elapsed&lt;br /&gt;
wall-clock time, which is what makes the &#039;&#039;allocation&#039;&#039; independent of&lt;br /&gt;
RTT (see [[#6. RTT behaviour|Section 6]]).&lt;br /&gt;
&lt;br /&gt;
The unit of control is the &#039;&#039;&#039;aggregate&#039;&#039;&#039;, kept in the IPCP per&lt;br /&gt;
&amp;lt;code&amp;gt;(destination address, QoS cube)&amp;lt;/code&amp;gt;. Every flow toward that&lt;br /&gt;
destination at that QoS shares one controller and one rate: a new&lt;br /&gt;
flow joins the aggregate at its current rate, and a departing flow&lt;br /&gt;
leaves the rate to the others. The pacer divides the aggregate rate&lt;br /&gt;
fairly across the member flows (SFQ), so per-flow fairness within an&lt;br /&gt;
aggregate is a scheduler property, distinct from the rate law&lt;br /&gt;
described here.&lt;br /&gt;
&lt;br /&gt;
== 2. Forwarder marking ==&lt;br /&gt;
&lt;br /&gt;
A forwarder marks a packet as a function of the packets already queued&lt;br /&gt;
on the outgoing link:&lt;br /&gt;
&lt;br /&gt;
  mark = queued / Q                         (Q = 4 packets)&lt;br /&gt;
  ecn  = MAX(ecn, mark)                     (saturating, 8-bit field)&lt;br /&gt;
&lt;br /&gt;
The quantum &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; is a layer-wide constant: the marks it&lt;br /&gt;
quantizes are combined by MAX across hops, so all forwarders in a&lt;br /&gt;
layer MUST use the same quantum for the signal to be comparable.&lt;br /&gt;
&lt;br /&gt;
Two properties:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-bit magnitude.&#039;&#039;&#039; The mark is an integer proportional to&lt;br /&gt;
  queue depth, quantized in steps of &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; packets: a coarse&lt;br /&gt;
  readout of the standing queue.&lt;br /&gt;
* &#039;&#039;&#039;MAX along the path.&#039;&#039;&#039; A packet crossing several forwarders carries&lt;br /&gt;
  the maximum mark, i.e. the deepest queue on its path. MAX keeps the&lt;br /&gt;
  signal range-stable and monotone across hops.&lt;br /&gt;
&lt;br /&gt;
There is a &#039;&#039;&#039;dead zone&#039;&#039;&#039;: for &amp;lt;code&amp;gt;queued &amp;lt; Q&amp;lt;/code&amp;gt; the mark is 0.&lt;br /&gt;
&lt;br /&gt;
=== Capacity stamping ===&lt;br /&gt;
&lt;br /&gt;
Each forwarder also measures the capacity of its outgoing link and&lt;br /&gt;
stamps it into the packet. The estimate is a busy-period drain rate:&lt;br /&gt;
the queue is observed at most once per millisecond, and a measurement&lt;br /&gt;
window stays open until 16 packets have drained, so its length&lt;br /&gt;
&#039;&#039;&#039;self-scales with the link rate&#039;&#039;&#039; &amp;amp;mdash; the millisecond cadence&lt;br /&gt;
bounds it on fast links, the 16-packet drain time on slow ones&lt;br /&gt;
(~19 ms at 10 Mbit/s). A window in which more than 1/8 of&lt;br /&gt;
the arrivals found the queue empty is discarded as unsaturated;&lt;br /&gt;
occasional empty observations (token-bucket shapers grazing zero) are&lt;br /&gt;
tolerated. The drain rate of a saturated queue is the link rate; a max&lt;br /&gt;
filter with slow decay (1/16 per window) converges on it from below. A&lt;br /&gt;
window that opened or closed on an empty queue may have drained into&lt;br /&gt;
buffers below at above-wire rate, so it may lower the estimate but&lt;br /&gt;
never raise it. The code on the wire is quarter-log2&lt;br /&gt;
(&amp;lt;code&amp;gt;cap8 = 4 log2 C&amp;lt;/code&amp;gt;, ~19% per step, 0 = unknown) and each hop&lt;br /&gt;
MIN-combines its own code into the byte, so a packet arrives carrying&lt;br /&gt;
the bottleneck&#039;s rate. A hop that has never been backlogged stamps&lt;br /&gt;
nothing; since only backlogged hops matter, the signal exists exactly&lt;br /&gt;
when it is needed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. Receiver estimate ==&lt;br /&gt;
&lt;br /&gt;
The receiver converts marks into a smoothed estimate &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; over a&lt;br /&gt;
time window &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt; that &#039;&#039;&#039;adapts to the incoming byte rate&#039;&#039;&#039;. It is&lt;br /&gt;
a &#039;&#039;&#039;boxcar (rectangular) time-integral mean&#039;&#039;&#039;. Per packet, with&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;Delta;t&amp;lt;/code&amp;gt; the gap since the previous packet:&lt;br /&gt;
&lt;br /&gt;
  A += ecn * MIN(&amp;amp;Delta;t, W)              dwell-weighted; one packet&lt;br /&gt;
                                          weighs at most one window&lt;br /&gt;
  B += S                                  accumulate bytes this window&lt;br /&gt;
  ...&lt;br /&gt;
  at window close (elapsed &amp;gt;= W, or B &amp;gt;= 2 * B_target with at&lt;br /&gt;
                   least the window floor elapsed):&lt;br /&gt;
      ece = 32 * A / elapsed              mean over the actual window&lt;br /&gt;
      W  += (B_target * elapsed / B - W) / 4    nudge toward the size&lt;br /&gt;
                                                that holds the target&lt;br /&gt;
      W   = clamp(W, ~1.05 ms, ~4.3 s)&lt;br /&gt;
      A   = 0 ; B = 0                     hard reset&lt;br /&gt;
&lt;br /&gt;
At each close the next window is nudged by a quarter-weight moving&lt;br /&gt;
average toward the size that would hold about 16 packets at the&lt;br /&gt;
measured byte rate, from an initial ~67 ms. The target is byte-based (&amp;lt;code&amp;gt;B_target&amp;lt;/code&amp;gt; = 16,000 bytes), so&lt;br /&gt;
&amp;quot;16 packets&amp;quot; is exact only at ~1000-byte packets, and the window&lt;br /&gt;
converges to it over several closes. The mean always divides by the&lt;br /&gt;
&#039;&#039;&#039;actual&#039;&#039;&#039; elapsed window, so a rate step leaves the current&lt;br /&gt;
estimate exact and only re-sizes the &#039;&#039;next&#039;&#039; window. The effect is a roughly constant sample&lt;br /&gt;
count &amp;amp;mdash; ~16 packets &amp;amp;mdash; from about 30 kbit/s to 122 Mbit/s;&lt;br /&gt;
above that the window floors at ~1 ms (a cadence still carrying&lt;br /&gt;
thousands of samples at 10&amp;amp;ndash;100 GbE), and below ~30 kbit/s it&lt;br /&gt;
saturates at the ~4.3 s ceiling. The averaging clock stretches with&lt;br /&gt;
the flow the way TCP&#039;s ACK clock stretches with the RTT. A speed-up&lt;br /&gt;
escapes a stretched window early: once twice the target bytes arrive&lt;br /&gt;
the window closes anyway (the window floor keeps that cadence bounded&lt;br /&gt;
at high rate). The fed-back &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; is thus a piecewise-constant&lt;br /&gt;
staircase whose step period tracks the rate. Two edge cases:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Onset&#039;&#039;&#039; (first mark after an idle estimate) emits the&lt;br /&gt;
  instantaneous mark undiluted, so a starting queue is reported without&lt;br /&gt;
  a full window of delay.&lt;br /&gt;
* &#039;&#039;&#039;Gap&#039;&#039;&#039; longer than 4 windows resets the window and emits the raw&lt;br /&gt;
  sample. The threshold is &#039;&#039;&#039;rate-relative&#039;&#039;&#039;: idle means a few&lt;br /&gt;
  &#039;&#039;current&#039;&#039; windows of silence, so a slow flow&#039;s normal inter-packet&lt;br /&gt;
  gap never reads as idle, while the in-window dwell clamp above bounds&lt;br /&gt;
  what a genuine pause can add to the mean before the restart fires.&lt;br /&gt;
&lt;br /&gt;
Alongside the mark integral, the receiver keeps the MIN of the nonzero&lt;br /&gt;
capacity codes seen in the current window; each emitted &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
carries that minimum (the raw packet&#039;s code on onset/gap restarts) and&lt;br /&gt;
the fold then resets, so a reroute to a faster path can raise the&lt;br /&gt;
fed-back capacity within one window.&lt;br /&gt;
&lt;br /&gt;
The estimate is fed back to the sender on the reverse flow.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 4. Sender pacer ==&lt;br /&gt;
&lt;br /&gt;
The sender paces with an SFQ virtual clock. The context keeps a&lt;br /&gt;
virtual time &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;; each flow keeps a finish tag &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
  V    += r * &amp;amp;Delta;t                     virtual time advances by bytes served&lt;br /&gt;
  s     = MAX(F, V)                  a packet behind the clock starts now&lt;br /&gt;
  F     = s + S                      the one after it queues behind&lt;br /&gt;
  wait  = (s - V) / r                time until this packet may go&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;wait&amp;lt;/code&amp;gt; becomes the flow&#039;s scheduling deadline: a packet&lt;br /&gt;
already behind the virtual clock is sent immediately&lt;br /&gt;
(&amp;lt;code&amp;gt;wait = 0&amp;lt;/code&amp;gt;); one ahead waits, deferring only its own flow.&lt;br /&gt;
A clock advance across an idle gap longer than 50 ms credits at most&lt;br /&gt;
the calling flow&#039;s owed lead plus one burst (50 ms of service, at&lt;br /&gt;
least one packet): a paced flow slower than one packet per 50 ms&lt;br /&gt;
receives its &#039;&#039;&#039;true&#039;&#039;&#039; elapsed service &amp;amp;mdash; a time-capped credit&lt;br /&gt;
would decay such a flow without bound &amp;amp;mdash; while an idle flow still&lt;br /&gt;
cannot bank an unbounded burst.&lt;br /&gt;
&lt;br /&gt;
The virtual time &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; is shared by every flow in the context;&lt;br /&gt;
the finish tags are per flow. Because every flow computes its start&lt;br /&gt;
tag &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; against the same &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;, a flow that has just sent&lt;br /&gt;
carries a finish tag ahead of &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; and waits, while an idle flow&lt;br /&gt;
(finish tag at or behind &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;) starts immediately &amp;amp;mdash; this is&lt;br /&gt;
what fairly divides the aggregate rate &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; across them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 5. Rate control (AIMD + PD) ==&lt;br /&gt;
&lt;br /&gt;
The rate law runs on packet sends, at most once per millisecond, and&lt;br /&gt;
also on feedback arrivals so that a sender with no traffic of its own&lt;br /&gt;
still reacts. Each increase term and the proportional decrease are&lt;br /&gt;
scaled by elapsed wall-clock time, each by its own clock: the&lt;br /&gt;
increase terms use time banked and &#039;&#039;&#039;capped&#039;&#039;&#039; at 50 ms (an idle gap&lt;br /&gt;
cannot bank an unbounded ramp), while the proportional decrease uses&lt;br /&gt;
the &#039;&#039;&#039;uncapped&#039;&#039;&#039; elapsed time (a starved sender still cuts by the&lt;br /&gt;
right amount). The one-sided derivative (Section 5.3) is a per-delta&lt;br /&gt;
term.&lt;br /&gt;
&lt;br /&gt;
=== 5.1. Slow start ===&lt;br /&gt;
&lt;br /&gt;
Before the first congestion signal the rate ramps exponentially with a&lt;br /&gt;
20 ms time constant:&lt;br /&gt;
&lt;br /&gt;
  r += r * &amp;amp;Delta;t / T_ss                  (T_ss = 20 ms)&lt;br /&gt;
&lt;br /&gt;
Slow start ends the first time a congestion signal arrives &amp;amp;mdash;&lt;br /&gt;
fed back from the receiver or observed at the sender&#039;s own first hop&lt;br /&gt;
&amp;amp;mdash; and never resumes.&lt;br /&gt;
&lt;br /&gt;
Slow start belongs to the aggregate, so it runs &#039;&#039;&#039;once per&lt;br /&gt;
(destination, QoS cube)&#039;&#039;&#039;, on the first flow. A flow arriving at an&lt;br /&gt;
established aggregate rides the existing estimates: it enters at the&lt;br /&gt;
aggregate&#039;s current rate and receives its fair share of it through&lt;br /&gt;
the pacer (Section 4), with no probing of its own.&lt;br /&gt;
&lt;br /&gt;
=== 5.2. Increase: additive plus proportional probe (always on) ===&lt;br /&gt;
&lt;br /&gt;
Past slow start, two increase terms are applied every step, both&lt;br /&gt;
regardless of congestion:&lt;br /&gt;
&lt;br /&gt;
  r += a * &amp;amp;Delta;t                         additive; a defaults to 2^16 B/s^2&lt;br /&gt;
  r += r * &amp;amp;Delta;t / T_probe               proportional; T_probe = 8 s&lt;br /&gt;
&lt;br /&gt;
The additive term is a fixed per-context slope (bytes/s per second),&lt;br /&gt;
scaled to the path capacity (Section 5.5). The proportional probe&lt;br /&gt;
grows the rate by a fixed fraction per unit time (e-folds over&lt;br /&gt;
&amp;lt;code&amp;gt;T_probe&amp;lt;/code&amp;gt;): a flow recovers in the same number of steps at any&lt;br /&gt;
link rate; the fixed additive term&#039;s effect vanishes relative to a&lt;br /&gt;
fast link. Both are the &amp;quot;probing&amp;quot;&lt;br /&gt;
pressure the multiplicative decrease balances at equilibrium; the&lt;br /&gt;
probe&#039;s cost is a rate-independent standing-queue floor (Section 7).&lt;br /&gt;
&lt;br /&gt;
=== 5.3. Multiplicative decrease (proportional + derivative) ===&lt;br /&gt;
&lt;br /&gt;
With congestion level &amp;lt;code&amp;gt;m&amp;lt;/code&amp;gt; (the fed-back &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;, or a local&lt;br /&gt;
first-hop mark as fallback), the decrease is the sum of a proportional&lt;br /&gt;
term and a one-sided derivative term &amp;amp;mdash; a PD controller on the&lt;br /&gt;
congestion signal:&lt;br /&gt;
&lt;br /&gt;
  mark = MIN(m, M)                                   M = 512&lt;br /&gt;
  rise = MAX(m - m_prev, 0), capped at M             one-sided derivative&lt;br /&gt;
&lt;br /&gt;
  cut  = r * rise / (4 * M)                          derivative, gain 1/4&lt;br /&gt;
  cut += r * mark * &amp;amp;Delta;t_ms / (1000 * M)              proportional&lt;br /&gt;
  cut  = MIN(cut, r / 2)                             at most a halving&lt;br /&gt;
&lt;br /&gt;
  r     -= cut&lt;br /&gt;
  m_prev = m&lt;br /&gt;
&lt;br /&gt;
* The &#039;&#039;&#039;proportional&#039;&#039;&#039; term is scaled by honest elapsed time in&lt;br /&gt;
  milliseconds, so a starved sender that has not run the controller&lt;br /&gt;
  for a while still cuts by the right amount.&lt;br /&gt;
* The &#039;&#039;&#039;derivative&#039;&#039;&#039; term (&amp;lt;code&amp;gt;rise&amp;lt;/code&amp;gt;) reacts to an &#039;&#039;increase&#039;&#039; in&lt;br /&gt;
  the congestion level between samples; it is one-sided and per-delta.&lt;br /&gt;
  It sharpens the reaction at the onset of congestion.&lt;br /&gt;
* The cut is clamped to &amp;lt;code&amp;gt;r/2&amp;lt;/code&amp;gt; per step, bounding the decrease to&lt;br /&gt;
  a halving.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;M = 512 = 32 &amp;amp;times; 16&amp;lt;/code&amp;gt; defines &amp;quot;full congestion&amp;quot; as a mean&lt;br /&gt;
&amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; of 16, i.e. a mean queue of &amp;lt;code&amp;gt;16 &amp;amp;times; Q = 64&amp;lt;/code&amp;gt; packets.&lt;br /&gt;
&lt;br /&gt;
=== 5.4. Staleness ===&lt;br /&gt;
&lt;br /&gt;
Feedback arrives once per receiver window, and the window stretches&lt;br /&gt;
with the flow&#039;s byte rate (Section 3) &amp;amp;mdash; so the staleness horizon&lt;br /&gt;
must stretch with it, or a slow flow&#039;s mark would age out &#039;&#039;between&#039;&#039;&lt;br /&gt;
feedbacks and the rate would ramp against a congested path. The&lt;br /&gt;
sender mirrors the receiver: the horizon is four target windows of&lt;br /&gt;
bytes at the current rate, floored at ~268 ms so fast flows keep a&lt;br /&gt;
fixed horizon; at the default rate floor it is ~8 s. A signal older&lt;br /&gt;
than the horizon is aged out (both the fed-back estimate and the&lt;br /&gt;
local mark), freeing the rate to ramp once congestion clears. The&lt;br /&gt;
rate is clamped to&lt;br /&gt;
&amp;lt;code&amp;gt;[r_min, r_max]&amp;lt;/code&amp;gt; on every step.&lt;br /&gt;
&lt;br /&gt;
=== 5.5. Capacity-derived floor and slope ===&lt;br /&gt;
&lt;br /&gt;
The fed-back capacity code sets the two rate-scale constants of the&lt;br /&gt;
controller, per context:&lt;br /&gt;
&lt;br /&gt;
  target = decode(cap8) / 32              clamped to [2^13, 2^32] B/s&lt;br /&gt;
  r_min += (target - r_min) / 2           per feedback (moving average)&lt;br /&gt;
  a      = r_min&lt;br /&gt;
&lt;br /&gt;
The defaults are fixed constants (&amp;lt;code&amp;gt;r_min = 2^13 B/s&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;a = 2^16 B/s^2&amp;lt;/code&amp;gt;), used on paths that never report a capacity;&lt;br /&gt;
with the estimator live the fairness floor and the post-cut recovery&lt;br /&gt;
slope scale with the bottleneck, and capacity engages wherever&lt;br /&gt;
&amp;lt;code&amp;gt;C/32&amp;lt;/code&amp;gt; clears the default floor, i.e. above ~2 Mbit/s. A&lt;br /&gt;
capacity older than 16 staleness horizons (~4.3 s for fast flows)&lt;br /&gt;
reverts both to the defaults. That horizon deliberately outlives the&lt;br /&gt;
feedback horizon: feedback stops the moment the marks clear, which is&lt;br /&gt;
exactly when the recovery slope is needed; the onset-fresh capacity&lt;br /&gt;
(Section 3) re-seeds it on the first mark of the next episode either&lt;br /&gt;
way.&lt;br /&gt;
&lt;br /&gt;
=== 5.6. Ramp and recovery ===&lt;br /&gt;
&lt;br /&gt;
Three mechanisms set how fast an aggregate reaches a fast link&#039;s&lt;br /&gt;
rate, each owning one regime:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;From scratch: slow start.&#039;&#039;&#039; The exponential ramp reaches any&lt;br /&gt;
  physical link rate in well under a second (~200 ms from the seed to&lt;br /&gt;
  10 Gbit/s; each further doubling of link speed costs one more&lt;br /&gt;
  ~14 ms doubling time). On an uncongested path nothing marks, so&lt;br /&gt;
  slow start runs until the aggregate arrives at its bottleneck.&lt;br /&gt;
* &#039;&#039;&#039;After a cut: the probe.&#039;&#039;&#039; A decrease is at most a halving per&lt;br /&gt;
  step, and the mark ages out on the rate-relative horizon once&lt;br /&gt;
  congestion clears (Section 5.4); the proportional probe then heals&lt;br /&gt;
  a halving in &amp;lt;code&amp;gt;T_probe &amp;amp;middot; ln 2 &amp;amp;asymp; 5.5 s&amp;lt;/code&amp;gt; at any&lt;br /&gt;
  link rate.&lt;br /&gt;
* &#039;&#039;&#039;At the bottom: the capacity floor.&#039;&#039;&#039; The rate sits at most a&lt;br /&gt;
  factor 32 below a measured bottleneck, and the additive slope&lt;br /&gt;
  refills &amp;lt;code&amp;gt;C/32&amp;lt;/code&amp;gt; per second, so the deepest hole is bounded:&lt;br /&gt;
  floor to full capacity in tens of seconds, a halving in seconds.&lt;br /&gt;
&lt;br /&gt;
These compose because a deep cut requires sustained congestion,&lt;br /&gt;
sustained congestion backlogs the bottleneck queue, and a backlogged&lt;br /&gt;
queue is exactly when its capacity is measured and stamped&lt;br /&gt;
(Section 2): whenever the controller is cut deep, the scaled floor is&lt;br /&gt;
live. The default floor only governs paths that never congested the&lt;br /&gt;
sender &amp;amp;mdash; where there is nothing to recover from. This is why&lt;br /&gt;
slow start can end for good: the floor and the probe cover every&lt;br /&gt;
later recovery.&lt;br /&gt;
&lt;br /&gt;
=== 5.7. Regimes ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   +-------------+   first mark seen    +----------------+&lt;br /&gt;
   | slow start  |---------------------&amp;gt;|  congestion    |&lt;br /&gt;
   | r *= e^(t/T)|                      |  avoidance     |&lt;br /&gt;
   +-------------+                      |                |&lt;br /&gt;
                                        | m == 0 -&amp;gt; AI   |&lt;br /&gt;
                                        | m  &amp;gt; 0 -&amp;gt; AIPD |&lt;br /&gt;
                                        +----------------+&lt;br /&gt;
    m = fed-back ece, or the local first-hop mark when ece == 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 6. RTT behaviour ==&lt;br /&gt;
&lt;br /&gt;
Because every increase and decrease is scaled by wall-clock time,&lt;br /&gt;
two flows of different RTT that share a bottleneck obey the &#039;&#039;same&#039;&#039;&lt;br /&gt;
rate law and converge to the &#039;&#039;same&#039;&#039; rate. The steady-state&lt;br /&gt;
&#039;&#039;&#039;allocation is RTT-independent&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
This is a property of the equilibrium, not of the dynamics. The&lt;br /&gt;
feedback delay in the loop &#039;&#039;is&#039;&#039; the path RTT &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;, and the&lt;br /&gt;
controller does not measure or compensate for it. At low rate the&lt;br /&gt;
receiver window (up to its ~4.3 s ceiling) is the dominant lag &amp;amp;mdash;&lt;br /&gt;
the loop is slow there in proportion to how slow the flow is, exactly&lt;br /&gt;
as a long-RTT TCP is &amp;amp;mdash; while as the rate rises the window shrinks&lt;br /&gt;
toward its ~1 ms floor, so the window&#039;s contribution to the loop delay&lt;br /&gt;
&#039;&#039;falls&#039;&#039; with capacity and the path RTT &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; becomes the&lt;br /&gt;
irreducible term. Two mechanisms keep the loop damped: the adaptive&lt;br /&gt;
window removes the delay-dominated corner at low &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;, and a&lt;br /&gt;
bucketed &#039;&#039;&#039;rate-velocity damping&#039;&#039;&#039; (a washout: each ~31 ms bucket&lt;br /&gt;
pulls the rate a quarter of the way back toward its value at the&lt;br /&gt;
previous bucket) supplies the damping the otherwise&lt;br /&gt;
near-double-integrator lacks, which is what holds the loop together at&lt;br /&gt;
high capacity and moderate &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;. What remains is the loop gain&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;prop; &amp;amp;radic;C&amp;lt;/code&amp;gt;: at very high capacity &#039;&#039;and&#039;&#039; large&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; the &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt; phase budget is the&lt;br /&gt;
binding constraint, and no window or damping trick removes it (see&lt;br /&gt;
[[#8. Limitations|Section 8]]). A precise statement is therefore:&lt;br /&gt;
&#039;&#039;&#039;RTT-fair in equilibrium, and dynamically stable up to a&lt;br /&gt;
capacity-dependent RTT limit set by &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 7. Fairness ==&lt;br /&gt;
&lt;br /&gt;
At equilibrium the two increase terms balance the proportional&lt;br /&gt;
decrease, &amp;lt;code&amp;gt;a + r/T_probe = r &amp;amp;middot; m*/M&amp;lt;/code&amp;gt;, which pins the&lt;br /&gt;
congestion level to &amp;lt;code&amp;gt;m* = M &amp;amp;middot; (a/r + 1/T_probe)&amp;lt;/code&amp;gt; and, since&lt;br /&gt;
&amp;lt;code&amp;gt;ece = 8q&amp;lt;/code&amp;gt; (one mark unit per &amp;lt;code&amp;gt;Q = 4&amp;lt;/code&amp;gt; packets, 32 fixed-point&lt;br /&gt;
steps per unit), a standing queue of&lt;br /&gt;
&lt;br /&gt;
  q* = 64 * a / r  +  64 / T_probe        (packets, r in bytes/s)&lt;br /&gt;
&lt;br /&gt;
The additive part (&amp;lt;code&amp;gt;64 &amp;amp;middot; a / r&amp;lt;/code&amp;gt;) vanishes with rate under&lt;br /&gt;
the default slope; with the capacity-derived slope (&amp;lt;code&amp;gt;a = C/32&amp;lt;/code&amp;gt;,&lt;br /&gt;
Section 5.5) it becomes &amp;lt;code&amp;gt;2 C / r&amp;lt;/code&amp;gt; &amp;amp;mdash; about two packets per&lt;br /&gt;
competing flow, rate-independent. The proportional-probe part&lt;br /&gt;
(&amp;lt;code&amp;gt;64 / T_probe&amp;lt;/code&amp;gt;) is a rate-independent floor (~8 packets at&lt;br /&gt;
8 s). That floor is the cost of rate-independent convergence, and it&lt;br /&gt;
keeps &amp;lt;code&amp;gt;q*&amp;lt;/code&amp;gt; above the marking threshold at high rate.&lt;br /&gt;
&lt;br /&gt;
Two flows at the same bottleneck see the same &amp;lt;code&amp;gt;m*&amp;lt;/code&amp;gt;, so each&lt;br /&gt;
solves &amp;lt;code&amp;gt;r_i = a / (m*/M - 1/T_probe)&amp;lt;/code&amp;gt; to the same rate. Under&lt;br /&gt;
the network-utility-maximization view this corresponds to&lt;br /&gt;
&#039;&#039;&#039;proportional fairness&#039;&#039;&#039; (equal weights), which is max-min fair at&lt;br /&gt;
a single bottleneck.&lt;br /&gt;
&lt;br /&gt;
Measurement fidelity at low rate comes from the estimator itself:&lt;br /&gt;
the averaging window stretches with the flow&lt;br /&gt;
(Section 3), so a CA-limited flow keeps ~16 packets per window at any&lt;br /&gt;
rate down to ~30 kbit/s, and the rate-relative gap threshold keeps its&lt;br /&gt;
inter-packet spacing from reading as idle. The default floor&lt;br /&gt;
(&amp;lt;code&amp;gt;2^13&amp;lt;/code&amp;gt; B/s) only bounds the extremes (window ceiling,&lt;br /&gt;
staleness horizon, pacer arithmetic). The floor scales with the&lt;br /&gt;
fed-back path capacity (&amp;lt;code&amp;gt;r_min = C/32&amp;lt;/code&amp;gt;, Section 5.5), so&lt;br /&gt;
roughly 32 CA-limited flows fit above it at any link class. Flows&lt;br /&gt;
whose paths differ in bottleneck class derive different slopes, so&lt;br /&gt;
fairness across them is capacity-weighted; see&lt;br /&gt;
[[#8. Limitations|Section 8]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 9. Architectural fit ==&lt;br /&gt;
&lt;br /&gt;
Each property the preceding sections build toward attaches to a&lt;br /&gt;
structural feature of the recursive architecture. This section makes&lt;br /&gt;
the mapping explicit.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Congestion avoidance is fully orthogonal to ARQ and to flow&lt;br /&gt;
  control.&#039;&#039;&#039; Three concerns, three mechanisms, two scopes: FRCP&lt;br /&gt;
  provides retransmission (ARQ) and flow control (the peer pacing the&lt;br /&gt;
  sender) end-to-end, per flow; congestion avoidance runs in the&lt;br /&gt;
  IPCP, per aggregate. Each signal means one thing. A loss triggers&lt;br /&gt;
  a retransmission and nothing else &amp;amp;mdash; a lossy link reads as&lt;br /&gt;
  lossy, with the congestion verdict left to the marks (Section 8&#039;s&lt;br /&gt;
  &amp;quot;no loss response&amp;quot; is this orthogonality stated from the other&lt;br /&gt;
  side). The peer&#039;s flow-control window paces the endpoint and&lt;br /&gt;
  nothing else &amp;amp;mdash; back-pressure from a slow receiver stays&lt;br /&gt;
  distinct from congestion in the network. A retransmitted packet is&lt;br /&gt;
  ordinary traffic to the pacer. TCP folds all three into one window&lt;br /&gt;
  machine, where the receive window bounds the congestion window and&lt;br /&gt;
  loss serves as both reliability trigger and congestion signal;&lt;br /&gt;
  here each mechanism can be reasoned about, tested and evolved&lt;br /&gt;
  alone.&lt;br /&gt;
* &#039;&#039;&#039;Congestion avoidance sits below application choice.&#039;&#039;&#039; Every&lt;br /&gt;
  flow in the layer is paced by the same rate law, whatever its QoS:&lt;br /&gt;
  a greedy raw sender shares a bottleneck fairly with a reliable&lt;br /&gt;
  stream because the control is a property of the layer, on the&lt;br /&gt;
  aggregate, rather than a courtesy of the endpoint transport.&lt;br /&gt;
* &#039;&#039;&#039;A layer defines its own PCI, so the signal can be rich.&#039;&#039;&#039;&lt;br /&gt;
  Every member of a layer is enrolled into it: the layer is a single&lt;br /&gt;
  administrative domain by construction, at whatever scope it spans,&lt;br /&gt;
  and its header is layer-internal. Forwarders therefore write a&lt;br /&gt;
  multi-bit queue magnitude and a capacity byte directly into the&lt;br /&gt;
  packet. The datacenter schemes of&lt;br /&gt;
  [[#10. Heritage and positioning|Section 10]] require exactly such a&lt;br /&gt;
  domain and find it only inside one operator&#039;s fabric; a recursive&lt;br /&gt;
  layer supplies it everywhere.&lt;br /&gt;
* &#039;&#039;&#039;Layer-internal addressing makes the aggregate well-defined.&#039;&#039;&#039;&lt;br /&gt;
  Flows in a layer run between layer addresses, so the&lt;br /&gt;
  (destination, QoS cube) aggregate &amp;amp;mdash; RFC 3124&#039;s macroflow&lt;br /&gt;
  &amp;amp;mdash; falls out of the naming structure. Controller state scales&lt;br /&gt;
  with destinations, and each QoS cube keeps its own loop, so&lt;br /&gt;
  service classes keep separate fates.&lt;br /&gt;
* &#039;&#039;&#039;Flows are allocated, so feedback has a channel.&#039;&#039;&#039; Every flow&lt;br /&gt;
  has state at both ends and a reverse direction; the receiver&#039;s&lt;br /&gt;
  &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; and the path capacity ride the flow allocator&#039;s&lt;br /&gt;
  existing exchange.&lt;br /&gt;
* &#039;&#039;&#039;There is no transport ack clock, and none is relied on.&#039;&#039;&#039; The&lt;br /&gt;
  layer must control raw flows, which carry no acknowledgements at&lt;br /&gt;
  all, so the controller paces on wall-clock time &amp;amp;mdash; and that&lt;br /&gt;
  constraint yields the RTT-independent allocation of Section 6. An&lt;br /&gt;
  architectural restriction becomes the controller&#039;s distinguishing&lt;br /&gt;
  property.&lt;br /&gt;
* &#039;&#039;&#039;Recursion scales the scheme.&#039;&#039;&#039; Each layer controls congestion&lt;br /&gt;
  over its own scope and timescale; many N-flows ride one N-1 flow,&lt;br /&gt;
  so aggregation compounds down the stack. The capacity a layer&lt;br /&gt;
  measures at its egress queue is the rate the layer below actually&lt;br /&gt;
  provides &amp;amp;mdash; a paced, shared lower flow rather than a nominal&lt;br /&gt;
  wire speed &amp;amp;mdash; so the signal stays meaningful at every level,&lt;br /&gt;
  and pushback cascades layer by layer.&lt;br /&gt;
&lt;br /&gt;
The fit has a boundary: at a shim over legacy media the layer below&lt;br /&gt;
neither enrolls nor marks, which is the deployment edge behind&lt;br /&gt;
Section 8&#039;s no-loss-response limitation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 10. Heritage and positioning ==&lt;br /&gt;
&lt;br /&gt;
mb-ecn is best understood as a rate-based ECN controller in the DECbit /&lt;br /&gt;
QCN line, adapted to an acknowledgement-less recursive layer.&lt;br /&gt;
&lt;br /&gt;
The core of the scheme dates to the first Ouroboros&lt;br /&gt;
congestion-avoidance policy (2020): a multi-bit queue-depth mark&lt;br /&gt;
relayed by forwarders to the receiver, a smoothed multi-bit&lt;br /&gt;
&amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; fed back on the reverse flow at the same 1/32 fixed&lt;br /&gt;
point used today, and a sender that paced packets from a time budget&lt;br /&gt;
and ran slow start, additive increase and multiplicative decrease on&lt;br /&gt;
wall-clock time slots &amp;amp;mdash; rate-based and acknowledgement-free from&lt;br /&gt;
the start, with RTT-independent allocation already an objective. The&lt;br /&gt;
present algorithm keeps that skeleton and reworks every estimator&lt;br /&gt;
around it: per-path aggregation with SFQ pacing, the rate-adaptive&lt;br /&gt;
boxcar receiver, elapsed-time-scaled AIMD with a derivative term and&lt;br /&gt;
rate-velocity damping, and the in-band capacity signal.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;DECbit&#039;&#039;&#039; (Ramakrishnan &amp;amp;amp; Jain, 1988; ToCS 1990) established&lt;br /&gt;
  binary congestion feedback set by the switch on queue occupancy.&lt;br /&gt;
  mb-ecn keeps the switch-sets-on-queue idea and carries a&lt;br /&gt;
  &#039;&#039;magnitude&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;The Congestion Manager&#039;&#039;&#039; (Balakrishnan et al., SIGCOMM 1999;&lt;br /&gt;
  RFC 3124, 2001) aggregates congestion state at the end host: one&lt;br /&gt;
  controller per macroflow, a scheduler apportioning its rate across&lt;br /&gt;
  the member streams, and new streams joining at the ensemble&#039;s&lt;br /&gt;
  current state instead of probing from scratch. mb-ecn&#039;s&lt;br /&gt;
  (destination, QoS cube) aggregate is the same idea placed inside&lt;br /&gt;
  the IPCP: every layer manages its own macroflows, recursively, the&lt;br /&gt;
  SFQ pacer plays the CM scheduler&#039;s role (Section 4), and a joining&lt;br /&gt;
  flow rides the aggregate&#039;s estimates (Section 5.1). The CM gathers&lt;br /&gt;
  its signal from transport feedback at the edge; mb-ecn reads it&lt;br /&gt;
  off the wire from the forwarders.&lt;br /&gt;
* &#039;&#039;&#039;DCTCP&#039;&#039;&#039; (Alizadeh et al., SIGCOMM 2010) recovers a queue&lt;br /&gt;
  magnitude at the endpoint by averaging a single threshold bit. mb-ecn&lt;br /&gt;
  instead reads the magnitude off the wire, then time-averages it at&lt;br /&gt;
  the receiver. The additive-increase / proportional-decrease structure&lt;br /&gt;
  is shared.&lt;br /&gt;
* &#039;&#039;&#039;QCN&#039;&#039;&#039; (IEEE 802.1Qau, 2010) and &#039;&#039;&#039;DCQCN&#039;&#039;&#039; (Zhu et al., SIGCOMM&lt;br /&gt;
  2015) contribute the rate-based (not window-based) ECN control&lt;br /&gt;
  skeleton. mb-ecn&#039;s proportional decrease plus a probing increase&lt;br /&gt;
  mirrors that skeleton; the increase here is wall-clock scaled rather&lt;br /&gt;
  than byte-counter/timer based.&lt;br /&gt;
* &#039;&#039;&#039;HPCC&#039;&#039;&#039; (Li et al., SIGCOMM 2019) and &#039;&#039;&#039;PowerTCP&#039;&#039;&#039; (Addanki et&lt;br /&gt;
  al., NSDI 2022) combine a queue term &#039;&#039;and&#039;&#039; a rate term (via in-band&lt;br /&gt;
  telemetry or delay). mb-ecn now carries a coarse in-band rate (the&lt;br /&gt;
  quarter-log2 path MIN, Section 2) but uses it only to scale the&lt;br /&gt;
  controller&#039;s gains; the congestion &#039;&#039;signal&#039;&#039; remains queue-only,&lt;br /&gt;
  which is still the main functional difference (Section 8).&lt;br /&gt;
* &#039;&#039;&#039;L4S / TCP Prague&#039;&#039;&#039; (RFC 9330&amp;amp;ndash;9332, 2023) make&lt;br /&gt;
  RTT-independence a requirement and use a high-frequency single-bit&lt;br /&gt;
  signal. mb-ecn shares the RTT-independent &#039;&#039;allocation&#039;&#039; goal but&lt;br /&gt;
  reaches it by wall-clock-scaled rate control, with a low-frequency&lt;br /&gt;
  multi-bit signal.&lt;br /&gt;
&lt;br /&gt;
The novel combination is the multi-bit magnitude on the wire fused with&lt;br /&gt;
a wall-clock-scaled rate pacer in a setting with no per-flow ack clock.&lt;br /&gt;
The trade-off, made explicit above, is a queue-only signal and a&lt;br /&gt;
reactivity capped by the averaging window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 11. Parameters ==&lt;br /&gt;
&lt;br /&gt;
These are the values every figure in this document instantiates. They&lt;br /&gt;
are &#039;&#039;&#039;coupled&#039;&#039;&#039;: the mechanisms compose only while certain relations&lt;br /&gt;
hold &amp;amp;mdash; the gap-restart horizon at the rate floor must exceed a&lt;br /&gt;
packet&#039;s service time (or a floor-rate flow restart-loops at onset),&lt;br /&gt;
a floor-rate flow&#039;s target window must fit under the window ceiling,&lt;br /&gt;
the control interval must sit under the damping bucket under the idle&lt;br /&gt;
credit cap, the rate floor must not exceed the slow-start seed, and&lt;br /&gt;
the derived-floor ceiling must stay under the rate ceiling. Retune&lt;br /&gt;
them as a set, against those invariants.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| Marking quantum &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; || 4 packets || Mark onset and step; layer-wide&lt;br /&gt;
|-&lt;br /&gt;
| Feedback resolution || 1/32 &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; unit || Fixed-point LSB of &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Full-congestion reference &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; || 512 || Mean queue of 64 packets&lt;br /&gt;
|-&lt;br /&gt;
| Receiver window target || 16 packets (16,000 bytes) || Samples per window&lt;br /&gt;
|-&lt;br /&gt;
| Initial receiver window || ~67 ms || Before rate is known&lt;br /&gt;
|-&lt;br /&gt;
| Receiver window bounds || ~1.05 ms &amp;amp;ndash; ~4.3 s || Clamp on &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Window resize weight || 1/4 || Moving average toward target&lt;br /&gt;
|-&lt;br /&gt;
| Early window close || 2 &amp;amp;times; target bytes || Speed-up escape&lt;br /&gt;
|-&lt;br /&gt;
| Gap restart || 4 windows || Idle horizon (rate-relative)&lt;br /&gt;
|-&lt;br /&gt;
| Additive slope &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; (default) || 2^16 B/s^2 || Lower clamp of derived slope&lt;br /&gt;
|-&lt;br /&gt;
| Probe time constant &amp;lt;code&amp;gt;T_probe&amp;lt;/code&amp;gt; || 8 s || Rate-proportional recovery&lt;br /&gt;
|-&lt;br /&gt;
| Derivative gain || 1/4 || Onset sharpening&lt;br /&gt;
|-&lt;br /&gt;
| Rate floor &amp;lt;code&amp;gt;r_min&amp;lt;/code&amp;gt; (default) || 2^13 B/s || Lower clamp of derived floor&lt;br /&gt;
|-&lt;br /&gt;
| Slow-start seed rate || 2^16 B/s || Initial rate&lt;br /&gt;
|-&lt;br /&gt;
| Slow-start time constant || 20 ms || Exponential ramp&lt;br /&gt;
|-&lt;br /&gt;
| Rate ceiling &amp;lt;code&amp;gt;r_max&amp;lt;/code&amp;gt; || 2^37 B/s || Upper clamp&lt;br /&gt;
|-&lt;br /&gt;
| Capacity scale || &amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt; / 32 || Floor and slope from bottleneck&lt;br /&gt;
|-&lt;br /&gt;
| Capacity smoothing || 1/2 || Moving average per feedback&lt;br /&gt;
|-&lt;br /&gt;
| Capacity staleness || 16 &amp;amp;times; feedback horizon || Revert to defaults&lt;br /&gt;
|-&lt;br /&gt;
| Derived floor ceiling || 2^32 B/s || Upper clamp on derived floor&lt;br /&gt;
|-&lt;br /&gt;
| Control interval || 1 ms || Minimum spacing of rate updates&lt;br /&gt;
|-&lt;br /&gt;
| Idle credit cap || 50 ms || Burst allowance / increase banking&lt;br /&gt;
|-&lt;br /&gt;
| Feedback staleness floor || ~268 ms || Floor of rate-relative horizon&lt;br /&gt;
|-&lt;br /&gt;
| Damping bucket || ~31 ms || Rate-velocity washout cadence&lt;br /&gt;
|-&lt;br /&gt;
| Damping fraction || 1/4 || Pullback per bucket&lt;br /&gt;
|-&lt;br /&gt;
| Estimator fold spacing || 1 ms || Minimum measurement cadence&lt;br /&gt;
|-&lt;br /&gt;
| Estimator window closure || 16 packets drained || Self-scaling window&lt;br /&gt;
|-&lt;br /&gt;
| Estimator window staleness || ~134 ms || Discard across traffic gaps&lt;br /&gt;
|-&lt;br /&gt;
| Estimator idle tolerance || 1/8 of arrivals || Unsaturated-window rejection&lt;br /&gt;
|-&lt;br /&gt;
| Estimator decay || 1/16 per window || Max-filter convergence from below&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 12. References ==&lt;br /&gt;
&lt;br /&gt;
=== 12.1. Source ===&lt;br /&gt;
&lt;br /&gt;
The implementation follows the algorithm above:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/ca/mb-ecn.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mb-ecn.h&amp;lt;/code&amp;gt; &amp;amp;mdash;&lt;br /&gt;
  receiver estimator, pacer and rate control.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/ca/ops.h&amp;lt;/code&amp;gt; &amp;amp;mdash; CA policy interface.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/cap.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cap.h&amp;lt;/code&amp;gt; &amp;amp;mdash; link-capacity&lt;br /&gt;
  estimator and quarter-log2 codec.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/dt.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fa.c&amp;lt;/code&amp;gt; &amp;amp;mdash; per-hop marking&lt;br /&gt;
  and stamping, and the feedback carrying &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;cap8&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/psched.c&amp;lt;/code&amp;gt; &amp;amp;mdash; consumes the pacing deadline.&lt;br /&gt;
&lt;br /&gt;
=== 12.2. Literature ===&lt;br /&gt;
&lt;br /&gt;
* K. K. Ramakrishnan, R. Jain, &amp;quot;A Binary Feedback Scheme for Congestion&lt;br /&gt;
  Avoidance in Computer Networks&amp;quot; (DECbit), ACM ToCS, 1990.&lt;br /&gt;
* H. Balakrishnan, H. S. Rahul, S. Seshan, &amp;quot;An Integrated Congestion&lt;br /&gt;
  Management Architecture for Internet Hosts&amp;quot;, SIGCOMM 1999;&lt;br /&gt;
  H. Balakrishnan, S. Seshan, &amp;quot;The Congestion Manager&amp;quot;, RFC 3124, 2001.&lt;br /&gt;
* M. Alizadeh et al., &amp;quot;Data Center TCP (DCTCP)&amp;quot;, SIGCOMM 2010.&lt;br /&gt;
* Y. Zhu et al., &amp;quot;Congestion Control for Large-Scale RDMA Deployments&amp;quot;&lt;br /&gt;
  (DCQCN), SIGCOMM 2015.&lt;br /&gt;
* R. Mittal et al., &amp;quot;TIMELY: RTT-based Congestion Control&amp;quot;, SIGCOMM 2015.&lt;br /&gt;
* Y. Li et al., &amp;quot;HPCC: High Precision Congestion Control&amp;quot;, SIGCOMM 2019.&lt;br /&gt;
* V. Addanki et al., &amp;quot;PowerTCP&amp;quot;, NSDI 2022.&lt;br /&gt;
* K. De Schepper, B. Briscoe (eds.), &amp;quot;Low Latency, Low Loss, and&lt;br /&gt;
  Scalable Throughput (L4S)&amp;quot;, RFC 9330/9331/9332.&lt;br /&gt;
* F. Kelly, &amp;quot;Charging and Rate Control for Elastic Traffic&amp;quot;, 1997;&lt;br /&gt;
  R. Srikant, &amp;quot;The Mathematics of Internet Congestion Control&amp;quot;, 2004.&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1937</id>
		<title>Congestion avoidance</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1937"/>
		<updated>2026-07-11T20:46:20Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 8. Limitations */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:mb-ecn - Multi-bit ECN Congestion Avoidance}}&lt;br /&gt;
&lt;br /&gt;
mb-ecn is a congestion-avoidance (CA) policy for the Ouroboros unicast&lt;br /&gt;
IPCP. It lives in the IPC Process: the flow-and-retransmission task&lt;br /&gt;
(FRCP) provides reliability, in-order delivery and flow control&lt;br /&gt;
end-to-end; congestion avoidance is orthogonal and runs per IPCP&lt;br /&gt;
layer. Within a layer, the IPCP controls &#039;&#039;&#039;aggregates&#039;&#039;&#039;, one loop&lt;br /&gt;
per (destination address, QoS cube), shared by every flow toward that&lt;br /&gt;
destination. The alternative policy is &amp;lt;code&amp;gt;nop&amp;lt;/code&amp;gt; (no congestion&lt;br /&gt;
avoidance).&lt;br /&gt;
&lt;br /&gt;
This document describes the algorithm: what the policy does and why.&lt;br /&gt;
The implementation follows it ([[#12. References|Section 12]] maps the&lt;br /&gt;
pieces to source files). It does not claim properties the algorithm&lt;br /&gt;
does not have; [[#8. Limitations|Section 8]] states the known gaps&lt;br /&gt;
explicitly.&lt;br /&gt;
&lt;br /&gt;
The keywords &amp;quot;MUST&amp;quot;, &amp;quot;SHOULD&amp;quot;, &amp;quot;MAY&amp;quot; etc. are used only where they&lt;br /&gt;
appear in all capitals, per BCP 14 (RFC 2119, RFC 8174).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt;&lt;br /&gt;
:Paced send rate, bytes per second.&lt;br /&gt;
;&amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bottleneck capacity, bytes per second.&lt;br /&gt;
;&amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt;&lt;br /&gt;
:Packet size in bytes.&lt;br /&gt;
;&amp;lt;code&amp;gt;q&amp;lt;/code&amp;gt;&lt;br /&gt;
:Standing queue occupancy at the bottleneck, in packets.&lt;br /&gt;
;&amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt;&lt;br /&gt;
:Marking quantum, packets per mark unit (4).&lt;br /&gt;
;&amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt;&lt;br /&gt;
:Per-packet congestion mark set by a forwarder, an unsigned integer&lt;br /&gt;
 (on-wire field is 8-bit).&lt;br /&gt;
;&amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
:Receiver-side congestion estimate, fixed point with LSB = 1/32 of an&lt;br /&gt;
 &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; unit, i.e. &amp;lt;code&amp;gt;ece = 32 &amp;amp;times; (time-mean ecn)&amp;lt;/code&amp;gt;.&lt;br /&gt;
;&amp;lt;code&amp;gt;cap8&amp;lt;/code&amp;gt;&lt;br /&gt;
:Path-capacity code carried per packet, quarter-log2:&lt;br /&gt;
 &amp;lt;code&amp;gt;C &amp;amp;asymp; 2^(cap8 / 4)&amp;lt;/code&amp;gt; bytes/s, 0 = unknown.&lt;br /&gt;
;&amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt;&lt;br /&gt;
:Receiver averaging window, nanoseconds.&lt;br /&gt;
;&amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt;&lt;br /&gt;
:Additive-increase slope, bytes/s per second.&lt;br /&gt;
;&amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt;&lt;br /&gt;
:Full-congestion reference level of &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; (512).&lt;br /&gt;
;&amp;lt;code&amp;gt;&amp;amp;Delta;t&amp;lt;/code&amp;gt;&lt;br /&gt;
:Elapsed wall-clock time between two events, nanoseconds.&lt;br /&gt;
;&amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;&lt;br /&gt;
:Path round-trip time (feedback delay). The controller does not&lt;br /&gt;
 measure it.&lt;br /&gt;
&lt;br /&gt;
Rate is the control variable throughout.&lt;br /&gt;
&lt;br /&gt;
All concrete figures in this document &amp;amp;mdash; window bounds, horizons,&lt;br /&gt;
rate thresholds &amp;amp;mdash; are instances of the parameter values in&lt;br /&gt;
[[#11. Parameters|Section 11]]. The mechanisms and the relations&lt;br /&gt;
between parameters are what the algorithm fixes; retuning the&lt;br /&gt;
parameters moves the figures together.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== 1. Overview ==&lt;br /&gt;
&lt;br /&gt;
mb-ecn is a rate-based, ECN-driven controller with three moving parts,&lt;br /&gt;
one at each point of the loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        sender                                  forwarder&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
      |  SFQ virtual-clock pacer       |       |  ecn = MAX(ecn, queue/Q) |&lt;br /&gt;
      |  rate r, paced per packet      | data  |  (deepest queue wins)    |&lt;br /&gt;
      |  AIMD + PD control on r        |------&amp;gt;|  cap8 = MIN(cap8, link)  |&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
                 ^                                        |&lt;br /&gt;
                 | ece + cap8 feedback                    | marked data&lt;br /&gt;
                 |                                        v&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
      |  reads downstream ece,         |&amp;lt;------|  receiver                |&lt;br /&gt;
      |  updates the rate on feedback, |  ece  |  time-mean of ecn over   |&lt;br /&gt;
      |  scales floor/slope to cap8    |  cap8 |  an adaptive window      |&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# The &#039;&#039;&#039;forwarder&#039;&#039;&#039; marks each packet with a &#039;&#039;multi-bit&#039;&#039; magnitude&lt;br /&gt;
  proportional to its standing queue, and MIN-stamps its measured&lt;br /&gt;
  outgoing-link capacity into the same PCI.&lt;br /&gt;
# The &#039;&#039;&#039;receiver&#039;&#039;&#039; turns the stream of marks into a time-averaged&lt;br /&gt;
  congestion estimate &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; and feeds it back to the sender,&lt;br /&gt;
  together with the window minimum of the path-capacity codes.&lt;br /&gt;
# The &#039;&#039;&#039;sender&#039;&#039;&#039; paces its rate with a start-time fair-queuing (SFQ)&lt;br /&gt;
  virtual clock, and adjusts that rate with additive-increase /&lt;br /&gt;
  multiplicative-decrease (AIMD) driven by &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;; the rate floor&lt;br /&gt;
  and the additive slope derive from the fed-back path capacity.&lt;br /&gt;
&lt;br /&gt;
The controller runs on packet sends and on feedback arrivals. All&lt;br /&gt;
increase/decrease steps are scaled by elapsed&lt;br /&gt;
wall-clock time, which is what makes the &#039;&#039;allocation&#039;&#039; independent of&lt;br /&gt;
RTT (see [[#6. RTT behaviour|Section 6]]).&lt;br /&gt;
&lt;br /&gt;
The unit of control is the &#039;&#039;&#039;aggregate&#039;&#039;&#039;, kept in the IPCP per&lt;br /&gt;
&amp;lt;code&amp;gt;(destination address, QoS cube)&amp;lt;/code&amp;gt;. Every flow toward that&lt;br /&gt;
destination at that QoS shares one controller and one rate: a new&lt;br /&gt;
flow joins the aggregate at its current rate, and a departing flow&lt;br /&gt;
leaves the rate to the others. The pacer divides the aggregate rate&lt;br /&gt;
fairly across the member flows (SFQ), so per-flow fairness within an&lt;br /&gt;
aggregate is a scheduler property, distinct from the rate law&lt;br /&gt;
described here.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2. Forwarder marking ==&lt;br /&gt;
&lt;br /&gt;
A forwarder marks a packet as a function of the packets already queued&lt;br /&gt;
on the outgoing link:&lt;br /&gt;
&lt;br /&gt;
  mark = queued / Q                         (Q = 4 packets)&lt;br /&gt;
  ecn  = MAX(ecn, mark)                     (saturating, 8-bit field)&lt;br /&gt;
&lt;br /&gt;
The quantum &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; is a layer-wide constant: the marks it&lt;br /&gt;
quantizes are combined by MAX across hops, so all forwarders in a&lt;br /&gt;
layer MUST use the same quantum for the signal to be comparable.&lt;br /&gt;
&lt;br /&gt;
Two properties:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-bit magnitude.&#039;&#039;&#039; The mark is an integer proportional to&lt;br /&gt;
  queue depth, quantized in steps of &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; packets: a coarse&lt;br /&gt;
  readout of the standing queue.&lt;br /&gt;
* &#039;&#039;&#039;MAX along the path.&#039;&#039;&#039; A packet crossing several forwarders carries&lt;br /&gt;
  the maximum mark, i.e. the deepest queue on its path. MAX keeps the&lt;br /&gt;
  signal range-stable and monotone across hops.&lt;br /&gt;
&lt;br /&gt;
There is a &#039;&#039;&#039;dead zone&#039;&#039;&#039;: for &amp;lt;code&amp;gt;queued &amp;lt; Q&amp;lt;/code&amp;gt; the mark is 0.&lt;br /&gt;
&lt;br /&gt;
=== Capacity stamping ===&lt;br /&gt;
&lt;br /&gt;
Each forwarder also measures the capacity of its outgoing link and&lt;br /&gt;
stamps it into the packet. The estimate is a busy-period drain rate:&lt;br /&gt;
the queue is observed at most once per millisecond, and a measurement&lt;br /&gt;
window stays open until 16 packets have drained, so its length&lt;br /&gt;
&#039;&#039;&#039;self-scales with the link rate&#039;&#039;&#039; &amp;amp;mdash; the millisecond cadence&lt;br /&gt;
bounds it on fast links, the 16-packet drain time on slow ones&lt;br /&gt;
(~19 ms at 10 Mbit/s). A window in which more than 1/8 of&lt;br /&gt;
the arrivals found the queue empty is discarded as unsaturated;&lt;br /&gt;
occasional empty observations (token-bucket shapers grazing zero) are&lt;br /&gt;
tolerated. The drain rate of a saturated queue is the link rate; a max&lt;br /&gt;
filter with slow decay (1/16 per window) converges on it from below. A&lt;br /&gt;
window that opened or closed on an empty queue may have drained into&lt;br /&gt;
buffers below at above-wire rate, so it may lower the estimate but&lt;br /&gt;
never raise it. The code on the wire is quarter-log2&lt;br /&gt;
(&amp;lt;code&amp;gt;cap8 = 4 log2 C&amp;lt;/code&amp;gt;, ~19% per step, 0 = unknown) and each hop&lt;br /&gt;
MIN-combines its own code into the byte, so a packet arrives carrying&lt;br /&gt;
the bottleneck&#039;s rate. A hop that has never been backlogged stamps&lt;br /&gt;
nothing; since only backlogged hops matter, the signal exists exactly&lt;br /&gt;
when it is needed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. Receiver estimate ==&lt;br /&gt;
&lt;br /&gt;
The receiver converts marks into a smoothed estimate &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; over a&lt;br /&gt;
time window &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt; that &#039;&#039;&#039;adapts to the incoming byte rate&#039;&#039;&#039;. It is&lt;br /&gt;
a &#039;&#039;&#039;boxcar (rectangular) time-integral mean&#039;&#039;&#039;. Per packet, with&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;Delta;t&amp;lt;/code&amp;gt; the gap since the previous packet:&lt;br /&gt;
&lt;br /&gt;
  A += ecn * MIN(&amp;amp;Delta;t, W)              dwell-weighted; one packet&lt;br /&gt;
                                          weighs at most one window&lt;br /&gt;
  B += S                                  accumulate bytes this window&lt;br /&gt;
  ...&lt;br /&gt;
  at window close (elapsed &amp;gt;= W, or B &amp;gt;= 2 * B_target with at&lt;br /&gt;
                   least the window floor elapsed):&lt;br /&gt;
      ece = 32 * A / elapsed              mean over the actual window&lt;br /&gt;
      W  += (B_target * elapsed / B - W) / 4    nudge toward the size&lt;br /&gt;
                                                that holds the target&lt;br /&gt;
      W   = clamp(W, ~1.05 ms, ~4.3 s)&lt;br /&gt;
      A   = 0 ; B = 0                     hard reset&lt;br /&gt;
&lt;br /&gt;
At each close the next window is nudged by a quarter-weight moving&lt;br /&gt;
average toward the size that would hold about 16 packets at the&lt;br /&gt;
measured byte rate, from an initial ~67 ms. The target is byte-based (&amp;lt;code&amp;gt;B_target&amp;lt;/code&amp;gt; = 16,000 bytes), so&lt;br /&gt;
&amp;quot;16 packets&amp;quot; is exact only at ~1000-byte packets, and the window&lt;br /&gt;
converges to it over several closes. The mean always divides by the&lt;br /&gt;
&#039;&#039;&#039;actual&#039;&#039;&#039; elapsed window, so a rate step leaves the current&lt;br /&gt;
estimate exact and only re-sizes the &#039;&#039;next&#039;&#039; window. The effect is a roughly constant sample&lt;br /&gt;
count &amp;amp;mdash; ~16 packets &amp;amp;mdash; from about 30 kbit/s to 122 Mbit/s;&lt;br /&gt;
above that the window floors at ~1 ms (a cadence still carrying&lt;br /&gt;
thousands of samples at 10&amp;amp;ndash;100 GbE), and below ~30 kbit/s it&lt;br /&gt;
saturates at the ~4.3 s ceiling. The averaging clock stretches with&lt;br /&gt;
the flow the way TCP&#039;s ACK clock stretches with the RTT. A speed-up&lt;br /&gt;
escapes a stretched window early: once twice the target bytes arrive&lt;br /&gt;
the window closes anyway (the window floor keeps that cadence bounded&lt;br /&gt;
at high rate). The fed-back &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; is thus a piecewise-constant&lt;br /&gt;
staircase whose step period tracks the rate. Two edge cases:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Onset&#039;&#039;&#039; (first mark after an idle estimate) emits the&lt;br /&gt;
  instantaneous mark undiluted, so a starting queue is reported without&lt;br /&gt;
  a full window of delay.&lt;br /&gt;
* &#039;&#039;&#039;Gap&#039;&#039;&#039; longer than 4 windows resets the window and emits the raw&lt;br /&gt;
  sample. The threshold is &#039;&#039;&#039;rate-relative&#039;&#039;&#039;: idle means a few&lt;br /&gt;
  &#039;&#039;current&#039;&#039; windows of silence, so a slow flow&#039;s normal inter-packet&lt;br /&gt;
  gap never reads as idle, while the in-window dwell clamp above bounds&lt;br /&gt;
  what a genuine pause can add to the mean before the restart fires.&lt;br /&gt;
&lt;br /&gt;
Alongside the mark integral, the receiver keeps the MIN of the nonzero&lt;br /&gt;
capacity codes seen in the current window; each emitted &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
carries that minimum (the raw packet&#039;s code on onset/gap restarts) and&lt;br /&gt;
the fold then resets, so a reroute to a faster path can raise the&lt;br /&gt;
fed-back capacity within one window.&lt;br /&gt;
&lt;br /&gt;
The estimate is fed back to the sender on the reverse flow.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 4. Sender pacer ==&lt;br /&gt;
&lt;br /&gt;
The sender paces with an SFQ virtual clock. The context keeps a&lt;br /&gt;
virtual time &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;; each flow keeps a finish tag &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
  V    += r * &amp;amp;Delta;t                     virtual time advances by bytes served&lt;br /&gt;
  s     = MAX(F, V)                  a packet behind the clock starts now&lt;br /&gt;
  F     = s + S                      the one after it queues behind&lt;br /&gt;
  wait  = (s - V) / r                time until this packet may go&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;wait&amp;lt;/code&amp;gt; becomes the flow&#039;s scheduling deadline: a packet&lt;br /&gt;
already behind the virtual clock is sent immediately&lt;br /&gt;
(&amp;lt;code&amp;gt;wait = 0&amp;lt;/code&amp;gt;); one ahead waits, deferring only its own flow.&lt;br /&gt;
A clock advance across an idle gap longer than 50 ms credits at most&lt;br /&gt;
the calling flow&#039;s owed lead plus one burst (50 ms of service, at&lt;br /&gt;
least one packet): a paced flow slower than one packet per 50 ms&lt;br /&gt;
receives its &#039;&#039;&#039;true&#039;&#039;&#039; elapsed service &amp;amp;mdash; a time-capped credit&lt;br /&gt;
would decay such a flow without bound &amp;amp;mdash; while an idle flow still&lt;br /&gt;
cannot bank an unbounded burst.&lt;br /&gt;
&lt;br /&gt;
The virtual time &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; is shared by every flow in the context;&lt;br /&gt;
the finish tags are per flow. Because every flow computes its start&lt;br /&gt;
tag &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; against the same &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;, a flow that has just sent&lt;br /&gt;
carries a finish tag ahead of &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; and waits, while an idle flow&lt;br /&gt;
(finish tag at or behind &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;) starts immediately &amp;amp;mdash; this is&lt;br /&gt;
what fairly divides the aggregate rate &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; across them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 5. Rate control (AIMD + PD) ==&lt;br /&gt;
&lt;br /&gt;
The rate law runs on packet sends, at most once per millisecond, and&lt;br /&gt;
also on feedback arrivals so that a sender with no traffic of its own&lt;br /&gt;
still reacts. Each increase term and the proportional decrease are&lt;br /&gt;
scaled by elapsed wall-clock time, each by its own clock: the&lt;br /&gt;
increase terms use time banked and &#039;&#039;&#039;capped&#039;&#039;&#039; at 50 ms (an idle gap&lt;br /&gt;
cannot bank an unbounded ramp), while the proportional decrease uses&lt;br /&gt;
the &#039;&#039;&#039;uncapped&#039;&#039;&#039; elapsed time (a starved sender still cuts by the&lt;br /&gt;
right amount). The one-sided derivative (Section 5.3) is a per-delta&lt;br /&gt;
term.&lt;br /&gt;
&lt;br /&gt;
=== 5.1. Slow start ===&lt;br /&gt;
&lt;br /&gt;
Before the first congestion signal the rate ramps exponentially with a&lt;br /&gt;
20 ms time constant:&lt;br /&gt;
&lt;br /&gt;
  r += r * &amp;amp;Delta;t / T_ss                  (T_ss = 20 ms)&lt;br /&gt;
&lt;br /&gt;
Slow start ends the first time a congestion signal arrives &amp;amp;mdash;&lt;br /&gt;
fed back from the receiver or observed at the sender&#039;s own first hop&lt;br /&gt;
&amp;amp;mdash; and never resumes.&lt;br /&gt;
&lt;br /&gt;
Slow start belongs to the aggregate, so it runs &#039;&#039;&#039;once per&lt;br /&gt;
(destination, QoS cube)&#039;&#039;&#039;, on the first flow. A flow arriving at an&lt;br /&gt;
established aggregate rides the existing estimates: it enters at the&lt;br /&gt;
aggregate&#039;s current rate and receives its fair share of it through&lt;br /&gt;
the pacer (Section 4), with no probing of its own.&lt;br /&gt;
&lt;br /&gt;
=== 5.2. Increase: additive plus proportional probe (always on) ===&lt;br /&gt;
&lt;br /&gt;
Past slow start, two increase terms are applied every step, both&lt;br /&gt;
regardless of congestion:&lt;br /&gt;
&lt;br /&gt;
  r += a * &amp;amp;Delta;t                         additive; a defaults to 2^16 B/s^2&lt;br /&gt;
  r += r * &amp;amp;Delta;t / T_probe               proportional; T_probe = 8 s&lt;br /&gt;
&lt;br /&gt;
The additive term is a fixed per-context slope (bytes/s per second),&lt;br /&gt;
scaled to the path capacity (Section 5.5). The proportional probe&lt;br /&gt;
grows the rate by a fixed fraction per unit time (e-folds over&lt;br /&gt;
&amp;lt;code&amp;gt;T_probe&amp;lt;/code&amp;gt;): a flow recovers in the same number of steps at any&lt;br /&gt;
link rate; the fixed additive term&#039;s effect vanishes relative to a&lt;br /&gt;
fast link. Both are the &amp;quot;probing&amp;quot;&lt;br /&gt;
pressure the multiplicative decrease balances at equilibrium; the&lt;br /&gt;
probe&#039;s cost is a rate-independent standing-queue floor (Section 7).&lt;br /&gt;
&lt;br /&gt;
=== 5.3. Multiplicative decrease (proportional + derivative) ===&lt;br /&gt;
&lt;br /&gt;
With congestion level &amp;lt;code&amp;gt;m&amp;lt;/code&amp;gt; (the fed-back &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;, or a local&lt;br /&gt;
first-hop mark as fallback), the decrease is the sum of a proportional&lt;br /&gt;
term and a one-sided derivative term &amp;amp;mdash; a PD controller on the&lt;br /&gt;
congestion signal:&lt;br /&gt;
&lt;br /&gt;
  mark = MIN(m, M)                                   M = 512&lt;br /&gt;
  rise = MAX(m - m_prev, 0), capped at M             one-sided derivative&lt;br /&gt;
&lt;br /&gt;
  cut  = r * rise / (4 * M)                          derivative, gain 1/4&lt;br /&gt;
  cut += r * mark * &amp;amp;Delta;t_ms / (1000 * M)              proportional&lt;br /&gt;
  cut  = MIN(cut, r / 2)                             at most a halving&lt;br /&gt;
&lt;br /&gt;
  r     -= cut&lt;br /&gt;
  m_prev = m&lt;br /&gt;
&lt;br /&gt;
* The &#039;&#039;&#039;proportional&#039;&#039;&#039; term is scaled by honest elapsed time in&lt;br /&gt;
  milliseconds, so a starved sender that has not run the controller&lt;br /&gt;
  for a while still cuts by the right amount.&lt;br /&gt;
* The &#039;&#039;&#039;derivative&#039;&#039;&#039; term (&amp;lt;code&amp;gt;rise&amp;lt;/code&amp;gt;) reacts to an &#039;&#039;increase&#039;&#039; in&lt;br /&gt;
  the congestion level between samples; it is one-sided and per-delta.&lt;br /&gt;
  It sharpens the reaction at the onset of congestion.&lt;br /&gt;
* The cut is clamped to &amp;lt;code&amp;gt;r/2&amp;lt;/code&amp;gt; per step, bounding the decrease to&lt;br /&gt;
  a halving.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;M = 512 = 32 &amp;amp;times; 16&amp;lt;/code&amp;gt; defines &amp;quot;full congestion&amp;quot; as a mean&lt;br /&gt;
&amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; of 16, i.e. a mean queue of &amp;lt;code&amp;gt;16 &amp;amp;times; Q = 64&amp;lt;/code&amp;gt; packets.&lt;br /&gt;
&lt;br /&gt;
=== 5.4. Staleness ===&lt;br /&gt;
&lt;br /&gt;
Feedback arrives once per receiver window, and the window stretches&lt;br /&gt;
with the flow&#039;s byte rate (Section 3) &amp;amp;mdash; so the staleness horizon&lt;br /&gt;
must stretch with it, or a slow flow&#039;s mark would age out &#039;&#039;between&#039;&#039;&lt;br /&gt;
feedbacks and the rate would ramp against a congested path. The&lt;br /&gt;
sender mirrors the receiver: the horizon is four target windows of&lt;br /&gt;
bytes at the current rate, floored at ~268 ms so fast flows keep a&lt;br /&gt;
fixed horizon; at the default rate floor it is ~8 s. A signal older&lt;br /&gt;
than the horizon is aged out (both the fed-back estimate and the&lt;br /&gt;
local mark), freeing the rate to ramp once congestion clears. The&lt;br /&gt;
rate is clamped to&lt;br /&gt;
&amp;lt;code&amp;gt;[r_min, r_max]&amp;lt;/code&amp;gt; on every step.&lt;br /&gt;
&lt;br /&gt;
=== 5.5. Capacity-derived floor and slope ===&lt;br /&gt;
&lt;br /&gt;
The fed-back capacity code sets the two rate-scale constants of the&lt;br /&gt;
controller, per context:&lt;br /&gt;
&lt;br /&gt;
  target = decode(cap8) / 32              clamped to [2^13, 2^32] B/s&lt;br /&gt;
  r_min += (target - r_min) / 2           per feedback (moving average)&lt;br /&gt;
  a      = r_min&lt;br /&gt;
&lt;br /&gt;
The defaults are fixed constants (&amp;lt;code&amp;gt;r_min = 2^13 B/s&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;a = 2^16 B/s^2&amp;lt;/code&amp;gt;), used on paths that never report a capacity;&lt;br /&gt;
with the estimator live the fairness floor and the post-cut recovery&lt;br /&gt;
slope scale with the bottleneck, and capacity engages wherever&lt;br /&gt;
&amp;lt;code&amp;gt;C/32&amp;lt;/code&amp;gt; clears the default floor, i.e. above ~2 Mbit/s. A&lt;br /&gt;
capacity older than 16 staleness horizons (~4.3 s for fast flows)&lt;br /&gt;
reverts both to the defaults. That horizon deliberately outlives the&lt;br /&gt;
feedback horizon: feedback stops the moment the marks clear, which is&lt;br /&gt;
exactly when the recovery slope is needed; the onset-fresh capacity&lt;br /&gt;
(Section 3) re-seeds it on the first mark of the next episode either&lt;br /&gt;
way.&lt;br /&gt;
&lt;br /&gt;
=== 5.6. Ramp and recovery ===&lt;br /&gt;
&lt;br /&gt;
Three mechanisms set how fast an aggregate reaches a fast link&#039;s&lt;br /&gt;
rate, each owning one regime:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;From scratch: slow start.&#039;&#039;&#039; The exponential ramp reaches any&lt;br /&gt;
  physical link rate in well under a second (~200 ms from the seed to&lt;br /&gt;
  10 Gbit/s; each further doubling of link speed costs one more&lt;br /&gt;
  ~14 ms doubling time). On an uncongested path nothing marks, so&lt;br /&gt;
  slow start runs until the aggregate arrives at its bottleneck.&lt;br /&gt;
* &#039;&#039;&#039;After a cut: the probe.&#039;&#039;&#039; A decrease is at most a halving per&lt;br /&gt;
  step, and the mark ages out on the rate-relative horizon once&lt;br /&gt;
  congestion clears (Section 5.4); the proportional probe then heals&lt;br /&gt;
  a halving in &amp;lt;code&amp;gt;T_probe &amp;amp;middot; ln 2 &amp;amp;asymp; 5.5 s&amp;lt;/code&amp;gt; at any&lt;br /&gt;
  link rate.&lt;br /&gt;
* &#039;&#039;&#039;At the bottom: the capacity floor.&#039;&#039;&#039; The rate sits at most a&lt;br /&gt;
  factor 32 below a measured bottleneck, and the additive slope&lt;br /&gt;
  refills &amp;lt;code&amp;gt;C/32&amp;lt;/code&amp;gt; per second, so the deepest hole is bounded:&lt;br /&gt;
  floor to full capacity in tens of seconds, a halving in seconds.&lt;br /&gt;
&lt;br /&gt;
These compose because a deep cut requires sustained congestion,&lt;br /&gt;
sustained congestion backlogs the bottleneck queue, and a backlogged&lt;br /&gt;
queue is exactly when its capacity is measured and stamped&lt;br /&gt;
(Section 2): whenever the controller is cut deep, the scaled floor is&lt;br /&gt;
live. The default floor only governs paths that never congested the&lt;br /&gt;
sender &amp;amp;mdash; where there is nothing to recover from. This is why&lt;br /&gt;
slow start can end for good: the floor and the probe cover every&lt;br /&gt;
later recovery.&lt;br /&gt;
&lt;br /&gt;
=== 5.7. Regimes ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   +-------------+   first mark seen    +----------------+&lt;br /&gt;
   | slow start  |---------------------&amp;gt;|  congestion    |&lt;br /&gt;
   | r *= e^(t/T)|                      |  avoidance     |&lt;br /&gt;
   +-------------+                      |                |&lt;br /&gt;
                                        | m == 0 -&amp;gt; AI   |&lt;br /&gt;
                                        | m  &amp;gt; 0 -&amp;gt; AIPD |&lt;br /&gt;
                                        +----------------+&lt;br /&gt;
    m = fed-back ece, or the local first-hop mark when ece == 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 6. RTT behaviour ==&lt;br /&gt;
&lt;br /&gt;
Because every increase and decrease is scaled by wall-clock time,&lt;br /&gt;
two flows of different RTT that share a bottleneck obey the &#039;&#039;same&#039;&#039;&lt;br /&gt;
rate law and converge to the &#039;&#039;same&#039;&#039; rate. The steady-state&lt;br /&gt;
&#039;&#039;&#039;allocation is RTT-independent&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
This is a property of the equilibrium, not of the dynamics. The&lt;br /&gt;
feedback delay in the loop &#039;&#039;is&#039;&#039; the path RTT &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;, and the&lt;br /&gt;
controller does not measure or compensate for it. At low rate the&lt;br /&gt;
receiver window (up to its ~4.3 s ceiling) is the dominant lag &amp;amp;mdash;&lt;br /&gt;
the loop is slow there in proportion to how slow the flow is, exactly&lt;br /&gt;
as a long-RTT TCP is &amp;amp;mdash; while as the rate rises the window shrinks&lt;br /&gt;
toward its ~1 ms floor, so the window&#039;s contribution to the loop delay&lt;br /&gt;
&#039;&#039;falls&#039;&#039; with capacity and the path RTT &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; becomes the&lt;br /&gt;
irreducible term. Two mechanisms keep the loop damped: the adaptive&lt;br /&gt;
window removes the delay-dominated corner at low &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;, and a&lt;br /&gt;
bucketed &#039;&#039;&#039;rate-velocity damping&#039;&#039;&#039; (a washout: each ~31 ms bucket&lt;br /&gt;
pulls the rate a quarter of the way back toward its value at the&lt;br /&gt;
previous bucket) supplies the damping the otherwise&lt;br /&gt;
near-double-integrator lacks, which is what holds the loop together at&lt;br /&gt;
high capacity and moderate &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;. What remains is the loop gain&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;prop; &amp;amp;radic;C&amp;lt;/code&amp;gt;: at very high capacity &#039;&#039;and&#039;&#039; large&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; the &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt; phase budget is the&lt;br /&gt;
binding constraint, and no window or damping trick removes it (see&lt;br /&gt;
[[#8. Limitations|Section 8]]). A precise statement is therefore:&lt;br /&gt;
&#039;&#039;&#039;RTT-fair in equilibrium, and dynamically stable up to a&lt;br /&gt;
capacity-dependent RTT limit set by &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 7. Fairness ==&lt;br /&gt;
&lt;br /&gt;
At equilibrium the two increase terms balance the proportional&lt;br /&gt;
decrease, &amp;lt;code&amp;gt;a + r/T_probe = r &amp;amp;middot; m*/M&amp;lt;/code&amp;gt;, which pins the&lt;br /&gt;
congestion level to &amp;lt;code&amp;gt;m* = M &amp;amp;middot; (a/r + 1/T_probe)&amp;lt;/code&amp;gt; and, since&lt;br /&gt;
&amp;lt;code&amp;gt;ece = 8q&amp;lt;/code&amp;gt; (one mark unit per &amp;lt;code&amp;gt;Q = 4&amp;lt;/code&amp;gt; packets, 32 fixed-point&lt;br /&gt;
steps per unit), a standing queue of&lt;br /&gt;
&lt;br /&gt;
  q* = 64 * a / r  +  64 / T_probe        (packets, r in bytes/s)&lt;br /&gt;
&lt;br /&gt;
The additive part (&amp;lt;code&amp;gt;64 &amp;amp;middot; a / r&amp;lt;/code&amp;gt;) vanishes with rate under&lt;br /&gt;
the default slope; with the capacity-derived slope (&amp;lt;code&amp;gt;a = C/32&amp;lt;/code&amp;gt;,&lt;br /&gt;
Section 5.5) it becomes &amp;lt;code&amp;gt;2 C / r&amp;lt;/code&amp;gt; &amp;amp;mdash; about two packets per&lt;br /&gt;
competing flow, rate-independent. The proportional-probe part&lt;br /&gt;
(&amp;lt;code&amp;gt;64 / T_probe&amp;lt;/code&amp;gt;) is a rate-independent floor (~8 packets at&lt;br /&gt;
8 s). That floor is the cost of rate-independent convergence, and it&lt;br /&gt;
keeps &amp;lt;code&amp;gt;q*&amp;lt;/code&amp;gt; above the marking threshold at high rate.&lt;br /&gt;
&lt;br /&gt;
Two flows at the same bottleneck see the same &amp;lt;code&amp;gt;m*&amp;lt;/code&amp;gt;, so each&lt;br /&gt;
solves &amp;lt;code&amp;gt;r_i = a / (m*/M - 1/T_probe)&amp;lt;/code&amp;gt; to the same rate. Under&lt;br /&gt;
the network-utility-maximization view this corresponds to&lt;br /&gt;
&#039;&#039;&#039;proportional fairness&#039;&#039;&#039; (equal weights), which is max-min fair at&lt;br /&gt;
a single bottleneck.&lt;br /&gt;
&lt;br /&gt;
Measurement fidelity at low rate comes from the estimator itself:&lt;br /&gt;
the averaging window stretches with the flow&lt;br /&gt;
(Section 3), so a CA-limited flow keeps ~16 packets per window at any&lt;br /&gt;
rate down to ~30 kbit/s, and the rate-relative gap threshold keeps its&lt;br /&gt;
inter-packet spacing from reading as idle. The default floor&lt;br /&gt;
(&amp;lt;code&amp;gt;2^13&amp;lt;/code&amp;gt; B/s) only bounds the extremes (window ceiling,&lt;br /&gt;
staleness horizon, pacer arithmetic). The floor scales with the&lt;br /&gt;
fed-back path capacity (&amp;lt;code&amp;gt;r_min = C/32&amp;lt;/code&amp;gt;, Section 5.5), so&lt;br /&gt;
roughly 32 CA-limited flows fit above it at any link class. Flows&lt;br /&gt;
whose paths differ in bottleneck class derive different slopes, so&lt;br /&gt;
fairness across them is capacity-weighted; see&lt;br /&gt;
[[#8. Limitations|Section 8]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 9. Architectural fit ==&lt;br /&gt;
&lt;br /&gt;
Each property the preceding sections build toward attaches to a&lt;br /&gt;
structural feature of the recursive architecture. This section makes&lt;br /&gt;
the mapping explicit.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Congestion avoidance is fully orthogonal to ARQ and to flow&lt;br /&gt;
  control.&#039;&#039;&#039; Three concerns, three mechanisms, two scopes: FRCP&lt;br /&gt;
  provides retransmission (ARQ) and flow control (the peer pacing the&lt;br /&gt;
  sender) end-to-end, per flow; congestion avoidance runs in the&lt;br /&gt;
  IPCP, per aggregate. Each signal means one thing. A loss triggers&lt;br /&gt;
  a retransmission and nothing else &amp;amp;mdash; a lossy link reads as&lt;br /&gt;
  lossy, with the congestion verdict left to the marks (Section 8&#039;s&lt;br /&gt;
  &amp;quot;no loss response&amp;quot; is this orthogonality stated from the other&lt;br /&gt;
  side). The peer&#039;s flow-control window paces the endpoint and&lt;br /&gt;
  nothing else &amp;amp;mdash; back-pressure from a slow receiver stays&lt;br /&gt;
  distinct from congestion in the network. A retransmitted packet is&lt;br /&gt;
  ordinary traffic to the pacer. TCP folds all three into one window&lt;br /&gt;
  machine, where the receive window bounds the congestion window and&lt;br /&gt;
  loss serves as both reliability trigger and congestion signal;&lt;br /&gt;
  here each mechanism can be reasoned about, tested and evolved&lt;br /&gt;
  alone.&lt;br /&gt;
* &#039;&#039;&#039;Congestion avoidance sits below application choice.&#039;&#039;&#039; Every&lt;br /&gt;
  flow in the layer is paced by the same rate law, whatever its QoS:&lt;br /&gt;
  a greedy raw sender shares a bottleneck fairly with a reliable&lt;br /&gt;
  stream because the control is a property of the layer, on the&lt;br /&gt;
  aggregate, rather than a courtesy of the endpoint transport.&lt;br /&gt;
* &#039;&#039;&#039;A layer defines its own PCI, so the signal can be rich.&#039;&#039;&#039;&lt;br /&gt;
  Every member of a layer is enrolled into it: the layer is a single&lt;br /&gt;
  administrative domain by construction, at whatever scope it spans,&lt;br /&gt;
  and its header is layer-internal. Forwarders therefore write a&lt;br /&gt;
  multi-bit queue magnitude and a capacity byte directly into the&lt;br /&gt;
  packet. The datacenter schemes of&lt;br /&gt;
  [[#10. Heritage and positioning|Section 10]] require exactly such a&lt;br /&gt;
  domain and find it only inside one operator&#039;s fabric; a recursive&lt;br /&gt;
  layer supplies it everywhere.&lt;br /&gt;
* &#039;&#039;&#039;Layer-internal addressing makes the aggregate well-defined.&#039;&#039;&#039;&lt;br /&gt;
  Flows in a layer run between layer addresses, so the&lt;br /&gt;
  (destination, QoS cube) aggregate &amp;amp;mdash; RFC 3124&#039;s macroflow&lt;br /&gt;
  &amp;amp;mdash; falls out of the naming structure. Controller state scales&lt;br /&gt;
  with destinations, and each QoS cube keeps its own loop, so&lt;br /&gt;
  service classes keep separate fates.&lt;br /&gt;
* &#039;&#039;&#039;Flows are allocated, so feedback has a channel.&#039;&#039;&#039; Every flow&lt;br /&gt;
  has state at both ends and a reverse direction; the receiver&#039;s&lt;br /&gt;
  &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; and the path capacity ride the flow allocator&#039;s&lt;br /&gt;
  existing exchange.&lt;br /&gt;
* &#039;&#039;&#039;There is no transport ack clock, and none is relied on.&#039;&#039;&#039; The&lt;br /&gt;
  layer must control raw flows, which carry no acknowledgements at&lt;br /&gt;
  all, so the controller paces on wall-clock time &amp;amp;mdash; and that&lt;br /&gt;
  constraint yields the RTT-independent allocation of Section 6. An&lt;br /&gt;
  architectural restriction becomes the controller&#039;s distinguishing&lt;br /&gt;
  property.&lt;br /&gt;
* &#039;&#039;&#039;Recursion scales the scheme.&#039;&#039;&#039; Each layer controls congestion&lt;br /&gt;
  over its own scope and timescale; many N-flows ride one N-1 flow,&lt;br /&gt;
  so aggregation compounds down the stack. The capacity a layer&lt;br /&gt;
  measures at its egress queue is the rate the layer below actually&lt;br /&gt;
  provides &amp;amp;mdash; a paced, shared lower flow rather than a nominal&lt;br /&gt;
  wire speed &amp;amp;mdash; so the signal stays meaningful at every level,&lt;br /&gt;
  and pushback cascades layer by layer.&lt;br /&gt;
&lt;br /&gt;
The fit has a boundary: at a shim over legacy media the layer below&lt;br /&gt;
neither enrolls nor marks, which is the deployment edge behind&lt;br /&gt;
Section 8&#039;s no-loss-response limitation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 10. Heritage and positioning ==&lt;br /&gt;
&lt;br /&gt;
mb-ecn is best understood as a rate-based ECN controller in the DECbit /&lt;br /&gt;
QCN line, adapted to an acknowledgement-less recursive layer.&lt;br /&gt;
&lt;br /&gt;
The core of the scheme dates to the first Ouroboros&lt;br /&gt;
congestion-avoidance policy (2020): a multi-bit queue-depth mark&lt;br /&gt;
relayed by forwarders to the receiver, a smoothed multi-bit&lt;br /&gt;
&amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; fed back on the reverse flow at the same 1/32 fixed&lt;br /&gt;
point used today, and a sender that paced packets from a time budget&lt;br /&gt;
and ran slow start, additive increase and multiplicative decrease on&lt;br /&gt;
wall-clock time slots &amp;amp;mdash; rate-based and acknowledgement-free from&lt;br /&gt;
the start, with RTT-independent allocation already an objective. The&lt;br /&gt;
present algorithm keeps that skeleton and reworks every estimator&lt;br /&gt;
around it: per-path aggregation with SFQ pacing, the rate-adaptive&lt;br /&gt;
boxcar receiver, elapsed-time-scaled AIMD with a derivative term and&lt;br /&gt;
rate-velocity damping, and the in-band capacity signal.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;DECbit&#039;&#039;&#039; (Ramakrishnan &amp;amp;amp; Jain, 1988; ToCS 1990) established&lt;br /&gt;
  binary congestion feedback set by the switch on queue occupancy.&lt;br /&gt;
  mb-ecn keeps the switch-sets-on-queue idea and carries a&lt;br /&gt;
  &#039;&#039;magnitude&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;The Congestion Manager&#039;&#039;&#039; (Balakrishnan et al., SIGCOMM 1999;&lt;br /&gt;
  RFC 3124, 2001) aggregates congestion state at the end host: one&lt;br /&gt;
  controller per macroflow, a scheduler apportioning its rate across&lt;br /&gt;
  the member streams, and new streams joining at the ensemble&#039;s&lt;br /&gt;
  current state instead of probing from scratch. mb-ecn&#039;s&lt;br /&gt;
  (destination, QoS cube) aggregate is the same idea placed inside&lt;br /&gt;
  the IPCP: every layer manages its own macroflows, recursively, the&lt;br /&gt;
  SFQ pacer plays the CM scheduler&#039;s role (Section 4), and a joining&lt;br /&gt;
  flow rides the aggregate&#039;s estimates (Section 5.1). The CM gathers&lt;br /&gt;
  its signal from transport feedback at the edge; mb-ecn reads it&lt;br /&gt;
  off the wire from the forwarders.&lt;br /&gt;
* &#039;&#039;&#039;DCTCP&#039;&#039;&#039; (Alizadeh et al., SIGCOMM 2010) recovers a queue&lt;br /&gt;
  magnitude at the endpoint by averaging a single threshold bit. mb-ecn&lt;br /&gt;
  instead reads the magnitude off the wire, then time-averages it at&lt;br /&gt;
  the receiver. The additive-increase / proportional-decrease structure&lt;br /&gt;
  is shared.&lt;br /&gt;
* &#039;&#039;&#039;QCN&#039;&#039;&#039; (IEEE 802.1Qau, 2010) and &#039;&#039;&#039;DCQCN&#039;&#039;&#039; (Zhu et al., SIGCOMM&lt;br /&gt;
  2015) contribute the rate-based (not window-based) ECN control&lt;br /&gt;
  skeleton. mb-ecn&#039;s proportional decrease plus a probing increase&lt;br /&gt;
  mirrors that skeleton; the increase here is wall-clock scaled rather&lt;br /&gt;
  than byte-counter/timer based.&lt;br /&gt;
* &#039;&#039;&#039;HPCC&#039;&#039;&#039; (Li et al., SIGCOMM 2019) and &#039;&#039;&#039;PowerTCP&#039;&#039;&#039; (Addanki et&lt;br /&gt;
  al., NSDI 2022) combine a queue term &#039;&#039;and&#039;&#039; a rate term (via in-band&lt;br /&gt;
  telemetry or delay). mb-ecn now carries a coarse in-band rate (the&lt;br /&gt;
  quarter-log2 path MIN, Section 2) but uses it only to scale the&lt;br /&gt;
  controller&#039;s gains; the congestion &#039;&#039;signal&#039;&#039; remains queue-only,&lt;br /&gt;
  which is still the main functional difference (Section 8).&lt;br /&gt;
* &#039;&#039;&#039;L4S / TCP Prague&#039;&#039;&#039; (RFC 9330&amp;amp;ndash;9332, 2023) make&lt;br /&gt;
  RTT-independence a requirement and use a high-frequency single-bit&lt;br /&gt;
  signal. mb-ecn shares the RTT-independent &#039;&#039;allocation&#039;&#039; goal but&lt;br /&gt;
  reaches it by wall-clock-scaled rate control, with a low-frequency&lt;br /&gt;
  multi-bit signal.&lt;br /&gt;
&lt;br /&gt;
The novel combination is the multi-bit magnitude on the wire fused with&lt;br /&gt;
a wall-clock-scaled rate pacer in a setting with no per-flow ack clock.&lt;br /&gt;
The trade-off, made explicit above, is a queue-only signal and a&lt;br /&gt;
reactivity capped by the averaging window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 11. Parameters ==&lt;br /&gt;
&lt;br /&gt;
These are the values every figure in this document instantiates. They&lt;br /&gt;
are &#039;&#039;&#039;coupled&#039;&#039;&#039;: the mechanisms compose only while certain relations&lt;br /&gt;
hold &amp;amp;mdash; the gap-restart horizon at the rate floor must exceed a&lt;br /&gt;
packet&#039;s service time (or a floor-rate flow restart-loops at onset),&lt;br /&gt;
a floor-rate flow&#039;s target window must fit under the window ceiling,&lt;br /&gt;
the control interval must sit under the damping bucket under the idle&lt;br /&gt;
credit cap, the rate floor must not exceed the slow-start seed, and&lt;br /&gt;
the derived-floor ceiling must stay under the rate ceiling. Retune&lt;br /&gt;
them as a set, against those invariants.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| Marking quantum &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; || 4 packets || Mark onset and step; layer-wide&lt;br /&gt;
|-&lt;br /&gt;
| Feedback resolution || 1/32 &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; unit || Fixed-point LSB of &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Full-congestion reference &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; || 512 || Mean queue of 64 packets&lt;br /&gt;
|-&lt;br /&gt;
| Receiver window target || 16 packets (16,000 bytes) || Samples per window&lt;br /&gt;
|-&lt;br /&gt;
| Initial receiver window || ~67 ms || Before rate is known&lt;br /&gt;
|-&lt;br /&gt;
| Receiver window bounds || ~1.05 ms &amp;amp;ndash; ~4.3 s || Clamp on &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Window resize weight || 1/4 || Moving average toward target&lt;br /&gt;
|-&lt;br /&gt;
| Early window close || 2 &amp;amp;times; target bytes || Speed-up escape&lt;br /&gt;
|-&lt;br /&gt;
| Gap restart || 4 windows || Idle horizon (rate-relative)&lt;br /&gt;
|-&lt;br /&gt;
| Additive slope &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; (default) || 2^16 B/s^2 || Lower clamp of derived slope&lt;br /&gt;
|-&lt;br /&gt;
| Probe time constant &amp;lt;code&amp;gt;T_probe&amp;lt;/code&amp;gt; || 8 s || Rate-proportional recovery&lt;br /&gt;
|-&lt;br /&gt;
| Derivative gain || 1/4 || Onset sharpening&lt;br /&gt;
|-&lt;br /&gt;
| Rate floor &amp;lt;code&amp;gt;r_min&amp;lt;/code&amp;gt; (default) || 2^13 B/s || Lower clamp of derived floor&lt;br /&gt;
|-&lt;br /&gt;
| Slow-start seed rate || 2^16 B/s || Initial rate&lt;br /&gt;
|-&lt;br /&gt;
| Slow-start time constant || 20 ms || Exponential ramp&lt;br /&gt;
|-&lt;br /&gt;
| Rate ceiling &amp;lt;code&amp;gt;r_max&amp;lt;/code&amp;gt; || 2^37 B/s || Upper clamp&lt;br /&gt;
|-&lt;br /&gt;
| Capacity scale || &amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt; / 32 || Floor and slope from bottleneck&lt;br /&gt;
|-&lt;br /&gt;
| Capacity smoothing || 1/2 || Moving average per feedback&lt;br /&gt;
|-&lt;br /&gt;
| Capacity staleness || 16 &amp;amp;times; feedback horizon || Revert to defaults&lt;br /&gt;
|-&lt;br /&gt;
| Derived floor ceiling || 2^32 B/s || Upper clamp on derived floor&lt;br /&gt;
|-&lt;br /&gt;
| Control interval || 1 ms || Minimum spacing of rate updates&lt;br /&gt;
|-&lt;br /&gt;
| Idle credit cap || 50 ms || Burst allowance / increase banking&lt;br /&gt;
|-&lt;br /&gt;
| Feedback staleness floor || ~268 ms || Floor of rate-relative horizon&lt;br /&gt;
|-&lt;br /&gt;
| Damping bucket || ~31 ms || Rate-velocity washout cadence&lt;br /&gt;
|-&lt;br /&gt;
| Damping fraction || 1/4 || Pullback per bucket&lt;br /&gt;
|-&lt;br /&gt;
| Estimator fold spacing || 1 ms || Minimum measurement cadence&lt;br /&gt;
|-&lt;br /&gt;
| Estimator window closure || 16 packets drained || Self-scaling window&lt;br /&gt;
|-&lt;br /&gt;
| Estimator window staleness || ~134 ms || Discard across traffic gaps&lt;br /&gt;
|-&lt;br /&gt;
| Estimator idle tolerance || 1/8 of arrivals || Unsaturated-window rejection&lt;br /&gt;
|-&lt;br /&gt;
| Estimator decay || 1/16 per window || Max-filter convergence from below&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 12. References ==&lt;br /&gt;
&lt;br /&gt;
=== 12.1. Source ===&lt;br /&gt;
&lt;br /&gt;
The implementation follows the algorithm above:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/ca/mb-ecn.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mb-ecn.h&amp;lt;/code&amp;gt; &amp;amp;mdash;&lt;br /&gt;
  receiver estimator, pacer and rate control.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/ca/ops.h&amp;lt;/code&amp;gt; &amp;amp;mdash; CA policy interface.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/cap.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cap.h&amp;lt;/code&amp;gt; &amp;amp;mdash; link-capacity&lt;br /&gt;
  estimator and quarter-log2 codec.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/dt.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fa.c&amp;lt;/code&amp;gt; &amp;amp;mdash; per-hop marking&lt;br /&gt;
  and stamping, and the feedback carrying &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;cap8&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/psched.c&amp;lt;/code&amp;gt; &amp;amp;mdash; consumes the pacing deadline.&lt;br /&gt;
&lt;br /&gt;
=== 12.2. Literature ===&lt;br /&gt;
&lt;br /&gt;
* K. K. Ramakrishnan, R. Jain, &amp;quot;A Binary Feedback Scheme for Congestion&lt;br /&gt;
  Avoidance in Computer Networks&amp;quot; (DECbit), ACM ToCS, 1990.&lt;br /&gt;
* H. Balakrishnan, H. S. Rahul, S. Seshan, &amp;quot;An Integrated Congestion&lt;br /&gt;
  Management Architecture for Internet Hosts&amp;quot;, SIGCOMM 1999;&lt;br /&gt;
  H. Balakrishnan, S. Seshan, &amp;quot;The Congestion Manager&amp;quot;, RFC 3124, 2001.&lt;br /&gt;
* M. Alizadeh et al., &amp;quot;Data Center TCP (DCTCP)&amp;quot;, SIGCOMM 2010.&lt;br /&gt;
* Y. Zhu et al., &amp;quot;Congestion Control for Large-Scale RDMA Deployments&amp;quot;&lt;br /&gt;
  (DCQCN), SIGCOMM 2015.&lt;br /&gt;
* R. Mittal et al., &amp;quot;TIMELY: RTT-based Congestion Control&amp;quot;, SIGCOMM 2015.&lt;br /&gt;
* Y. Li et al., &amp;quot;HPCC: High Precision Congestion Control&amp;quot;, SIGCOMM 2019.&lt;br /&gt;
* V. Addanki et al., &amp;quot;PowerTCP&amp;quot;, NSDI 2022.&lt;br /&gt;
* K. De Schepper, B. Briscoe (eds.), &amp;quot;Low Latency, Low Loss, and&lt;br /&gt;
  Scalable Throughput (L4S)&amp;quot;, RFC 9330/9331/9332.&lt;br /&gt;
* F. Kelly, &amp;quot;Charging and Rate Control for Elastic Traffic&amp;quot;, 1997;&lt;br /&gt;
  R. Srikant, &amp;quot;The Mathematics of Internet Congestion Control&amp;quot;, 2004.&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1936</id>
		<title>Congestion avoidance</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1936"/>
		<updated>2026-07-11T20:45:20Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 5.7. Regimes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:mb-ecn - Multi-bit ECN Congestion Avoidance}}&lt;br /&gt;
&lt;br /&gt;
mb-ecn is a congestion-avoidance (CA) policy for the Ouroboros unicast&lt;br /&gt;
IPCP. It lives in the IPC Process: the flow-and-retransmission task&lt;br /&gt;
(FRCP) provides reliability, in-order delivery and flow control&lt;br /&gt;
end-to-end; congestion avoidance is orthogonal and runs per IPCP&lt;br /&gt;
layer. Within a layer, the IPCP controls &#039;&#039;&#039;aggregates&#039;&#039;&#039;, one loop&lt;br /&gt;
per (destination address, QoS cube), shared by every flow toward that&lt;br /&gt;
destination. The alternative policy is &amp;lt;code&amp;gt;nop&amp;lt;/code&amp;gt; (no congestion&lt;br /&gt;
avoidance).&lt;br /&gt;
&lt;br /&gt;
This document describes the algorithm: what the policy does and why.&lt;br /&gt;
The implementation follows it ([[#12. References|Section 12]] maps the&lt;br /&gt;
pieces to source files). It does not claim properties the algorithm&lt;br /&gt;
does not have; [[#8. Limitations|Section 8]] states the known gaps&lt;br /&gt;
explicitly.&lt;br /&gt;
&lt;br /&gt;
The keywords &amp;quot;MUST&amp;quot;, &amp;quot;SHOULD&amp;quot;, &amp;quot;MAY&amp;quot; etc. are used only where they&lt;br /&gt;
appear in all capitals, per BCP 14 (RFC 2119, RFC 8174).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt;&lt;br /&gt;
:Paced send rate, bytes per second.&lt;br /&gt;
;&amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bottleneck capacity, bytes per second.&lt;br /&gt;
;&amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt;&lt;br /&gt;
:Packet size in bytes.&lt;br /&gt;
;&amp;lt;code&amp;gt;q&amp;lt;/code&amp;gt;&lt;br /&gt;
:Standing queue occupancy at the bottleneck, in packets.&lt;br /&gt;
;&amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt;&lt;br /&gt;
:Marking quantum, packets per mark unit (4).&lt;br /&gt;
;&amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt;&lt;br /&gt;
:Per-packet congestion mark set by a forwarder, an unsigned integer&lt;br /&gt;
 (on-wire field is 8-bit).&lt;br /&gt;
;&amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
:Receiver-side congestion estimate, fixed point with LSB = 1/32 of an&lt;br /&gt;
 &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; unit, i.e. &amp;lt;code&amp;gt;ece = 32 &amp;amp;times; (time-mean ecn)&amp;lt;/code&amp;gt;.&lt;br /&gt;
;&amp;lt;code&amp;gt;cap8&amp;lt;/code&amp;gt;&lt;br /&gt;
:Path-capacity code carried per packet, quarter-log2:&lt;br /&gt;
 &amp;lt;code&amp;gt;C &amp;amp;asymp; 2^(cap8 / 4)&amp;lt;/code&amp;gt; bytes/s, 0 = unknown.&lt;br /&gt;
;&amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt;&lt;br /&gt;
:Receiver averaging window, nanoseconds.&lt;br /&gt;
;&amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt;&lt;br /&gt;
:Additive-increase slope, bytes/s per second.&lt;br /&gt;
;&amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt;&lt;br /&gt;
:Full-congestion reference level of &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; (512).&lt;br /&gt;
;&amp;lt;code&amp;gt;&amp;amp;Delta;t&amp;lt;/code&amp;gt;&lt;br /&gt;
:Elapsed wall-clock time between two events, nanoseconds.&lt;br /&gt;
;&amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;&lt;br /&gt;
:Path round-trip time (feedback delay). The controller does not&lt;br /&gt;
 measure it.&lt;br /&gt;
&lt;br /&gt;
Rate is the control variable throughout.&lt;br /&gt;
&lt;br /&gt;
All concrete figures in this document &amp;amp;mdash; window bounds, horizons,&lt;br /&gt;
rate thresholds &amp;amp;mdash; are instances of the parameter values in&lt;br /&gt;
[[#11. Parameters|Section 11]]. The mechanisms and the relations&lt;br /&gt;
between parameters are what the algorithm fixes; retuning the&lt;br /&gt;
parameters moves the figures together.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== 1. Overview ==&lt;br /&gt;
&lt;br /&gt;
mb-ecn is a rate-based, ECN-driven controller with three moving parts,&lt;br /&gt;
one at each point of the loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        sender                                  forwarder&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
      |  SFQ virtual-clock pacer       |       |  ecn = MAX(ecn, queue/Q) |&lt;br /&gt;
      |  rate r, paced per packet      | data  |  (deepest queue wins)    |&lt;br /&gt;
      |  AIMD + PD control on r        |------&amp;gt;|  cap8 = MIN(cap8, link)  |&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
                 ^                                        |&lt;br /&gt;
                 | ece + cap8 feedback                    | marked data&lt;br /&gt;
                 |                                        v&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
      |  reads downstream ece,         |&amp;lt;------|  receiver                |&lt;br /&gt;
      |  updates the rate on feedback, |  ece  |  time-mean of ecn over   |&lt;br /&gt;
      |  scales floor/slope to cap8    |  cap8 |  an adaptive window      |&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# The &#039;&#039;&#039;forwarder&#039;&#039;&#039; marks each packet with a &#039;&#039;multi-bit&#039;&#039; magnitude&lt;br /&gt;
  proportional to its standing queue, and MIN-stamps its measured&lt;br /&gt;
  outgoing-link capacity into the same PCI.&lt;br /&gt;
# The &#039;&#039;&#039;receiver&#039;&#039;&#039; turns the stream of marks into a time-averaged&lt;br /&gt;
  congestion estimate &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; and feeds it back to the sender,&lt;br /&gt;
  together with the window minimum of the path-capacity codes.&lt;br /&gt;
# The &#039;&#039;&#039;sender&#039;&#039;&#039; paces its rate with a start-time fair-queuing (SFQ)&lt;br /&gt;
  virtual clock, and adjusts that rate with additive-increase /&lt;br /&gt;
  multiplicative-decrease (AIMD) driven by &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;; the rate floor&lt;br /&gt;
  and the additive slope derive from the fed-back path capacity.&lt;br /&gt;
&lt;br /&gt;
The controller runs on packet sends and on feedback arrivals. All&lt;br /&gt;
increase/decrease steps are scaled by elapsed&lt;br /&gt;
wall-clock time, which is what makes the &#039;&#039;allocation&#039;&#039; independent of&lt;br /&gt;
RTT (see [[#6. RTT behaviour|Section 6]]).&lt;br /&gt;
&lt;br /&gt;
The unit of control is the &#039;&#039;&#039;aggregate&#039;&#039;&#039;, kept in the IPCP per&lt;br /&gt;
&amp;lt;code&amp;gt;(destination address, QoS cube)&amp;lt;/code&amp;gt;. Every flow toward that&lt;br /&gt;
destination at that QoS shares one controller and one rate: a new&lt;br /&gt;
flow joins the aggregate at its current rate, and a departing flow&lt;br /&gt;
leaves the rate to the others. The pacer divides the aggregate rate&lt;br /&gt;
fairly across the member flows (SFQ), so per-flow fairness within an&lt;br /&gt;
aggregate is a scheduler property, distinct from the rate law&lt;br /&gt;
described here.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2. Forwarder marking ==&lt;br /&gt;
&lt;br /&gt;
A forwarder marks a packet as a function of the packets already queued&lt;br /&gt;
on the outgoing link:&lt;br /&gt;
&lt;br /&gt;
  mark = queued / Q                         (Q = 4 packets)&lt;br /&gt;
  ecn  = MAX(ecn, mark)                     (saturating, 8-bit field)&lt;br /&gt;
&lt;br /&gt;
The quantum &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; is a layer-wide constant: the marks it&lt;br /&gt;
quantizes are combined by MAX across hops, so all forwarders in a&lt;br /&gt;
layer MUST use the same quantum for the signal to be comparable.&lt;br /&gt;
&lt;br /&gt;
Two properties:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-bit magnitude.&#039;&#039;&#039; The mark is an integer proportional to&lt;br /&gt;
  queue depth, quantized in steps of &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; packets: a coarse&lt;br /&gt;
  readout of the standing queue.&lt;br /&gt;
* &#039;&#039;&#039;MAX along the path.&#039;&#039;&#039; A packet crossing several forwarders carries&lt;br /&gt;
  the maximum mark, i.e. the deepest queue on its path. MAX keeps the&lt;br /&gt;
  signal range-stable and monotone across hops.&lt;br /&gt;
&lt;br /&gt;
There is a &#039;&#039;&#039;dead zone&#039;&#039;&#039;: for &amp;lt;code&amp;gt;queued &amp;lt; Q&amp;lt;/code&amp;gt; the mark is 0.&lt;br /&gt;
&lt;br /&gt;
=== Capacity stamping ===&lt;br /&gt;
&lt;br /&gt;
Each forwarder also measures the capacity of its outgoing link and&lt;br /&gt;
stamps it into the packet. The estimate is a busy-period drain rate:&lt;br /&gt;
the queue is observed at most once per millisecond, and a measurement&lt;br /&gt;
window stays open until 16 packets have drained, so its length&lt;br /&gt;
&#039;&#039;&#039;self-scales with the link rate&#039;&#039;&#039; &amp;amp;mdash; the millisecond cadence&lt;br /&gt;
bounds it on fast links, the 16-packet drain time on slow ones&lt;br /&gt;
(~19 ms at 10 Mbit/s). A window in which more than 1/8 of&lt;br /&gt;
the arrivals found the queue empty is discarded as unsaturated;&lt;br /&gt;
occasional empty observations (token-bucket shapers grazing zero) are&lt;br /&gt;
tolerated. The drain rate of a saturated queue is the link rate; a max&lt;br /&gt;
filter with slow decay (1/16 per window) converges on it from below. A&lt;br /&gt;
window that opened or closed on an empty queue may have drained into&lt;br /&gt;
buffers below at above-wire rate, so it may lower the estimate but&lt;br /&gt;
never raise it. The code on the wire is quarter-log2&lt;br /&gt;
(&amp;lt;code&amp;gt;cap8 = 4 log2 C&amp;lt;/code&amp;gt;, ~19% per step, 0 = unknown) and each hop&lt;br /&gt;
MIN-combines its own code into the byte, so a packet arrives carrying&lt;br /&gt;
the bottleneck&#039;s rate. A hop that has never been backlogged stamps&lt;br /&gt;
nothing; since only backlogged hops matter, the signal exists exactly&lt;br /&gt;
when it is needed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. Receiver estimate ==&lt;br /&gt;
&lt;br /&gt;
The receiver converts marks into a smoothed estimate &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; over a&lt;br /&gt;
time window &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt; that &#039;&#039;&#039;adapts to the incoming byte rate&#039;&#039;&#039;. It is&lt;br /&gt;
a &#039;&#039;&#039;boxcar (rectangular) time-integral mean&#039;&#039;&#039;. Per packet, with&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;Delta;t&amp;lt;/code&amp;gt; the gap since the previous packet:&lt;br /&gt;
&lt;br /&gt;
  A += ecn * MIN(&amp;amp;Delta;t, W)              dwell-weighted; one packet&lt;br /&gt;
                                          weighs at most one window&lt;br /&gt;
  B += S                                  accumulate bytes this window&lt;br /&gt;
  ...&lt;br /&gt;
  at window close (elapsed &amp;gt;= W, or B &amp;gt;= 2 * B_target with at&lt;br /&gt;
                   least the window floor elapsed):&lt;br /&gt;
      ece = 32 * A / elapsed              mean over the actual window&lt;br /&gt;
      W  += (B_target * elapsed / B - W) / 4    nudge toward the size&lt;br /&gt;
                                                that holds the target&lt;br /&gt;
      W   = clamp(W, ~1.05 ms, ~4.3 s)&lt;br /&gt;
      A   = 0 ; B = 0                     hard reset&lt;br /&gt;
&lt;br /&gt;
At each close the next window is nudged by a quarter-weight moving&lt;br /&gt;
average toward the size that would hold about 16 packets at the&lt;br /&gt;
measured byte rate, from an initial ~67 ms. The target is byte-based (&amp;lt;code&amp;gt;B_target&amp;lt;/code&amp;gt; = 16,000 bytes), so&lt;br /&gt;
&amp;quot;16 packets&amp;quot; is exact only at ~1000-byte packets, and the window&lt;br /&gt;
converges to it over several closes. The mean always divides by the&lt;br /&gt;
&#039;&#039;&#039;actual&#039;&#039;&#039; elapsed window, so a rate step leaves the current&lt;br /&gt;
estimate exact and only re-sizes the &#039;&#039;next&#039;&#039; window. The effect is a roughly constant sample&lt;br /&gt;
count &amp;amp;mdash; ~16 packets &amp;amp;mdash; from about 30 kbit/s to 122 Mbit/s;&lt;br /&gt;
above that the window floors at ~1 ms (a cadence still carrying&lt;br /&gt;
thousands of samples at 10&amp;amp;ndash;100 GbE), and below ~30 kbit/s it&lt;br /&gt;
saturates at the ~4.3 s ceiling. The averaging clock stretches with&lt;br /&gt;
the flow the way TCP&#039;s ACK clock stretches with the RTT. A speed-up&lt;br /&gt;
escapes a stretched window early: once twice the target bytes arrive&lt;br /&gt;
the window closes anyway (the window floor keeps that cadence bounded&lt;br /&gt;
at high rate). The fed-back &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; is thus a piecewise-constant&lt;br /&gt;
staircase whose step period tracks the rate. Two edge cases:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Onset&#039;&#039;&#039; (first mark after an idle estimate) emits the&lt;br /&gt;
  instantaneous mark undiluted, so a starting queue is reported without&lt;br /&gt;
  a full window of delay.&lt;br /&gt;
* &#039;&#039;&#039;Gap&#039;&#039;&#039; longer than 4 windows resets the window and emits the raw&lt;br /&gt;
  sample. The threshold is &#039;&#039;&#039;rate-relative&#039;&#039;&#039;: idle means a few&lt;br /&gt;
  &#039;&#039;current&#039;&#039; windows of silence, so a slow flow&#039;s normal inter-packet&lt;br /&gt;
  gap never reads as idle, while the in-window dwell clamp above bounds&lt;br /&gt;
  what a genuine pause can add to the mean before the restart fires.&lt;br /&gt;
&lt;br /&gt;
Alongside the mark integral, the receiver keeps the MIN of the nonzero&lt;br /&gt;
capacity codes seen in the current window; each emitted &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
carries that minimum (the raw packet&#039;s code on onset/gap restarts) and&lt;br /&gt;
the fold then resets, so a reroute to a faster path can raise the&lt;br /&gt;
fed-back capacity within one window.&lt;br /&gt;
&lt;br /&gt;
The estimate is fed back to the sender on the reverse flow.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 4. Sender pacer ==&lt;br /&gt;
&lt;br /&gt;
The sender paces with an SFQ virtual clock. The context keeps a&lt;br /&gt;
virtual time &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;; each flow keeps a finish tag &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
  V    += r * &amp;amp;Delta;t                     virtual time advances by bytes served&lt;br /&gt;
  s     = MAX(F, V)                  a packet behind the clock starts now&lt;br /&gt;
  F     = s + S                      the one after it queues behind&lt;br /&gt;
  wait  = (s - V) / r                time until this packet may go&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;wait&amp;lt;/code&amp;gt; becomes the flow&#039;s scheduling deadline: a packet&lt;br /&gt;
already behind the virtual clock is sent immediately&lt;br /&gt;
(&amp;lt;code&amp;gt;wait = 0&amp;lt;/code&amp;gt;); one ahead waits, deferring only its own flow.&lt;br /&gt;
A clock advance across an idle gap longer than 50 ms credits at most&lt;br /&gt;
the calling flow&#039;s owed lead plus one burst (50 ms of service, at&lt;br /&gt;
least one packet): a paced flow slower than one packet per 50 ms&lt;br /&gt;
receives its &#039;&#039;&#039;true&#039;&#039;&#039; elapsed service &amp;amp;mdash; a time-capped credit&lt;br /&gt;
would decay such a flow without bound &amp;amp;mdash; while an idle flow still&lt;br /&gt;
cannot bank an unbounded burst.&lt;br /&gt;
&lt;br /&gt;
The virtual time &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; is shared by every flow in the context;&lt;br /&gt;
the finish tags are per flow. Because every flow computes its start&lt;br /&gt;
tag &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; against the same &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;, a flow that has just sent&lt;br /&gt;
carries a finish tag ahead of &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; and waits, while an idle flow&lt;br /&gt;
(finish tag at or behind &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;) starts immediately &amp;amp;mdash; this is&lt;br /&gt;
what fairly divides the aggregate rate &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; across them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 5. Rate control (AIMD + PD) ==&lt;br /&gt;
&lt;br /&gt;
The rate law runs on packet sends, at most once per millisecond, and&lt;br /&gt;
also on feedback arrivals so that a sender with no traffic of its own&lt;br /&gt;
still reacts. Each increase term and the proportional decrease are&lt;br /&gt;
scaled by elapsed wall-clock time, each by its own clock: the&lt;br /&gt;
increase terms use time banked and &#039;&#039;&#039;capped&#039;&#039;&#039; at 50 ms (an idle gap&lt;br /&gt;
cannot bank an unbounded ramp), while the proportional decrease uses&lt;br /&gt;
the &#039;&#039;&#039;uncapped&#039;&#039;&#039; elapsed time (a starved sender still cuts by the&lt;br /&gt;
right amount). The one-sided derivative (Section 5.3) is a per-delta&lt;br /&gt;
term.&lt;br /&gt;
&lt;br /&gt;
=== 5.1. Slow start ===&lt;br /&gt;
&lt;br /&gt;
Before the first congestion signal the rate ramps exponentially with a&lt;br /&gt;
20 ms time constant:&lt;br /&gt;
&lt;br /&gt;
  r += r * &amp;amp;Delta;t / T_ss                  (T_ss = 20 ms)&lt;br /&gt;
&lt;br /&gt;
Slow start ends the first time a congestion signal arrives &amp;amp;mdash;&lt;br /&gt;
fed back from the receiver or observed at the sender&#039;s own first hop&lt;br /&gt;
&amp;amp;mdash; and never resumes.&lt;br /&gt;
&lt;br /&gt;
Slow start belongs to the aggregate, so it runs &#039;&#039;&#039;once per&lt;br /&gt;
(destination, QoS cube)&#039;&#039;&#039;, on the first flow. A flow arriving at an&lt;br /&gt;
established aggregate rides the existing estimates: it enters at the&lt;br /&gt;
aggregate&#039;s current rate and receives its fair share of it through&lt;br /&gt;
the pacer (Section 4), with no probing of its own.&lt;br /&gt;
&lt;br /&gt;
=== 5.2. Increase: additive plus proportional probe (always on) ===&lt;br /&gt;
&lt;br /&gt;
Past slow start, two increase terms are applied every step, both&lt;br /&gt;
regardless of congestion:&lt;br /&gt;
&lt;br /&gt;
  r += a * &amp;amp;Delta;t                         additive; a defaults to 2^16 B/s^2&lt;br /&gt;
  r += r * &amp;amp;Delta;t / T_probe               proportional; T_probe = 8 s&lt;br /&gt;
&lt;br /&gt;
The additive term is a fixed per-context slope (bytes/s per second),&lt;br /&gt;
scaled to the path capacity (Section 5.5). The proportional probe&lt;br /&gt;
grows the rate by a fixed fraction per unit time (e-folds over&lt;br /&gt;
&amp;lt;code&amp;gt;T_probe&amp;lt;/code&amp;gt;): a flow recovers in the same number of steps at any&lt;br /&gt;
link rate; the fixed additive term&#039;s effect vanishes relative to a&lt;br /&gt;
fast link. Both are the &amp;quot;probing&amp;quot;&lt;br /&gt;
pressure the multiplicative decrease balances at equilibrium; the&lt;br /&gt;
probe&#039;s cost is a rate-independent standing-queue floor (Section 7).&lt;br /&gt;
&lt;br /&gt;
=== 5.3. Multiplicative decrease (proportional + derivative) ===&lt;br /&gt;
&lt;br /&gt;
With congestion level &amp;lt;code&amp;gt;m&amp;lt;/code&amp;gt; (the fed-back &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;, or a local&lt;br /&gt;
first-hop mark as fallback), the decrease is the sum of a proportional&lt;br /&gt;
term and a one-sided derivative term &amp;amp;mdash; a PD controller on the&lt;br /&gt;
congestion signal:&lt;br /&gt;
&lt;br /&gt;
  mark = MIN(m, M)                                   M = 512&lt;br /&gt;
  rise = MAX(m - m_prev, 0), capped at M             one-sided derivative&lt;br /&gt;
&lt;br /&gt;
  cut  = r * rise / (4 * M)                          derivative, gain 1/4&lt;br /&gt;
  cut += r * mark * &amp;amp;Delta;t_ms / (1000 * M)              proportional&lt;br /&gt;
  cut  = MIN(cut, r / 2)                             at most a halving&lt;br /&gt;
&lt;br /&gt;
  r     -= cut&lt;br /&gt;
  m_prev = m&lt;br /&gt;
&lt;br /&gt;
* The &#039;&#039;&#039;proportional&#039;&#039;&#039; term is scaled by honest elapsed time in&lt;br /&gt;
  milliseconds, so a starved sender that has not run the controller&lt;br /&gt;
  for a while still cuts by the right amount.&lt;br /&gt;
* The &#039;&#039;&#039;derivative&#039;&#039;&#039; term (&amp;lt;code&amp;gt;rise&amp;lt;/code&amp;gt;) reacts to an &#039;&#039;increase&#039;&#039; in&lt;br /&gt;
  the congestion level between samples; it is one-sided and per-delta.&lt;br /&gt;
  It sharpens the reaction at the onset of congestion.&lt;br /&gt;
* The cut is clamped to &amp;lt;code&amp;gt;r/2&amp;lt;/code&amp;gt; per step, bounding the decrease to&lt;br /&gt;
  a halving.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;M = 512 = 32 &amp;amp;times; 16&amp;lt;/code&amp;gt; defines &amp;quot;full congestion&amp;quot; as a mean&lt;br /&gt;
&amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; of 16, i.e. a mean queue of &amp;lt;code&amp;gt;16 &amp;amp;times; Q = 64&amp;lt;/code&amp;gt; packets.&lt;br /&gt;
&lt;br /&gt;
=== 5.4. Staleness ===&lt;br /&gt;
&lt;br /&gt;
Feedback arrives once per receiver window, and the window stretches&lt;br /&gt;
with the flow&#039;s byte rate (Section 3) &amp;amp;mdash; so the staleness horizon&lt;br /&gt;
must stretch with it, or a slow flow&#039;s mark would age out &#039;&#039;between&#039;&#039;&lt;br /&gt;
feedbacks and the rate would ramp against a congested path. The&lt;br /&gt;
sender mirrors the receiver: the horizon is four target windows of&lt;br /&gt;
bytes at the current rate, floored at ~268 ms so fast flows keep a&lt;br /&gt;
fixed horizon; at the default rate floor it is ~8 s. A signal older&lt;br /&gt;
than the horizon is aged out (both the fed-back estimate and the&lt;br /&gt;
local mark), freeing the rate to ramp once congestion clears. The&lt;br /&gt;
rate is clamped to&lt;br /&gt;
&amp;lt;code&amp;gt;[r_min, r_max]&amp;lt;/code&amp;gt; on every step.&lt;br /&gt;
&lt;br /&gt;
=== 5.5. Capacity-derived floor and slope ===&lt;br /&gt;
&lt;br /&gt;
The fed-back capacity code sets the two rate-scale constants of the&lt;br /&gt;
controller, per context:&lt;br /&gt;
&lt;br /&gt;
  target = decode(cap8) / 32              clamped to [2^13, 2^32] B/s&lt;br /&gt;
  r_min += (target - r_min) / 2           per feedback (moving average)&lt;br /&gt;
  a      = r_min&lt;br /&gt;
&lt;br /&gt;
The defaults are fixed constants (&amp;lt;code&amp;gt;r_min = 2^13 B/s&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;a = 2^16 B/s^2&amp;lt;/code&amp;gt;), used on paths that never report a capacity;&lt;br /&gt;
with the estimator live the fairness floor and the post-cut recovery&lt;br /&gt;
slope scale with the bottleneck, and capacity engages wherever&lt;br /&gt;
&amp;lt;code&amp;gt;C/32&amp;lt;/code&amp;gt; clears the default floor, i.e. above ~2 Mbit/s. A&lt;br /&gt;
capacity older than 16 staleness horizons (~4.3 s for fast flows)&lt;br /&gt;
reverts both to the defaults. That horizon deliberately outlives the&lt;br /&gt;
feedback horizon: feedback stops the moment the marks clear, which is&lt;br /&gt;
exactly when the recovery slope is needed; the onset-fresh capacity&lt;br /&gt;
(Section 3) re-seeds it on the first mark of the next episode either&lt;br /&gt;
way.&lt;br /&gt;
&lt;br /&gt;
=== 5.6. Ramp and recovery ===&lt;br /&gt;
&lt;br /&gt;
Three mechanisms set how fast an aggregate reaches a fast link&#039;s&lt;br /&gt;
rate, each owning one regime:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;From scratch: slow start.&#039;&#039;&#039; The exponential ramp reaches any&lt;br /&gt;
  physical link rate in well under a second (~200 ms from the seed to&lt;br /&gt;
  10 Gbit/s; each further doubling of link speed costs one more&lt;br /&gt;
  ~14 ms doubling time). On an uncongested path nothing marks, so&lt;br /&gt;
  slow start runs until the aggregate arrives at its bottleneck.&lt;br /&gt;
* &#039;&#039;&#039;After a cut: the probe.&#039;&#039;&#039; A decrease is at most a halving per&lt;br /&gt;
  step, and the mark ages out on the rate-relative horizon once&lt;br /&gt;
  congestion clears (Section 5.4); the proportional probe then heals&lt;br /&gt;
  a halving in &amp;lt;code&amp;gt;T_probe &amp;amp;middot; ln 2 &amp;amp;asymp; 5.5 s&amp;lt;/code&amp;gt; at any&lt;br /&gt;
  link rate.&lt;br /&gt;
* &#039;&#039;&#039;At the bottom: the capacity floor.&#039;&#039;&#039; The rate sits at most a&lt;br /&gt;
  factor 32 below a measured bottleneck, and the additive slope&lt;br /&gt;
  refills &amp;lt;code&amp;gt;C/32&amp;lt;/code&amp;gt; per second, so the deepest hole is bounded:&lt;br /&gt;
  floor to full capacity in tens of seconds, a halving in seconds.&lt;br /&gt;
&lt;br /&gt;
These compose because a deep cut requires sustained congestion,&lt;br /&gt;
sustained congestion backlogs the bottleneck queue, and a backlogged&lt;br /&gt;
queue is exactly when its capacity is measured and stamped&lt;br /&gt;
(Section 2): whenever the controller is cut deep, the scaled floor is&lt;br /&gt;
live. The default floor only governs paths that never congested the&lt;br /&gt;
sender &amp;amp;mdash; where there is nothing to recover from. This is why&lt;br /&gt;
slow start can end for good: the floor and the probe cover every&lt;br /&gt;
later recovery.&lt;br /&gt;
&lt;br /&gt;
=== 5.7. Regimes ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   +-------------+   first mark seen    +----------------+&lt;br /&gt;
   | slow start  |---------------------&amp;gt;|  congestion    |&lt;br /&gt;
   | r *= e^(t/T)|                      |  avoidance     |&lt;br /&gt;
   +-------------+                      |                |&lt;br /&gt;
                                        | m == 0 -&amp;gt; AI   |&lt;br /&gt;
                                        | m  &amp;gt; 0 -&amp;gt; AIPD |&lt;br /&gt;
                                        +----------------+&lt;br /&gt;
    m = fed-back ece, or the local first-hop mark when ece == 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 6. RTT behaviour ==&lt;br /&gt;
&lt;br /&gt;
Because every increase and decrease is scaled by wall-clock time,&lt;br /&gt;
two flows of different RTT that share a bottleneck obey the &#039;&#039;same&#039;&#039;&lt;br /&gt;
rate law and converge to the &#039;&#039;same&#039;&#039; rate. The steady-state&lt;br /&gt;
&#039;&#039;&#039;allocation is RTT-independent&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
This is a property of the equilibrium, not of the dynamics. The&lt;br /&gt;
feedback delay in the loop &#039;&#039;is&#039;&#039; the path RTT &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;, and the&lt;br /&gt;
controller does not measure or compensate for it. At low rate the&lt;br /&gt;
receiver window (up to its ~4.3 s ceiling) is the dominant lag &amp;amp;mdash;&lt;br /&gt;
the loop is slow there in proportion to how slow the flow is, exactly&lt;br /&gt;
as a long-RTT TCP is &amp;amp;mdash; while as the rate rises the window shrinks&lt;br /&gt;
toward its ~1 ms floor, so the window&#039;s contribution to the loop delay&lt;br /&gt;
&#039;&#039;falls&#039;&#039; with capacity and the path RTT &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; becomes the&lt;br /&gt;
irreducible term. Two mechanisms keep the loop damped: the adaptive&lt;br /&gt;
window removes the delay-dominated corner at low &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;, and a&lt;br /&gt;
bucketed &#039;&#039;&#039;rate-velocity damping&#039;&#039;&#039; (a washout: each ~31 ms bucket&lt;br /&gt;
pulls the rate a quarter of the way back toward its value at the&lt;br /&gt;
previous bucket) supplies the damping the otherwise&lt;br /&gt;
near-double-integrator lacks, which is what holds the loop together at&lt;br /&gt;
high capacity and moderate &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;. What remains is the loop gain&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;prop; &amp;amp;radic;C&amp;lt;/code&amp;gt;: at very high capacity &#039;&#039;and&#039;&#039; large&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; the &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt; phase budget is the&lt;br /&gt;
binding constraint, and no window or damping trick removes it (see&lt;br /&gt;
[[#8. Limitations|Section 8]]). A precise statement is therefore:&lt;br /&gt;
&#039;&#039;&#039;RTT-fair in equilibrium, and dynamically stable up to a&lt;br /&gt;
capacity-dependent RTT limit set by &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 7. Fairness ==&lt;br /&gt;
&lt;br /&gt;
At equilibrium the two increase terms balance the proportional&lt;br /&gt;
decrease, &amp;lt;code&amp;gt;a + r/T_probe = r &amp;amp;middot; m*/M&amp;lt;/code&amp;gt;, which pins the&lt;br /&gt;
congestion level to &amp;lt;code&amp;gt;m* = M &amp;amp;middot; (a/r + 1/T_probe)&amp;lt;/code&amp;gt; and, since&lt;br /&gt;
&amp;lt;code&amp;gt;ece = 8q&amp;lt;/code&amp;gt; (one mark unit per &amp;lt;code&amp;gt;Q = 4&amp;lt;/code&amp;gt; packets, 32 fixed-point&lt;br /&gt;
steps per unit), a standing queue of&lt;br /&gt;
&lt;br /&gt;
  q* = 64 * a / r  +  64 / T_probe        (packets, r in bytes/s)&lt;br /&gt;
&lt;br /&gt;
The additive part (&amp;lt;code&amp;gt;64 &amp;amp;middot; a / r&amp;lt;/code&amp;gt;) vanishes with rate under&lt;br /&gt;
the default slope; with the capacity-derived slope (&amp;lt;code&amp;gt;a = C/32&amp;lt;/code&amp;gt;,&lt;br /&gt;
Section 5.5) it becomes &amp;lt;code&amp;gt;2 C / r&amp;lt;/code&amp;gt; &amp;amp;mdash; about two packets per&lt;br /&gt;
competing flow, rate-independent. The proportional-probe part&lt;br /&gt;
(&amp;lt;code&amp;gt;64 / T_probe&amp;lt;/code&amp;gt;) is a rate-independent floor (~8 packets at&lt;br /&gt;
8 s). That floor is the cost of rate-independent convergence, and it&lt;br /&gt;
keeps &amp;lt;code&amp;gt;q*&amp;lt;/code&amp;gt; above the marking threshold at high rate.&lt;br /&gt;
&lt;br /&gt;
Two flows at the same bottleneck see the same &amp;lt;code&amp;gt;m*&amp;lt;/code&amp;gt;, so each&lt;br /&gt;
solves &amp;lt;code&amp;gt;r_i = a / (m*/M - 1/T_probe)&amp;lt;/code&amp;gt; to the same rate. Under&lt;br /&gt;
the network-utility-maximization view this corresponds to&lt;br /&gt;
&#039;&#039;&#039;proportional fairness&#039;&#039;&#039; (equal weights), which is max-min fair at&lt;br /&gt;
a single bottleneck.&lt;br /&gt;
&lt;br /&gt;
Measurement fidelity at low rate comes from the estimator itself:&lt;br /&gt;
the averaging window stretches with the flow&lt;br /&gt;
(Section 3), so a CA-limited flow keeps ~16 packets per window at any&lt;br /&gt;
rate down to ~30 kbit/s, and the rate-relative gap threshold keeps its&lt;br /&gt;
inter-packet spacing from reading as idle. The default floor&lt;br /&gt;
(&amp;lt;code&amp;gt;2^13&amp;lt;/code&amp;gt; B/s) only bounds the extremes (window ceiling,&lt;br /&gt;
staleness horizon, pacer arithmetic). The floor scales with the&lt;br /&gt;
fed-back path capacity (&amp;lt;code&amp;gt;r_min = C/32&amp;lt;/code&amp;gt;, Section 5.5), so&lt;br /&gt;
roughly 32 CA-limited flows fit above it at any link class. Flows&lt;br /&gt;
whose paths differ in bottleneck class derive different slopes, so&lt;br /&gt;
fairness across them is capacity-weighted; see&lt;br /&gt;
[[#8. Limitations|Section 8]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 8. Limitations ==&lt;br /&gt;
&lt;br /&gt;
These are properties the algorithm does &#039;&#039;&#039;not&#039;&#039;&#039; have. They are&lt;br /&gt;
listed so the policy is not credited with more than it does.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Queue-only signal; no bandwidth/rate term.&#039;&#039;&#039; The reference&lt;br /&gt;
  &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; is a queue level, so mb-ecn is a standing-queue&lt;br /&gt;
  controller: it must build a queue (&amp;lt;code&amp;gt;q* = 64 &amp;amp;middot; a / r +&lt;br /&gt;
  64 / T_probe&amp;lt;/code&amp;gt;) to see any signal, and cannot hold high utilization&lt;br /&gt;
  at an empty queue the way a controller with a rate/bandwidth estimate&lt;br /&gt;
  can. The proportional-probe floor (~8 packets) keeps &amp;lt;code&amp;gt;q*&amp;lt;/code&amp;gt;&lt;br /&gt;
  above the marking threshold at all rates, at the cost of a persistent&lt;br /&gt;
  standing queue.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Aggregation is per destination, per QoS cube.&#039;&#039;&#039; The aggregate&lt;br /&gt;
  stands in for the path: flows toward the same destination are&lt;br /&gt;
  assumed to share their bottleneck, and flows toward different&lt;br /&gt;
  destinations keep independent loops even when they queue at the&lt;br /&gt;
  same hop. Each such loop converges to its own fair share&lt;br /&gt;
  (Section 7), so the cost is duplicated state and probing at the&lt;br /&gt;
  shared queue rather than unfairness. A future option is to identify&lt;br /&gt;
  the queue in the packet: a forwarder stamping a queue identifier&lt;br /&gt;
  alongside its mark would let senders key their aggregates on the&lt;br /&gt;
  actual bottleneck rather than on the destination, pooling the&lt;br /&gt;
  estimate and the probing across everything that queues there.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Capacity is learned, with gaps.&#039;&#039;&#039; The floor and slope scale with&lt;br /&gt;
  an in-band path-capacity estimate (Sections 2, 5.5), but the estimate&lt;br /&gt;
  has limits of its own. A hop only learns its link rate while its&lt;br /&gt;
  queue is backlogged, so a rerouted or freshly established bottleneck&lt;br /&gt;
  contributes &#039;&#039;unknown&#039;&#039; at first and the path MIN can transiently&lt;br /&gt;
  come from faster hops (the sender&#039;s smoothing and the next feedback&lt;br /&gt;
  bound the excursion). The drain measurement is order-of-magnitude by&lt;br /&gt;
  design: quarter-log2 code (~19% per step), burst noise bounded by a&lt;br /&gt;
  16-packet minimum window and a max filter, nothing learned below&lt;br /&gt;
  ~120 packets per second of busy drain (16 packets inside the&lt;br /&gt;
  ~134 ms staleness cap; at MTU that is ~1.4 Mbit/s, below which&lt;br /&gt;
  capacity/32 would sit under the default floor anyway). The derived&lt;br /&gt;
  floor clamps to &amp;lt;code&amp;gt;[2^13, 2^32]&amp;lt;/code&amp;gt; B/s, and flows with&lt;br /&gt;
  different path bottlenecks get capacity-weighted, not equal, shares&lt;br /&gt;
  at a common queue (Section 7).&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;The low end trades reactivity, not correctness.&#039;&#039;&#039; Below&lt;br /&gt;
  ~30 kbit/s the averaging window clips at its ~4.3 s ceiling and&lt;br /&gt;
  holds fewer samples; below ~64 kbit/s of offered load an&lt;br /&gt;
  &#039;&#039;app-limited&#039;&#039; flow spaces packets past the gap threshold and rides&lt;br /&gt;
  the raw onset branch, as it always did. Feedback cadence and the&lt;br /&gt;
  staleness horizon at the floor are seconds &amp;amp;mdash; a slow flow&lt;br /&gt;
  converges slowly, TCP-style; nothing diverges, but nothing is fast&lt;br /&gt;
  either.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;No loss response.&#039;&#039;&#039; The controller reacts only to ECN marks. Loss&lt;br /&gt;
  is handled by FRCP retransmission, out of the CA&#039;s view. At a bottleneck&lt;br /&gt;
  that drops instead of marking (e.g. a classic drop-tail queue at the&lt;br /&gt;
  Ethernet/UDP shim), mb-ecn sees no signal and additive increase keeps&lt;br /&gt;
  ramping. mb-ecn is intended for Ouroboros-native bottlenecks where the&lt;br /&gt;
  forwarder marks; it does not implement a classic-bottleneck fallback.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Dynamic stability has a capacity-dependent RTT limit.&#039;&#039;&#039; As stated&lt;br /&gt;
  in Section 6, the allocation is RTT-independent but the dynamics are&lt;br /&gt;
  not: the binding constraint is &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt; with&lt;br /&gt;
  &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;prop; &amp;amp;radic;C&amp;lt;/code&amp;gt;. The adaptive window and the washout push&lt;br /&gt;
  this limit out substantially (e.g. a single flow at 10 GbE stays stable&lt;br /&gt;
  to ~50 ms RTT in the fluid model), but the extreme long-fat-network&lt;br /&gt;
  corner (100 GbE at &amp;amp;ge; 50 ms) is not reachable by a window/damping&lt;br /&gt;
  controller and would need explicit-rate signalling.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fluid-model caveat.&#039;&#039;&#039; The offline model in the simulation tooling&lt;br /&gt;
  captures the aggregate rate law (sawtooth, standing queue) with a&lt;br /&gt;
  boxcar receiver, but sizes the adaptive window in the rate domain&lt;br /&gt;
  rather than the byte-folded form of the algorithm above, and treats&lt;br /&gt;
  all flows identically, so it is a guide to stability &#039;&#039;trends&#039;&#039;, not&lt;br /&gt;
  a fairness or exact-quantitative oracle at high link rate.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 9. Architectural fit ==&lt;br /&gt;
&lt;br /&gt;
Each property the preceding sections build toward attaches to a&lt;br /&gt;
structural feature of the recursive architecture. This section makes&lt;br /&gt;
the mapping explicit.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Congestion avoidance is fully orthogonal to ARQ and to flow&lt;br /&gt;
  control.&#039;&#039;&#039; Three concerns, three mechanisms, two scopes: FRCP&lt;br /&gt;
  provides retransmission (ARQ) and flow control (the peer pacing the&lt;br /&gt;
  sender) end-to-end, per flow; congestion avoidance runs in the&lt;br /&gt;
  IPCP, per aggregate. Each signal means one thing. A loss triggers&lt;br /&gt;
  a retransmission and nothing else &amp;amp;mdash; a lossy link reads as&lt;br /&gt;
  lossy, with the congestion verdict left to the marks (Section 8&#039;s&lt;br /&gt;
  &amp;quot;no loss response&amp;quot; is this orthogonality stated from the other&lt;br /&gt;
  side). The peer&#039;s flow-control window paces the endpoint and&lt;br /&gt;
  nothing else &amp;amp;mdash; back-pressure from a slow receiver stays&lt;br /&gt;
  distinct from congestion in the network. A retransmitted packet is&lt;br /&gt;
  ordinary traffic to the pacer. TCP folds all three into one window&lt;br /&gt;
  machine, where the receive window bounds the congestion window and&lt;br /&gt;
  loss serves as both reliability trigger and congestion signal;&lt;br /&gt;
  here each mechanism can be reasoned about, tested and evolved&lt;br /&gt;
  alone.&lt;br /&gt;
* &#039;&#039;&#039;Congestion avoidance sits below application choice.&#039;&#039;&#039; Every&lt;br /&gt;
  flow in the layer is paced by the same rate law, whatever its QoS:&lt;br /&gt;
  a greedy raw sender shares a bottleneck fairly with a reliable&lt;br /&gt;
  stream because the control is a property of the layer, on the&lt;br /&gt;
  aggregate, rather than a courtesy of the endpoint transport.&lt;br /&gt;
* &#039;&#039;&#039;A layer defines its own PCI, so the signal can be rich.&#039;&#039;&#039;&lt;br /&gt;
  Every member of a layer is enrolled into it: the layer is a single&lt;br /&gt;
  administrative domain by construction, at whatever scope it spans,&lt;br /&gt;
  and its header is layer-internal. Forwarders therefore write a&lt;br /&gt;
  multi-bit queue magnitude and a capacity byte directly into the&lt;br /&gt;
  packet. The datacenter schemes of&lt;br /&gt;
  [[#10. Heritage and positioning|Section 10]] require exactly such a&lt;br /&gt;
  domain and find it only inside one operator&#039;s fabric; a recursive&lt;br /&gt;
  layer supplies it everywhere.&lt;br /&gt;
* &#039;&#039;&#039;Layer-internal addressing makes the aggregate well-defined.&#039;&#039;&#039;&lt;br /&gt;
  Flows in a layer run between layer addresses, so the&lt;br /&gt;
  (destination, QoS cube) aggregate &amp;amp;mdash; RFC 3124&#039;s macroflow&lt;br /&gt;
  &amp;amp;mdash; falls out of the naming structure. Controller state scales&lt;br /&gt;
  with destinations, and each QoS cube keeps its own loop, so&lt;br /&gt;
  service classes keep separate fates.&lt;br /&gt;
* &#039;&#039;&#039;Flows are allocated, so feedback has a channel.&#039;&#039;&#039; Every flow&lt;br /&gt;
  has state at both ends and a reverse direction; the receiver&#039;s&lt;br /&gt;
  &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; and the path capacity ride the flow allocator&#039;s&lt;br /&gt;
  existing exchange.&lt;br /&gt;
* &#039;&#039;&#039;There is no transport ack clock, and none is relied on.&#039;&#039;&#039; The&lt;br /&gt;
  layer must control raw flows, which carry no acknowledgements at&lt;br /&gt;
  all, so the controller paces on wall-clock time &amp;amp;mdash; and that&lt;br /&gt;
  constraint yields the RTT-independent allocation of Section 6. An&lt;br /&gt;
  architectural restriction becomes the controller&#039;s distinguishing&lt;br /&gt;
  property.&lt;br /&gt;
* &#039;&#039;&#039;Recursion scales the scheme.&#039;&#039;&#039; Each layer controls congestion&lt;br /&gt;
  over its own scope and timescale; many N-flows ride one N-1 flow,&lt;br /&gt;
  so aggregation compounds down the stack. The capacity a layer&lt;br /&gt;
  measures at its egress queue is the rate the layer below actually&lt;br /&gt;
  provides &amp;amp;mdash; a paced, shared lower flow rather than a nominal&lt;br /&gt;
  wire speed &amp;amp;mdash; so the signal stays meaningful at every level,&lt;br /&gt;
  and pushback cascades layer by layer.&lt;br /&gt;
&lt;br /&gt;
The fit has a boundary: at a shim over legacy media the layer below&lt;br /&gt;
neither enrolls nor marks, which is the deployment edge behind&lt;br /&gt;
Section 8&#039;s no-loss-response limitation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 10. Heritage and positioning ==&lt;br /&gt;
&lt;br /&gt;
mb-ecn is best understood as a rate-based ECN controller in the DECbit /&lt;br /&gt;
QCN line, adapted to an acknowledgement-less recursive layer.&lt;br /&gt;
&lt;br /&gt;
The core of the scheme dates to the first Ouroboros&lt;br /&gt;
congestion-avoidance policy (2020): a multi-bit queue-depth mark&lt;br /&gt;
relayed by forwarders to the receiver, a smoothed multi-bit&lt;br /&gt;
&amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; fed back on the reverse flow at the same 1/32 fixed&lt;br /&gt;
point used today, and a sender that paced packets from a time budget&lt;br /&gt;
and ran slow start, additive increase and multiplicative decrease on&lt;br /&gt;
wall-clock time slots &amp;amp;mdash; rate-based and acknowledgement-free from&lt;br /&gt;
the start, with RTT-independent allocation already an objective. The&lt;br /&gt;
present algorithm keeps that skeleton and reworks every estimator&lt;br /&gt;
around it: per-path aggregation with SFQ pacing, the rate-adaptive&lt;br /&gt;
boxcar receiver, elapsed-time-scaled AIMD with a derivative term and&lt;br /&gt;
rate-velocity damping, and the in-band capacity signal.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;DECbit&#039;&#039;&#039; (Ramakrishnan &amp;amp;amp; Jain, 1988; ToCS 1990) established&lt;br /&gt;
  binary congestion feedback set by the switch on queue occupancy.&lt;br /&gt;
  mb-ecn keeps the switch-sets-on-queue idea and carries a&lt;br /&gt;
  &#039;&#039;magnitude&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;The Congestion Manager&#039;&#039;&#039; (Balakrishnan et al., SIGCOMM 1999;&lt;br /&gt;
  RFC 3124, 2001) aggregates congestion state at the end host: one&lt;br /&gt;
  controller per macroflow, a scheduler apportioning its rate across&lt;br /&gt;
  the member streams, and new streams joining at the ensemble&#039;s&lt;br /&gt;
  current state instead of probing from scratch. mb-ecn&#039;s&lt;br /&gt;
  (destination, QoS cube) aggregate is the same idea placed inside&lt;br /&gt;
  the IPCP: every layer manages its own macroflows, recursively, the&lt;br /&gt;
  SFQ pacer plays the CM scheduler&#039;s role (Section 4), and a joining&lt;br /&gt;
  flow rides the aggregate&#039;s estimates (Section 5.1). The CM gathers&lt;br /&gt;
  its signal from transport feedback at the edge; mb-ecn reads it&lt;br /&gt;
  off the wire from the forwarders.&lt;br /&gt;
* &#039;&#039;&#039;DCTCP&#039;&#039;&#039; (Alizadeh et al., SIGCOMM 2010) recovers a queue&lt;br /&gt;
  magnitude at the endpoint by averaging a single threshold bit. mb-ecn&lt;br /&gt;
  instead reads the magnitude off the wire, then time-averages it at&lt;br /&gt;
  the receiver. The additive-increase / proportional-decrease structure&lt;br /&gt;
  is shared.&lt;br /&gt;
* &#039;&#039;&#039;QCN&#039;&#039;&#039; (IEEE 802.1Qau, 2010) and &#039;&#039;&#039;DCQCN&#039;&#039;&#039; (Zhu et al., SIGCOMM&lt;br /&gt;
  2015) contribute the rate-based (not window-based) ECN control&lt;br /&gt;
  skeleton. mb-ecn&#039;s proportional decrease plus a probing increase&lt;br /&gt;
  mirrors that skeleton; the increase here is wall-clock scaled rather&lt;br /&gt;
  than byte-counter/timer based.&lt;br /&gt;
* &#039;&#039;&#039;HPCC&#039;&#039;&#039; (Li et al., SIGCOMM 2019) and &#039;&#039;&#039;PowerTCP&#039;&#039;&#039; (Addanki et&lt;br /&gt;
  al., NSDI 2022) combine a queue term &#039;&#039;and&#039;&#039; a rate term (via in-band&lt;br /&gt;
  telemetry or delay). mb-ecn now carries a coarse in-band rate (the&lt;br /&gt;
  quarter-log2 path MIN, Section 2) but uses it only to scale the&lt;br /&gt;
  controller&#039;s gains; the congestion &#039;&#039;signal&#039;&#039; remains queue-only,&lt;br /&gt;
  which is still the main functional difference (Section 8).&lt;br /&gt;
* &#039;&#039;&#039;L4S / TCP Prague&#039;&#039;&#039; (RFC 9330&amp;amp;ndash;9332, 2023) make&lt;br /&gt;
  RTT-independence a requirement and use a high-frequency single-bit&lt;br /&gt;
  signal. mb-ecn shares the RTT-independent &#039;&#039;allocation&#039;&#039; goal but&lt;br /&gt;
  reaches it by wall-clock-scaled rate control, with a low-frequency&lt;br /&gt;
  multi-bit signal.&lt;br /&gt;
&lt;br /&gt;
The novel combination is the multi-bit magnitude on the wire fused with&lt;br /&gt;
a wall-clock-scaled rate pacer in a setting with no per-flow ack clock.&lt;br /&gt;
The trade-off, made explicit above, is a queue-only signal and a&lt;br /&gt;
reactivity capped by the averaging window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 11. Parameters ==&lt;br /&gt;
&lt;br /&gt;
These are the values every figure in this document instantiates. They&lt;br /&gt;
are &#039;&#039;&#039;coupled&#039;&#039;&#039;: the mechanisms compose only while certain relations&lt;br /&gt;
hold &amp;amp;mdash; the gap-restart horizon at the rate floor must exceed a&lt;br /&gt;
packet&#039;s service time (or a floor-rate flow restart-loops at onset),&lt;br /&gt;
a floor-rate flow&#039;s target window must fit under the window ceiling,&lt;br /&gt;
the control interval must sit under the damping bucket under the idle&lt;br /&gt;
credit cap, the rate floor must not exceed the slow-start seed, and&lt;br /&gt;
the derived-floor ceiling must stay under the rate ceiling. Retune&lt;br /&gt;
them as a set, against those invariants.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| Marking quantum &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; || 4 packets || Mark onset and step; layer-wide&lt;br /&gt;
|-&lt;br /&gt;
| Feedback resolution || 1/32 &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; unit || Fixed-point LSB of &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Full-congestion reference &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; || 512 || Mean queue of 64 packets&lt;br /&gt;
|-&lt;br /&gt;
| Receiver window target || 16 packets (16,000 bytes) || Samples per window&lt;br /&gt;
|-&lt;br /&gt;
| Initial receiver window || ~67 ms || Before rate is known&lt;br /&gt;
|-&lt;br /&gt;
| Receiver window bounds || ~1.05 ms &amp;amp;ndash; ~4.3 s || Clamp on &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Window resize weight || 1/4 || Moving average toward target&lt;br /&gt;
|-&lt;br /&gt;
| Early window close || 2 &amp;amp;times; target bytes || Speed-up escape&lt;br /&gt;
|-&lt;br /&gt;
| Gap restart || 4 windows || Idle horizon (rate-relative)&lt;br /&gt;
|-&lt;br /&gt;
| Additive slope &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; (default) || 2^16 B/s^2 || Lower clamp of derived slope&lt;br /&gt;
|-&lt;br /&gt;
| Probe time constant &amp;lt;code&amp;gt;T_probe&amp;lt;/code&amp;gt; || 8 s || Rate-proportional recovery&lt;br /&gt;
|-&lt;br /&gt;
| Derivative gain || 1/4 || Onset sharpening&lt;br /&gt;
|-&lt;br /&gt;
| Rate floor &amp;lt;code&amp;gt;r_min&amp;lt;/code&amp;gt; (default) || 2^13 B/s || Lower clamp of derived floor&lt;br /&gt;
|-&lt;br /&gt;
| Slow-start seed rate || 2^16 B/s || Initial rate&lt;br /&gt;
|-&lt;br /&gt;
| Slow-start time constant || 20 ms || Exponential ramp&lt;br /&gt;
|-&lt;br /&gt;
| Rate ceiling &amp;lt;code&amp;gt;r_max&amp;lt;/code&amp;gt; || 2^37 B/s || Upper clamp&lt;br /&gt;
|-&lt;br /&gt;
| Capacity scale || &amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt; / 32 || Floor and slope from bottleneck&lt;br /&gt;
|-&lt;br /&gt;
| Capacity smoothing || 1/2 || Moving average per feedback&lt;br /&gt;
|-&lt;br /&gt;
| Capacity staleness || 16 &amp;amp;times; feedback horizon || Revert to defaults&lt;br /&gt;
|-&lt;br /&gt;
| Derived floor ceiling || 2^32 B/s || Upper clamp on derived floor&lt;br /&gt;
|-&lt;br /&gt;
| Control interval || 1 ms || Minimum spacing of rate updates&lt;br /&gt;
|-&lt;br /&gt;
| Idle credit cap || 50 ms || Burst allowance / increase banking&lt;br /&gt;
|-&lt;br /&gt;
| Feedback staleness floor || ~268 ms || Floor of rate-relative horizon&lt;br /&gt;
|-&lt;br /&gt;
| Damping bucket || ~31 ms || Rate-velocity washout cadence&lt;br /&gt;
|-&lt;br /&gt;
| Damping fraction || 1/4 || Pullback per bucket&lt;br /&gt;
|-&lt;br /&gt;
| Estimator fold spacing || 1 ms || Minimum measurement cadence&lt;br /&gt;
|-&lt;br /&gt;
| Estimator window closure || 16 packets drained || Self-scaling window&lt;br /&gt;
|-&lt;br /&gt;
| Estimator window staleness || ~134 ms || Discard across traffic gaps&lt;br /&gt;
|-&lt;br /&gt;
| Estimator idle tolerance || 1/8 of arrivals || Unsaturated-window rejection&lt;br /&gt;
|-&lt;br /&gt;
| Estimator decay || 1/16 per window || Max-filter convergence from below&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 12. References ==&lt;br /&gt;
&lt;br /&gt;
=== 12.1. Source ===&lt;br /&gt;
&lt;br /&gt;
The implementation follows the algorithm above:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/ca/mb-ecn.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mb-ecn.h&amp;lt;/code&amp;gt; &amp;amp;mdash;&lt;br /&gt;
  receiver estimator, pacer and rate control.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/ca/ops.h&amp;lt;/code&amp;gt; &amp;amp;mdash; CA policy interface.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/cap.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cap.h&amp;lt;/code&amp;gt; &amp;amp;mdash; link-capacity&lt;br /&gt;
  estimator and quarter-log2 codec.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/dt.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fa.c&amp;lt;/code&amp;gt; &amp;amp;mdash; per-hop marking&lt;br /&gt;
  and stamping, and the feedback carrying &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;cap8&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/psched.c&amp;lt;/code&amp;gt; &amp;amp;mdash; consumes the pacing deadline.&lt;br /&gt;
&lt;br /&gt;
=== 12.2. Literature ===&lt;br /&gt;
&lt;br /&gt;
* K. K. Ramakrishnan, R. Jain, &amp;quot;A Binary Feedback Scheme for Congestion&lt;br /&gt;
  Avoidance in Computer Networks&amp;quot; (DECbit), ACM ToCS, 1990.&lt;br /&gt;
* H. Balakrishnan, H. S. Rahul, S. Seshan, &amp;quot;An Integrated Congestion&lt;br /&gt;
  Management Architecture for Internet Hosts&amp;quot;, SIGCOMM 1999;&lt;br /&gt;
  H. Balakrishnan, S. Seshan, &amp;quot;The Congestion Manager&amp;quot;, RFC 3124, 2001.&lt;br /&gt;
* M. Alizadeh et al., &amp;quot;Data Center TCP (DCTCP)&amp;quot;, SIGCOMM 2010.&lt;br /&gt;
* Y. Zhu et al., &amp;quot;Congestion Control for Large-Scale RDMA Deployments&amp;quot;&lt;br /&gt;
  (DCQCN), SIGCOMM 2015.&lt;br /&gt;
* R. Mittal et al., &amp;quot;TIMELY: RTT-based Congestion Control&amp;quot;, SIGCOMM 2015.&lt;br /&gt;
* Y. Li et al., &amp;quot;HPCC: High Precision Congestion Control&amp;quot;, SIGCOMM 2019.&lt;br /&gt;
* V. Addanki et al., &amp;quot;PowerTCP&amp;quot;, NSDI 2022.&lt;br /&gt;
* K. De Schepper, B. Briscoe (eds.), &amp;quot;Low Latency, Low Loss, and&lt;br /&gt;
  Scalable Throughput (L4S)&amp;quot;, RFC 9330/9331/9332.&lt;br /&gt;
* F. Kelly, &amp;quot;Charging and Rate Control for Elastic Traffic&amp;quot;, 1997;&lt;br /&gt;
  R. Srikant, &amp;quot;The Mathematics of Internet Congestion Control&amp;quot;, 2004.&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Data_Transfer_Protocol&amp;diff=1935</id>
		<title>Ouroboros Data Transfer Protocol</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Data_Transfer_Protocol&amp;diff=1935"/>
		<updated>2026-07-11T20:44:26Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* Fields associated with the network end-to-end layer */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Ouroboros Data Transfer Protocol (ODTP) is the &#039;&#039;hop-by-hop&#039;&#039; protocol that forwards packets to their destination, and is similar to the Internet Protocol, but with further simplifications.&lt;br /&gt;
&lt;br /&gt;
ODTP is internal to the [[Data Transfer Component]] in the unicast IPCP. The details of this protocol do not need to be known outside these modules, not to the other components of a unicast IPCP, not to the IRMd, and definitely not to the upper layer application.&lt;br /&gt;
&lt;br /&gt;
== Protocol Header ==&lt;br /&gt;
&lt;br /&gt;
The field widths are not that important, but an optimized version should take into account memory alignment. The Data Transfer Protocol has 3 fields associated with the Data Transfer Component (the network forwarding layer in the model), and 2 fields associated with the flow allocator (the end-to-end network layer in the model).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
  0                   1                   2                   3&lt;br /&gt;
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                      Destination Address                      +&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |  Time-to-Live |      QoS      |      ECN      |      CAP8     |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                              EID                              +&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                           N + 1 Data                          +&lt;br /&gt;
 .                                                               .&lt;br /&gt;
 .                                                               .&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fields associated with the network forwarding layer ===&lt;br /&gt;
&lt;br /&gt;
==== Destination address ====&lt;br /&gt;
&lt;br /&gt;
This specifies the address to forward the packet to. The width of this field is configurable based on various preferences and the size of the envisioned network. The Ouroboros default is 64 bits. &lt;br /&gt;
&lt;br /&gt;
==== Time-to-Live ====&lt;br /&gt;
&lt;br /&gt;
Similar to IPv4 (in IPv6 this field is replaced by the Hop Limit), this is decremented at each hop to ensures that packets don’t get forwarded forever in the network, for instance due to (transient) loops in the forwarding path. The Ouroboros default for the width is one octet (byte), limiting the Maximum Packet Lifetime in the network to 255 seconds. The initial TTL value for a flow can be based on the maximum delay requested by the application.&lt;br /&gt;
&lt;br /&gt;
==== QoS ====&lt;br /&gt;
&lt;br /&gt;
Ouroboros supports Quality of Service via a number of methods, and this field is used to prioritize scheduling of the packets when forwarding. For instance, if the network gets congested and queues start filling up, higher priority packets (e.g. a voice call) get scheduled more often than lower priority packets (e.g. a file download). By default this field is one octet long.&lt;br /&gt;
&lt;br /&gt;
=== Fields associated with the network end-to-end layer ===&lt;br /&gt;
&lt;br /&gt;
==== EID ==== &lt;br /&gt;
&lt;br /&gt;
The Endpoint Identifier (EID) field specified the endpoint for which to deliver the packet. This endpoint uniquely identifies an application/service at the destination, so this field is not identical in function to the &#039;&#039;protocol&#039;&#039; or &#039;&#039;next header&#039;&#039; field in IPv4 or IPv6, but rather it more akin to the function of the (ephemeral) &#039;&#039;port&#039;&#039; field in UDP or TCP. Note that&lt;br /&gt;
&lt;br /&gt;
# There is no reason for an N-layer protocol to know anything about the (N + 1)-Layer protocol it is transporting (nor the other way around).&lt;br /&gt;
# There are no &#039;&#039;well-known&#039;&#039; Endpoint IDs; every application flow has random EIDs and there is no relation between an EID and an application type. &lt;br /&gt;
&lt;br /&gt;
The width of this field is configurable, but for security, it should be reasonably long to avoid an attacker guessing valid EID values (the figure shows 64 bits, which is the value used in the prototype). For efficiency, it should be easy to map and EID to a flow descriptor at the endpoints. The value of this field is chosen locally by each endpoint IPCP during flow allocation.&lt;br /&gt;
&lt;br /&gt;
While Ouroboros does not have well-known EIDs, it does have a notion of &#039;&#039;reserved EID&#039;&#039;s for functions that are &#039;&#039;internal&#039;&#039; to an N-layer, such as the [[Flow Allocator | flow allocator]] and the [[directory]]. These EIDs should not be fixed, unique for the layer and can be communicated during Enrollment.&lt;br /&gt;
&lt;br /&gt;
==== ECN ====&lt;br /&gt;
&lt;br /&gt;
This field specifies Explicit Congestion Notification (ECN), with similar intent as the ECN bits in the Type-of-Service field in IPv4 or Traffic Class field in IPv6. The Ouroboros ECN field is by default one octet wide, and its value can be set to an increasing value as packets are queued deeper and deeper in a congested routers’ forwarding queues. Ouroboros enforces Forward ECN (FECN). See [[Congestion avoidance]].&lt;br /&gt;
&lt;br /&gt;
==== CAP8 ====&lt;br /&gt;
&lt;br /&gt;
This field specifies the capacity of the link that is flagged by Explicit Congestion Notification (ECN). See [[Congestion avoidance]].&lt;br /&gt;
&lt;br /&gt;
=== Notable fields not present in ODTP ===&lt;br /&gt;
&lt;br /&gt;
==== Version ====&lt;br /&gt;
&lt;br /&gt;
There is no need for a version field in the ODTP protocol, even if future changes to the fields are made, or other fields are added. The Data Transfer protocol specification is agreed between IPCPs during [[enrollment]].&lt;br /&gt;
&lt;br /&gt;
==== Source address ====&lt;br /&gt;
&lt;br /&gt;
There is no need for a source address in the header. The source address is exchanged during [[Flow Allocation | flow allocation]], and when sending a packet, the destination can be found in the flow allocator&#039;s flow table. This is motivated by the following observations:&lt;br /&gt;
# Intermediate forwarding functions do not need to know the source address to forward a packet to the destination&lt;br /&gt;
# The rate of change of the source address (if at all) is generally much lower than the packet rate&lt;br /&gt;
# Since this field is not needed for forwarding, the source address is rife for abuse&lt;br /&gt;
&lt;br /&gt;
The edge case where an application needs to send a single packet to a destination, or very infrequently, can be covered by piggybacking data on the first flow allocation message.&lt;br /&gt;
&lt;br /&gt;
==== Protocol/Next Header ====&lt;br /&gt;
The protocol (IPv4) / Next Header (IPv6) field identifies the protocol on top of the Internet Protocol, which is a clear layer independence violation. ODTP has an Endpoint Identifier field that combines the Protocol and Port functions, and is (nearly) always &#039;ephemeral&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Length ==== &lt;br /&gt;
&lt;br /&gt;
Not needed, because the syntax of the ODTP network forwarding layer protocol is agreed during enrollment.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
The implementation of this protocol is part of the unicast IPCP, and is complete, pending some optimizations.&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Data_Transfer_Protocol&amp;diff=1934</id>
		<title>Ouroboros Data Transfer Protocol</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Data_Transfer_Protocol&amp;diff=1934"/>
		<updated>2026-07-11T20:43:28Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* Protocol Header */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Ouroboros Data Transfer Protocol (ODTP) is the &#039;&#039;hop-by-hop&#039;&#039; protocol that forwards packets to their destination, and is similar to the Internet Protocol, but with further simplifications.&lt;br /&gt;
&lt;br /&gt;
ODTP is internal to the [[Data Transfer Component]] in the unicast IPCP. The details of this protocol do not need to be known outside these modules, not to the other components of a unicast IPCP, not to the IRMd, and definitely not to the upper layer application.&lt;br /&gt;
&lt;br /&gt;
== Protocol Header ==&lt;br /&gt;
&lt;br /&gt;
The field widths are not that important, but an optimized version should take into account memory alignment. The Data Transfer Protocol has 3 fields associated with the Data Transfer Component (the network forwarding layer in the model), and 2 fields associated with the flow allocator (the end-to-end network layer in the model).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
  0                   1                   2                   3&lt;br /&gt;
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                      Destination Address                      +&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |  Time-to-Live |      QoS      |      ECN      |      CAP8     |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                              EID                              +&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                           N + 1 Data                          +&lt;br /&gt;
 .                                                               .&lt;br /&gt;
 .                                                               .&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fields associated with the network forwarding layer ===&lt;br /&gt;
&lt;br /&gt;
==== Destination address ====&lt;br /&gt;
&lt;br /&gt;
This specifies the address to forward the packet to. The width of this field is configurable based on various preferences and the size of the envisioned network. The Ouroboros default is 64 bits. &lt;br /&gt;
&lt;br /&gt;
==== Time-to-Live ====&lt;br /&gt;
&lt;br /&gt;
Similar to IPv4 (in IPv6 this field is replaced by the Hop Limit), this is decremented at each hop to ensures that packets don’t get forwarded forever in the network, for instance due to (transient) loops in the forwarding path. The Ouroboros default for the width is one octet (byte), limiting the Maximum Packet Lifetime in the network to 255 seconds. The initial TTL value for a flow can be based on the maximum delay requested by the application.&lt;br /&gt;
&lt;br /&gt;
==== QoS ====&lt;br /&gt;
&lt;br /&gt;
Ouroboros supports Quality of Service via a number of methods, and this field is used to prioritize scheduling of the packets when forwarding. For instance, if the network gets congested and queues start filling up, higher priority packets (e.g. a voice call) get scheduled more often than lower priority packets (e.g. a file download). By default this field is one octet long.&lt;br /&gt;
&lt;br /&gt;
=== Fields associated with the network end-to-end layer ===&lt;br /&gt;
&lt;br /&gt;
==== EID ==== &lt;br /&gt;
&lt;br /&gt;
The Endpoint Identifier (EID) field specified the endpoint for which to deliver the packet. This endpoint uniquely identifies an application/service at the destination, so this field is not identical in function to the &#039;&#039;protocol&#039;&#039; or &#039;&#039;next header&#039;&#039; field in IPv4 or IPv6, but rather it more akin to the function of the (ephemeral) &#039;&#039;port&#039;&#039; field in UDP or TCP. Note that&lt;br /&gt;
&lt;br /&gt;
# There is no reason for an N-layer protocol to know anything about the (N + 1)-Layer protocol it is transporting (nor the other way around).&lt;br /&gt;
# There are no &#039;&#039;well-known&#039;&#039; Endpoint IDs; every application flow has random EIDs and there is no relation between an EID and an application type. &lt;br /&gt;
&lt;br /&gt;
The width of this field is configurable, but for security, it should be reasonably long to avoid an attacker guessing valid EID values (the figure shows 64 bits, which is the value used in the prototype). For efficiency, it should be easy to map and EID to a flow descriptor at the endpoints. The value of this field is chosen locally by each endpoint IPCP during flow allocation.&lt;br /&gt;
&lt;br /&gt;
While Ouroboros does not have well-known EIDs, it does have a notion of &#039;&#039;reserved EID&#039;&#039;s for functions that are &#039;&#039;internal&#039;&#039; to an N-layer, such as the [[Flow Allocator | flow allocator]] and the [[directory]]. These EIDs should not be fixed, unique for the layer and can be communicated during Enrollment.&lt;br /&gt;
&lt;br /&gt;
==== ECN ====&lt;br /&gt;
&lt;br /&gt;
This field specifies Explicit Congestion Notification (ECN), with similar intent as the ECN bits in the Type-of-Service field in IPv4 or Traffic Class field in IPv6. The Ouroboros ECN field is by default one octet wide, and its value can be set to an increasing value as packets are queued deeper and deeper in a congested routers’ forwarding queues. Ouroboros enforces Forward ECN (FECN). See [[Congestion Avoidance]].&lt;br /&gt;
&lt;br /&gt;
==== CAP8 ====&lt;br /&gt;
&lt;br /&gt;
This field specifies the capacity of the link that is flagged by Explicit Congestion Notification (ECN). See [[Congestion Avoidance]].&lt;br /&gt;
&lt;br /&gt;
=== Notable fields not present in ODTP ===&lt;br /&gt;
&lt;br /&gt;
==== Version ====&lt;br /&gt;
&lt;br /&gt;
There is no need for a version field in the ODTP protocol, even if future changes to the fields are made, or other fields are added. The Data Transfer protocol specification is agreed between IPCPs during [[enrollment]].&lt;br /&gt;
&lt;br /&gt;
==== Source address ====&lt;br /&gt;
&lt;br /&gt;
There is no need for a source address in the header. The source address is exchanged during [[Flow Allocation | flow allocation]], and when sending a packet, the destination can be found in the flow allocator&#039;s flow table. This is motivated by the following observations:&lt;br /&gt;
# Intermediate forwarding functions do not need to know the source address to forward a packet to the destination&lt;br /&gt;
# The rate of change of the source address (if at all) is generally much lower than the packet rate&lt;br /&gt;
# Since this field is not needed for forwarding, the source address is rife for abuse&lt;br /&gt;
&lt;br /&gt;
The edge case where an application needs to send a single packet to a destination, or very infrequently, can be covered by piggybacking data on the first flow allocation message.&lt;br /&gt;
&lt;br /&gt;
==== Protocol/Next Header ====&lt;br /&gt;
The protocol (IPv4) / Next Header (IPv6) field identifies the protocol on top of the Internet Protocol, which is a clear layer independence violation. ODTP has an Endpoint Identifier field that combines the Protocol and Port functions, and is (nearly) always &#039;ephemeral&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Length ==== &lt;br /&gt;
&lt;br /&gt;
Not needed, because the syntax of the ODTP network forwarding layer protocol is agreed during enrollment.&lt;br /&gt;
&lt;br /&gt;
== Implementation ==&lt;br /&gt;
&lt;br /&gt;
The implementation of this protocol is part of the unicast IPCP, and is complete, pending some optimizations.&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Protocols&amp;diff=1933</id>
		<title>Ouroboros Protocols</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Protocols&amp;diff=1933"/>
		<updated>2026-07-11T20:41:40Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* Data Transfer Protocol */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Under construction}}&lt;br /&gt;
&lt;br /&gt;
The protocols in Ouroboros are designed with strong adherence to the principles of [https://en.wikipedia.org/wiki/Separation_of_concerns separation of concerns] and [https://en.wikipedia.org/wiki/Separation_of_mechanism_and_policy separation of mechanism and policy] in mind to prevent network ossification and protocol ossification from occuring.&lt;br /&gt;
&lt;br /&gt;
There are 5 core protocols in Ouroboros.&lt;br /&gt;
&lt;br /&gt;
== Data Transfer Protocol ==&lt;br /&gt;
Main page: [[Ouroboros Data Transfer Protocol]]&lt;br /&gt;
&lt;br /&gt;
The Data Transfer Protocol resides in the [[Ouroboros Functional Layering|network forwarding layer]]. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
  0                   1                   2                   3&lt;br /&gt;
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                      Destination Address                      +&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |  Time-to-Live |      QoS      |      ECN      |      CAP8     |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                              EID                              +&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                           N + 1 Data                          +&lt;br /&gt;
 .                                                               .&lt;br /&gt;
 .                                                               .&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Flow encryption header ==&lt;br /&gt;
&lt;br /&gt;
Every encrypted packet is a fixed-size cleartext &#039;&#039;&#039;selector&#039;&#039;&#039; header,&lt;br /&gt;
followed by the AEAD ciphertext of the inner FRCP packet, followed by&lt;br /&gt;
the AEAD authentication &#039;&#039;&#039;tag&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    +----------------+------------------------------+--------------+&lt;br /&gt;
    | selector (6 B) |   E(inner FRCP packet)       |  tag (16 B)  |&lt;br /&gt;
    +----------------+------------------------------+--------------+&lt;br /&gt;
     \__ cleartext _/ \______ ciphertext __________/ \__ cleartext _/&lt;br /&gt;
     \________________ authenticated (AAD + AEAD) ________________/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;selector&lt;br /&gt;
:6-octet cleartext key-selection header. It is read &#039;&#039;&#039;before&#039;&#039;&#039; decryption to select the key and reconstruct the nonce, and it is bound into the AEAD as Associated Data.&lt;br /&gt;
;ciphertext&lt;br /&gt;
:AEAD encryption of the inner packet under the selected sub-key and reconstructed nonce.&lt;br /&gt;
;tag&lt;br /&gt;
:AEAD authentication tag, 16 octets (128 bits) for AEAD ciphers; absent for non-AEAD ciphers.&lt;br /&gt;
&lt;br /&gt;
== Flow and Retransmission Control Protocol ==&lt;br /&gt;
Main page: [[Flow and Retransmission Control Protocol]]&lt;br /&gt;
&lt;br /&gt;
The Flow and Retransmission Control Protocol resides in the [[Ouroboros Functional Layering|application end-to-end layer]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |             flags             |              hcs              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            window                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            seqno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            ackno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                     payload (variable) ...&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&lt;br /&gt;
  flags   - feature/type bitmap (see 1.2).&lt;br /&gt;
  hcs     - CRC-16-CCITT-FALSE Header Check Sequence (HCS) over&lt;br /&gt;
            flags + window + seqno + ackno (+ stream extension when&lt;br /&gt;
            present); the two octets of the hcs field itself are&lt;br /&gt;
            omitted from the CRC input.  Verified on receive before&lt;br /&gt;
            any flag-driven dispatch.&lt;br /&gt;
  window  - receiver-advertised right window edge (valid iff FC).&lt;br /&gt;
  seqno   - per-flow sequence number.&lt;br /&gt;
  ackno   - cumulative Acknowledgement (ACK) (valid iff ACK).&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Flow Allocation Protocol ==&lt;br /&gt;
Main page: [[Ouroboros Flow Allocation Protocol]]&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 *  0                   1                   2                   3&lt;br /&gt;
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ---+&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                      id (128 bits)                            +    |&lt;br /&gt;
 * |                  Unique flow allocation ID                    |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                     timestamp (64 bits)                       +    |&lt;br /&gt;
 * |                UTC nanoseconds since epoch                    |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |         crt_len (16 bits)     |                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                  certificate (variable)                       +    |&lt;br /&gt;
 * |               X.509 certificate, DER encoded                  |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |F|R|     kex_len (14 bits)     |                               |    | Signed&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +    |&lt;br /&gt;
 * |                                                               |    | Region&lt;br /&gt;
 * +                 kex_data (variable)                           +    |&lt;br /&gt;
 * |      public key (DER/raw) or ciphertext (KEM)                 |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |       cipher_len (16 bits)    |                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                    cipher (variable)                          +    |&lt;br /&gt;
 * |               symmetric cipher name (UTF-8)                   |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |        data_len (16 bits)     |                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                    data (variable)                            +    |&lt;br /&gt;
 * |              Piggybacked application data                     |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ---+&lt;br /&gt;
 * |        sig_len (16 bits)      |                               |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +&lt;br /&gt;
 * |                                                               |&lt;br /&gt;
 * +                     signature (variable)                      +&lt;br /&gt;
 * |                  DSA signature over signed region             |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 *&lt;br /&gt;
 * kex_len field bit layout:&lt;br /&gt;
 *   F (bit 15): Format - 0 = X.509 DER, 1 = Raw/Hybrid&lt;br /&gt;
 *   R (bit 14): Role   - 0 = Server encaps, 1 = Client encaps&lt;br /&gt;
 *               (R is ignored for non-KEM algorithms)&lt;br /&gt;
 *   Bits 0-13:  Length (0-16383 bytes)&lt;br /&gt;
 */&lt;br /&gt;
The Flow Allocation Protocol resides in the [[Ouroboros Functional Layering|network end-to-end layer]].&lt;br /&gt;
&lt;br /&gt;
== Connection Establishment Protocol ==&lt;br /&gt;
Main page: [[Ouroboros Connection Establishment Protocol]]&lt;br /&gt;
&lt;br /&gt;
== Enrollment Protocol ==&lt;br /&gt;
Main page: [[Ouroboros Enrolment Protocol]]&lt;br /&gt;
&lt;br /&gt;
The Enrollment protocol is best seen as one of the application protocols for IPCPs.&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1932</id>
		<title>Congestion avoidance</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Congestion_avoidance&amp;diff=1932"/>
		<updated>2026-07-11T20:39:27Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: Created page with &amp;quot;{{DISPLAYTITLE:mb-ecn - Multi-bit ECN Congestion Avoidance}}  mb-ecn is a congestion-avoidance (CA) policy for the Ouroboros unicast IPCP. It lives in the IPC Process: the flow-and-retransmission task (FRCP) provides reliability, in-order delivery and flow control end-to-end; congestion avoidance is orthogonal and runs per IPCP layer. Within a layer, the IPCP controls &amp;#039;&amp;#039;&amp;#039;aggregates&amp;#039;&amp;#039;&amp;#039;, one loop per (destination address, QoS cube), shared by every flow toward that destina...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:mb-ecn - Multi-bit ECN Congestion Avoidance}}&lt;br /&gt;
&lt;br /&gt;
mb-ecn is a congestion-avoidance (CA) policy for the Ouroboros unicast&lt;br /&gt;
IPCP. It lives in the IPC Process: the flow-and-retransmission task&lt;br /&gt;
(FRCP) provides reliability, in-order delivery and flow control&lt;br /&gt;
end-to-end; congestion avoidance is orthogonal and runs per IPCP&lt;br /&gt;
layer. Within a layer, the IPCP controls &#039;&#039;&#039;aggregates&#039;&#039;&#039;, one loop&lt;br /&gt;
per (destination address, QoS cube), shared by every flow toward that&lt;br /&gt;
destination. The alternative policy is &amp;lt;code&amp;gt;nop&amp;lt;/code&amp;gt; (no congestion&lt;br /&gt;
avoidance).&lt;br /&gt;
&lt;br /&gt;
This document describes the algorithm: what the policy does and why.&lt;br /&gt;
The implementation follows it ([[#12. References|Section 12]] maps the&lt;br /&gt;
pieces to source files). It does not claim properties the algorithm&lt;br /&gt;
does not have; [[#8. Limitations|Section 8]] states the known gaps&lt;br /&gt;
explicitly.&lt;br /&gt;
&lt;br /&gt;
The keywords &amp;quot;MUST&amp;quot;, &amp;quot;SHOULD&amp;quot;, &amp;quot;MAY&amp;quot; etc. are used only where they&lt;br /&gt;
appear in all capitals, per BCP 14 (RFC 2119, RFC 8174).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt;&lt;br /&gt;
:Paced send rate, bytes per second.&lt;br /&gt;
;&amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bottleneck capacity, bytes per second.&lt;br /&gt;
;&amp;lt;code&amp;gt;S&amp;lt;/code&amp;gt;&lt;br /&gt;
:Packet size in bytes.&lt;br /&gt;
;&amp;lt;code&amp;gt;q&amp;lt;/code&amp;gt;&lt;br /&gt;
:Standing queue occupancy at the bottleneck, in packets.&lt;br /&gt;
;&amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt;&lt;br /&gt;
:Marking quantum, packets per mark unit (4).&lt;br /&gt;
;&amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt;&lt;br /&gt;
:Per-packet congestion mark set by a forwarder, an unsigned integer&lt;br /&gt;
 (on-wire field is 8-bit).&lt;br /&gt;
;&amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
:Receiver-side congestion estimate, fixed point with LSB = 1/32 of an&lt;br /&gt;
 &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; unit, i.e. &amp;lt;code&amp;gt;ece = 32 &amp;amp;times; (time-mean ecn)&amp;lt;/code&amp;gt;.&lt;br /&gt;
;&amp;lt;code&amp;gt;cap8&amp;lt;/code&amp;gt;&lt;br /&gt;
:Path-capacity code carried per packet, quarter-log2:&lt;br /&gt;
 &amp;lt;code&amp;gt;C &amp;amp;asymp; 2^(cap8 / 4)&amp;lt;/code&amp;gt; bytes/s, 0 = unknown.&lt;br /&gt;
;&amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt;&lt;br /&gt;
:Receiver averaging window, nanoseconds.&lt;br /&gt;
;&amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt;&lt;br /&gt;
:Additive-increase slope, bytes/s per second.&lt;br /&gt;
;&amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt;&lt;br /&gt;
:Full-congestion reference level of &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; (512).&lt;br /&gt;
;&amp;lt;code&amp;gt;&amp;amp;Delta;t&amp;lt;/code&amp;gt;&lt;br /&gt;
:Elapsed wall-clock time between two events, nanoseconds.&lt;br /&gt;
;&amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;&lt;br /&gt;
:Path round-trip time (feedback delay). The controller does not&lt;br /&gt;
 measure it.&lt;br /&gt;
&lt;br /&gt;
Rate is the control variable throughout.&lt;br /&gt;
&lt;br /&gt;
All concrete figures in this document &amp;amp;mdash; window bounds, horizons,&lt;br /&gt;
rate thresholds &amp;amp;mdash; are instances of the parameter values in&lt;br /&gt;
[[#11. Parameters|Section 11]]. The mechanisms and the relations&lt;br /&gt;
between parameters are what the algorithm fixes; retuning the&lt;br /&gt;
parameters moves the figures together.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== 1. Overview ==&lt;br /&gt;
&lt;br /&gt;
mb-ecn is a rate-based, ECN-driven controller with three moving parts,&lt;br /&gt;
one at each point of the loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        sender                                  forwarder&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
      |  SFQ virtual-clock pacer       |       |  ecn = MAX(ecn, queue/Q) |&lt;br /&gt;
      |  rate r, paced per packet      | data  |  (deepest queue wins)    |&lt;br /&gt;
      |  AIMD + PD control on r        |------&amp;gt;|  cap8 = MIN(cap8, link)  |&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
                 ^                                        |&lt;br /&gt;
                 | ece + cap8 feedback                    | marked data&lt;br /&gt;
                 |                                        v&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
      |  reads downstream ece,         |&amp;lt;------|  receiver                |&lt;br /&gt;
      |  updates the rate on feedback, |  ece  |  time-mean of ecn over   |&lt;br /&gt;
      |  scales floor/slope to cap8    |  cap8 |  an adaptive window      |&lt;br /&gt;
      +--------------------------------+       +--------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# The &#039;&#039;&#039;forwarder&#039;&#039;&#039; marks each packet with a &#039;&#039;multi-bit&#039;&#039; magnitude&lt;br /&gt;
  proportional to its standing queue, and MIN-stamps its measured&lt;br /&gt;
  outgoing-link capacity into the same PCI.&lt;br /&gt;
# The &#039;&#039;&#039;receiver&#039;&#039;&#039; turns the stream of marks into a time-averaged&lt;br /&gt;
  congestion estimate &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; and feeds it back to the sender,&lt;br /&gt;
  together with the window minimum of the path-capacity codes.&lt;br /&gt;
# The &#039;&#039;&#039;sender&#039;&#039;&#039; paces its rate with a start-time fair-queuing (SFQ)&lt;br /&gt;
  virtual clock, and adjusts that rate with additive-increase /&lt;br /&gt;
  multiplicative-decrease (AIMD) driven by &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;; the rate floor&lt;br /&gt;
  and the additive slope derive from the fed-back path capacity.&lt;br /&gt;
&lt;br /&gt;
The controller runs on packet sends and on feedback arrivals. All&lt;br /&gt;
increase/decrease steps are scaled by elapsed&lt;br /&gt;
wall-clock time, which is what makes the &#039;&#039;allocation&#039;&#039; independent of&lt;br /&gt;
RTT (see [[#6. RTT behaviour|Section 6]]).&lt;br /&gt;
&lt;br /&gt;
The unit of control is the &#039;&#039;&#039;aggregate&#039;&#039;&#039;, kept in the IPCP per&lt;br /&gt;
&amp;lt;code&amp;gt;(destination address, QoS cube)&amp;lt;/code&amp;gt;. Every flow toward that&lt;br /&gt;
destination at that QoS shares one controller and one rate: a new&lt;br /&gt;
flow joins the aggregate at its current rate, and a departing flow&lt;br /&gt;
leaves the rate to the others. The pacer divides the aggregate rate&lt;br /&gt;
fairly across the member flows (SFQ), so per-flow fairness within an&lt;br /&gt;
aggregate is a scheduler property, distinct from the rate law&lt;br /&gt;
described here.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2. Forwarder marking ==&lt;br /&gt;
&lt;br /&gt;
A forwarder marks a packet as a function of the packets already queued&lt;br /&gt;
on the outgoing link:&lt;br /&gt;
&lt;br /&gt;
  mark = queued / Q                         (Q = 4 packets)&lt;br /&gt;
  ecn  = MAX(ecn, mark)                     (saturating, 8-bit field)&lt;br /&gt;
&lt;br /&gt;
The quantum &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; is a layer-wide constant: the marks it&lt;br /&gt;
quantizes are combined by MAX across hops, so all forwarders in a&lt;br /&gt;
layer MUST use the same quantum for the signal to be comparable.&lt;br /&gt;
&lt;br /&gt;
Two properties:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Multi-bit magnitude.&#039;&#039;&#039; The mark is an integer proportional to&lt;br /&gt;
  queue depth, quantized in steps of &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; packets: a coarse&lt;br /&gt;
  readout of the standing queue.&lt;br /&gt;
* &#039;&#039;&#039;MAX along the path.&#039;&#039;&#039; A packet crossing several forwarders carries&lt;br /&gt;
  the maximum mark, i.e. the deepest queue on its path. MAX keeps the&lt;br /&gt;
  signal range-stable and monotone across hops.&lt;br /&gt;
&lt;br /&gt;
There is a &#039;&#039;&#039;dead zone&#039;&#039;&#039;: for &amp;lt;code&amp;gt;queued &amp;lt; Q&amp;lt;/code&amp;gt; the mark is 0.&lt;br /&gt;
&lt;br /&gt;
=== Capacity stamping ===&lt;br /&gt;
&lt;br /&gt;
Each forwarder also measures the capacity of its outgoing link and&lt;br /&gt;
stamps it into the packet. The estimate is a busy-period drain rate:&lt;br /&gt;
the queue is observed at most once per millisecond, and a measurement&lt;br /&gt;
window stays open until 16 packets have drained, so its length&lt;br /&gt;
&#039;&#039;&#039;self-scales with the link rate&#039;&#039;&#039; &amp;amp;mdash; the millisecond cadence&lt;br /&gt;
bounds it on fast links, the 16-packet drain time on slow ones&lt;br /&gt;
(~19 ms at 10 Mbit/s). A window in which more than 1/8 of&lt;br /&gt;
the arrivals found the queue empty is discarded as unsaturated;&lt;br /&gt;
occasional empty observations (token-bucket shapers grazing zero) are&lt;br /&gt;
tolerated. The drain rate of a saturated queue is the link rate; a max&lt;br /&gt;
filter with slow decay (1/16 per window) converges on it from below. A&lt;br /&gt;
window that opened or closed on an empty queue may have drained into&lt;br /&gt;
buffers below at above-wire rate, so it may lower the estimate but&lt;br /&gt;
never raise it. The code on the wire is quarter-log2&lt;br /&gt;
(&amp;lt;code&amp;gt;cap8 = 4 log2 C&amp;lt;/code&amp;gt;, ~19% per step, 0 = unknown) and each hop&lt;br /&gt;
MIN-combines its own code into the byte, so a packet arrives carrying&lt;br /&gt;
the bottleneck&#039;s rate. A hop that has never been backlogged stamps&lt;br /&gt;
nothing; since only backlogged hops matter, the signal exists exactly&lt;br /&gt;
when it is needed.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. Receiver estimate ==&lt;br /&gt;
&lt;br /&gt;
The receiver converts marks into a smoothed estimate &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; over a&lt;br /&gt;
time window &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt; that &#039;&#039;&#039;adapts to the incoming byte rate&#039;&#039;&#039;. It is&lt;br /&gt;
a &#039;&#039;&#039;boxcar (rectangular) time-integral mean&#039;&#039;&#039;. Per packet, with&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;Delta;t&amp;lt;/code&amp;gt; the gap since the previous packet:&lt;br /&gt;
&lt;br /&gt;
  A += ecn * MIN(&amp;amp;Delta;t, W)              dwell-weighted; one packet&lt;br /&gt;
                                          weighs at most one window&lt;br /&gt;
  B += S                                  accumulate bytes this window&lt;br /&gt;
  ...&lt;br /&gt;
  at window close (elapsed &amp;gt;= W, or B &amp;gt;= 2 * B_target with at&lt;br /&gt;
                   least the window floor elapsed):&lt;br /&gt;
      ece = 32 * A / elapsed              mean over the actual window&lt;br /&gt;
      W  += (B_target * elapsed / B - W) / 4    nudge toward the size&lt;br /&gt;
                                                that holds the target&lt;br /&gt;
      W   = clamp(W, ~1.05 ms, ~4.3 s)&lt;br /&gt;
      A   = 0 ; B = 0                     hard reset&lt;br /&gt;
&lt;br /&gt;
At each close the next window is nudged by a quarter-weight moving&lt;br /&gt;
average toward the size that would hold about 16 packets at the&lt;br /&gt;
measured byte rate, from an initial ~67 ms. The target is byte-based (&amp;lt;code&amp;gt;B_target&amp;lt;/code&amp;gt; = 16,000 bytes), so&lt;br /&gt;
&amp;quot;16 packets&amp;quot; is exact only at ~1000-byte packets, and the window&lt;br /&gt;
converges to it over several closes. The mean always divides by the&lt;br /&gt;
&#039;&#039;&#039;actual&#039;&#039;&#039; elapsed window, so a rate step leaves the current&lt;br /&gt;
estimate exact and only re-sizes the &#039;&#039;next&#039;&#039; window. The effect is a roughly constant sample&lt;br /&gt;
count &amp;amp;mdash; ~16 packets &amp;amp;mdash; from about 30 kbit/s to 122 Mbit/s;&lt;br /&gt;
above that the window floors at ~1 ms (a cadence still carrying&lt;br /&gt;
thousands of samples at 10&amp;amp;ndash;100 GbE), and below ~30 kbit/s it&lt;br /&gt;
saturates at the ~4.3 s ceiling. The averaging clock stretches with&lt;br /&gt;
the flow the way TCP&#039;s ACK clock stretches with the RTT. A speed-up&lt;br /&gt;
escapes a stretched window early: once twice the target bytes arrive&lt;br /&gt;
the window closes anyway (the window floor keeps that cadence bounded&lt;br /&gt;
at high rate). The fed-back &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; is thus a piecewise-constant&lt;br /&gt;
staircase whose step period tracks the rate. Two edge cases:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Onset&#039;&#039;&#039; (first mark after an idle estimate) emits the&lt;br /&gt;
  instantaneous mark undiluted, so a starting queue is reported without&lt;br /&gt;
  a full window of delay.&lt;br /&gt;
* &#039;&#039;&#039;Gap&#039;&#039;&#039; longer than 4 windows resets the window and emits the raw&lt;br /&gt;
  sample. The threshold is &#039;&#039;&#039;rate-relative&#039;&#039;&#039;: idle means a few&lt;br /&gt;
  &#039;&#039;current&#039;&#039; windows of silence, so a slow flow&#039;s normal inter-packet&lt;br /&gt;
  gap never reads as idle, while the in-window dwell clamp above bounds&lt;br /&gt;
  what a genuine pause can add to the mean before the restart fires.&lt;br /&gt;
&lt;br /&gt;
Alongside the mark integral, the receiver keeps the MIN of the nonzero&lt;br /&gt;
capacity codes seen in the current window; each emitted &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
carries that minimum (the raw packet&#039;s code on onset/gap restarts) and&lt;br /&gt;
the fold then resets, so a reroute to a faster path can raise the&lt;br /&gt;
fed-back capacity within one window.&lt;br /&gt;
&lt;br /&gt;
The estimate is fed back to the sender on the reverse flow.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 4. Sender pacer ==&lt;br /&gt;
&lt;br /&gt;
The sender paces with an SFQ virtual clock. The context keeps a&lt;br /&gt;
virtual time &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;; each flow keeps a finish tag &amp;lt;code&amp;gt;F&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
  V    += r * &amp;amp;Delta;t                     virtual time advances by bytes served&lt;br /&gt;
  s     = MAX(F, V)                  a packet behind the clock starts now&lt;br /&gt;
  F     = s + S                      the one after it queues behind&lt;br /&gt;
  wait  = (s - V) / r                time until this packet may go&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;wait&amp;lt;/code&amp;gt; becomes the flow&#039;s scheduling deadline: a packet&lt;br /&gt;
already behind the virtual clock is sent immediately&lt;br /&gt;
(&amp;lt;code&amp;gt;wait = 0&amp;lt;/code&amp;gt;); one ahead waits, deferring only its own flow.&lt;br /&gt;
A clock advance across an idle gap longer than 50 ms credits at most&lt;br /&gt;
the calling flow&#039;s owed lead plus one burst (50 ms of service, at&lt;br /&gt;
least one packet): a paced flow slower than one packet per 50 ms&lt;br /&gt;
receives its &#039;&#039;&#039;true&#039;&#039;&#039; elapsed service &amp;amp;mdash; a time-capped credit&lt;br /&gt;
would decay such a flow without bound &amp;amp;mdash; while an idle flow still&lt;br /&gt;
cannot bank an unbounded burst.&lt;br /&gt;
&lt;br /&gt;
The virtual time &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; is shared by every flow in the context;&lt;br /&gt;
the finish tags are per flow. Because every flow computes its start&lt;br /&gt;
tag &amp;lt;code&amp;gt;s&amp;lt;/code&amp;gt; against the same &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;, a flow that has just sent&lt;br /&gt;
carries a finish tag ahead of &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt; and waits, while an idle flow&lt;br /&gt;
(finish tag at or behind &amp;lt;code&amp;gt;V&amp;lt;/code&amp;gt;) starts immediately &amp;amp;mdash; this is&lt;br /&gt;
what fairly divides the aggregate rate &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; across them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 5. Rate control (AIMD + PD) ==&lt;br /&gt;
&lt;br /&gt;
The rate law runs on packet sends, at most once per millisecond, and&lt;br /&gt;
also on feedback arrivals so that a sender with no traffic of its own&lt;br /&gt;
still reacts. Each increase term and the proportional decrease are&lt;br /&gt;
scaled by elapsed wall-clock time, each by its own clock: the&lt;br /&gt;
increase terms use time banked and &#039;&#039;&#039;capped&#039;&#039;&#039; at 50 ms (an idle gap&lt;br /&gt;
cannot bank an unbounded ramp), while the proportional decrease uses&lt;br /&gt;
the &#039;&#039;&#039;uncapped&#039;&#039;&#039; elapsed time (a starved sender still cuts by the&lt;br /&gt;
right amount). The one-sided derivative (Section 5.3) is a per-delta&lt;br /&gt;
term.&lt;br /&gt;
&lt;br /&gt;
=== 5.1. Slow start ===&lt;br /&gt;
&lt;br /&gt;
Before the first congestion signal the rate ramps exponentially with a&lt;br /&gt;
20 ms time constant:&lt;br /&gt;
&lt;br /&gt;
  r += r * &amp;amp;Delta;t / T_ss                  (T_ss = 20 ms)&lt;br /&gt;
&lt;br /&gt;
Slow start ends the first time a congestion signal arrives &amp;amp;mdash;&lt;br /&gt;
fed back from the receiver or observed at the sender&#039;s own first hop&lt;br /&gt;
&amp;amp;mdash; and never resumes.&lt;br /&gt;
&lt;br /&gt;
Slow start belongs to the aggregate, so it runs &#039;&#039;&#039;once per&lt;br /&gt;
(destination, QoS cube)&#039;&#039;&#039;, on the first flow. A flow arriving at an&lt;br /&gt;
established aggregate rides the existing estimates: it enters at the&lt;br /&gt;
aggregate&#039;s current rate and receives its fair share of it through&lt;br /&gt;
the pacer (Section 4), with no probing of its own.&lt;br /&gt;
&lt;br /&gt;
=== 5.2. Increase: additive plus proportional probe (always on) ===&lt;br /&gt;
&lt;br /&gt;
Past slow start, two increase terms are applied every step, both&lt;br /&gt;
regardless of congestion:&lt;br /&gt;
&lt;br /&gt;
  r += a * &amp;amp;Delta;t                         additive; a defaults to 2^16 B/s^2&lt;br /&gt;
  r += r * &amp;amp;Delta;t / T_probe               proportional; T_probe = 8 s&lt;br /&gt;
&lt;br /&gt;
The additive term is a fixed per-context slope (bytes/s per second),&lt;br /&gt;
scaled to the path capacity (Section 5.5). The proportional probe&lt;br /&gt;
grows the rate by a fixed fraction per unit time (e-folds over&lt;br /&gt;
&amp;lt;code&amp;gt;T_probe&amp;lt;/code&amp;gt;): a flow recovers in the same number of steps at any&lt;br /&gt;
link rate; the fixed additive term&#039;s effect vanishes relative to a&lt;br /&gt;
fast link. Both are the &amp;quot;probing&amp;quot;&lt;br /&gt;
pressure the multiplicative decrease balances at equilibrium; the&lt;br /&gt;
probe&#039;s cost is a rate-independent standing-queue floor (Section 7).&lt;br /&gt;
&lt;br /&gt;
=== 5.3. Multiplicative decrease (proportional + derivative) ===&lt;br /&gt;
&lt;br /&gt;
With congestion level &amp;lt;code&amp;gt;m&amp;lt;/code&amp;gt; (the fed-back &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;, or a local&lt;br /&gt;
first-hop mark as fallback), the decrease is the sum of a proportional&lt;br /&gt;
term and a one-sided derivative term &amp;amp;mdash; a PD controller on the&lt;br /&gt;
congestion signal:&lt;br /&gt;
&lt;br /&gt;
  mark = MIN(m, M)                                   M = 512&lt;br /&gt;
  rise = MAX(m - m_prev, 0), capped at M             one-sided derivative&lt;br /&gt;
&lt;br /&gt;
  cut  = r * rise / (4 * M)                          derivative, gain 1/4&lt;br /&gt;
  cut += r * mark * &amp;amp;Delta;t_ms / (1000 * M)              proportional&lt;br /&gt;
  cut  = MIN(cut, r / 2)                             at most a halving&lt;br /&gt;
&lt;br /&gt;
  r     -= cut&lt;br /&gt;
  m_prev = m&lt;br /&gt;
&lt;br /&gt;
* The &#039;&#039;&#039;proportional&#039;&#039;&#039; term is scaled by honest elapsed time in&lt;br /&gt;
  milliseconds, so a starved sender that has not run the controller&lt;br /&gt;
  for a while still cuts by the right amount.&lt;br /&gt;
* The &#039;&#039;&#039;derivative&#039;&#039;&#039; term (&amp;lt;code&amp;gt;rise&amp;lt;/code&amp;gt;) reacts to an &#039;&#039;increase&#039;&#039; in&lt;br /&gt;
  the congestion level between samples; it is one-sided and per-delta.&lt;br /&gt;
  It sharpens the reaction at the onset of congestion.&lt;br /&gt;
* The cut is clamped to &amp;lt;code&amp;gt;r/2&amp;lt;/code&amp;gt; per step, bounding the decrease to&lt;br /&gt;
  a halving.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;M = 512 = 32 &amp;amp;times; 16&amp;lt;/code&amp;gt; defines &amp;quot;full congestion&amp;quot; as a mean&lt;br /&gt;
&amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; of 16, i.e. a mean queue of &amp;lt;code&amp;gt;16 &amp;amp;times; Q = 64&amp;lt;/code&amp;gt; packets.&lt;br /&gt;
&lt;br /&gt;
=== 5.4. Staleness ===&lt;br /&gt;
&lt;br /&gt;
Feedback arrives once per receiver window, and the window stretches&lt;br /&gt;
with the flow&#039;s byte rate (Section 3) &amp;amp;mdash; so the staleness horizon&lt;br /&gt;
must stretch with it, or a slow flow&#039;s mark would age out &#039;&#039;between&#039;&#039;&lt;br /&gt;
feedbacks and the rate would ramp against a congested path. The&lt;br /&gt;
sender mirrors the receiver: the horizon is four target windows of&lt;br /&gt;
bytes at the current rate, floored at ~268 ms so fast flows keep a&lt;br /&gt;
fixed horizon; at the default rate floor it is ~8 s. A signal older&lt;br /&gt;
than the horizon is aged out (both the fed-back estimate and the&lt;br /&gt;
local mark), freeing the rate to ramp once congestion clears. The&lt;br /&gt;
rate is clamped to&lt;br /&gt;
&amp;lt;code&amp;gt;[r_min, r_max]&amp;lt;/code&amp;gt; on every step.&lt;br /&gt;
&lt;br /&gt;
=== 5.5. Capacity-derived floor and slope ===&lt;br /&gt;
&lt;br /&gt;
The fed-back capacity code sets the two rate-scale constants of the&lt;br /&gt;
controller, per context:&lt;br /&gt;
&lt;br /&gt;
  target = decode(cap8) / 32              clamped to [2^13, 2^32] B/s&lt;br /&gt;
  r_min += (target - r_min) / 2           per feedback (moving average)&lt;br /&gt;
  a      = r_min&lt;br /&gt;
&lt;br /&gt;
The defaults are fixed constants (&amp;lt;code&amp;gt;r_min = 2^13 B/s&amp;lt;/code&amp;gt;,&lt;br /&gt;
&amp;lt;code&amp;gt;a = 2^16 B/s^2&amp;lt;/code&amp;gt;), used on paths that never report a capacity;&lt;br /&gt;
with the estimator live the fairness floor and the post-cut recovery&lt;br /&gt;
slope scale with the bottleneck, and capacity engages wherever&lt;br /&gt;
&amp;lt;code&amp;gt;C/32&amp;lt;/code&amp;gt; clears the default floor, i.e. above ~2 Mbit/s. A&lt;br /&gt;
capacity older than 16 staleness horizons (~4.3 s for fast flows)&lt;br /&gt;
reverts both to the defaults. That horizon deliberately outlives the&lt;br /&gt;
feedback horizon: feedback stops the moment the marks clear, which is&lt;br /&gt;
exactly when the recovery slope is needed; the onset-fresh capacity&lt;br /&gt;
(Section 3) re-seeds it on the first mark of the next episode either&lt;br /&gt;
way.&lt;br /&gt;
&lt;br /&gt;
=== 5.6. Ramp and recovery ===&lt;br /&gt;
&lt;br /&gt;
Three mechanisms set how fast an aggregate reaches a fast link&#039;s&lt;br /&gt;
rate, each owning one regime:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;From scratch: slow start.&#039;&#039;&#039; The exponential ramp reaches any&lt;br /&gt;
  physical link rate in well under a second (~200 ms from the seed to&lt;br /&gt;
  10 Gbit/s; each further doubling of link speed costs one more&lt;br /&gt;
  ~14 ms doubling time). On an uncongested path nothing marks, so&lt;br /&gt;
  slow start runs until the aggregate arrives at its bottleneck.&lt;br /&gt;
* &#039;&#039;&#039;After a cut: the probe.&#039;&#039;&#039; A decrease is at most a halving per&lt;br /&gt;
  step, and the mark ages out on the rate-relative horizon once&lt;br /&gt;
  congestion clears (Section 5.4); the proportional probe then heals&lt;br /&gt;
  a halving in &amp;lt;code&amp;gt;T_probe &amp;amp;middot; ln 2 &amp;amp;asymp; 5.5 s&amp;lt;/code&amp;gt; at any&lt;br /&gt;
  link rate.&lt;br /&gt;
* &#039;&#039;&#039;At the bottom: the capacity floor.&#039;&#039;&#039; The rate sits at most a&lt;br /&gt;
  factor 32 below a measured bottleneck, and the additive slope&lt;br /&gt;
  refills &amp;lt;code&amp;gt;C/32&amp;lt;/code&amp;gt; per second, so the deepest hole is bounded:&lt;br /&gt;
  floor to full capacity in tens of seconds, a halving in seconds.&lt;br /&gt;
&lt;br /&gt;
These compose because a deep cut requires sustained congestion,&lt;br /&gt;
sustained congestion backlogs the bottleneck queue, and a backlogged&lt;br /&gt;
queue is exactly when its capacity is measured and stamped&lt;br /&gt;
(Section 2): whenever the controller is cut deep, the scaled floor is&lt;br /&gt;
live. The default floor only governs paths that never congested the&lt;br /&gt;
sender &amp;amp;mdash; where there is nothing to recover from. This is why&lt;br /&gt;
slow start can end for good: the floor and the probe cover every&lt;br /&gt;
later recovery.&lt;br /&gt;
&lt;br /&gt;
=== 5.7. Regimes ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   +-------------+   first mark seen    +----------------+&lt;br /&gt;
   | slow start  |---------------------&amp;gt;|  congestion    |&lt;br /&gt;
   | r *= e^(t/T)|                       |  avoidance     |&lt;br /&gt;
   +-------------+                       |                |&lt;br /&gt;
                                         | m == 0 ---&amp;gt; additive increase&lt;br /&gt;
                                         | m  &amp;gt; 0 ---&amp;gt; AI + PD decrease&lt;br /&gt;
                                         +----------------+&lt;br /&gt;
    m = fed-back ece, or the local first-hop mark when ece == 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 6. RTT behaviour ==&lt;br /&gt;
&lt;br /&gt;
Because every increase and decrease is scaled by wall-clock time,&lt;br /&gt;
two flows of different RTT that share a bottleneck obey the &#039;&#039;same&#039;&#039;&lt;br /&gt;
rate law and converge to the &#039;&#039;same&#039;&#039; rate. The steady-state&lt;br /&gt;
&#039;&#039;&#039;allocation is RTT-independent&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
This is a property of the equilibrium, not of the dynamics. The&lt;br /&gt;
feedback delay in the loop &#039;&#039;is&#039;&#039; the path RTT &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;, and the&lt;br /&gt;
controller does not measure or compensate for it. At low rate the&lt;br /&gt;
receiver window (up to its ~4.3 s ceiling) is the dominant lag &amp;amp;mdash;&lt;br /&gt;
the loop is slow there in proportion to how slow the flow is, exactly&lt;br /&gt;
as a long-RTT TCP is &amp;amp;mdash; while as the rate rises the window shrinks&lt;br /&gt;
toward its ~1 ms floor, so the window&#039;s contribution to the loop delay&lt;br /&gt;
&#039;&#039;falls&#039;&#039; with capacity and the path RTT &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; becomes the&lt;br /&gt;
irreducible term. Two mechanisms keep the loop damped: the adaptive&lt;br /&gt;
window removes the delay-dominated corner at low &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;, and a&lt;br /&gt;
bucketed &#039;&#039;&#039;rate-velocity damping&#039;&#039;&#039; (a washout: each ~31 ms bucket&lt;br /&gt;
pulls the rate a quarter of the way back toward its value at the&lt;br /&gt;
previous bucket) supplies the damping the otherwise&lt;br /&gt;
near-double-integrator lacks, which is what holds the loop together at&lt;br /&gt;
high capacity and moderate &amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt;. What remains is the loop gain&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;prop; &amp;amp;radic;C&amp;lt;/code&amp;gt;: at very high capacity &#039;&#039;and&#039;&#039; large&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;amp;tau;&amp;lt;/code&amp;gt; the &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt; phase budget is the&lt;br /&gt;
binding constraint, and no window or damping trick removes it (see&lt;br /&gt;
[[#8. Limitations|Section 8]]). A precise statement is therefore:&lt;br /&gt;
&#039;&#039;&#039;RTT-fair in equilibrium, and dynamically stable up to a&lt;br /&gt;
capacity-dependent RTT limit set by &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt;.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 7. Fairness ==&lt;br /&gt;
&lt;br /&gt;
At equilibrium the two increase terms balance the proportional&lt;br /&gt;
decrease, &amp;lt;code&amp;gt;a + r/T_probe = r &amp;amp;middot; m*/M&amp;lt;/code&amp;gt;, which pins the&lt;br /&gt;
congestion level to &amp;lt;code&amp;gt;m* = M &amp;amp;middot; (a/r + 1/T_probe)&amp;lt;/code&amp;gt; and, since&lt;br /&gt;
&amp;lt;code&amp;gt;ece = 8q&amp;lt;/code&amp;gt; (one mark unit per &amp;lt;code&amp;gt;Q = 4&amp;lt;/code&amp;gt; packets, 32 fixed-point&lt;br /&gt;
steps per unit), a standing queue of&lt;br /&gt;
&lt;br /&gt;
  q* = 64 * a / r  +  64 / T_probe        (packets, r in bytes/s)&lt;br /&gt;
&lt;br /&gt;
The additive part (&amp;lt;code&amp;gt;64 &amp;amp;middot; a / r&amp;lt;/code&amp;gt;) vanishes with rate under&lt;br /&gt;
the default slope; with the capacity-derived slope (&amp;lt;code&amp;gt;a = C/32&amp;lt;/code&amp;gt;,&lt;br /&gt;
Section 5.5) it becomes &amp;lt;code&amp;gt;2 C / r&amp;lt;/code&amp;gt; &amp;amp;mdash; about two packets per&lt;br /&gt;
competing flow, rate-independent. The proportional-probe part&lt;br /&gt;
(&amp;lt;code&amp;gt;64 / T_probe&amp;lt;/code&amp;gt;) is a rate-independent floor (~8 packets at&lt;br /&gt;
8 s). That floor is the cost of rate-independent convergence, and it&lt;br /&gt;
keeps &amp;lt;code&amp;gt;q*&amp;lt;/code&amp;gt; above the marking threshold at high rate.&lt;br /&gt;
&lt;br /&gt;
Two flows at the same bottleneck see the same &amp;lt;code&amp;gt;m*&amp;lt;/code&amp;gt;, so each&lt;br /&gt;
solves &amp;lt;code&amp;gt;r_i = a / (m*/M - 1/T_probe)&amp;lt;/code&amp;gt; to the same rate. Under&lt;br /&gt;
the network-utility-maximization view this corresponds to&lt;br /&gt;
&#039;&#039;&#039;proportional fairness&#039;&#039;&#039; (equal weights), which is max-min fair at&lt;br /&gt;
a single bottleneck.&lt;br /&gt;
&lt;br /&gt;
Measurement fidelity at low rate comes from the estimator itself:&lt;br /&gt;
the averaging window stretches with the flow&lt;br /&gt;
(Section 3), so a CA-limited flow keeps ~16 packets per window at any&lt;br /&gt;
rate down to ~30 kbit/s, and the rate-relative gap threshold keeps its&lt;br /&gt;
inter-packet spacing from reading as idle. The default floor&lt;br /&gt;
(&amp;lt;code&amp;gt;2^13&amp;lt;/code&amp;gt; B/s) only bounds the extremes (window ceiling,&lt;br /&gt;
staleness horizon, pacer arithmetic). The floor scales with the&lt;br /&gt;
fed-back path capacity (&amp;lt;code&amp;gt;r_min = C/32&amp;lt;/code&amp;gt;, Section 5.5), so&lt;br /&gt;
roughly 32 CA-limited flows fit above it at any link class. Flows&lt;br /&gt;
whose paths differ in bottleneck class derive different slopes, so&lt;br /&gt;
fairness across them is capacity-weighted; see&lt;br /&gt;
[[#8. Limitations|Section 8]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 8. Limitations ==&lt;br /&gt;
&lt;br /&gt;
These are properties the algorithm does &#039;&#039;&#039;not&#039;&#039;&#039; have. They are&lt;br /&gt;
listed so the policy is not credited with more than it does.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Queue-only signal; no bandwidth/rate term.&#039;&#039;&#039; The reference&lt;br /&gt;
  &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; is a queue level, so mb-ecn is a standing-queue&lt;br /&gt;
  controller: it must build a queue (&amp;lt;code&amp;gt;q* = 64 &amp;amp;middot; a / r +&lt;br /&gt;
  64 / T_probe&amp;lt;/code&amp;gt;) to see any signal, and cannot hold high utilization&lt;br /&gt;
  at an empty queue the way a controller with a rate/bandwidth estimate&lt;br /&gt;
  can. The proportional-probe floor (~8 packets) keeps &amp;lt;code&amp;gt;q*&amp;lt;/code&amp;gt;&lt;br /&gt;
  above the marking threshold at all rates, at the cost of a persistent&lt;br /&gt;
  standing queue.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Aggregation is per destination, per QoS cube.&#039;&#039;&#039; The aggregate&lt;br /&gt;
  stands in for the path: flows toward the same destination are&lt;br /&gt;
  assumed to share their bottleneck, and flows toward different&lt;br /&gt;
  destinations keep independent loops even when they queue at the&lt;br /&gt;
  same hop. Each such loop converges to its own fair share&lt;br /&gt;
  (Section 7), so the cost is duplicated state and probing at the&lt;br /&gt;
  shared queue rather than unfairness. A future option is to identify&lt;br /&gt;
  the queue in the packet: a forwarder stamping a queue identifier&lt;br /&gt;
  alongside its mark would let senders key their aggregates on the&lt;br /&gt;
  actual bottleneck rather than on the destination, pooling the&lt;br /&gt;
  estimate and the probing across everything that queues there.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Capacity is learned, with gaps.&#039;&#039;&#039; The floor and slope scale with&lt;br /&gt;
  an in-band path-capacity estimate (Sections 2, 5.5), but the estimate&lt;br /&gt;
  has limits of its own. A hop only learns its link rate while its&lt;br /&gt;
  queue is backlogged, so a rerouted or freshly established bottleneck&lt;br /&gt;
  contributes &#039;&#039;unknown&#039;&#039; at first and the path MIN can transiently&lt;br /&gt;
  come from faster hops (the sender&#039;s smoothing and the next feedback&lt;br /&gt;
  bound the excursion). The drain measurement is order-of-magnitude by&lt;br /&gt;
  design: quarter-log2 code (~19% per step), burst noise bounded by a&lt;br /&gt;
  16-packet minimum window and a max filter, nothing learned below&lt;br /&gt;
  ~120 packets per second of busy drain (16 packets inside the&lt;br /&gt;
  ~134 ms staleness cap; at MTU that is ~1.4 Mbit/s, below which&lt;br /&gt;
  capacity/32 would sit under the default floor anyway). The derived&lt;br /&gt;
  floor clamps to &amp;lt;code&amp;gt;[2^13, 2^32]&amp;lt;/code&amp;gt; B/s, and flows with&lt;br /&gt;
  different path bottlenecks get capacity-weighted, not equal, shares&lt;br /&gt;
  at a common queue (Section 7).&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;The low end trades reactivity, not correctness.&#039;&#039;&#039; Below&lt;br /&gt;
  ~30 kbit/s the averaging window clips at its ~4.3 s ceiling and&lt;br /&gt;
  holds fewer samples; below ~64 kbit/s of offered load an&lt;br /&gt;
  &#039;&#039;app-limited&#039;&#039; flow spaces packets past the gap threshold and rides&lt;br /&gt;
  the raw onset branch, as it always did. Feedback cadence and the&lt;br /&gt;
  staleness horizon at the floor are seconds &amp;amp;mdash; a slow flow&lt;br /&gt;
  converges slowly, TCP-style; nothing diverges, but nothing is fast&lt;br /&gt;
  either.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;No loss response.&#039;&#039;&#039; The controller reacts only to ECN marks. Loss&lt;br /&gt;
  is handled by FRCP retransmission, out of the CA&#039;s view. At a bottleneck&lt;br /&gt;
  that drops instead of marking (e.g. a classic drop-tail queue at the&lt;br /&gt;
  Ethernet/UDP shim), mb-ecn sees no signal and additive increase keeps&lt;br /&gt;
  ramping. mb-ecn is intended for Ouroboros-native bottlenecks where the&lt;br /&gt;
  forwarder marks; it does not implement a classic-bottleneck fallback.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Dynamic stability has a capacity-dependent RTT limit.&#039;&#039;&#039; As stated&lt;br /&gt;
  in Section 6, the allocation is RTT-independent but the dynamics are&lt;br /&gt;
  not: the binding constraint is &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;middot; &amp;amp;tau;&amp;lt;/code&amp;gt; with&lt;br /&gt;
  &amp;lt;code&amp;gt;&amp;amp;radic;G &amp;amp;prop; &amp;amp;radic;C&amp;lt;/code&amp;gt;. The adaptive window and the washout push&lt;br /&gt;
  this limit out substantially (e.g. a single flow at 10 GbE stays stable&lt;br /&gt;
  to ~50 ms RTT in the fluid model), but the extreme long-fat-network&lt;br /&gt;
  corner (100 GbE at &amp;amp;ge; 50 ms) is not reachable by a window/damping&lt;br /&gt;
  controller and would need explicit-rate signalling.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Fluid-model caveat.&#039;&#039;&#039; The offline model in the simulation tooling&lt;br /&gt;
  captures the aggregate rate law (sawtooth, standing queue) with a&lt;br /&gt;
  boxcar receiver, but sizes the adaptive window in the rate domain&lt;br /&gt;
  rather than the byte-folded form of the algorithm above, and treats&lt;br /&gt;
  all flows identically, so it is a guide to stability &#039;&#039;trends&#039;&#039;, not&lt;br /&gt;
  a fairness or exact-quantitative oracle at high link rate.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 9. Architectural fit ==&lt;br /&gt;
&lt;br /&gt;
Each property the preceding sections build toward attaches to a&lt;br /&gt;
structural feature of the recursive architecture. This section makes&lt;br /&gt;
the mapping explicit.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Congestion avoidance is fully orthogonal to ARQ and to flow&lt;br /&gt;
  control.&#039;&#039;&#039; Three concerns, three mechanisms, two scopes: FRCP&lt;br /&gt;
  provides retransmission (ARQ) and flow control (the peer pacing the&lt;br /&gt;
  sender) end-to-end, per flow; congestion avoidance runs in the&lt;br /&gt;
  IPCP, per aggregate. Each signal means one thing. A loss triggers&lt;br /&gt;
  a retransmission and nothing else &amp;amp;mdash; a lossy link reads as&lt;br /&gt;
  lossy, with the congestion verdict left to the marks (Section 8&#039;s&lt;br /&gt;
  &amp;quot;no loss response&amp;quot; is this orthogonality stated from the other&lt;br /&gt;
  side). The peer&#039;s flow-control window paces the endpoint and&lt;br /&gt;
  nothing else &amp;amp;mdash; back-pressure from a slow receiver stays&lt;br /&gt;
  distinct from congestion in the network. A retransmitted packet is&lt;br /&gt;
  ordinary traffic to the pacer. TCP folds all three into one window&lt;br /&gt;
  machine, where the receive window bounds the congestion window and&lt;br /&gt;
  loss serves as both reliability trigger and congestion signal;&lt;br /&gt;
  here each mechanism can be reasoned about, tested and evolved&lt;br /&gt;
  alone.&lt;br /&gt;
* &#039;&#039;&#039;Congestion avoidance sits below application choice.&#039;&#039;&#039; Every&lt;br /&gt;
  flow in the layer is paced by the same rate law, whatever its QoS:&lt;br /&gt;
  a greedy raw sender shares a bottleneck fairly with a reliable&lt;br /&gt;
  stream because the control is a property of the layer, on the&lt;br /&gt;
  aggregate, rather than a courtesy of the endpoint transport.&lt;br /&gt;
* &#039;&#039;&#039;A layer defines its own PCI, so the signal can be rich.&#039;&#039;&#039;&lt;br /&gt;
  Every member of a layer is enrolled into it: the layer is a single&lt;br /&gt;
  administrative domain by construction, at whatever scope it spans,&lt;br /&gt;
  and its header is layer-internal. Forwarders therefore write a&lt;br /&gt;
  multi-bit queue magnitude and a capacity byte directly into the&lt;br /&gt;
  packet. The datacenter schemes of&lt;br /&gt;
  [[#10. Heritage and positioning|Section 10]] require exactly such a&lt;br /&gt;
  domain and find it only inside one operator&#039;s fabric; a recursive&lt;br /&gt;
  layer supplies it everywhere.&lt;br /&gt;
* &#039;&#039;&#039;Layer-internal addressing makes the aggregate well-defined.&#039;&#039;&#039;&lt;br /&gt;
  Flows in a layer run between layer addresses, so the&lt;br /&gt;
  (destination, QoS cube) aggregate &amp;amp;mdash; RFC 3124&#039;s macroflow&lt;br /&gt;
  &amp;amp;mdash; falls out of the naming structure. Controller state scales&lt;br /&gt;
  with destinations, and each QoS cube keeps its own loop, so&lt;br /&gt;
  service classes keep separate fates.&lt;br /&gt;
* &#039;&#039;&#039;Flows are allocated, so feedback has a channel.&#039;&#039;&#039; Every flow&lt;br /&gt;
  has state at both ends and a reverse direction; the receiver&#039;s&lt;br /&gt;
  &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; and the path capacity ride the flow allocator&#039;s&lt;br /&gt;
  existing exchange.&lt;br /&gt;
* &#039;&#039;&#039;There is no transport ack clock, and none is relied on.&#039;&#039;&#039; The&lt;br /&gt;
  layer must control raw flows, which carry no acknowledgements at&lt;br /&gt;
  all, so the controller paces on wall-clock time &amp;amp;mdash; and that&lt;br /&gt;
  constraint yields the RTT-independent allocation of Section 6. An&lt;br /&gt;
  architectural restriction becomes the controller&#039;s distinguishing&lt;br /&gt;
  property.&lt;br /&gt;
* &#039;&#039;&#039;Recursion scales the scheme.&#039;&#039;&#039; Each layer controls congestion&lt;br /&gt;
  over its own scope and timescale; many N-flows ride one N-1 flow,&lt;br /&gt;
  so aggregation compounds down the stack. The capacity a layer&lt;br /&gt;
  measures at its egress queue is the rate the layer below actually&lt;br /&gt;
  provides &amp;amp;mdash; a paced, shared lower flow rather than a nominal&lt;br /&gt;
  wire speed &amp;amp;mdash; so the signal stays meaningful at every level,&lt;br /&gt;
  and pushback cascades layer by layer.&lt;br /&gt;
&lt;br /&gt;
The fit has a boundary: at a shim over legacy media the layer below&lt;br /&gt;
neither enrolls nor marks, which is the deployment edge behind&lt;br /&gt;
Section 8&#039;s no-loss-response limitation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 10. Heritage and positioning ==&lt;br /&gt;
&lt;br /&gt;
mb-ecn is best understood as a rate-based ECN controller in the DECbit /&lt;br /&gt;
QCN line, adapted to an acknowledgement-less recursive layer.&lt;br /&gt;
&lt;br /&gt;
The core of the scheme dates to the first Ouroboros&lt;br /&gt;
congestion-avoidance policy (2020): a multi-bit queue-depth mark&lt;br /&gt;
relayed by forwarders to the receiver, a smoothed multi-bit&lt;br /&gt;
&amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; fed back on the reverse flow at the same 1/32 fixed&lt;br /&gt;
point used today, and a sender that paced packets from a time budget&lt;br /&gt;
and ran slow start, additive increase and multiplicative decrease on&lt;br /&gt;
wall-clock time slots &amp;amp;mdash; rate-based and acknowledgement-free from&lt;br /&gt;
the start, with RTT-independent allocation already an objective. The&lt;br /&gt;
present algorithm keeps that skeleton and reworks every estimator&lt;br /&gt;
around it: per-path aggregation with SFQ pacing, the rate-adaptive&lt;br /&gt;
boxcar receiver, elapsed-time-scaled AIMD with a derivative term and&lt;br /&gt;
rate-velocity damping, and the in-band capacity signal.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;DECbit&#039;&#039;&#039; (Ramakrishnan &amp;amp;amp; Jain, 1988; ToCS 1990) established&lt;br /&gt;
  binary congestion feedback set by the switch on queue occupancy.&lt;br /&gt;
  mb-ecn keeps the switch-sets-on-queue idea and carries a&lt;br /&gt;
  &#039;&#039;magnitude&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;The Congestion Manager&#039;&#039;&#039; (Balakrishnan et al., SIGCOMM 1999;&lt;br /&gt;
  RFC 3124, 2001) aggregates congestion state at the end host: one&lt;br /&gt;
  controller per macroflow, a scheduler apportioning its rate across&lt;br /&gt;
  the member streams, and new streams joining at the ensemble&#039;s&lt;br /&gt;
  current state instead of probing from scratch. mb-ecn&#039;s&lt;br /&gt;
  (destination, QoS cube) aggregate is the same idea placed inside&lt;br /&gt;
  the IPCP: every layer manages its own macroflows, recursively, the&lt;br /&gt;
  SFQ pacer plays the CM scheduler&#039;s role (Section 4), and a joining&lt;br /&gt;
  flow rides the aggregate&#039;s estimates (Section 5.1). The CM gathers&lt;br /&gt;
  its signal from transport feedback at the edge; mb-ecn reads it&lt;br /&gt;
  off the wire from the forwarders.&lt;br /&gt;
* &#039;&#039;&#039;DCTCP&#039;&#039;&#039; (Alizadeh et al., SIGCOMM 2010) recovers a queue&lt;br /&gt;
  magnitude at the endpoint by averaging a single threshold bit. mb-ecn&lt;br /&gt;
  instead reads the magnitude off the wire, then time-averages it at&lt;br /&gt;
  the receiver. The additive-increase / proportional-decrease structure&lt;br /&gt;
  is shared.&lt;br /&gt;
* &#039;&#039;&#039;QCN&#039;&#039;&#039; (IEEE 802.1Qau, 2010) and &#039;&#039;&#039;DCQCN&#039;&#039;&#039; (Zhu et al., SIGCOMM&lt;br /&gt;
  2015) contribute the rate-based (not window-based) ECN control&lt;br /&gt;
  skeleton. mb-ecn&#039;s proportional decrease plus a probing increase&lt;br /&gt;
  mirrors that skeleton; the increase here is wall-clock scaled rather&lt;br /&gt;
  than byte-counter/timer based.&lt;br /&gt;
* &#039;&#039;&#039;HPCC&#039;&#039;&#039; (Li et al., SIGCOMM 2019) and &#039;&#039;&#039;PowerTCP&#039;&#039;&#039; (Addanki et&lt;br /&gt;
  al., NSDI 2022) combine a queue term &#039;&#039;and&#039;&#039; a rate term (via in-band&lt;br /&gt;
  telemetry or delay). mb-ecn now carries a coarse in-band rate (the&lt;br /&gt;
  quarter-log2 path MIN, Section 2) but uses it only to scale the&lt;br /&gt;
  controller&#039;s gains; the congestion &#039;&#039;signal&#039;&#039; remains queue-only,&lt;br /&gt;
  which is still the main functional difference (Section 8).&lt;br /&gt;
* &#039;&#039;&#039;L4S / TCP Prague&#039;&#039;&#039; (RFC 9330&amp;amp;ndash;9332, 2023) make&lt;br /&gt;
  RTT-independence a requirement and use a high-frequency single-bit&lt;br /&gt;
  signal. mb-ecn shares the RTT-independent &#039;&#039;allocation&#039;&#039; goal but&lt;br /&gt;
  reaches it by wall-clock-scaled rate control, with a low-frequency&lt;br /&gt;
  multi-bit signal.&lt;br /&gt;
&lt;br /&gt;
The novel combination is the multi-bit magnitude on the wire fused with&lt;br /&gt;
a wall-clock-scaled rate pacer in a setting with no per-flow ack clock.&lt;br /&gt;
The trade-off, made explicit above, is a queue-only signal and a&lt;br /&gt;
reactivity capped by the averaging window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 11. Parameters ==&lt;br /&gt;
&lt;br /&gt;
These are the values every figure in this document instantiates. They&lt;br /&gt;
are &#039;&#039;&#039;coupled&#039;&#039;&#039;: the mechanisms compose only while certain relations&lt;br /&gt;
hold &amp;amp;mdash; the gap-restart horizon at the rate floor must exceed a&lt;br /&gt;
packet&#039;s service time (or a floor-rate flow restart-loops at onset),&lt;br /&gt;
a floor-rate flow&#039;s target window must fit under the window ceiling,&lt;br /&gt;
the control interval must sit under the damping bucket under the idle&lt;br /&gt;
credit cap, the rate floor must not exceed the slow-start seed, and&lt;br /&gt;
the derived-floor ceiling must stay under the rate ceiling. Retune&lt;br /&gt;
them as a set, against those invariants.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| Marking quantum &amp;lt;code&amp;gt;Q&amp;lt;/code&amp;gt; || 4 packets || Mark onset and step; layer-wide&lt;br /&gt;
|-&lt;br /&gt;
| Feedback resolution || 1/32 &amp;lt;code&amp;gt;ecn&amp;lt;/code&amp;gt; unit || Fixed-point LSB of &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Full-congestion reference &amp;lt;code&amp;gt;M&amp;lt;/code&amp;gt; || 512 || Mean queue of 64 packets&lt;br /&gt;
|-&lt;br /&gt;
| Receiver window target || 16 packets (16,000 bytes) || Samples per window&lt;br /&gt;
|-&lt;br /&gt;
| Initial receiver window || ~67 ms || Before rate is known&lt;br /&gt;
|-&lt;br /&gt;
| Receiver window bounds || ~1.05 ms &amp;amp;ndash; ~4.3 s || Clamp on &amp;lt;code&amp;gt;W&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Window resize weight || 1/4 || Moving average toward target&lt;br /&gt;
|-&lt;br /&gt;
| Early window close || 2 &amp;amp;times; target bytes || Speed-up escape&lt;br /&gt;
|-&lt;br /&gt;
| Gap restart || 4 windows || Idle horizon (rate-relative)&lt;br /&gt;
|-&lt;br /&gt;
| Additive slope &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; (default) || 2^16 B/s^2 || Lower clamp of derived slope&lt;br /&gt;
|-&lt;br /&gt;
| Probe time constant &amp;lt;code&amp;gt;T_probe&amp;lt;/code&amp;gt; || 8 s || Rate-proportional recovery&lt;br /&gt;
|-&lt;br /&gt;
| Derivative gain || 1/4 || Onset sharpening&lt;br /&gt;
|-&lt;br /&gt;
| Rate floor &amp;lt;code&amp;gt;r_min&amp;lt;/code&amp;gt; (default) || 2^13 B/s || Lower clamp of derived floor&lt;br /&gt;
|-&lt;br /&gt;
| Slow-start seed rate || 2^16 B/s || Initial rate&lt;br /&gt;
|-&lt;br /&gt;
| Slow-start time constant || 20 ms || Exponential ramp&lt;br /&gt;
|-&lt;br /&gt;
| Rate ceiling &amp;lt;code&amp;gt;r_max&amp;lt;/code&amp;gt; || 2^37 B/s || Upper clamp&lt;br /&gt;
|-&lt;br /&gt;
| Capacity scale || &amp;lt;code&amp;gt;C&amp;lt;/code&amp;gt; / 32 || Floor and slope from bottleneck&lt;br /&gt;
|-&lt;br /&gt;
| Capacity smoothing || 1/2 || Moving average per feedback&lt;br /&gt;
|-&lt;br /&gt;
| Capacity staleness || 16 &amp;amp;times; feedback horizon || Revert to defaults&lt;br /&gt;
|-&lt;br /&gt;
| Derived floor ceiling || 2^32 B/s || Upper clamp on derived floor&lt;br /&gt;
|-&lt;br /&gt;
| Control interval || 1 ms || Minimum spacing of rate updates&lt;br /&gt;
|-&lt;br /&gt;
| Idle credit cap || 50 ms || Burst allowance / increase banking&lt;br /&gt;
|-&lt;br /&gt;
| Feedback staleness floor || ~268 ms || Floor of rate-relative horizon&lt;br /&gt;
|-&lt;br /&gt;
| Damping bucket || ~31 ms || Rate-velocity washout cadence&lt;br /&gt;
|-&lt;br /&gt;
| Damping fraction || 1/4 || Pullback per bucket&lt;br /&gt;
|-&lt;br /&gt;
| Estimator fold spacing || 1 ms || Minimum measurement cadence&lt;br /&gt;
|-&lt;br /&gt;
| Estimator window closure || 16 packets drained || Self-scaling window&lt;br /&gt;
|-&lt;br /&gt;
| Estimator window staleness || ~134 ms || Discard across traffic gaps&lt;br /&gt;
|-&lt;br /&gt;
| Estimator idle tolerance || 1/8 of arrivals || Unsaturated-window rejection&lt;br /&gt;
|-&lt;br /&gt;
| Estimator decay || 1/16 per window || Max-filter convergence from below&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 12. References ==&lt;br /&gt;
&lt;br /&gt;
=== 12.1. Source ===&lt;br /&gt;
&lt;br /&gt;
The implementation follows the algorithm above:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/ca/mb-ecn.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mb-ecn.h&amp;lt;/code&amp;gt; &amp;amp;mdash;&lt;br /&gt;
  receiver estimator, pacer and rate control.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/ca/ops.h&amp;lt;/code&amp;gt; &amp;amp;mdash; CA policy interface.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/cap.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;cap.h&amp;lt;/code&amp;gt; &amp;amp;mdash; link-capacity&lt;br /&gt;
  estimator and quarter-log2 codec.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/dt.c&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fa.c&amp;lt;/code&amp;gt; &amp;amp;mdash; per-hop marking&lt;br /&gt;
  and stamping, and the feedback carrying &amp;lt;code&amp;gt;ece&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;cap8&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;src/ipcpd/unicast/psched.c&amp;lt;/code&amp;gt; &amp;amp;mdash; consumes the pacing deadline.&lt;br /&gt;
&lt;br /&gt;
=== 12.2. Literature ===&lt;br /&gt;
&lt;br /&gt;
* K. K. Ramakrishnan, R. Jain, &amp;quot;A Binary Feedback Scheme for Congestion&lt;br /&gt;
  Avoidance in Computer Networks&amp;quot; (DECbit), ACM ToCS, 1990.&lt;br /&gt;
* H. Balakrishnan, H. S. Rahul, S. Seshan, &amp;quot;An Integrated Congestion&lt;br /&gt;
  Management Architecture for Internet Hosts&amp;quot;, SIGCOMM 1999;&lt;br /&gt;
  H. Balakrishnan, S. Seshan, &amp;quot;The Congestion Manager&amp;quot;, RFC 3124, 2001.&lt;br /&gt;
* M. Alizadeh et al., &amp;quot;Data Center TCP (DCTCP)&amp;quot;, SIGCOMM 2010.&lt;br /&gt;
* Y. Zhu et al., &amp;quot;Congestion Control for Large-Scale RDMA Deployments&amp;quot;&lt;br /&gt;
  (DCQCN), SIGCOMM 2015.&lt;br /&gt;
* R. Mittal et al., &amp;quot;TIMELY: RTT-based Congestion Control&amp;quot;, SIGCOMM 2015.&lt;br /&gt;
* Y. Li et al., &amp;quot;HPCC: High Precision Congestion Control&amp;quot;, SIGCOMM 2019.&lt;br /&gt;
* V. Addanki et al., &amp;quot;PowerTCP&amp;quot;, NSDI 2022.&lt;br /&gt;
* K. De Schepper, B. Briscoe (eds.), &amp;quot;Low Latency, Low Loss, and&lt;br /&gt;
  Scalable Throughput (L4S)&amp;quot;, RFC 9330/9331/9332.&lt;br /&gt;
* F. Kelly, &amp;quot;Charging and Rate Control for Elastic Traffic&amp;quot;, 1997;&lt;br /&gt;
  R. Srikant, &amp;quot;The Mathematics of Internet Congestion Control&amp;quot;, 2004.&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Functional_Layering&amp;diff=1931</id>
		<title>Ouroboros Functional Layering</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Functional_Layering&amp;diff=1931"/>
		<updated>2026-07-11T20:39:15Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* Network end-to-end layer / Flow Allocator */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[File: O7s Model_01.png|border|right|thumb|frame|Ouroboros network model - functional layering]]&lt;br /&gt;
&lt;br /&gt;
The Ouroboros model is the result of countless architectural refinements made during the (still ongoing) implementation of the Ouroboros prototype. The layered model [[Ouroboros multicast conjecture|treats broadcast/multicast as distinct from unicast]]. All layers in the model have a well-defined service API, which allows them to appear as black boxes to one another. &lt;br /&gt;
&lt;br /&gt;
The unicast model has 5 layers: the &#039;&#039;&#039;application layer&#039;&#039;&#039;, the &#039;&#039;&#039;application end-to-end layer&#039;&#039;&#039;, the &#039;&#039;&#039;network end-to-end layer&#039;&#039;&#039;, the &#039;&#039;&#039;network forwarding layer&#039;&#039;&#039; and the &#039;&#039;&#039;transmission layer&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
Broadcast is implemented by a &#039;&#039;&#039;network flooding layer&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Applications request network services using the &#039;&#039;&#039;Unicast API&#039;&#039;&#039; and/or &#039;&#039;&#039;Broadcast API&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
For unicast, the application end-to-end layer interfaces with the network-end-to end layer via the &#039;&#039;&#039;Network IPC API&#039;&#039;&#039;, which is invisible to the application itself. The network IPC API forms the demarcation line between &#039;the application&#039; and &#039;the network&#039;. &lt;br /&gt;
&lt;br /&gt;
The Broadcast API interfaces directly with the network flooding layer. Furthermore, a network (both of the unicast and broadcast variant) is seen as a distributed application, so it also inherits the application end-to-end layer, giving rise to a &#039;&#039;&#039;repeating network structure&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
This page provides an overview of this model as it currently stands, and some insights in how it compares to other models such as the TCP/IP Internet, Location/Identifier split and the Recursive InterNetwork Architecture (RINA).&lt;br /&gt;
&lt;br /&gt;
= Unicast model =&lt;br /&gt;
&lt;br /&gt;
[[Image: O7s Model_Unicast.png|border|right|540px|Ouroboros network model - Unicast]]&lt;br /&gt;
&lt;br /&gt;
== Unicast API ==&lt;br /&gt;
&lt;br /&gt;
The [[Unicast API]] provides the interface for an application to create, manage and destroy unicast flows and read and write from and to these flows. The API is network-agnostic and provides application primitives for synchronous and asynchronous Inter-Process Communication. It supports message-based and (byte)stream-based communication.&lt;br /&gt;
&lt;br /&gt;
== Application end-to-end layer ==&lt;br /&gt;
The application end-to-end layer provides the functionality to establish flows and make packet transmission on that flow reliable and secure. Unicast flows are initiated by a client process towards a server process, identified by a  [[Glossary#Name|service name]]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The application end-to-end layer can provide the following operations:&lt;br /&gt;
* Encryption (Symmetric key)&lt;br /&gt;
* Reliability, implemented by the [[Flow_and_Retransmission_Control_Protocol|FRCP protocol]]&lt;br /&gt;
** Fragmentation&lt;br /&gt;
** In order delivery&lt;br /&gt;
** Discarding duplicate packets&lt;br /&gt;
** Automated-Repeat Request (ARQ)&lt;br /&gt;
** Flow control&lt;br /&gt;
* Integrity (hash-based checks such as CRC32)&lt;br /&gt;
* Liveness monitoring&lt;br /&gt;
&lt;br /&gt;
All the above functionality is optional, and if reliability (FRCP) is disabled, we call the service a &#039;&#039;&#039;raw flow&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Establishment of the flow, authentication and symmetric key distribution are implemented using a &#039;&#039;&#039;2-way handshake&#039;&#039;&#039; each. If the MTU allows, the authentication and symmetric key exchanges can be piggybacked onto the flow establishment request/reply in a single combined 2-way exchange, so within a 1 Round-Trip Time. See [[Flow Allocation]] for more details.&lt;br /&gt;
&lt;br /&gt;
The application end-to-end layer uses the network IPC API to interface into the network end-to-end layer below.&lt;br /&gt;
&lt;br /&gt;
== Network end-to-end layer / Flow Allocator ==&lt;br /&gt;
The network end-to-end layer is responsible for creating a &#039;&#039;network flow&#039;&#039; in a suitable Unicast Layer between two Unicast IPCPs (designated the source and destination IPCP) that implements a &#039;&#039;client flow&#039;&#039; (between two &#039;&#039;end-user&#039;&#039; processes, designated the &#039;&#039;client process&#039;&#039; and &#039;&#039;server process&#039;&#039;). The source and destination IPCP reside in the same systems as the respective client and server end-user processes.&lt;br /&gt;
&lt;br /&gt;
We often refer to the Network End-to-End layer as the [[Flow allocator|flow allocator]] after the core component in the IPCP that implements it.&lt;br /&gt;
&lt;br /&gt;
It provides four core functions:&lt;br /&gt;
# Name-to-address resolution: given a service name, find an address for a suitable IPCP that can serve as a destination for the network flow. The &#039;&#039;&#039;directory&#039;&#039;&#039; service holds this mapping for the layer.&lt;br /&gt;
# Flow allocation: create shared state between the source and destination IPCP associated with a flow&lt;br /&gt;
## Multiplexing: generate a local Endpoint Identifier for a flow&lt;br /&gt;
## Map this local Endpoint Identifier to the peer address&lt;br /&gt;
# [[Congestion avoidance]]: Monitor the network flow for congestion and police throughput as needed.&lt;br /&gt;
&lt;br /&gt;
The network-layer flow allocation exchange maps the application-requested QoS to a network traffic class. The application-level request/response is carried over by the network-level request/response handshake to fit the complete flow allocation process (application-level and network-level) within a single round-trip.&lt;br /&gt;
&lt;br /&gt;
The network end-to-end layer provides the interface for the application end-to-end layer on top, so these two layers always go hand-in-hand.&lt;br /&gt;
&lt;br /&gt;
== Network Forwarding layer ==&lt;br /&gt;
&lt;br /&gt;
The network forwarding layer is responsible for forwarding [[Ouroboros Data Transfer Protocol]] packets from the source IPCP to the destination IPCP, based on their addresses and QoS class.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;forwarding function&#039;&#039;&#039; takes the destination address and decides on which flow(s) to forward the packet, usually implemented as a table (forwarding table).&lt;br /&gt;
In order to do this, distance information needs to be available at each IPCP, which we call the routing dissemination function.&lt;br /&gt;
&lt;br /&gt;
== Transmission layer ==&lt;br /&gt;
&lt;br /&gt;
At the bottom we find the &#039;Transmission layer&#039;, which is the abstraction for a point-to-point communications channel whose operation is completely independent to all other components of O7s. This can be the wire protocol over a physical medium (copper wire, wireless broadcast, machine RAM, ...) or a network technology such as Ethernet, IP, UDP, Bluetooth, ... to allow constructions as O7s-over-UDP, O7s-over-Ethernet.&lt;br /&gt;
&lt;br /&gt;
This Transmission layer is best seen as a special case of the network forwarding/flooding layer to build a 0-Layer to stop the recursion. It is coupled to its own specifically tailored network end-to-end layer to interface with the application end-to-end layer above (as these 2 end-to-end layers always go hand-in-hand). This network end-to-end layer at least needs to implement a minimal flow allocator if the tranmssion layer is a dumb link, but when the transmission layer wraps a legacy network technology, it may be beneficial to have all features of a network-end-to-end layer.&lt;br /&gt;
&lt;br /&gt;
= Broadcast model =&lt;br /&gt;
&lt;br /&gt;
[[Image: O7s Model_Broadcast.png|border|right|540px|Ouroboros network model - Broadcast]]&lt;br /&gt;
&lt;br /&gt;
The broadcast model has two main additions to the unicast model, the Broadcast API and the network flooding layer.&lt;br /&gt;
&lt;br /&gt;
== Broadcast API ==&lt;br /&gt;
&lt;br /&gt;
The [[Broadcast API]] provides the interface for an application to join and leave broadcast flows, and read and write from and to such flows. The API is network-agnostic and provides application primitives for synchronous and asynchronous IPC. It supports message-based and (byte)stream-based communication. QoS for Broadcast flows is (inherently) limited when compared to the options available for Unicast Flows. A Broadcast flow maps directly to the concept of a [[Broadcast Layer]] (see below).&lt;br /&gt;
&lt;br /&gt;
== Network Flooding layer ==&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;network flooding layer&#039;&#039; is responsible for flooding packets from the input data transfer flow to all other data transfer flows. This operation is in essence stateless.&lt;br /&gt;
&lt;br /&gt;
The network flooding layer interfaces with an application end-to-end layer to provide the data-transfer flows that make up the links of the Broadcast Layer, which typically form a tree (but a Directed Acyclic Graph is a sufficient condition).&lt;br /&gt;
&lt;br /&gt;
= Layers and recursive networking =&lt;br /&gt;
&lt;br /&gt;
As an application end-to-end layer pairs with a corresponding network application end-to-layer, and the network flooding and network forwarding layers are part of a &#039;&#039;network application&#039;&#039;, it is clear that the Ouroboros Unicast Model gives rise to a &#039;ham-and-cheese sandwich&#039; structure that can be stacked repeatedly. If the network IPC API between the application and network end-to-end layers is a single universal standard, then all the Layers (capital L) in this sandwich can be moved around at will. This is what is called a &#039;&#039;&#039;recursive network&#039;&#039;&#039;. The Ouroboros model hints very strongly at recursion, but does not require it.&lt;br /&gt;
&lt;br /&gt;
== Unicast Layer ==&lt;br /&gt;
&lt;br /&gt;
The Unicast Layer consists of a &#039;&#039;three-layer application&#039;&#039; that contains implementations of a network end-to-end layer and a network forwarding layer (and inherits the application end-to-end layer). This (abstract) application is called a [[Unicast IPCP]].&lt;br /&gt;
The Unicast IPCPs are equals as defined by mechanism (which functionality to implement, etc), but not identical as each mechanism can be implemented by different policies. As an example, the routing functionality can be link-state in one Layer, but path-vector in another Layer.&lt;br /&gt;
Within the same Layer, they are also not identical, but some network-wide policies will need to be the same in all IPCPs in a Layer.&lt;br /&gt;
&lt;br /&gt;
== Broadcast Layer ==&lt;br /&gt;
&lt;br /&gt;
The Broadcast Layer consists of a &#039;&#039;two-layer application&#039;&#039; that contains implementations of a network flooding layer (and inherits the application end-to-end layer). This (abstract) application is called a [[Broadcast IPCP]].&lt;br /&gt;
&lt;br /&gt;
[[Ouroboros Multicast|Multicast]] is not a distinct concept in the O7s model, but rather the combination of 2 processes: &lt;br /&gt;
# Enrolling a Broadcast IPCP in a Broadcast Layer that consists of other Broadcast IPCPs in systems that are home to the set of applications that would be designated as a &#039;multicast group&#039;, and&lt;br /&gt;
# The application then using that Broadcast Layer.&lt;br /&gt;
&lt;br /&gt;
== 0-Layers ==&lt;br /&gt;
&lt;br /&gt;
A Unicast 0-Layer is a Layer consisting of &#039;&#039;two-layer applications&#039;&#039; that implement a - possibly tailored and limited - network end-to-end layer to interface with applications (using the Unicast API) above. Examples of these are IPCPs to run O7s over UDP, Ethernet, RAM, etc.&lt;br /&gt;
&lt;br /&gt;
A Broadcast 0-Layer is a &#039;&#039;two-layer application&#039;&#039; that implements a - possibly tailored and limited - network flooding layer to interface with broadcast applications (using the Broadcast API) above. This is not shown in the model figures, but it is the trivial case of an O7s application over a &#039;legacy&#039; broadcast technology. Examples are IPCPs implementing flooding over RAM, or wrapping Ethernet or IP Broadcast.&lt;br /&gt;
&lt;br /&gt;
= Relation to TCP/IP model =&lt;br /&gt;
&lt;br /&gt;
[[Image: O7s Model_vs_TCP.png|border|right|540px|Ouroboros network model vs TCP/IP model]]&lt;br /&gt;
&lt;br /&gt;
Main Page: [[Ouroboros and TCP/IP]]&lt;br /&gt;
&lt;br /&gt;
This section provides a high level architectural overview of the prime differences between the Ouroboros model and the [https://en.wikipedia.org/wiki/Internet_protocol_suite 5-Layer Internet model] associated with the TCP/IP protocol stack.&lt;br /&gt;
&lt;br /&gt;
Some differences are directly apparent:&lt;br /&gt;
# The &#039;&#039;&#039;Transport Layer&#039;&#039;&#039; is replaced with two end-to-end layers, split between the network and the application,&lt;br /&gt;
# The &#039;&#039;&#039;Network Layer&#039;&#039;&#039; has two counterparts: the network forwarding layer for unicast and the network flooding layer for broadcast,&lt;br /&gt;
# The &#039;&#039;&#039;Data Link Layer&#039;&#039;&#039; is missing altogether,&lt;br /&gt;
# The &#039;&#039;&#039;Physical Layer&#039;&#039;&#039; is now called the transmission layer.&lt;br /&gt;
&lt;br /&gt;
== Splitting the Transport Layer ==&lt;br /&gt;
The key difference is that functionality that is associated with the Transport Layer (the TCP/UDP protocols) is moved into two independent layers. The application end-to-end layer takes over [[Glossary#Automated_Repeat_reQuest|ARQ]] and flow control, while the network end-to-end layer is in charge of multiplexing, name-to-address resolution and congestion control. This split of the Transport Layer is actually quite similar to the sublayering (Link Logical Control /Media Access Control) of the Data Link Layer in the Internet Model. The name-to-address resolution functionality (roughly equivalent to DNS SRV resolve) is also inside the end-to-end network layer instead of an (essentially optional) function that the application needs to perform. More specifically, the O7s network IPC API requires only a &#039;&#039;service name&#039;&#039; so it can resolve addresses internally, whereas the TCP/IP model Transport Layer needs an IP address and port, leaving it up to the application to resolve the address.&lt;br /&gt;
&lt;br /&gt;
== Untangling the Network Layer ==&lt;br /&gt;
&lt;br /&gt;
The network layer functionality is very similar between O7s and the Internet model. The main difference is that broadcast is split off in the O7s model, and that the network forwarding layer does not hold references to the higher-level layers (so, no &#039;Protocol&#039; field).&lt;br /&gt;
&lt;br /&gt;
== No Data Link Layer ==&lt;br /&gt;
&lt;br /&gt;
In the Internet model, the Data Link Layer is split up into two sub-layers, called the Link Logical Control (LLC) and Media Access Control (MAC) layers. When looking at the functionality that is associated with these layers, the O7s application end-to-end layer maps directly to link logical control and the O7s network end-to-end layer maps directly to the MAC layer (name-to-address mapping, multiplexing), so there is no need anymore for this layer to be distinct in the O7s model.&lt;br /&gt;
&lt;br /&gt;
== Transmission layer instead of Physical Layer ==&lt;br /&gt;
&lt;br /&gt;
The final change in the model is to abstract the Physical Layer to include (virtual) legacy networks and therefore a more generic &#039;end of the line&#039; for the model than only a physical medium. The distinct property is that it does not reuse the O7s API and therefore does not interface into the application end-to-end layer. Instead it has functionality that is directly tailored to the specific technology it wraps.&lt;br /&gt;
&lt;br /&gt;
= Relation to Location/Identifier split =&lt;br /&gt;
&lt;br /&gt;
[[Image: O7s Model_vs_LocId.png|border|right|540px|Ouroboros network model vs Loc/Id split]]&lt;br /&gt;
&lt;br /&gt;
Loc/Id split starts from the observation that the application is tightly coupled to network (IP) addresses through the Transport Layer via a certain port. This hampers application mobility, as a change in network (IP) address breaks the TCP connection used by the application. Loc/Id split proposes to semantically break up the network layer address into two separate parts, an &#039;&#039;identifier&#039;&#039; that is location-independent and specifies the &#039;&#039;who&#039;&#039; at the Transport Layer, and a &#039;&#039;locator&#039;&#039; that is location-dependent and specifies the &#039;&#039;where&#039;&#039; at the network layer. An IPv6 address is 128 bits wide, providing ample material to accommodate this split.&lt;br /&gt;
&lt;br /&gt;
Architecturally, this boils down to a name-to-address resolution step - identifier-to-locator - in a &#039;&#039;Sub-Layer&#039;&#039;&amp;lt;ref&amp;gt;[https://doi.org/10.1109/COMST.2017.2728478 Locator/Identifier Split Networking: A Promising Future Internet Architecture]&amp;lt;/ref&amp;gt; between the Transport Layer (bound to the identifier) and the Network Layer (using the locator). Changing locator during mobility therefore does not break the Transport Layer connection. While the proposed solution works, the core tenet of Loc/Id split that &#039;&#039;IP addresses are semantically overloaded&#039;&#039;&amp;lt;ref&amp;gt;[https://www.rfc-editor.org/rfc/rfc4984#section-2.2 rfc4984]&amp;lt;/ref&amp;gt; is nonsense. The actual problem is a missing (service) name and DNS providing only synonyms for addresses; DNS SRV is a step towards naming applications/services (L5), but DNS/SRV still directly maps these names to the L3 address and L4 port; the application directly binds to the L3/L4 identifiers instead of the L5 name. Both pieces of the puzzle are known, they just need to be put together.&lt;br /&gt;
&lt;br /&gt;
Taken as a whole, Loc/Id split applications perform two lookups: domain name to identifier, and then identifier to locator. This indirection is needed for Loc/Id split to stay compatible with the TCP/IP protocol stack, but it is redundant from the perspective of O7s. It is more efficient to bind applications to &#039;&#039;service names&#039;&#039;, and resolve a &#039;&#039;service name&#039;&#039; to an address in a single lookup.&lt;br /&gt;
&lt;br /&gt;
The O7s model splits the Transport Layer (see above), adding a service name-to-address resolution mechanism to the network end-to-end layer. The big functional differences with the Sub-Layer proposed in Loc/id and the layers in O7s are that the network end-to-end layer also takes over congestion control and multiplexing from the Transport Layer, and that the remaining functionality - the application end-to-end layer - is considered part of the application.&lt;br /&gt;
&lt;br /&gt;
= Relation to Recursive InterNetwork Architecure (RINA) =&lt;br /&gt;
&lt;br /&gt;
[[Image: O7s Model_vs_RINA.png||border|right|540px|Ouroboros network model vs RINA model]]&lt;br /&gt;
&lt;br /&gt;
Main Page: [[Ouroboros and RINA]]&lt;br /&gt;
&lt;br /&gt;
The original objective of the Ouroboros prototype was to build a portable user-space implementation of RINA for POSIX systems that could be tailored to embedded devices. As this prototype started off as an implementation of principles outlined by RINA, the Ouroboros model inherits a lot of concepts and terminology from RINA. As such, that&#039;s where the credit for those ideas goes. As the prototype evolved, architectural changes were made that further simplified things.&lt;br /&gt;
&lt;br /&gt;
== Splitting data transfer control from data transfer ==&lt;br /&gt;
&lt;br /&gt;
RINA sees the [[Glossary#IP_fragmentation_problem|IP fragmentation problem]] as evidence that the TCP/IP split was incorrect&amp;lt;ref&amp;gt;[https://ieeexplore.ieee.org/document/6126673 How in the Heck do you lose a layer!?]&amp;lt;/ref&amp;gt;. The solution put forward in RINA is to keep the functionality of TCP and IP in a single logical layer and in a single protocol, called the Error and Flow Control Protocol (EFCP). EFCP is still subdivided in two component protocol (called Data Transfer Protocol (DTP) and Data Transfer Control Protocol (DTCP), but these two protocol &#039;&#039;share a state vector&#039;&#039; and are therefore not fully independent.&lt;br /&gt;
&lt;br /&gt;
During the implementation of EFCP in Ouroboros we found that the functionality in EFCP (and thus TCP + IP) could (and therefore should) be split not in 2, but &#039;&#039;3&#039;&#039; independent layers: the application end-to-end layer, the network end-to-end layer and the network forwarding layer. The IP fragmentation problem is solved by putting the fragmentation bits in the [[Flow and Retransmission Control Protocol]] rather than the [[Ouroboros Data Transfer Protocol|Data Transfer Protocol]] and the Flow Allocator reports the path maximum transmission unit (MTU) for a flow as input for the fragmentation function.&lt;br /&gt;
&lt;br /&gt;
== O7s Layers vs RINA DIFs ==&lt;br /&gt;
&lt;br /&gt;
In RINA, DTP and DTCP are in the same Layer. Since end-user programs do not typically perform packet forwarding, there is therefore a hard distinction between &#039;normal&#039; end-user (distributed) applications and the (distributed) application that is responsible for (reliable) packet transfer. The EFCP protocol provides the necessary functionality for this reliable packet communication between processes, and therefore a process that implements EFCP is called an Inter-Process Communication process (IPCP), a term that originated in Livermore&#039;s LINCS network. The distributed application consisting of such IPCPs is called a Distributed IPC Facility (DIF). In contrast, a regular end-user distributed application, consists of distributed application processes (DAPs) and is called a Distributed Application Facility (DAF).&lt;br /&gt;
&lt;br /&gt;
The 3-layer functional split in O7s raised a question for the location of FRCP: should it be part of the DIF, or part of the Application? It was immediately clear that putting it in the application solved some questions that arise with the RINA architecture.&lt;br /&gt;
# End-user application multi-homing (i.e. flows over different networks) is still a problem in RINA: only IPCPs can efficiently multi-home. In O7s all processes can effectively multi-home.&lt;br /&gt;
# Loss of IPCP state associated with DTCP (sequence numbers, DTCP windows) after crash of an IPCP is not easily recoverable even when the application process survives.&lt;br /&gt;
# Reliable flow allocation over a shim-DIF (needed by the IPCP) required a hack for the IPCP to allocate a flows over &#039;&#039;itself&#039;&#039;.&lt;br /&gt;
# If an IPCP/DIF is needed to provided reliable IPC between two programs, who performs reliable IPC between those programs and the IPCP/DIF? The answer that the OS provides test-and-set is not satisfactory as it requires some (admittedly very reasonable) assumptions outside of the model that are not trivial.&lt;br /&gt;
&lt;br /&gt;
With the application end-to-end layer, the distinction between DIF and DAF is not present in the O7s model. Nevertheless, awaiting more satisfactory terminology O7s still refers to processes making up an O7s Layers as &#039;&#039;IPCPs&#039;&#039;, fully aware of the confusion this may cause.&lt;br /&gt;
&lt;br /&gt;
= Notes on Layering in O7s =&lt;br /&gt;
&lt;br /&gt;
The model as explained above shows Broadcast Layers over Unicast Layers, and Unicast over Unicast Layers. What about the other options?&lt;br /&gt;
&lt;br /&gt;
== Unicast over Broadcast ==&lt;br /&gt;
&lt;br /&gt;
From the perspective of the O7s Layers, the &#039;&#039;general case&#039;&#039; of Unicast over Broadcast -- by which we mean that some &#039;&#039;data packets&#039;&#039; will be flooded to multiple next-hop IPCPs -- brings with it that Unicast IPCPs would have to be able to identify whether arriving data packets are for them or not. This problem inherently has limited scalability and its utility within O7s is as of yet unclear and requires some further study. In absence of compelling reasons to add this, we currently haven&#039;t added this to the general model. This does not imply that a Unicast Layer can not use &#039;&#039;dedicated&#039;&#039; Broadcast Layers to implement certain functionalities - an example is routing dissemination in the network forwarding layer.&lt;br /&gt;
&lt;br /&gt;
Since Ethernet does implement unicast over a broadcast domain, we have a dedicated page discussing how [[Ouroboros and Ethernet|Ethernet maps to Ouroboros]].&lt;br /&gt;
&lt;br /&gt;
== Broadcast over Broadcast ==&lt;br /&gt;
&lt;br /&gt;
The case where one Broadcast Layer makes use of another Broadcast Layer is also technically feasible. Its utility is also still unclear, which is why it&#039;s not currently elaborated on, but allowing this will have little impact on the model.&lt;br /&gt;
&lt;br /&gt;
== Mixed and/or combined implementation ==&lt;br /&gt;
&lt;br /&gt;
Why not merge broadcast and unicast functionality in a single Layer? This is definitely feasible and anyone is free to implement such an IPCP that does both. The net outcome will be a Layer that has a a broadcast network as well as a unicast network at the full scope of that Layer, which may be useful for some particular use cases. However, from a &#039;&#039;model&#039;&#039; perspective, Unicast and Broadcast are always two distinct Layers, and mixing the implementation does not alter this fact.&lt;br /&gt;
&lt;br /&gt;
Similarly, a program can be written that implements multiple IPCP modules internally, instead of starting up a process per &#039;IPCP&#039;.&lt;br /&gt;
&lt;br /&gt;
= References =&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Protocols&amp;diff=1929</id>
		<title>Ouroboros Protocols</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Protocols&amp;diff=1929"/>
		<updated>2026-06-13T08:23:07Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* Flow encryption header */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Under construction}}&lt;br /&gt;
&lt;br /&gt;
The protocols in Ouroboros are designed with strong adherence to the principles of [https://en.wikipedia.org/wiki/Separation_of_concerns separation of concerns] and [https://en.wikipedia.org/wiki/Separation_of_mechanism_and_policy separation of mechanism and policy] in mind to prevent network ossification and protocol ossification from occuring.&lt;br /&gt;
&lt;br /&gt;
There are 5 core protocols in Ouroboros.&lt;br /&gt;
&lt;br /&gt;
== Data Transfer Protocol ==&lt;br /&gt;
Main page: [[Ouroboros Data Transfer Protocol]]&lt;br /&gt;
&lt;br /&gt;
The Data Transfer Protocol resides in the [[Ouroboros Functional Layering|network forwarding layer]]. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
  0                   1                   2                   3&lt;br /&gt;
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                      Destination Address                      +&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |  Time-to-Live |      QoS      |      ECN      |    PADDING    |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                              EID                              +&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                           N + 1 Data                          +&lt;br /&gt;
 .                                                               .&lt;br /&gt;
 .                                                               .&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Flow encryption header ==&lt;br /&gt;
&lt;br /&gt;
Every encrypted packet is a fixed-size cleartext &#039;&#039;&#039;selector&#039;&#039;&#039; header,&lt;br /&gt;
followed by the AEAD ciphertext of the inner FRCP packet, followed by&lt;br /&gt;
the AEAD authentication &#039;&#039;&#039;tag&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    +----------------+------------------------------+--------------+&lt;br /&gt;
    | selector (6 B) |   E(inner FRCP packet)       |  tag (16 B)  |&lt;br /&gt;
    +----------------+------------------------------+--------------+&lt;br /&gt;
     \__ cleartext _/ \______ ciphertext __________/ \__ cleartext _/&lt;br /&gt;
     \________________ authenticated (AAD + AEAD) ________________/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;selector&lt;br /&gt;
:6-octet cleartext key-selection header. It is read &#039;&#039;&#039;before&#039;&#039;&#039; decryption to select the key and reconstruct the nonce, and it is bound into the AEAD as Associated Data.&lt;br /&gt;
;ciphertext&lt;br /&gt;
:AEAD encryption of the inner packet under the selected sub-key and reconstructed nonce.&lt;br /&gt;
;tag&lt;br /&gt;
:AEAD authentication tag, 16 octets (128 bits) for AEAD ciphers; absent for non-AEAD ciphers.&lt;br /&gt;
&lt;br /&gt;
== Flow and Retransmission Control Protocol ==&lt;br /&gt;
Main page: [[Flow and Retransmission Control Protocol]]&lt;br /&gt;
&lt;br /&gt;
The Flow and Retransmission Control Protocol resides in the [[Ouroboros Functional Layering|application end-to-end layer]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |             flags             |              hcs              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            window                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            seqno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            ackno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                     payload (variable) ...&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&lt;br /&gt;
  flags   - feature/type bitmap (see 1.2).&lt;br /&gt;
  hcs     - CRC-16-CCITT-FALSE Header Check Sequence (HCS) over&lt;br /&gt;
            flags + window + seqno + ackno (+ stream extension when&lt;br /&gt;
            present); the two octets of the hcs field itself are&lt;br /&gt;
            omitted from the CRC input.  Verified on receive before&lt;br /&gt;
            any flag-driven dispatch.&lt;br /&gt;
  window  - receiver-advertised right window edge (valid iff FC).&lt;br /&gt;
  seqno   - per-flow sequence number.&lt;br /&gt;
  ackno   - cumulative Acknowledgement (ACK) (valid iff ACK).&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Flow Allocation Protocol ==&lt;br /&gt;
Main page: [[Ouroboros Flow Allocation Protocol]]&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 *  0                   1                   2                   3&lt;br /&gt;
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ---+&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                      id (128 bits)                            +    |&lt;br /&gt;
 * |                  Unique flow allocation ID                    |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                     timestamp (64 bits)                       +    |&lt;br /&gt;
 * |                UTC nanoseconds since epoch                    |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |         crt_len (16 bits)     |                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                  certificate (variable)                       +    |&lt;br /&gt;
 * |               X.509 certificate, DER encoded                  |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |F|R|     kex_len (14 bits)     |                               |    | Signed&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +    |&lt;br /&gt;
 * |                                                               |    | Region&lt;br /&gt;
 * +                 kex_data (variable)                           +    |&lt;br /&gt;
 * |      public key (DER/raw) or ciphertext (KEM)                 |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |       cipher_len (16 bits)    |                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                    cipher (variable)                          +    |&lt;br /&gt;
 * |               symmetric cipher name (UTF-8)                   |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |        data_len (16 bits)     |                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                    data (variable)                            +    |&lt;br /&gt;
 * |              Piggybacked application data                     |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ---+&lt;br /&gt;
 * |        sig_len (16 bits)      |                               |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +&lt;br /&gt;
 * |                                                               |&lt;br /&gt;
 * +                     signature (variable)                      +&lt;br /&gt;
 * |                  DSA signature over signed region             |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 *&lt;br /&gt;
 * kex_len field bit layout:&lt;br /&gt;
 *   F (bit 15): Format - 0 = X.509 DER, 1 = Raw/Hybrid&lt;br /&gt;
 *   R (bit 14): Role   - 0 = Server encaps, 1 = Client encaps&lt;br /&gt;
 *               (R is ignored for non-KEM algorithms)&lt;br /&gt;
 *   Bits 0-13:  Length (0-16383 bytes)&lt;br /&gt;
 */&lt;br /&gt;
The Flow Allocation Protocol resides in the [[Ouroboros Functional Layering|network end-to-end layer]].&lt;br /&gt;
&lt;br /&gt;
== Connection Establishment Protocol ==&lt;br /&gt;
Main page: [[Ouroboros Connection Establishment Protocol]]&lt;br /&gt;
&lt;br /&gt;
== Enrollment Protocol ==&lt;br /&gt;
Main page: [[Ouroboros Enrolment Protocol]]&lt;br /&gt;
&lt;br /&gt;
The Enrollment protocol is best seen as one of the application protocols for IPCPs.&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Protocols&amp;diff=1928</id>
		<title>Ouroboros Protocols</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Protocols&amp;diff=1928"/>
		<updated>2026-06-13T08:21:50Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* Flow encryption header */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Under construction}}&lt;br /&gt;
&lt;br /&gt;
The protocols in Ouroboros are designed with strong adherence to the principles of [https://en.wikipedia.org/wiki/Separation_of_concerns separation of concerns] and [https://en.wikipedia.org/wiki/Separation_of_mechanism_and_policy separation of mechanism and policy] in mind to prevent network ossification and protocol ossification from occuring.&lt;br /&gt;
&lt;br /&gt;
There are 5 core protocols in Ouroboros.&lt;br /&gt;
&lt;br /&gt;
== Data Transfer Protocol ==&lt;br /&gt;
Main page: [[Ouroboros Data Transfer Protocol]]&lt;br /&gt;
&lt;br /&gt;
The Data Transfer Protocol resides in the [[Ouroboros Functional Layering|network forwarding layer]]. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
  0                   1                   2                   3&lt;br /&gt;
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                      Destination Address                      +&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |  Time-to-Live |      QoS      |      ECN      |    PADDING    |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                              EID                              +&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                           N + 1 Data                          +&lt;br /&gt;
 .                                                               .&lt;br /&gt;
 .                                                               .&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Flow encryption header ==&lt;br /&gt;
&lt;br /&gt;
Every encrypted packet is a fixed-size cleartext &#039;&#039;&#039;selector&#039;&#039;&#039; header,&lt;br /&gt;
followed by the AEAD ciphertext of the inner FRCP packet, followed by&lt;br /&gt;
the AEAD authentication &#039;&#039;&#039;tag&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    +----------------+------------------------------+--------------+&lt;br /&gt;
    | selector (6 B) |   E(inner FRCP packet)       |  tag (16 B)  |&lt;br /&gt;
    +----------------+------------------------------+--------------+&lt;br /&gt;
     \__ cleartext _/ \______ ciphertext __________/ \__ cleartext _/&lt;br /&gt;
     \________________ authenticated (AAD + AEAD) ________________/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;selector&lt;br /&gt;
:6-octet cleartext key-selection header (see [[#1.2. Selector|Section 1.2]]). It is read &#039;&#039;&#039;before&#039;&#039;&#039; decryption to select the key and reconstruct the nonce, and it is bound into the AEAD as Associated Data (see [[#4. AEAD and AAD binding|Section 4]]).&lt;br /&gt;
;ciphertext&lt;br /&gt;
:AEAD encryption of the inner packet under the selected sub-key and reconstructed nonce. The supported ciphers are stream-mode, so the ciphertext length equals the plaintext length.&lt;br /&gt;
;tag&lt;br /&gt;
:AEAD authentication tag, 16 octets (128 bits) for AEAD ciphers; absent for non-AEAD ciphers (see [[#4. AEAD and AAD binding|Section 4]]).&lt;br /&gt;
&lt;br /&gt;
== Flow and Retransmission Control Protocol ==&lt;br /&gt;
Main page: [[Flow and Retransmission Control Protocol]]&lt;br /&gt;
&lt;br /&gt;
The Flow and Retransmission Control Protocol resides in the [[Ouroboros Functional Layering|application end-to-end layer]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |             flags             |              hcs              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            window                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            seqno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            ackno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                     payload (variable) ...&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&lt;br /&gt;
  flags   - feature/type bitmap (see 1.2).&lt;br /&gt;
  hcs     - CRC-16-CCITT-FALSE Header Check Sequence (HCS) over&lt;br /&gt;
            flags + window + seqno + ackno (+ stream extension when&lt;br /&gt;
            present); the two octets of the hcs field itself are&lt;br /&gt;
            omitted from the CRC input.  Verified on receive before&lt;br /&gt;
            any flag-driven dispatch.&lt;br /&gt;
  window  - receiver-advertised right window edge (valid iff FC).&lt;br /&gt;
  seqno   - per-flow sequence number.&lt;br /&gt;
  ackno   - cumulative Acknowledgement (ACK) (valid iff ACK).&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Flow Allocation Protocol ==&lt;br /&gt;
Main page: [[Ouroboros Flow Allocation Protocol]]&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 *  0                   1                   2                   3&lt;br /&gt;
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ---+&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                      id (128 bits)                            +    |&lt;br /&gt;
 * |                  Unique flow allocation ID                    |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                     timestamp (64 bits)                       +    |&lt;br /&gt;
 * |                UTC nanoseconds since epoch                    |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |         crt_len (16 bits)     |                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                  certificate (variable)                       +    |&lt;br /&gt;
 * |               X.509 certificate, DER encoded                  |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |F|R|     kex_len (14 bits)     |                               |    | Signed&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +    |&lt;br /&gt;
 * |                                                               |    | Region&lt;br /&gt;
 * +                 kex_data (variable)                           +    |&lt;br /&gt;
 * |      public key (DER/raw) or ciphertext (KEM)                 |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |       cipher_len (16 bits)    |                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                    cipher (variable)                          +    |&lt;br /&gt;
 * |               symmetric cipher name (UTF-8)                   |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |        data_len (16 bits)     |                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                    data (variable)                            +    |&lt;br /&gt;
 * |              Piggybacked application data                     |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ---+&lt;br /&gt;
 * |        sig_len (16 bits)      |                               |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +&lt;br /&gt;
 * |                                                               |&lt;br /&gt;
 * +                     signature (variable)                      +&lt;br /&gt;
 * |                  DSA signature over signed region             |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 *&lt;br /&gt;
 * kex_len field bit layout:&lt;br /&gt;
 *   F (bit 15): Format - 0 = X.509 DER, 1 = Raw/Hybrid&lt;br /&gt;
 *   R (bit 14): Role   - 0 = Server encaps, 1 = Client encaps&lt;br /&gt;
 *               (R is ignored for non-KEM algorithms)&lt;br /&gt;
 *   Bits 0-13:  Length (0-16383 bytes)&lt;br /&gt;
 */&lt;br /&gt;
The Flow Allocation Protocol resides in the [[Ouroboros Functional Layering|network end-to-end layer]].&lt;br /&gt;
&lt;br /&gt;
== Connection Establishment Protocol ==&lt;br /&gt;
Main page: [[Ouroboros Connection Establishment Protocol]]&lt;br /&gt;
&lt;br /&gt;
== Enrollment Protocol ==&lt;br /&gt;
Main page: [[Ouroboros Enrolment Protocol]]&lt;br /&gt;
&lt;br /&gt;
The Enrollment protocol is best seen as one of the application protocols for IPCPs.&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Protocols&amp;diff=1927</id>
		<title>Ouroboros Protocols</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Protocols&amp;diff=1927"/>
		<updated>2026-06-13T08:16:10Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* Data Transfer Protocol */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Under construction}}&lt;br /&gt;
&lt;br /&gt;
The protocols in Ouroboros are designed with strong adherence to the principles of [https://en.wikipedia.org/wiki/Separation_of_concerns separation of concerns] and [https://en.wikipedia.org/wiki/Separation_of_mechanism_and_policy separation of mechanism and policy] in mind to prevent network ossification and protocol ossification from occuring.&lt;br /&gt;
&lt;br /&gt;
There are 5 core protocols in Ouroboros.&lt;br /&gt;
&lt;br /&gt;
== Data Transfer Protocol ==&lt;br /&gt;
Main page: [[Ouroboros Data Transfer Protocol]]&lt;br /&gt;
&lt;br /&gt;
The Data Transfer Protocol resides in the [[Ouroboros Functional Layering|network forwarding layer]]. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
  0                   1                   2                   3&lt;br /&gt;
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                      Destination Address                      +&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |  Time-to-Live |      QoS      |      ECN      |    PADDING    |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                              EID                              +&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                           N + 1 Data                          +&lt;br /&gt;
 .                                                               .&lt;br /&gt;
 .                                                               .&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Flow encryption header ==&lt;br /&gt;
&lt;br /&gt;
== Flow and Retransmission Control Protocol ==&lt;br /&gt;
Main page: [[Flow and Retransmission Control Protocol]]&lt;br /&gt;
&lt;br /&gt;
The Flow and Retransmission Control Protocol resides in the [[Ouroboros Functional Layering|application end-to-end layer]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |             flags             |              hcs              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            window                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            seqno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            ackno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                     payload (variable) ...&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&lt;br /&gt;
  flags   - feature/type bitmap (see 1.2).&lt;br /&gt;
  hcs     - CRC-16-CCITT-FALSE Header Check Sequence (HCS) over&lt;br /&gt;
            flags + window + seqno + ackno (+ stream extension when&lt;br /&gt;
            present); the two octets of the hcs field itself are&lt;br /&gt;
            omitted from the CRC input.  Verified on receive before&lt;br /&gt;
            any flag-driven dispatch.&lt;br /&gt;
  window  - receiver-advertised right window edge (valid iff FC).&lt;br /&gt;
  seqno   - per-flow sequence number.&lt;br /&gt;
  ackno   - cumulative Acknowledgement (ACK) (valid iff ACK).&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Flow Allocation Protocol ==&lt;br /&gt;
Main page: [[Ouroboros Flow Allocation Protocol]]&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 *  0                   1                   2                   3&lt;br /&gt;
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ---+&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                      id (128 bits)                            +    |&lt;br /&gt;
 * |                  Unique flow allocation ID                    |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                     timestamp (64 bits)                       +    |&lt;br /&gt;
 * |                UTC nanoseconds since epoch                    |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |         crt_len (16 bits)     |                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                  certificate (variable)                       +    |&lt;br /&gt;
 * |               X.509 certificate, DER encoded                  |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |F|R|     kex_len (14 bits)     |                               |    | Signed&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +    |&lt;br /&gt;
 * |                                                               |    | Region&lt;br /&gt;
 * +                 kex_data (variable)                           +    |&lt;br /&gt;
 * |      public key (DER/raw) or ciphertext (KEM)                 |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |       cipher_len (16 bits)    |                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                    cipher (variable)                          +    |&lt;br /&gt;
 * |               symmetric cipher name (UTF-8)                   |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |        data_len (16 bits)     |                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                    data (variable)                            +    |&lt;br /&gt;
 * |              Piggybacked application data                     |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ---+&lt;br /&gt;
 * |        sig_len (16 bits)      |                               |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +&lt;br /&gt;
 * |                                                               |&lt;br /&gt;
 * +                     signature (variable)                      +&lt;br /&gt;
 * |                  DSA signature over signed region             |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 *&lt;br /&gt;
 * kex_len field bit layout:&lt;br /&gt;
 *   F (bit 15): Format - 0 = X.509 DER, 1 = Raw/Hybrid&lt;br /&gt;
 *   R (bit 14): Role   - 0 = Server encaps, 1 = Client encaps&lt;br /&gt;
 *               (R is ignored for non-KEM algorithms)&lt;br /&gt;
 *   Bits 0-13:  Length (0-16383 bytes)&lt;br /&gt;
 */&lt;br /&gt;
The Flow Allocation Protocol resides in the [[Ouroboros Functional Layering|network end-to-end layer]].&lt;br /&gt;
&lt;br /&gt;
== Connection Establishment Protocol ==&lt;br /&gt;
Main page: [[Ouroboros Connection Establishment Protocol]]&lt;br /&gt;
&lt;br /&gt;
== Enrollment Protocol ==&lt;br /&gt;
Main page: [[Ouroboros Enrolment Protocol]]&lt;br /&gt;
&lt;br /&gt;
The Enrollment protocol is best seen as one of the application protocols for IPCPs.&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1925</id>
		<title>Flow and Retransmission Control Protocol</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1925"/>
		<updated>2026-05-18T16:11:20Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 8. Retransmission */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:FRCP - Flow and Retransmission Control Protocol}}&lt;br /&gt;
&lt;br /&gt;
FRCP runs end-to-end between two peers over a flow.  It delivers&lt;br /&gt;
reliability, in-order delivery, flow control, and liveness.&lt;br /&gt;
Congestion Control (CC) is not in FRCP - that lives in the IPC&lt;br /&gt;
Process (IPCP) Congestion Avoidance (CA) policies, orthogonal to&lt;br /&gt;
FRCP.  Flow allocation, naming, and IPCP lifecycle are handled by&lt;br /&gt;
the IPC Resource Manager daemon (IRMd).&lt;br /&gt;
&lt;br /&gt;
FRCT (Flow and Retransmission Control Task) is the libouroboros&lt;br /&gt;
implementation of FRCP; the task lives in &amp;lt;code&amp;gt;src/lib/frct.c&amp;lt;/code&amp;gt;.  The&lt;br /&gt;
remainder of this document describes the FRCP wire protocol and the&lt;br /&gt;
behaviour FRCT realises.  Code symbols retain the &amp;lt;code&amp;gt;FRCT_&amp;lt;/code&amp;gt; prefix&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, ...) because they belong to the implementing&lt;br /&gt;
task; this document references them verbatim.&lt;br /&gt;
&lt;br /&gt;
The keywords &amp;quot;MUST&amp;quot;, &amp;quot;MUST NOT&amp;quot;, &amp;quot;REQUIRED&amp;quot;, &amp;quot;SHALL&amp;quot;, &amp;quot;SHALL NOT&amp;quot;,&lt;br /&gt;
&amp;quot;SHOULD&amp;quot;, &amp;quot;SHOULD NOT&amp;quot;, &amp;quot;RECOMMENDED&amp;quot;, &amp;quot;MAY&amp;quot;, and &amp;quot;OPTIONAL&amp;quot; in&lt;br /&gt;
this document are to be interpreted as described in &amp;lt;code&amp;gt;BCP&amp;lt;/code&amp;gt; 14 (Best&lt;br /&gt;
Current Practice; RFC 2119, RFC 8174) when, and only when, they&lt;br /&gt;
appear in all capitals.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:Unsigned 32-bit / 8-bit integers (kernel-C style).&lt;br /&gt;
;&amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:Nanoseconds.&lt;br /&gt;
&lt;br /&gt;
Modular sequence-number comparators (32-bit, modulo 2^32):&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;before(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;(int32_t)(a - b) &amp;amp;lt; 0&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;after(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;before(b, a)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Used throughout for &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; ordering checks.&lt;br /&gt;
&lt;br /&gt;
Round-Trip Time (RTT) abbreviations used throughout:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;&lt;br /&gt;
:Smoothed RTT estimate (RFC 6298).&lt;br /&gt;
;&amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt;&lt;br /&gt;
:Mean deviation of RTT (Linux variance estimator).&lt;br /&gt;
;&amp;lt;code&amp;gt;EWMA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Exponentially Weighted Moving Average.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTO&amp;lt;/code&amp;gt;&lt;br /&gt;
:Retransmission Timeout, &amp;lt;code&amp;gt;max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Timer-bound symbols &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer, ACK delay) and &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer,&lt;br /&gt;
retransmission window) are defined in [[#8. Retransmission|Section 8]]; &amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; (Maximum&lt;br /&gt;
Packet Lifetime) is introduced in [[#2.1. Per-flow state|Section 2.1]] (the &amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; field)&lt;br /&gt;
with heritage in [[#15. Heritage and adopted techniques|Section 15]].&lt;br /&gt;
&lt;br /&gt;
Wire-format diagrams follow the IETF convention: bit 0 is the&lt;br /&gt;
leftmost (most significant) bit and fields are in network byte&lt;br /&gt;
order unless stated otherwise.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1. Wire format ==&lt;br /&gt;
&lt;br /&gt;
=== 1.1. PCI header ===&lt;br /&gt;
&lt;br /&gt;
Fixed 16-octet base Protocol-Control Information (PCI) header&lt;br /&gt;
prefixed to every FRCP packet (RFC convention: bit 0 leftmost,&lt;br /&gt;
most-significant bit first).  All multi-byte fields are in network byte order. DATA packets on&lt;br /&gt;
stream-mode flows carry an additional 8-octet extension (see&lt;br /&gt;
[[#1.5. Stream PCI extension|Section 1.5]]); SACK and RTTP carry their own payloads after the&lt;br /&gt;
base PCI.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |             flags             |              hcs              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            window                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            seqno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            ackno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                     payload (variable) ...&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt;&lt;br /&gt;
:feature/type bitmap (see [[#1.2. Flag bits|Section 1.2]]).&lt;br /&gt;
;&amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;&lt;br /&gt;
:CRC-16-CCITT-FALSE Header Check Sequence (HCS) over &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; (+ stream extension when present); the two octets of the &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt; field itself are omitted from the CRC input.  Verified on receive before any flag-driven dispatch.&lt;br /&gt;
;&amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt;&lt;br /&gt;
:receiver-advertised right window edge (valid iff FC).&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-flow sequence number.&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;&lt;br /&gt;
:cumulative Acknowledgement (ACK) (valid iff ACK).&lt;br /&gt;
&lt;br /&gt;
A single packet can simultaneously carry DATA + ACK + FC (Flow&lt;br /&gt;
Control) + RXM (Retransmission) by ORing flag bits; the PCI&lt;br /&gt;
multiplexes control on the same wire frame in the spirit of SCTP&lt;br /&gt;
chunk bundling (RFC 9260 sec. 6.10) and QUIC frame multiplexing&lt;br /&gt;
(RFC 9000 sec. 12.4).  DATA-bearing packets carry the caller&#039;s&lt;br /&gt;
payload after the PCI; SACK (Selective Acknowledgement) and RTTP&lt;br /&gt;
(Round-Trip Time Probe) carry their own typed payloads after the&lt;br /&gt;
PCI.&lt;br /&gt;
&lt;br /&gt;
Optional framing (per-flow, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]]).  On the wire, the&lt;br /&gt;
order from inside out is:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Layer !! Scope&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| The FRCP packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body + CRC-32 ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| CRC-32 covers the body only (PCI is in HCS); appended iff &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; on DATA, or on every SACK packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ AEAD-wrap of above ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| Iff Authenticated Encryption with Associated Data (AEAD) is enabled.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* HCS in the PCI covers the header fields on every packet and is verified before any flag-driven dispatch.&lt;br /&gt;
* The CRC-32 trailer (IEEE 802.3 / zlib reflected polynomial &amp;lt;code&amp;gt;0xEDB88320&amp;lt;/code&amp;gt;, init &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;, xor-out &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;) covers the body on DATA when &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; and on every SACK packet. The PCI is not under the CRC (Cyclic Redundancy Check) because the HCS already protects it. It is appended before AEAD encryption and therefore rides inside the AEAD wrap when both are active; the AEAD tag (~2^-128 forgery probability) dominates the CRC (~2^-32) for integrity in that mode but the CRC trailer is currently retained.&lt;br /&gt;
* When encryption is enabled, the entire (possibly-CRC&#039;d) FRCP packet is wrapped with AEAD inside the shared-memory packet buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt;); the packet grows by the AEAD overhead, namely a leading nonce / Initialization Vector (IV) of &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_ivsz&amp;lt;/code&amp;gt;) and a trailing authentication tag of &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_tagsz&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Both CRC and AEAD are layered around the FRCP wire format and are not visible to the FRCP machinery itself.&lt;br /&gt;
&lt;br /&gt;
=== 1.2. Flag bits ===&lt;br /&gt;
&lt;br /&gt;
Flag bits are numbered most-significant-bit first to match the wire&lt;br /&gt;
diagram (bit numbering per [[#1.1. PCI header|Section 1.1]]; bit 0 is the MSB of the&lt;br /&gt;
16-bit &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; field and lands at wire-position 0 in network byte&lt;br /&gt;
order).  Bits 13..15 are reserved and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Bit !! Mask !! Name !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| 0 || &amp;lt;code&amp;gt;0x8000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt; || Carries caller payload&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &amp;lt;code&amp;gt;0x4000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DRF&amp;lt;/code&amp;gt; || Data Run Flag: start of a fresh run&lt;br /&gt;
|-&lt;br /&gt;
| 2 || &amp;lt;code&amp;gt;0x2000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt; || Acknowledgement: &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field valid&lt;br /&gt;
|-&lt;br /&gt;
| 3 || &amp;lt;code&amp;gt;0x1000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt; || Negative ACK; &amp;lt;code&amp;gt;seqno = arrival_seqno-1&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || &amp;lt;code&amp;gt;0x0800&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt; || Flow Control: &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; field valid (&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || &amp;lt;code&amp;gt;0x0400&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt; || Rendezvous probe (window-closed)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || &amp;lt;code&amp;gt;0x0200&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; || First Fragment (role bit 0; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || &amp;lt;code&amp;gt;0x0100&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; || Last Fragment (role bit 1; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 8 || &amp;lt;code&amp;gt;0x0080&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RXM&amp;lt;/code&amp;gt; || Retransmission&lt;br /&gt;
|-&lt;br /&gt;
| 9 || &amp;lt;code&amp;gt;0x0040&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt; || Selective ACK block list in payload&lt;br /&gt;
|-&lt;br /&gt;
| 10 || &amp;lt;code&amp;gt;0x0020&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt; || RTT Probe / echo (payload follows)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || &amp;lt;code&amp;gt;0x0010&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt; || Keepalive&lt;br /&gt;
|-&lt;br /&gt;
| 12 || &amp;lt;code&amp;gt;0x0008&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt; || End-of-stream marker (stream mode)&lt;br /&gt;
|-&lt;br /&gt;
| 13-15 || -- || -- || Reserved (MUST be zero)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair encodes the fragment role of a DATA-bearing&lt;br /&gt;
Service Data Unit (SDU), SCTP-style begin/end flags (RFC 9260&lt;br /&gt;
sec. 3.3.1):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FFGM !! LFGM !! Role&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || Sole / un-fragmented SDU (begin AND end)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || First fragment of a multi-fragment SDU&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Middle fragment&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Last fragment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Each fragment is carried in its own FRCP packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;;&lt;br /&gt;
FRTX (the FRCT Retransmission service mode, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]])&lt;br /&gt;
recovers individual fragments via the normal Retransmission Timeout&lt;br /&gt;
(RTO) / SACK / Recent Acknowledgement (RACK, RFC 8985) path.  The&lt;br /&gt;
receiver reassembles the SDU at consume time once the contiguous&lt;br /&gt;
[FIRST .. LAST] run has fully arrived.  On non-DATA packets the role&lt;br /&gt;
bits are unused and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
In stream mode (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;, see [[#16. Stream-mode flows|Section 16]]) there are&lt;br /&gt;
no SDU boundaries to encode, so &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST&lt;br /&gt;
be transmitted as zero.  End-of-stream uses a dedicated bit (&amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;,&lt;br /&gt;
bit 12) carried on a 0-byte DATA packet, emitted at write-half close&lt;br /&gt;
(&amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;FLOWFRDONLY&amp;lt;/code&amp;gt;), during linger drain, and at &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;;&lt;br /&gt;
emission is idempotent (first call wins).  After contiguous delivery&lt;br /&gt;
of the FIN-bearing slot, the receiver latches &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt; at the FIN&#039;s&lt;br /&gt;
start offset; &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns 0 (end-of-file, &amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) once buffered&lt;br /&gt;
bytes have been drained up to &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt;.  Per-byte position is&lt;br /&gt;
carried by the [start, end) extension ([[#1.5. Stream PCI extension|Section 1.5]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.3. SACK payload ===&lt;br /&gt;
&lt;br /&gt;
A SACK packet has the &amp;lt;code&amp;gt;FRCT_ACK | FRCT_FC | FRCT_SACK&amp;lt;/code&amp;gt; flag bits set&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI, the&lt;br /&gt;
payload is a 2-octet block count (network byte order), 2 octets of&lt;br /&gt;
padding to 4-byte align the block list, then &amp;lt;code&amp;gt;n_blocks&amp;lt;/code&amp;gt; pairs of&lt;br /&gt;
32-bit start/end seqnos describing &#039;&#039;present&#039;&#039; (received) ranges&lt;br /&gt;
above the cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |           n_blocks            |        padding (2 octets)     |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[0]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            end[0]                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[1]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
                       ... n_blocks pairs total ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;n_blocks &amp;amp;lt;= SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; (2048).  The per-flow effective cap is&lt;br /&gt;
further bounded by &amp;lt;code&amp;gt;(frag_mtu - PCI - 4) / 8&amp;lt;/code&amp;gt; blocks per packet; SACK&lt;br /&gt;
packets carry no stream extension, so PCI here is the 16-octet base&lt;br /&gt;
header even on stream-mode flows.&lt;br /&gt;
&lt;br /&gt;
Wire invariant: every block produced by the receiver, except an&lt;br /&gt;
optional leading Duplicate SACK (D-SACK) block as described below,&lt;br /&gt;
describes a range strictly above the cumulative ACK carried in the&lt;br /&gt;
PCI &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field (&amp;lt;code&amp;gt;after(start[i], ackno)&amp;lt;/code&amp;gt;).  This makes the D-SACK&lt;br /&gt;
convention below unambiguous; the receiver-side builder MUST&lt;br /&gt;
preserve it.&lt;br /&gt;
&lt;br /&gt;
Duplicate SACK (D-SACK, RFC 2883) is signalled in-band: no flag&lt;br /&gt;
bit, no extra framing.  Modular seqno arithmetic uses the&lt;br /&gt;
&amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators defined in the Notation block.&lt;br /&gt;
&lt;br /&gt;
Encoding.  When a duplicate is observed the receiver arms a&lt;br /&gt;
single-slot pending report (&amp;lt;code&amp;gt;dsack_seqno&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;dsack_valid&amp;lt;/code&amp;gt;,&lt;br /&gt;
latest-wins across multiple arms before the next emit).  On the&lt;br /&gt;
next outbound SACK the receiver prepends &amp;lt;code&amp;gt;block[0] = [dsack_seqno,&lt;br /&gt;
dsack_seqno + 1)&amp;lt;/code&amp;gt; - always a one-&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; range - and clears the&lt;br /&gt;
flag.  The three arm sites are listed in [[#10. Cumulative + selective ACK|Section 10]]; case-1 sites&lt;br /&gt;
yield &amp;lt;code&amp;gt;dsack_seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt; (the next &amp;lt;code&amp;gt;pci.ackno&amp;lt;/code&amp;gt;), and the&lt;br /&gt;
case-2 site (&amp;lt;code&amp;gt;rq_accept&amp;lt;/code&amp;gt; conflict) yields &amp;lt;code&amp;gt;dsack_seqno&amp;lt;/code&amp;gt; in&lt;br /&gt;
&amp;lt;code&amp;gt;[rcv_cr.lwe, rcv_cr.rwe)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Detection.  The sender classifies &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; by its relation to&lt;br /&gt;
&amp;lt;code&amp;gt;pci.ackno&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
;case 1 (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;before(blocks[0].start, pci.ackno)&amp;lt;/code&amp;gt; AND &amp;lt;code&amp;gt;pci.ackno - blocks[0].start &amp;amp;lt;= MAX_DSACK_LAG&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;== RQ_SIZE&amp;lt;/code&amp;gt;).  The lag bound rejects stale or spoofed reports beyond one receive window.&lt;br /&gt;
;case 2 (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;blocks[0]&amp;lt;/code&amp;gt; is a sub-range (with at least one endpoint differing) of some &amp;lt;code&amp;gt;blocks[i&amp;amp;gt;0]&amp;lt;/code&amp;gt; - i.e. the same packet&#039;s remaining SACK blocks already describe the duplicated &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; as received.&lt;br /&gt;
&lt;br /&gt;
On detect, the sender:&lt;br /&gt;
&lt;br /&gt;
* bumps &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; by 1, capped at &amp;lt;code&amp;gt;REO_WND_MULT_MAX&amp;lt;/code&amp;gt; (= 20), per RFC 8985 sec. 6.2 step 4;&lt;br /&gt;
* snapshots &amp;lt;code&amp;gt;dsack_lwe_snap = snd_cr.lwe&amp;lt;/code&amp;gt;, resetting the 16-cum-ACK halving counter so the multiplier doesn&#039;t decay while D-SACK evidence is still arriving;&lt;br /&gt;
* excludes &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; from the gap-marking loop (&amp;lt;code&amp;gt;n_real = n - 1&amp;lt;/code&amp;gt;), so a D-SACK alone never enters NewReno-careful recovery (see [[#8. Retransmission|Section 8]]); only non-D-SACK blocks count as gaps.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; halving cadence (once per 16 cumulatively-ACK&#039;d&lt;br /&gt;
seqnos since the most-recent D-SACK arrival or halve event) and&lt;br /&gt;
the reset-to-1 on a HoL RTO fire are both per the same RFC 8985&lt;br /&gt;
clause.  The clamp-and-skip path in the regular SACK-mark loop is&lt;br /&gt;
incidentally idempotent on any leftover case-1 or case-2 block&lt;br /&gt;
(&amp;lt;code&amp;gt;start &amp;amp;lt; snd_cr.lwe&amp;lt;/code&amp;gt; clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; and the inner loop&lt;br /&gt;
skips &amp;lt;code&amp;gt;k == snd_cr.lwe&amp;lt;/code&amp;gt;; case-2 re-NULLs slots already marked&lt;br /&gt;
received by later blocks), so block[0] is harmless even when fed&lt;br /&gt;
to the loop.&lt;br /&gt;
&lt;br /&gt;
=== 1.4. RTTP payload ===&lt;br /&gt;
&lt;br /&gt;
An RTTP (Round-Trip Time Probe) packet has only the &amp;lt;code&amp;gt;FRCT_RTTP&amp;lt;/code&amp;gt; flag&lt;br /&gt;
set (bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI,&lt;br /&gt;
the payload is 24 octets (packed):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          probe_id                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          echo_id                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +                  nonce (16 octets, echoed verbatim)           +&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:sender counter, 0 on reply, 0 reserved.&lt;br /&gt;
;&amp;lt;code&amp;gt;echo_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:peer&#039;s &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;, 0 on outbound probe.&lt;br /&gt;
;&amp;lt;code&amp;gt;nonce&amp;lt;/code&amp;gt;&lt;br /&gt;
:random, echoed unmodified, memcmp&#039;d to defeat spoof.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.5. Stream PCI extension ===&lt;br /&gt;
&lt;br /&gt;
A stream-mode flow (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;) carries an extra&lt;br /&gt;
8-octet extension after the 16-octet base PCI on every DATA packet&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            start                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                             end                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset of the first payload byte in the stream.&lt;br /&gt;
;&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset one past the last payload byte; &amp;lt;code&amp;gt;end - start&amp;lt;/code&amp;gt; equals the on-wire payload length.&lt;br /&gt;
&lt;br /&gt;
Total stream-mode PCI for DATA packets is 24 octets (16 base + 8&lt;br /&gt;
extension); control packets (SACK, RTTP, bare ACK, KA, etc.) retain&lt;br /&gt;
the 16-octet base PCI.  Stream mode MUST be negotiated at flow&lt;br /&gt;
allocation; the extension is present iff stream mode is in use,&lt;br /&gt;
never on a per-packet basis.  Both peers MUST treat &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; as&lt;br /&gt;
monotonic 32-bit byte offsets; when a slot reaches the head of the&lt;br /&gt;
contiguous run with &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; not equal to the prior packet&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; the&lt;br /&gt;
slot is silently dropped at delivery time ([[#16. Stream-mode flows|Section 16]]) rather&lt;br /&gt;
than rejected at stash.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC STREAM-frame reassembly model (RFC 9000 sec. 19.8):&lt;br /&gt;
each packet carries its packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (this PCI&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; field) and a&lt;br /&gt;
separate stream byte position (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;).  Separating the two&lt;br /&gt;
avoids TCP&#039;s conflation of packet identity with byte position which&lt;br /&gt;
forces Karn&#039;s algorithm for Round-Trip Time (RTT) sampling (no RTT&lt;br /&gt;
sample on retransmits, RFC 6298 sec. 3); FRCP applies the&lt;br /&gt;
Karn-equivalent gate via a combination of per-packet &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;,&lt;br /&gt;
per-slot &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; flags, and a sample-fence &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; (see [[#2.1. Per-flow state|Section 2.1]]&lt;br /&gt;
and [[#12. RTT estimation|Section 12]]).  FRCP&#039;s fixed-32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; wrap at 4 GiB of&lt;br /&gt;
wire bytes, narrower than QUIC&#039;s 62-bit varint offset (cf. RFC 9000&lt;br /&gt;
sec. 16); the on-wire wrap is handled by the same modular &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;&lt;br /&gt;
/ &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators ([[#1.3. SACK payload|Section 1.3]]) FRCP uses for seqnos, which&lt;br /&gt;
remain unambiguous as long as the in-flight byte window stays&lt;br /&gt;
strictly under 2 GiB (the half-range of the signed-int32 difference&lt;br /&gt;
in &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;).  The default per-flow ring is 1 MiB; the&lt;br /&gt;
implementation caps &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; at 128 MiB (&amp;lt;code&amp;gt;FRCT_STREAM_RING_SZ_MAX&amp;lt;/code&amp;gt;),&lt;br /&gt;
well below the 2 GiB half-range bound.  The runtime byte counters&lt;br /&gt;
exposed via FUSE (Filesystem in Userspace) in the Ouroboros&lt;br /&gt;
Resource Information Base (RIB, a virtual-filesystem introspection&lt;br /&gt;
bridge) are platform &amp;lt;code&amp;gt;size_t&amp;lt;/code&amp;gt; and do not wrap on 64-bit hosts.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2. Per-flow state and service modes ==&lt;br /&gt;
&lt;br /&gt;
=== 2.1. Per-flow state ===&lt;br /&gt;
&lt;br /&gt;
Each flow keeps a sender control record and a receiver control&lt;br /&gt;
record:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: oldest unacked &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (cumulative ACK boundary as seen by sender); rcv: next in-order &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; expected&lt;br /&gt;
;&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: peer-advertised right window edge; rcv: locally-advertised right window edge&lt;br /&gt;
;&amp;lt;code&amp;gt;cflags&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-direction feature flags: retransmission (&amp;lt;code&amp;gt;FRCTFRTX&amp;lt;/code&amp;gt;), receiver flow control (&amp;lt;code&amp;gt;FRCTFRESCNTL&amp;lt;/code&amp;gt;), linger-on-close (&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt;); see &amp;lt;code&amp;gt;&amp;amp;lt;ouroboros/fccntl.h&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: next &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; to send; rcv: force-ACK trigger - set on a stale or dup DATA so the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; emits a fresh cumulative ACK&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; counter for standalone ACK-bearing control packets (delayed ACK, SACK, final ACK on dealloc); not bumped on piggybacked ACK riding a DATA packet (which uses the DATA &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;).  Used by wire-dup ACK detection; rcv: incoming-ACK dedup tracker&lt;br /&gt;
;&amp;lt;code&amp;gt;act&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:last activity (used by inactivity / DRF)&lt;br /&gt;
;&amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:inactivity threshold; sender = &amp;lt;code&amp;gt;3*mpl + a + r + 1s&amp;lt;/code&amp;gt;, receiver = &amp;lt;code&amp;gt;2*mpl + a + r + 1s&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;mpl&amp;lt;/code&amp;gt; is the Maximum Packet Lifetime (delta-t terminology; see [[#15. Heritage and adopted techniques|Section 15]]); &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; are the FRCT a-timer and r-timer bounds (see [[#8. Retransmission|Section 8]]).  The asymmetry is load-bearing for pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
The sender holds a per-slot ring &amp;lt;code&amp;gt;snd_slots[RQ_SIZE]&amp;lt;/code&amp;gt; keyed by&lt;br /&gt;
&amp;lt;code&amp;gt;(seqno mod RQ_SIZE)&amp;lt;/code&amp;gt;.  Each slot tracks its retransmit entry (&amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;),&lt;br /&gt;
last-send timestamp, and retransmit flag bits: &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (a&lt;br /&gt;
retransmit is pending or has fired, gates the next RTT sample&lt;br /&gt;
under Karn) and &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (one-shot fast-retransmit staged for&lt;br /&gt;
this loss event).&lt;br /&gt;
&lt;br /&gt;
The receiver holds a parallel reorder ring &amp;lt;code&amp;gt;rcv_slots[RQ_SIZE]&amp;lt;/code&amp;gt;&lt;br /&gt;
(referred to as &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; in prose) holding stashed out-of-order&lt;br /&gt;
packet-buffer indexes; both FRTX and best-effort flows share this&lt;br /&gt;
path.  The invariant &amp;lt;code&amp;gt;rwe - lwe &amp;amp;lt;= RQ_SIZE&amp;lt;/code&amp;gt; holds: on each consume&lt;br /&gt;
the receiver advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; by the consumed count, capping the&lt;br /&gt;
receive window at &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; slots.&lt;br /&gt;
&lt;br /&gt;
A separate fence variable &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; is bumped on every retransmit&lt;br /&gt;
(timer-fire, SACK-driven, fast-rxm, NACK-driven) and on every&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; ([[#4. Sequence-number rotation (DRF)|Section 4]]) to mark the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; range whose RTT samples&lt;br /&gt;
MUST be discarded.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 2.2. Service modes (orthogonal axes) ===&lt;br /&gt;
&lt;br /&gt;
FRCP exposes its wire features as a vector of independent QoS&lt;br /&gt;
axes selected at flow allocation time.  All flows go through the&lt;br /&gt;
same &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive; the &amp;lt;code&amp;gt;qosspec_t&amp;lt;/code&amp;gt; passed&lt;br /&gt;
in determines which protocol machinery engages on the wire.  This&lt;br /&gt;
contrasts with the POSIX BSD socket model where TCP and UDP&lt;br /&gt;
require different socket types (&amp;lt;code&amp;gt;SOCK_STREAM&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;SOCK_DGRAM&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
The axes:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = unordered (no FRCP engagement: raw datagrams, no PCI on the wire, UDP-equivalent at this layer); 1 = message-ordered (FRCP engaged; SDU boundaries preserved across fragmentation); 2 = stream (byte-oriented, no SDU boundaries; FRTX required)&lt;br /&gt;
;&amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = lossless service requested: FRTX retransmit machinery engages ([[#8. Retransmission|Section 8]]); MUST be 0 for &amp;lt;code&amp;gt;service=2&amp;lt;/code&amp;gt;.  Non-zero = best-effort, FRTX off.&lt;br /&gt;
;&amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bit Error Rate tolerance.  0 = error-free service requested: a CRC trailer is appended after the body of DATA packets and verified on receive (added / checked outside the FRCP PCI; see [[#1.1. PCI header|Section 1.1]]).  Non-zero = peer accepts errors; trailer omitted.  SACK control packets carry a CRC32 trailer regardless of &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; gate applies to DATA only.&lt;br /&gt;
;&amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt;&lt;br /&gt;
:Peer-timeout (ms); 0 disables the keepalive timer.  Independent of FRCP engagement.&lt;br /&gt;
&lt;br /&gt;
Encryption is a separate per-flow attribute set at flow setup;&lt;br /&gt;
when enabled it wraps the FRCP packet (PCI + body, plus the CRC&lt;br /&gt;
trailer if any) under AEAD, expanding the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; by &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt;&lt;br /&gt;
octets (nonce / tag).  The CRC trailer is currently kept inside&lt;br /&gt;
the AEAD wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
&lt;br /&gt;
Reachable combinations exported by &amp;lt;code&amp;gt;include/ouroboros/qos.h&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Cube !! &amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; !! Engaged&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw&amp;lt;/code&amp;gt; || 0 || 1 || 1 || Raw passthrough&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw_safe&amp;lt;/code&amp;gt; || 0 || 1 || 0 || Raw + CRC trailer&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; || 1 || 1 || 1 || FRCP, no FRTX, no CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt_safe&amp;lt;/code&amp;gt; || 1 || 1 || 0 || FRCP, no FRTX, CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_msg&amp;lt;/code&amp;gt; || 1 || 0 || 0 || FRCP + FRTX&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_stream&amp;lt;/code&amp;gt; || 2 || 0 || 0 || FRCP + FRTX, stream&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Forced couplings actually enforced by the public API:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;service == SVC_STREAM&amp;lt;/code&amp;gt; (2) requires &amp;lt;code&amp;gt;loss == 0&amp;lt;/code&amp;gt;; &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;flow_accept&amp;lt;/code&amp;gt; reject the pair otherwise with &amp;lt;code&amp;gt;-EINVAL&amp;lt;/code&amp;gt;.&lt;br /&gt;
* FRTX requires FRCP engagement (&amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;); requesting &amp;lt;code&amp;gt;loss = 0&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;service = SVC_RAW&amp;lt;/code&amp;gt; is structurally a no-op because no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; is created.&lt;br /&gt;
* The &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; build flag globally forces &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;.  Note: this flag defaults to ON, so default builds ship with CRC disabled until &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; is set to OFF.&lt;br /&gt;
&lt;br /&gt;
Caveat: the API does NOT force &amp;lt;code&amp;gt;ber = 0&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; has &amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;, which means the PCI&lt;br /&gt;
itself is not CRC-protected on that cube; the HCS ([[#1.1. PCI header|Section 1.1]])&lt;br /&gt;
remains the only integrity check on the header.&lt;br /&gt;
&lt;br /&gt;
The FRCP-no-FRTX regime (&amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;loss &amp;amp;gt; 0&amp;lt;/code&amp;gt;) is meaningful&lt;br /&gt;
and live: sequence numbering, in-order delivery, flow-control&lt;br /&gt;
advertisement, KA, DRF rotation, and SDU fragmentation /&lt;br /&gt;
reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]) all run.  Lost packets are dropped&lt;br /&gt;
rather than retransmitted; a permanently-lost mid-fragment is&lt;br /&gt;
dropped via skip-past-gap once a later SDU is visible in the&lt;br /&gt;
reorder ring.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. Protocol parameters ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Slot ring / rcv window width&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;START_WINDOW&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Initial &amp;lt;code&amp;gt;rwe-lwe&amp;lt;/code&amp;gt; after rotate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MAX(250 us build-tunable, 1&amp;amp;lt;&amp;amp;lt;RXMQ_RES)&amp;lt;/code&amp;gt;; per-flow via &amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;FRCTSRTOMIN&amp;lt;/code&amp;gt;).  Default ~1 ms with &amp;lt;code&amp;gt;RXMQ_RES=20&amp;lt;/code&amp;gt;. || RTO floor; also floored at the retransmit-wheel resolution (~1 ms by default).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt; || 20 || Backoff shift cap&lt;br /&gt;
|-&lt;br /&gt;
| RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MIN(reo_wnd_mult * min_RTT/4, SRTT)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; scales on D-SACK, cap 20 || Reorder window; per RFC 8985 sec. 6.2; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per sec. 6.2 step 4&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; || 300 s (5 min, Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; windowed re-anchor&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_WND_MULT_MAX&amp;lt;/code&amp;gt; || 20 (RFC 8985 sec. 6.2 step 4) || &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_DECAY_PKTS&amp;lt;/code&amp;gt; || 16 (RFC 8985 sec. 6.2 step 4 / &amp;lt;code&amp;gt;RACK.reo_wnd_persist&amp;lt;/code&amp;gt;) || Fresh-ACK&#039;d seq count per halving&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_DSACK_LAG&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || D-SACK sanity cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTT_QUARANTINE&amp;lt;/code&amp;gt; || 32 (&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; steps) || NewReno gate pad&lt;br /&gt;
|-&lt;br /&gt;
| SACK rate-limit || &amp;lt;code&amp;gt;SACK_MIN_GAP_NS&amp;lt;/code&amp;gt; (250 us, fixed) || Min SACK gap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; || 2048 (wire cap; per-flow capped at &amp;lt;code&amp;gt;(frag_mtu-PCI-4)/8&amp;lt;/code&amp;gt;) || Per-SACK block cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; || 32 || Per-pass staged retransmit cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; || 3 (RFC 8985 default) || Hybrid fast-rxm trigger ([[#8. Retransmission|Section 8]])&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MDEV_MUL&amp;lt;/code&amp;gt; || 2 (build-tunable via &amp;lt;code&amp;gt;FRCT_RTO_MDEV_MULTIPLIER&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; shift in &amp;lt;code&amp;gt;RTO = srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| RTTP nonce || 16 octets || Echoed verbatim&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; || 8 || In-flight probes&lt;br /&gt;
|-&lt;br /&gt;
| RTT clamp || &amp;lt;code&amp;gt;16 * srtt&amp;lt;/code&amp;gt; || Probe-sample upper bound (ACK-derived RTT samples gated by Karn / recovery only)&lt;br /&gt;
|-&lt;br /&gt;
| Cold-probe cadence || 100 ms (rx-driven; see [[#12. RTT estimation|Section 12]]) || Pre-&amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; RTTP rate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt; || 100 ms || RDVS emit cadence&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; || 1 s || RDVS give-up&lt;br /&gt;
|-&lt;br /&gt;
| Delayed-ACK fire || &amp;lt;code&amp;gt;2 * TICTIME&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt; = FRCT tick granularity, default 5 ms; &amp;lt;code&amp;gt;2*TICTIME = 10 ms&amp;lt;/code&amp;gt; by default) || Fired after the first in-order DATA arrival; tick is build-tunable&lt;br /&gt;
|-&lt;br /&gt;
| NACK send cooldown || &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; when an &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; sample exists, else 100 ms || Pre-DRF NACK rate-limit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_SDU&amp;lt;/code&amp;gt; || 1 MiB || Max reassembled SDU; configurable per flow&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The per-flow fragment Maximum Transmission Unit (MTU) is computed&lt;br /&gt;
at flow setup from the lower IPCP&#039;s mtu minus encryption&lt;br /&gt;
&amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; and CRC trailer; there is no FRCT-level default or&lt;br /&gt;
environment-variable override.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 4. Sequence-number rotation (DRF) ==&lt;br /&gt;
&lt;br /&gt;
The DRF (Data Run Flag) bit on an outbound packet means &amp;quot;this is&lt;br /&gt;
the start of a fresh data run&amp;quot; and is set whenever the sender has&lt;br /&gt;
nothing in flight (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Independently of that, if the sender has been idle longer than&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.inact&amp;lt;/code&amp;gt; AND the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;),&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; rolls a random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; before the send and&lt;br /&gt;
resets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    snd_cr.seqno  = random()&lt;br /&gt;
    snd_cr.lwe    = snd_cr.seqno&lt;br /&gt;
    snd_cr.rwe    = snd_cr.seqno + START_WINDOW&lt;br /&gt;
    rtt_lwe       = snd_cr.seqno&lt;br /&gt;
    in_recovery   = false   (recovery state, see Section 8)&lt;br /&gt;
    recovery_high = snd_cr.seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The receiver, on observing rcv-side inactivity&lt;br /&gt;
(&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), requires a DRF on the next&lt;br /&gt;
DATA packet; otherwise it replies with a rate-limited NACK (see&lt;br /&gt;
below). Non-DATA control packets pass through without the DRF&lt;br /&gt;
requirement (no impact on receiver state). On DRF the receiver releases the &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots and&lt;br /&gt;
rebases&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    rcv_cr.lwe   = seqno&lt;br /&gt;
    rcv_cr.rwe   = seqno + RQ_SIZE&lt;br /&gt;
    rcv_cr.seqno = seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the inactive packet has DATA but no DRF, a rate-limited NACK is&lt;br /&gt;
fired back to the sender (cooldown per [[#3. Protocol parameters|Section 3]]); non-DATA stale&lt;br /&gt;
arrivals fall through to normal processing (no NACK, no drop).&lt;br /&gt;
&lt;br /&gt;
== 5. Send path ==&lt;br /&gt;
&lt;br /&gt;
# If the SDU exceeds &amp;lt;code&amp;gt;(frag_mtu - data_hdr_len)&amp;lt;/code&amp;gt;, the caller (&amp;lt;code&amp;gt;dev.c&amp;lt;/code&amp;gt;) fans it out into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - data_hdr_len))&amp;lt;/code&amp;gt; fragments, each emitted via &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; as its own DATA packet with a per-fragment role ([[#7.2. Fragmentation and reassembly|Section 7.2]]); both FRTX and best-effort flows fragment.  Raw flows (no FRCP engagement, &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) carry no PCI and return &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; for any SDU larger than one packet at the layer below.  An SDU that fits in a single packet is sent as SOLE.  &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; reserves PCI head room; sets DATA, plus DRF when the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
# &amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; if past sender inactivity and the pipe is empty ([[#4. Sequence-number rotation (DRF)|Section 4]]).&lt;br /&gt;
# Advertise FC (&amp;lt;code&amp;gt;pci.window = frcti_advert_rwe(frcti)&amp;lt;/code&amp;gt;, i.e. &amp;lt;code&amp;gt;rcv_cr.rwe&amp;lt;/code&amp;gt; clamped to &amp;lt;code&amp;gt;rcv_cr.lwe + ring_seq_cap&amp;lt;/code&amp;gt; in stream mode) when the receiver side is recent: &amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt; rcv_cr.inact&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Reliable mode (FRTX): leave &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; where it is; reset the slot at &amp;lt;code&amp;gt;RQ_SLOT(seqno)&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;snd_slots[p].time = now&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_slots[p].flags = 0&amp;lt;/code&amp;gt;); queue an &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; (saves a packet copy, arms a wheel timer at &amp;lt;code&amp;gt;now + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;).  Piggyback ACK (&amp;lt;code&amp;gt;pci.ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) while the a-timer for the most recent received DATA packet has not yet expired (&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt;= t_a&amp;lt;/code&amp;gt;); on piggyback, set &amp;lt;code&amp;gt;rcv_cr.seqno = rcv_cr.lwe&amp;lt;/code&amp;gt; so the next delayed-ACK fire is suppressed.  See [[#8. Retransmission|Section 8]] for &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; semantics.&lt;br /&gt;
# Best-effort mode (no FRTX): advance &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; immediately (&amp;lt;code&amp;gt;snd_cr.lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_cr.rwe = snd_cr.lwe + RQ_SIZE&amp;lt;/code&amp;gt;); no retransmit state.  No send-side RTT probe is armed in this mode (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt; requires an in-flight &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, which best-effort never has); the rx-driven cold seeder in &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; is the only probe path.&lt;br /&gt;
# In reliable mode, optionally arm an RTT probe ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 6. Receive path ==&lt;br /&gt;
&lt;br /&gt;
=== 6.1. Early-exit dispatch ===&lt;br /&gt;
&lt;br /&gt;
Keepalive (KA), RTT probe (RTTP), pre-DRF NACK, and rendezvous&lt;br /&gt;
(RDVS) packets short-circuit out of &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; before the locked&lt;br /&gt;
main path; each handler takes its own lock internally.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
      incoming packet&lt;br /&gt;
            |&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | KA?     |---yes--&amp;gt; ka_rcv  ; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RTTP?   |---yes--&amp;gt; rttp_rcv; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | NACK?   |---yes--&amp;gt; nack_rcv; return  (see Section 9)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RDVS?   |---yes--&amp;gt; rdv_rcv ; return  (reply bare FC, ackno=0)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       acquire wrlock; enter locked main path&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt;&lt;br /&gt;
:refresh &amp;lt;code&amp;gt;t_ka_rcv&amp;lt;/code&amp;gt;, honour piggybacked ACK.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt;&lt;br /&gt;
:probe (echo back nonce) or echo (verify nonce, sample RTT).&lt;br /&gt;
;&amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:pre-DRF, sender-side handler.  See [[#9. Pre-DRF NACK|Section 9]].&lt;br /&gt;
;&amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt;&lt;br /&gt;
:reply with a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;); &amp;lt;code&amp;gt;rdlock&amp;lt;/code&amp;gt; only.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 6.2. Locked main path ===&lt;br /&gt;
&lt;br /&gt;
Steps below run with the per-flow &amp;lt;code&amp;gt;frcti.lock&amp;lt;/code&amp;gt; held for writing&lt;br /&gt;
(&amp;lt;code&amp;gt;pthread_rwlock_wrlock&amp;lt;/code&amp;gt;) unless noted.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;&lt;br /&gt;
:Only meaningful when the receive side is stale.  On DRF (Data Run Flag): release &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots, rebase &amp;lt;code&amp;gt;rcv_cr&amp;lt;/code&amp;gt;, continue.  On stale DATA without DRF: fire a pre-DRF NACK if cooldown allows ([[#9. Pre-DRF NACK|Section 9]]), then discard the packet; on cooldown, drop without sending a NACK (a pending cumulative ACK from &amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; may still go out).  Non-DATA, non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; pure-DRF stale arrivals fall through after the DRF rebase branch.&lt;br /&gt;
&lt;br /&gt;
;DATA-only act refresh&lt;br /&gt;
:Refresh &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; only when &amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt; is set, so that non-DATA packets never block the next DRF rebase.&lt;br /&gt;
&lt;br /&gt;
;Wire-dup gate&lt;br /&gt;
:Before flag-driven dispatch, drop wire-duplicate ACKs and wire-duplicate DATA (&amp;lt;code&amp;gt;is_dup_ack&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt;).  The DATA check is bypassed for &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;-bearing arrivals so the piggybacked ACK / SACK / FC carried on a retransmitted DATA at an already-ACK&#039;d &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is still applied; the stale-in-window branch below then drops the packet.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Drop ACKs whose &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; falls outside &amp;lt;code&amp;gt;(snd_cr.lwe, snd_cr.seqno]&amp;lt;/code&amp;gt;.  If &amp;lt;code&amp;gt;ackno == snd_cr.lwe&amp;lt;/code&amp;gt; (non-advancing cumulative ACK), drive RACK fast-retransmit consideration ([[#8. Retransmission|Section 8]]).  Otherwise advance &amp;lt;code&amp;gt;snd_cr.lwe = ackno&amp;lt;/code&amp;gt;, collapse &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; to 0 (Karn-gated by &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; on the just-acknowledged slot, the old head-of-line), reset &amp;lt;code&amp;gt;dup_thresh&amp;lt;/code&amp;gt; to 0, update &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the send-time of the slot at &amp;lt;code&amp;gt;ackno-1&amp;lt;/code&amp;gt; (consumed by RACK and SACK below), decay &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per RFC 8985 sec. 6.2 step 4, exit NewReno-careful recovery (see [[#8. Retransmission|Section 8]]) on &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt;, and feed an RTT sample if eligible ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Walk the block list.  For each block (a present range above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;) NULL out &amp;lt;code&amp;gt;snd_slots[k].rxm&amp;lt;/code&amp;gt;, clear the slot&#039;s per-send flags, and advance &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the latest send-time covered (the Forward Acknowledgement / fack equivalent, Mathis &amp;amp;amp; Mahdavi 1996); the first block whose start clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; skips this fack update so that a head-of-line clamp does not falsely advance fack.  For un-SACKed gaps below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt;, stage a retransmit per slot that is (1) still owned (&amp;lt;code&amp;gt;rxm != NULL&amp;lt;/code&amp;gt;), (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (the RFC 8985 sec. 6.2 hybrid trigger).  Mark the slot &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; and NULL the &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; at stage time.  Capped at &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; staged retransmits per receive pass; what&#039;s left rides the next SACK.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bump &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; (clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt;, never shrinks) and mark window open.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bounds-check &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; against window.  On stale-dup (&amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;), set &amp;lt;code&amp;gt;rcv_cr.seqno = seqno&amp;lt;/code&amp;gt; to force a fresh ACK on the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt;, then drop.  On accept: both FRTX and best-effort stash the packet-buffer index into &amp;lt;code&amp;gt;rq[seqno mod RQ_SIZE]&amp;lt;/code&amp;gt;.  Fragments stash unchanged - the role bits are inspected only at consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]).  On out-of-order arrival, build a SACK reply if not rate-limited (per [[#3. Protocol parameters|Section 3]]) and not deduplicated against the previous &amp;lt;code&amp;gt;(rcv_cr.lwe, n_blocks)&amp;lt;/code&amp;gt; pair; D-SACK reports always bypass the dedup.  If both rate-limit and dedup suppress the reply, neither SACK nor delayed-ACK fires (the sender picks up the gap on its next ACK).  On in-order arrival, arm the delayed-ACK timer.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; exit&lt;br /&gt;
:Releases the per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;), then calls &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; synchronously after the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; release to surface any pending cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 7. Read path and reassembly ==&lt;br /&gt;
&lt;br /&gt;
=== 7.1. Read path ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns a full reassembled SDU (Service Data Unit) via&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; on every FRCP SDU-mode flow (FRTX or best-effort);&lt;br /&gt;
stream-mode is covered in [[#16. Stream-mode flows|Section 16]].  An incomplete head-of-line&lt;br /&gt;
(HoL) run yields &amp;lt;code&amp;gt;-EAGAIN&amp;lt;/code&amp;gt;; an oversized run yields &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; (the&lt;br /&gt;
run is dropped so the flow does not stall).  On best-effort flows,&lt;br /&gt;
a permanently-lost mid-fragment is dropped as soon as a later&lt;br /&gt;
complete SDU becomes visible in the ring ([[#7.2. Fragmentation and reassembly|Section 7.2]] skip-past-&lt;br /&gt;
gap).&lt;br /&gt;
&lt;br /&gt;
Raw flows carry no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt;, so &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns the next pending&lt;br /&gt;
packet-buffer index directly, with no role-bit inspection.  (Raw&lt;br /&gt;
service is selected via &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt; at flow allocation,&lt;br /&gt;
which suppresses &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; creation.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_pdu_ready&amp;lt;/code&amp;gt; is the no-advance peek used by &amp;lt;code&amp;gt;fevent&amp;lt;/code&amp;gt; (the&lt;br /&gt;
Ouroboros flow-event multiplexer, the &amp;lt;code&amp;gt;poll(2)&amp;lt;/code&amp;gt;-equivalent on&lt;br /&gt;
flows).  It returns ready only when the head-of-line run is&lt;br /&gt;
complete and the lead packet (a Protocol Data Unit, here one FRCP&lt;br /&gt;
packet) is present at &amp;lt;code&amp;gt;rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt;; any other state&lt;br /&gt;
(including the best-effort skip-past-gap case) returns not ready,&lt;br /&gt;
and &amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; is left to drop the broken prefix and re-&lt;br /&gt;
inspect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 7.2. Fragmentation and reassembly ===&lt;br /&gt;
&lt;br /&gt;
Send side (&amp;lt;code&amp;gt;flow_write_frag&amp;lt;/code&amp;gt;).  An SDU larger than&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - PCI)&amp;lt;/code&amp;gt; is split into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - PCI))&amp;lt;/code&amp;gt;&lt;br /&gt;
fragments; each fragment is its own FRCP packet with its own&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a per-fragment role flag pair ([[#1.2. Flag bits|Section 1.2]]).  Roles are&lt;br /&gt;
assigned at emit time:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! i !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=0&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=n-1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| else || &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A mid-loop allocation or transmit failure may yield a partial&lt;br /&gt;
write: the call returns the bytes already enqueued (&amp;lt;code&amp;gt;off &amp;amp;gt; 0&amp;lt;/code&amp;gt;) or&lt;br /&gt;
the underlying error (&amp;lt;code&amp;gt;off == 0&amp;lt;/code&amp;gt;).  Best-effort flows fragment&lt;br /&gt;
identically; on the receiver, a partial run with a permanently-&lt;br /&gt;
lost fragment is dropped when a later complete SDU is visible in&lt;br /&gt;
the ring (see skip-past-gap below).  Raw flows carry no PCI and&lt;br /&gt;
refuse anything larger than the layer&#039;s user MTU (&amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Wire-level recovery is fragment-agnostic on FRTX flows: each&lt;br /&gt;
fragment&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; flows through SACK / RACK / RTO / NACK exactly&lt;br /&gt;
as for a SOLE DATA packet, and reassembly does not re-enter the&lt;br /&gt;
loss-detection path.  Best-effort flows run the same &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
machinery (DRF, FC, ACK piggyback, pre-DRF NACK emit) but queue&lt;br /&gt;
no &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; state at the sender, so a lost MID is unrecoverable;&lt;br /&gt;
skip-past-gap handles it (below).&lt;br /&gt;
&lt;br /&gt;
Receive side.  Fragments stash into &amp;lt;code&amp;gt;rq[seqno]&amp;lt;/code&amp;gt; unchanged; role bits&lt;br /&gt;
are read only at consume time.  &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;, called from&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt;, walks the ring starting at the oldest still-&lt;br /&gt;
undelivered &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;base = rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt; (equal to &amp;lt;code&amp;gt;rcv_cr.lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
only when no partial run is in progress; during a partial run &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
has already advanced past &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;).  It produces one of three&lt;br /&gt;
outcomes:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Outcome !! Cause&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELIVER (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]=SOLE&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;), or &amp;lt;code&amp;gt;rq[base]=FIRST&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; follows in slots &amp;lt;code&amp;gt;[base+1..base+n-1]&amp;lt;/code&amp;gt; with all intermediate roles in &amp;lt;code&amp;gt;{MID,FIRST,LAST}&amp;lt;/code&amp;gt; contiguous.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DROP (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; without a preceding &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;); a &amp;lt;code&amp;gt;FIRST..[non-LAST]..new-FIRST&amp;lt;/code&amp;gt; or new-&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; mid-run (drop the broken prefix with &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; = run length minus 1, so the new &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; stays); or, on best-effort flows, a gap at &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; later in the ring (drop up to the new run start).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; absent or &amp;lt;code&amp;gt;FIRST..[non-LAST]&amp;lt;/code&amp;gt; with no later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; in the ring (FRTX waits for retx; best-effort waits for arrival).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DELIVER&amp;lt;/code&amp;gt; triggers &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;: a scatter-gather &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; of the &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;&lt;br /&gt;
consecutive fragments at &amp;lt;code&amp;gt;rq[base..base+n-1]&amp;lt;/code&amp;gt; directly into the&lt;br /&gt;
caller&#039;s buffer; each per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;) is&lt;br /&gt;
released and &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; advances by &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; was already advanced&lt;br /&gt;
incrementally as each contiguous fragment arrived; &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;&lt;br /&gt;
only restores the fixed-width invariant &amp;lt;code&amp;gt;rwe == lwe + RQ_SIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
No intermediate reassembly buffer is allocated.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; past the broken prefix (releasing the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;s)&lt;br /&gt;
and pulls &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; up to the new trailing edge if needed; the next&lt;br /&gt;
consume retries from the new &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;.  Oversize or arithmetically&lt;br /&gt;
overflowing delivery (sum of fragment lengths &amp;amp;gt; &amp;lt;code&amp;gt;max_rcv_sdu&amp;lt;/code&amp;gt;, sum&lt;br /&gt;
&amp;amp;gt; caller&#039;s buffer, or running-sum overflow) also drops the run&lt;br /&gt;
with &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skip-past-gap (best-effort only).  On FRTX, a gap in the run means&lt;br /&gt;
&amp;quot;waiting for retransmit&amp;quot; and &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;.&lt;br /&gt;
On best-effort flows the gap is permanent, so &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;&lt;br /&gt;
scans forward in the ring for the next &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;; if one is&lt;br /&gt;
visible within &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;, it returns &amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; for the broken prefix and&lt;br /&gt;
the consume loop retries at the new &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;.  Memory hold is bounded&lt;br /&gt;
by &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;; the partial releases on the next consume call once a&lt;br /&gt;
later complete run exists.  Voice-like flows (one &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; per SDU)&lt;br /&gt;
see no extra wait: any later &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; makes the prior gap droppable&lt;br /&gt;
immediately.&lt;br /&gt;
&lt;br /&gt;
The choice to defer reassembly to consume time keeps the receive&lt;br /&gt;
path zero-copy: fragments stay in the shared-memory ring until&lt;br /&gt;
the application pulls, and the SDU lands directly in the caller&#039;s&lt;br /&gt;
buffer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 8. Retransmission ==&lt;br /&gt;
&lt;br /&gt;
FRCP is bounded by two delta-t-derived timers (Watson 1981, see&lt;br /&gt;
[[#15. Heritage and adopted techniques|Section 15]]):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer): upper bound on ACK delay.  An ACK for a received DATA packet MUST NOT be emitted after &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; of receipt; an attempt to send an ACK after the a-timer has expired is suppressed.&lt;br /&gt;
* &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer): upper bound on retransmission.  A given DATA packet MUST NOT be retransmitted after &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; has elapsed since its first send (&amp;lt;code&amp;gt;t0&amp;lt;/code&amp;gt;); when the bound is hit, the flow is declared down (raising the Ouroboros asynchronous flow condition &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt;, which marks the flow dead to both endpoints) rather than retransmitted again.&lt;br /&gt;
&lt;br /&gt;
Each in-flight FRTX &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; owns one &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt;, armed in a hashed&lt;br /&gt;
timing wheel; the wheel deadline is the slot&#039;s next eligible&lt;br /&gt;
retransmit time.&lt;br /&gt;
&lt;br /&gt;
;RTO timer&lt;br /&gt;
:On fire (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;), re-emit with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, mark &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (Karn-suppress next ACK&#039;s RTT sample), and (for the head-of-line (HoL) slot only) bump &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; up to &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt;.  Wheel deadline is &amp;lt;code&amp;gt;t_send + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;.  Re-armed unless consumed.  The RTO timer also clears &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (re-arming fast-retransmit eligibility), resets &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; to 1 on a HoL fire (RFC 8985 sec. 6.2 step 4 reset clause), and marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; if its &amp;lt;code&amp;gt;frct_tx&amp;lt;/code&amp;gt; call fails.&lt;br /&gt;
&lt;br /&gt;
;r-timer guard&lt;br /&gt;
:Before any retransmit attempt, check &amp;lt;code&amp;gt;(now - t0)&amp;lt;/code&amp;gt; against &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;.  If exceeded, the slot is no longer eligible for retransmit.  Only the RTO timer (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;) treats r-timer expiry as terminal: it marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; (peer unreachable).  Fast-retransmit, SACK-driven retransmit, and NACK-driven head-of-line re-emit silently skip aged-out slots and defer the flow-down decision to the next RTO fire.&lt;br /&gt;
&lt;br /&gt;
;Fast retransmit (hybrid trigger, RFC 8985 sec. 6.2)&lt;br /&gt;
:On a non-advancing cumulative ACK with the scoreboard advanced, fire one fast retransmit when EITHER (a) the head-of-line slot&#039;s latest send is older than the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; ([[#3. Protocol parameters|Section 3]]) and not yet aged out, OR (b) the SACK &amp;lt;code&amp;gt;dup-thresh&amp;lt;/code&amp;gt; count above &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; reaches &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; (= 3, RFC 8985 sec. 6.2 step 4).  Fires at most once per non-advancing cumulative-ACK value, gated by &amp;lt;code&amp;gt;rack_fired_lwe&amp;lt;/code&amp;gt; (the &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; at which fast-retransmit last fired).  Set &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; on the slot (one-shot per-slot gate) and enter NewReno-style careful recovery (see NewReno below in this section).&lt;br /&gt;
:The RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; uses the RFC 8985 sec. 6.2 form &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor.  Before the first RTT sample seeds &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; falls back to &amp;lt;code&amp;gt;MIN(reo_wnd_mult * SRTT / 4, SRTT)&amp;lt;/code&amp;gt;, still floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt; (consistent with the windowed-minimum fallback described in [[#12. RTT estimation|Section 12]]).  &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt; is a windowed minimum over the last &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; = 5 min of RTT samples (matches the Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt; default) so a route change to a longer path eventually re-anchors the reorder window without relying on &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; growth alone.&lt;br /&gt;
&lt;br /&gt;
;SACK-driven retransmit&lt;br /&gt;
:For each gap below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt; whose slot is (1) still owned, (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (same hybrid as fast-retransmit, see [[#6.2. Locked main path|Section 6.2]]), re-emit.  Each SACK-driven retransmit re-arms a fresh &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; so a lost retransmit can still be recovered by its own RTO timer.&lt;br /&gt;
&lt;br /&gt;
;NewReno&lt;br /&gt;
:On entry, &amp;lt;code&amp;gt;recovery_high = snd_cr.seqno + RTT_QUARANTINE&amp;lt;/code&amp;gt;.  Exit when &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt; (the latter means everything sent has been acknowledged).  &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; also clears recovery.&lt;br /&gt;
&lt;br /&gt;
== 9. Pre-DRF NACK ==&lt;br /&gt;
&lt;br /&gt;
The two sides have different inactivity thresholds (&amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), so a receiver can detect &amp;quot;stale data run&amp;quot; before the sender&#039;s own DRF logic kicks in.  NACK is the receiver-driven nudge that asks the sender to re-transmit the head of the run.&lt;br /&gt;
&lt;br /&gt;
;Send (&amp;lt;code&amp;gt;frcti_nack_snd&amp;lt;/code&amp;gt;, called by &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;FRCT_INACT_NEED_NACK&amp;lt;/code&amp;gt;)&lt;br /&gt;
:When an incoming DATA packet has no DRF and rcv-side activity is older than &amp;lt;code&amp;gt;rcv_cr.inact&amp;lt;/code&amp;gt;, the receiver emits a bare packet with &amp;lt;code&amp;gt;flags = FRCT_NACK&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;seqno = arrival_seqno - 1&amp;lt;/code&amp;gt; (informational only, not consulted by the receive handler).  The cooldown in [[#3. Protocol parameters|Section 3]] rate-limits the burst.  Non-DATA non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; non-DATA DRF still rebases via the DRF branch.&lt;br /&gt;
&lt;br /&gt;
;Receive (&amp;lt;code&amp;gt;frcti_nack_rcv&amp;lt;/code&amp;gt;)&lt;br /&gt;
:Dispatched in the early-exit branch ([[#6.1. Early-exit dispatch|Section 6.1]]), before &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;.  The sender copies the head-of-line (HoL) &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; packet, marks the slot &amp;lt;code&amp;gt;SND_RTX | SND_FAST_RXM&amp;lt;/code&amp;gt; (Karn-suppress next ACK, one-shot fast-rxm gate), sets &amp;lt;code&amp;gt;rtt_lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, and re-emits via &amp;lt;code&amp;gt;fast_rxm_send&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt; and a refreshed &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;.  The original &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed - the NACK emit is additive to the normal retransmit machinery, not a replacement.  No-op if nothing is in flight, the HoL slot has aged past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, or the HoL &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; pointer has been cleared by SACK or RACK.&lt;br /&gt;
&lt;br /&gt;
NACK has exactly one role: lost first-of-run (DRF) packet recovery. Until the DRF packet arrives, the receiver cannot rebase its window, so any subsequent in-flight packets look stale to the receiver. The NACK fires the moment a stale receiver sees DATA without DRF, telling the sender to re-emit the head-of-line (DRF) packet at NACK-cooldown latency rather than waiting for the initial RTO (which is the configured default until &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded by the first probe round-trip).  Mid-stream loss is NOT NACK-driven; it is recovered by the sender&#039;s RTO, fast retransmit, and SACK-driven retransmit paths ([[#8. Retransmission|Section 8]]) only.&lt;br /&gt;
&lt;br /&gt;
The existing &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed on a NACK re-emit, so the RTO path remains the eventual fallback.&lt;br /&gt;
&lt;br /&gt;
== 10. Cumulative + selective ACK ==&lt;br /&gt;
&lt;br /&gt;
Cumulative ACK is &amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;.  On out-of-order arrival the&lt;br /&gt;
receiver also emits a SACK packet ([[#1.3. SACK payload|Section 1.3]]) whose payload lists&lt;br /&gt;
&#039;&#039;present&#039;&#039; blocks above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; (analogous to TCP SACK / QUIC ACK&lt;br /&gt;
ranges).  SACKs are rate-limited per [[#3. Protocol parameters|Section 3]] and suppressed when&lt;br /&gt;
neither &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; nor block count has changed since the last SACK.&lt;br /&gt;
&lt;br /&gt;
D-SACK reports (RFC 2883) are emitted in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; of an&lt;br /&gt;
otherwise normal SACK frame (see [[#1.3. SACK payload|Section 1.3]] for the encoding).&lt;br /&gt;
Two receiver triggers arm a pending D-SACK report (single-slot,&lt;br /&gt;
latest-wins):&lt;br /&gt;
&lt;br /&gt;
* DATA arrival with &amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;, both wire-dup (no RXM, &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt; path) and retransmit (RXM, post-FC branch) (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
* &amp;lt;code&amp;gt;rq_accept&amp;lt;/code&amp;gt; conflict, slot already occupied in &amp;lt;code&amp;gt;[lwe, rwe)&amp;lt;/code&amp;gt; (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
&lt;br /&gt;
When a D-SACK is pending and the standard scoreboard SACK would be&lt;br /&gt;
suppressed by dedup or rate-limit, the report is emitted as a&lt;br /&gt;
stand-alone SACK frame through the normal &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; path; when a&lt;br /&gt;
D-SACK report is pending the path bypasses dedup and the &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;&lt;br /&gt;
rate-limit, but the a-timer suppression on rcv inactivity still&lt;br /&gt;
applies.&lt;br /&gt;
&lt;br /&gt;
Bare ACKs are deferred via a per-flow delayed-ACK timer (one in&lt;br /&gt;
flight at a time, atomic test-and-set dedup; fires per [[#3. Protocol parameters|Section 3]]&lt;br /&gt;
after the first in-order arrival).  Suppressed if (1) no new&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, (2) rcv side is inactive (older than &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt;), or (3) the&lt;br /&gt;
sender just sent within &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;.  A pending D-SACK ride-through&lt;br /&gt;
bypasses (1) and (3); the a-timer gate (2) is unconditional.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 11. Flow control ==&lt;br /&gt;
&lt;br /&gt;
The receiver advertises &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; in every FC field.  The sender treats&lt;br /&gt;
its &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; as the absolute right edge: when&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt;= snd_cr.rwe&amp;lt;/code&amp;gt; the window is closed and &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt;&lt;br /&gt;
yields.  While closed, the sender periodically emits RDVS&lt;br /&gt;
(rendezvous) packets (cadence &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt;); the receiver replies with&lt;br /&gt;
a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;) that reopens the window.  Once the&lt;br /&gt;
window has been closed for longer than &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; the sender stops&lt;br /&gt;
emitting RDVS but does not tear the flow down - the writer keeps&lt;br /&gt;
blocking until either a peer-driven FC arrives or the KA&lt;br /&gt;
(keepalive) / r-timer marks the flow.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt; on receipt and MUST NOT shrink:&lt;br /&gt;
a backward &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is silently clamped to the current &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt;;&lt;br /&gt;
the FC packet still reopens the window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 12. RTT estimation ==&lt;br /&gt;
&lt;br /&gt;
Active RTTP probes ([[#1.4. RTTP payload|Section 1.4]]) carry a 32-bit &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt; (0&lt;br /&gt;
reserved) and a 16-byte random nonce echoed verbatim - defends&lt;br /&gt;
against spoofed replies.  A ring of &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; in-flight probes is&lt;br /&gt;
kept; an echo whose &amp;lt;code&amp;gt;(id, nonce)&amp;lt;/code&amp;gt; doesn&#039;t match the ring slot is&lt;br /&gt;
dropped.  A single RTTP sample is clamped to &amp;lt;code&amp;gt;RTT_CLAMP_MUL * srtt&amp;lt;/code&amp;gt;&lt;br /&gt;
(compile-time &amp;lt;code&amp;gt;RTT_CLAMP_MUL = 16&amp;lt;/code&amp;gt;) once &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded; the first&lt;br /&gt;
cold-probe sample feeds &amp;lt;code&amp;gt;rtt_update&amp;lt;/code&amp;gt; raw.&lt;br /&gt;
&lt;br /&gt;
Probe arming gates:&lt;br /&gt;
&lt;br /&gt;
;Cold (no &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; yet)&lt;br /&gt;
:the receive path arms at most one probe per 100 ms via &amp;lt;code&amp;gt;frcti_rcv_probe&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;PROBE_DUE_COLD&amp;lt;/code&amp;gt;); arming requires an incoming packet.  Active send-path arming bails while &amp;lt;code&amp;gt;srtt == 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
;Warm (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt;, called from &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt;)&lt;br /&gt;
:outstanding data (&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt; snd_cr.lwe&amp;lt;/code&amp;gt;), AND at least &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_rcv_rtt&amp;lt;/code&amp;gt; (last RTT receive of any kind), AND at least &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_snd_probe&amp;lt;/code&amp;gt; (last probe emit).&lt;br /&gt;
&lt;br /&gt;
Sample feeds either Linux&#039;s asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_LINUX_RTT_ESTIMATOR&amp;lt;/code&amp;gt;, default ON) or RFC 6298 symmetric EWMA&lt;br /&gt;
(compile option).  &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is floored at 10 ms when seeded from a&lt;br /&gt;
hint, at 1 us after every update (including the first seeding&lt;br /&gt;
sample); &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; floored at 100 ns.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;RTO = max(rto_min, 2 * srtt, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(the &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; floor is an FRCT addition not in RFC 6298).&lt;br /&gt;
Effective wheel deadline capped per [[#3. Protocol parameters|Section 3]].&lt;br /&gt;
&lt;br /&gt;
ACK-derived samples (&amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;rtt_sample_eligible&amp;lt;/code&amp;gt;), beyond&lt;br /&gt;
the cum-ACK advance gate in &amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno &amp;amp;gt; lwe&amp;lt;/code&amp;gt; and&lt;br /&gt;
&amp;lt;code&amp;gt;ackno &amp;amp;lt;= seqno&amp;lt;/code&amp;gt;), require all of: not in recovery; ACK packet does&lt;br /&gt;
not carry &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;; HoL slot&#039;s &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; bit clear; slot&#039;s &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;&lt;br /&gt;
pointer non-NULL (not SACK-consumed); &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; not below the &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
fence; &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; already seeded by an RTTP probe.  There is no ACK-only&lt;br /&gt;
seeding.&lt;br /&gt;
&lt;br /&gt;
Every eligible sample also feeds &amp;lt;code&amp;gt;RACK.min_RTT&amp;lt;/code&amp;gt; (RFC 8985 sec. 6.2)&lt;br /&gt;
via a windowed minimum: replace whenever the sample is strictly&lt;br /&gt;
smaller OR more than &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; (5 min, matches Linux&lt;br /&gt;
&amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) has elapsed since the current min was set.  The&lt;br /&gt;
downward branch is immediate (faster path picked up at once); the&lt;br /&gt;
upward branch is gated on the window (a transient queue burst does&lt;br /&gt;
not poison the estimate, but a sustained route change to a longer&lt;br /&gt;
path re-anchors &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; after at most one window).  Seeded from&lt;br /&gt;
&amp;lt;code&amp;gt;rtt_hint&amp;lt;/code&amp;gt; at &amp;lt;code&amp;gt;rtt_init&amp;lt;/code&amp;gt;; 0 acts as the unset sentinel and the base&lt;br /&gt;
in &amp;lt;code&amp;gt;rack_reorder_window&amp;lt;/code&amp;gt; falls back from &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt; (so&lt;br /&gt;
&amp;lt;code&amp;gt;R = mult * SRTT/4&amp;lt;/code&amp;gt;, capped at &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;, floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt;)&lt;br /&gt;
until the first sample.  See [[#6.2. Locked main path|Section 6.2]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 13. Liveness (keepalive) ==&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;code&amp;gt;qs.timeout &amp;amp;gt; 0&amp;lt;/code&amp;gt; a per-flow KA (keepalive) timer is armed.&lt;br /&gt;
Arming uses &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; for the deadline computation:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;deadline = min(snd_act + qs.timeout/4, rcv_act + qs.timeout)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(clamped to &amp;lt;code&amp;gt;now + qs.timeout/4&amp;lt;/code&amp;gt; if already past).  The timer fires&lt;br /&gt;
either on sender idleness (to send a KA) or on receiver idleness&lt;br /&gt;
(to declare the peer dead).  On fire (&amp;lt;code&amp;gt;ka_snd&amp;lt;/code&amp;gt;) the peer-dead test&lt;br /&gt;
uses &amp;lt;code&amp;gt;max(rcv_cr.act, t_ka_rcv)&amp;lt;/code&amp;gt; so a recent KA reply counts even&lt;br /&gt;
when no DATA has arrived:&lt;br /&gt;
&lt;br /&gt;
* If &amp;lt;code&amp;gt;now - max(rcv_cr.act, t_ka_rcv) &amp;amp;gt; qs.timeout&amp;lt;/code&amp;gt;, mark the flow &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; and notify the per-process flow-event set (&amp;lt;code&amp;gt;proc.fqset&amp;lt;/code&amp;gt;) with &amp;lt;code&amp;gt;FLOW_PEER&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Else if &amp;lt;code&amp;gt;snd_idle &amp;amp;gt; qs.timeout/4&amp;lt;/code&amp;gt;, emit a bare &amp;lt;code&amp;gt;KA | ACK&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) and re-arm.&lt;br /&gt;
* Else just re-arm.&lt;br /&gt;
&lt;br /&gt;
Note: &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; are the receive and transmit shared-memory&lt;br /&gt;
ring buffers.  The r-timer raises &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; on both (route is&lt;br /&gt;
broken); keepalive raises &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; only and notifies&lt;br /&gt;
the flow-event set (peer is silent, writer keeps &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; usable) -&lt;br /&gt;
distinct ACLs.  &amp;lt;code&amp;gt;qs.timeout == 0&amp;lt;/code&amp;gt; disables keepalive entirely; a&lt;br /&gt;
silent peer crash is then undetected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 14. Linger / teardown ==&lt;br /&gt;
&lt;br /&gt;
On &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt; computes a grace timeout&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;max(rcv_cr.act + rcv_cr.inact, snd_cr.act + snd_cr.inact) - now&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(floored at 0 and converted to seconds) and returns it; &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&lt;br /&gt;
forwards this to the IRMd as the dealloc grace.  The IRMd, not FRCT,&lt;br /&gt;
performs the wait.  Before computing the timeout, FRCT may emit a&lt;br /&gt;
final ACK when &amp;lt;code&amp;gt;rcv_cr.lwe != rcv_cr.seqno&amp;lt;/code&amp;gt; (the peer has not been&lt;br /&gt;
told the most recent cumulative ACK) AND the rcv side has been&lt;br /&gt;
active within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer not aged out).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt; is honoured only when &amp;lt;code&amp;gt;snd_cr.lwe &amp;amp;lt; edge&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;edge =&lt;br /&gt;
snd_fin_seqno&amp;lt;/code&amp;gt; after FIN has been sent in stream mode and&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno&amp;lt;/code&amp;gt; otherwise (data or FIN still in flight).  The drain&lt;br /&gt;
itself runs in &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&#039;s &amp;lt;code&amp;gt;while (FRCTI_LINGERING)&amp;lt;/code&amp;gt; loop, not in&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The fd is single-reader / single-writer (documented in the&lt;br /&gt;
manpages).  &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt; pumps &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; on every call (via&lt;br /&gt;
&amp;lt;code&amp;gt;flow_wait_window&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;flow_drain_rx_nb&amp;lt;/code&amp;gt;) and additionally blocks on&lt;br /&gt;
&amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; when the send window is closed.  A pure-writer thread thus&lt;br /&gt;
consumes ACKs without a dedicated reader.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 15. Heritage and adopted techniques ==&lt;br /&gt;
&lt;br /&gt;
Delta-t (Watson, 1981) is the primary heritage; FRCP descends from&lt;br /&gt;
the delta-t protocol family via the Recursive InterNetwork&lt;br /&gt;
Architecture (RINA; Day, &amp;quot;Patterns in Network Architecture&amp;quot;, 2008,&lt;br /&gt;
ch. 9).  Timer-based connection management&lt;br /&gt;
(no SYN/FIN handshake, per-flow state born on first DATA and&lt;br /&gt;
reclaimed after &amp;lt;code&amp;gt;t_mpl + a + r&amp;lt;/code&amp;gt; of silence), the DRF marker, and the&lt;br /&gt;
&amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; timers all come from delta-t.  See Watson,&lt;br /&gt;
&amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection&lt;br /&gt;
Management&amp;quot;, Computer Networks 5 (1981).&lt;br /&gt;
&lt;br /&gt;
The unified &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive and its&lt;br /&gt;
multi-axis QoS-cube argument ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) also come from RINA&lt;br /&gt;
(Day 2008, ch. 6; Grasa et al., &amp;quot;IRATI: investigating RINA as an&lt;br /&gt;
alternative to TCP/IP&amp;quot;, Computer Networks 92 (2015)) - reliability,&lt;br /&gt;
ordering, CRC presence, and encryption are flow attributes, not&lt;br /&gt;
separate sockets or protocols.&lt;br /&gt;
&lt;br /&gt;
The table below summarises additional adopted techniques and their&lt;br /&gt;
references.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FRCP mechanism !! Heritage !! Reference / note&lt;br /&gt;
|-&lt;br /&gt;
| Random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; || TCP ISN || RFC 6528 (Gont &amp;amp;amp; Bellovin, 2012).  QUIC PN-space reset (RFC 9000 sec. 12.3) is a structural analogue.&lt;br /&gt;
|-&lt;br /&gt;
| Cumulative ACK, left-window-edge advance || TCP || RFC 793 / RFC 9293&lt;br /&gt;
|-&lt;br /&gt;
| Receive window with non-shrink rule || TCP || RFC 793 sec. 3.7 / RFC 9293 sec. 3.8.6; RFC 1122 sec. 4.2.2.16 for the explicit non-shrink prohibition&lt;br /&gt;
|-&lt;br /&gt;
| Modular &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; arithmetic (&amp;lt;code&amp;gt;before&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;after&amp;lt;/code&amp;gt; helpers) || TCP || RFC 793 sec. 3.3 / RFC 9293 sec. 3.4&lt;br /&gt;
|-&lt;br /&gt;
| Selective ACK block list || TCP || RFC 2018 (Mathis et al., 1996).  Encoded as a typed FRCP packet rather than a TCP option, so framing is closer to QUIC ACK frames.  D-SACK (RFC 2883) carried in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt;; see [[#1.3. SACK payload|Section 1.3]].&lt;br /&gt;
|-&lt;br /&gt;
| NewReno-careful recovery with &amp;lt;code&amp;gt;recovery_high&amp;lt;/code&amp;gt; gate || TCP || RFC 6582 (Henderson et al., 2012); QUIC builds on the same model in RFC 9002 sec. 7.3.2.  Cwnd half absent (CC in IPCP).&lt;br /&gt;
|-&lt;br /&gt;
| RACK reordering window for fast retransmit || TCP || RFC 8985 (Cheng et al., 2021).  FRCP &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor against &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; collapse; matches RFC 8985 sec. 6.2 and Linux &amp;lt;code&amp;gt;tcp_rack_reo_wnd&amp;lt;/code&amp;gt;.  DSACK-driven &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; (sec. 6.2 step 4) is adopted; see [[#1.3. SACK payload|Section 1.3]] for the wire encoding.  The hybrid RACK-or-&amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; trigger from RFC 8985 sec. 6.2 step 4 is adopted ([[#8. Retransmission|Section 8]]).  QUIC&#039;s analogue in RFC 9002 sec. 6.1.2 uses &amp;lt;code&amp;gt;max(srtt, latest_rtt)&amp;lt;/code&amp;gt; as the base.&lt;br /&gt;
|-&lt;br /&gt;
| Karn&#039;s algorithm: no RTT sample on retransmits, RTO-collapse freeze || TCP || Karn &amp;amp;amp; Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, SIGCOMM 1987; RFC 6298 sec. 3.&lt;br /&gt;
|-&lt;br /&gt;
| RTO formula &amp;lt;code&amp;gt;RTO = max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt; || TCP || RFC 6298 (Paxson et al., 2011).  &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; = 250 us is below RFC 6298 sec. 2.4&#039;s 1 s SHOULD-floor - a recursive-layer choice.&lt;br /&gt;
|-&lt;br /&gt;
| Linux asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator (default) || Linux kernel || &amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;if(delta&amp;amp;lt;0) m&amp;amp;gt;&amp;amp;gt;=3&amp;lt;/code&amp;gt; dampening is a kernel divergence from RFC 6298.  RFC 6298 EWMA available behind a compile flag.&lt;br /&gt;
|-&lt;br /&gt;
| Delayed ACK with rate suppression || TCP || RFC 813 (Clark, 1982); RFC 1122 sec. 4.2.3.2; RFC 5681 sec. 4.2.  Single-deadline coalescing rather than &amp;quot;ack-every-other-segment&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| Zero-window-probe / persist-timer analogue (RDVS) || TCP || RFC 1122 sec. 4.2.2.17 / RFC 9293 sec. 3.8.6.1.  RDVS solicits an FC reply, distinct from QUIC &amp;lt;code&amp;gt;DATA_BLOCKED&amp;lt;/code&amp;gt; (RFC 9000 sec. 19.12), which is one-way notification.  &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; give-up departs from TCP.&lt;br /&gt;
|-&lt;br /&gt;
| Multiplexed control on a single PCI || SCTP / QUIC || SCTP chunk bundling (RFC 9260 sec. 6.10); QUIC frame multiplexing (RFC 9000 sec. 12.4).  Cleaner fit than TCP&#039;s separate-flag-bits design.&lt;br /&gt;
|-&lt;br /&gt;
| ACK ranges as multiple discontiguous acked blocks || QUIC || QUIC ACK frame (RFC 9000 sec. 19.3).  FRCP SACK is conceptually QUIC-frame-shaped even though encoded as absolute &amp;lt;code&amp;gt;[start,end]&amp;lt;/code&amp;gt; pairs.&lt;br /&gt;
|-&lt;br /&gt;
| Nonce-authenticated active RTT / liveness probing (RTTP) || QUIC &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;PATH_RESPONSE&amp;lt;/code&amp;gt; (RFC 9000 sec. 8.2, sec. 19.17, sec. 19.18).  WebRTC ICE consent-freshness (RFC 7675) is the same pattern.  QUIC&#039;s nonce is 8 octets; FRCP chooses 16.&lt;br /&gt;
|-&lt;br /&gt;
| Probing distinct from keepalive || QUIC || KA timer answers &amp;quot;peer alive?&amp;quot;, RTTP answers &amp;quot;path measurable?&amp;quot;, as in QUIC PING (RFC 9000 sec. 19.2) vs &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| Bare KA + ACK keepalive packets || QUIC / SCTP || QUIC PING (RFC 9000 sec. 19.2); SCTP HEARTBEAT / HEARTBEAT-ACK (RFC 9260 sec. 8.3).  SCTP HEARTBEAT also carries an opaque echoed blob, structurally similar to FRCP RTTP.&lt;br /&gt;
|-&lt;br /&gt;
| (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) fragment-role bits ([[#7.2. Fragmentation and reassembly|Section 7.2]]) || SCTP || RFC 9260 sec. 3.3.1 DATA chunk B/E bits encode the same four states (&amp;lt;code&amp;gt;B+E=SOLE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;B-only=FIRST&amp;lt;/code&amp;gt;, neither=&amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E-only=LAST&amp;lt;/code&amp;gt;).  Each fragment carries its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;/TSN and is independently retransmitted.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-offset reassembly (Sections [[#1.5. Stream PCI extension|1.5]], [[#16. Stream-mode flows|16]]) || QUIC || QUIC STREAM frame (RFC 9000 sec. 19.8) uses Offset + Length varints; FRCP uses fixed 32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;.  One stream per flow vs QUIC&#039;s many streams multiplexed.&lt;br /&gt;
|-&lt;br /&gt;
| FIN end-of-stream marker (Sections [[#1.2. Flag bits|1.2]], [[#16. Stream-mode flows|16]]) || TCP / QUIC || TCP FIN flag (RFC 9293 sec. 3.1) closes one half of the byte stream; QUIC STREAM frame FIN bit (RFC 9000 sec. 19.8) does the same per stream with an immutable final-size invariance (RFC 9000 sec. 4.5: the final size is fixed once observed).  FRCP&#039;s FIN consumes one packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (not one byte of stream space) and is idempotent on the sender side.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-credit flow control ([[#16. Stream-mode flows|Section 16]]) || QUIC || &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; (RFC 9000 sec. 4.1, sec. 19.10).  FRCP projects a per-flow byte budget onto the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;.  Single stream per flow collapses QUIC&#039;s &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.&lt;br /&gt;
|-&lt;br /&gt;
| Header protection (encrypted seqnos) || QUIC || QUIC RFC 9001 sec. 5.4 applies header protection on top of AEAD to mask the packet number.  FRCP&#039;s per-flow AEAD wrap ([[#16. Stream-mode flows|Section 16]]) is wider: it encrypts the entire PCI including &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; because the IPCP below already routes, so no destination connection-ID needs to stay in clear (cf. RFC 9000 sec. 5.2).&lt;br /&gt;
|-&lt;br /&gt;
| Two-bit fragment role polarity || SCTP || The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair follows SCTP B/E (begin = 1 / end = 1) rather than IPv4 MF (RFC 791 sec. 3.2), which has the inverse polarity (MF = 1 means NOT last).&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal reliability / ordering axes ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) || SCTP || PR-SCTP (RFC 3758, per-message partial reliability) and SCTP DATA U-bit (RFC 9260 sec. 3.3.1, per-message unordered) are the closest precedents for decoupling reliability from ordering; FRCP sets them per-flow rather than per-message.&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal CRC (&amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt;) || UDP-Lite || RFC 3828 (Larzon et al., 2004) lets the sender pick a per-packet Checksum Coverage and the receiver enforce a locally configured minimum (no in-band negotiation; sec. 3.1, sec. 3.3).  FRCP gates a full CRC trailer on &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; at flow setup.  Contrast TCP / SCTP (mandatory checksum) and QUIC (AEAD subsumes CRC).&lt;br /&gt;
|-&lt;br /&gt;
| Setup-time service negotiation || DCCP / SCTP / QUIC || DCCP Service Codes (RFC 4340 sec. 8.1.2, RFC 5595); SCTP INIT parameters (RFC 9260 sec. 3.3.2); QUIC transport parameters (RFC 9000 sec. 7.4).  All negotiate service properties at connection setup; only RINA&#039;s QoS cube exposes them as an orthogonal vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.1. Original to FRCP (no clean prior art) ===&lt;br /&gt;
&lt;br /&gt;
* Pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]): receiver-driven nudge exploiting &amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;.  Closest analogues are SCTP Gap Ack Blocks (RFC 9260 sec. 3.3.4) and DCCP Ack Vector (RFC 4340 sec. 11.4) - both let the receiver describe gaps to the sender, but neither targets the cross-epoch / pre-DRF case.&lt;br /&gt;
* &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; window-probe give-up: neither TCP (persist-timer probes until application or R2 abort, RFC 9293 sec. 3.8.6.1) nor QUIC has an explicit FC-give-up counter.  A recursive-network choice: outer layers can drop the flow.&lt;br /&gt;
* Skip-past-gap reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]): SCTP fragments and reassembles every flow regardless of reliability/ordering, using its own per-stream reassembly queue; QUIC fragments via STREAM offsets.  FRCP fragments best-effort flows too, but the receiver drops the broken prefix the moment a later run-start (&amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; role) is visible inside the &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;-wide reorder ring - no IP-frag-style timeout, no SCTP-style explicit abort.  If no later run-start arrives within the ring, &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt; and the partial run keeps its slots; the next inspect retries.  The trade-off: a permanently-lost &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; in a long isolated run holds slots until either a later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; appears in the ring or the writer stops, at which point the slots are reclaimed on flow teardown.&lt;br /&gt;
* Reassembly deferred to consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]), message mode only (&amp;lt;code&amp;gt;qos.service == SVC_MESSAGE&amp;lt;/code&amp;gt;): SCTP (RFC 9260 sec. 6.9), QUIC (RFC 9000 sec. 2.2), and TCP (RFC 9293) all hold reassembly state at the receive boundary.  FRCP message-mode leaves fragments in the shared-memory ring until &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; pulls and lands the SDU directly in the caller&#039;s buffer.  Stream mode ([[#16. Stream-mode flows|Section 16]]) uses the standard QUIC-style direct ring placement on receive and does not defer.  The optimisation is enabled by the Shared-Memory Subsystem (SSM) packet-buffer ring (see &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt; at [[#1.1. PCI header|Section 1.1]]); the analogue is OS-level scatter-gather I/O (&amp;lt;code&amp;gt;recvmsg+iovec&amp;lt;/code&amp;gt;), not a transport-layer prior art.&lt;br /&gt;
* TLP-equivalent tail-loss recovery (RFC 8985 sec. 7; RFC 9002 sec. 6.2): FRCP does not emit an explicit Tail Loss Probe packet, but the same goal is met implicitly by RACK loss detection ([[#8. Retransmission|Section 8]]) firing on a non-advancing cumulative ACK once the head-of-line slot ages past the RACK reorder window &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; - well below &amp;lt;code&amp;gt;RTO = max(2 * SRTT, SRTT + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.  A receiver-driven nudge is also available via the pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.2. Not adopted ===&lt;br /&gt;
&lt;br /&gt;
* Slow start, congestion window (cwnd), Additive Increase / Multiplicative Decrease (AIMD), NewReno cwnd inflation.  Congestion control lives in the IPCP CA policies and is driven by Explicit Congestion Notification (ECN, RFC 3168).&lt;br /&gt;
* Nagle / silly-window-syndrome (SWS) avoidance (RFC 896, RFC 1122 sec. 4.2.3.4).  (Deferred work, not adopted in the current spec.)&lt;br /&gt;
* TCP Timestamps (RFC 7323) / Protection Against Wrapped Sequences (PAWS) - RTT measurement uses RTTP, not per-segment timestamps.  A peer-supplied timestamp echoed on every ACK lets a malicious peer drive the &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; estimate arbitrarily low, collapsing the RTO and triggering a self-inflicted retransmit storm.  RTTP confines RTT measurement to nonce-authenticated probe round-trips, where a forged echo is rejected before it can reach the estimator.&lt;br /&gt;
* ECN (Explicit Congestion Notification) response inside FRCP (consumed by IPCP Congestion Avoidance / CA).&lt;br /&gt;
* IP-style fragment-offset reassembly (RFC 791 sec. 3.2; RFC 8200 sec. 4.5).  Message-mode FRCP relies on the FRCT &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; reorder ring keyed by &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (shared by FRTX and best-effort flows) to put fragments back in order; no separate offset field is needed and no IP-style hole-list reassembly buffer is kept.  Stream-mode FRCP does carry &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte offsets ([[#1.5. Stream PCI extension|Section 1.5]]) for direct ring placement on receive.&lt;br /&gt;
* QUIC STREAM offset+length framing on &#039;&#039;every&#039;&#039; flow (RFC 9000 sec. 19.8).  Message-mode FRCP uses the SCTP-style B/E flag-bit encoding (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) and skips the offsets; stream-mode FRCP adopts the QUIC offset model (heritage table above).&lt;br /&gt;
&lt;br /&gt;
== 16. Stream-mode flows ==&lt;br /&gt;
&lt;br /&gt;
When a flow is allocated with &amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt; both peers&lt;br /&gt;
switch to byte-stream semantics, layered on top of the FRTX reorder&lt;br /&gt;
machinery already described in Sections [[#6. Receive path|6]]-[[#8. Retransmission|8]].&lt;br /&gt;
&lt;br /&gt;
=== 16.1. Send ===&lt;br /&gt;
&lt;br /&gt;
The sender splits the caller&#039;s octets into chunks of at most&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt; octets (Sections [[#1.1. PCI header|1.1]]&lt;br /&gt;
and [[#1.5. Stream PCI extension|1.5]]).  Each chunk is one DATA packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte range copied from a monotonic stream counter.&lt;br /&gt;
In stream mode &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST be transmitted as&lt;br /&gt;
zero; the per-byte position is carried by the &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt;&lt;br /&gt;
extension instead.&lt;br /&gt;
&lt;br /&gt;
End-of-stream is signalled with a 0-byte DATA packet that has &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;&lt;br /&gt;
(bit 12) set, emitted on the FIN triggers listed in [[#1.2. Flag bits|Section 1.2]]&lt;br /&gt;
(WR-half close, &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, and any other path that yields the&lt;br /&gt;
final byte).  The sender MUST emit at most one FIN per flow; its&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; MUST equal &amp;lt;code&amp;gt;[final-byte, final-byte)&amp;lt;/code&amp;gt; (i.e., empty&lt;br /&gt;
interval at the final byte position; final-size invariance,&lt;br /&gt;
analogous to QUIC RFC 9000 sec. 4.5).  Idempotency is enforced by&lt;br /&gt;
an &amp;lt;code&amp;gt;snd_fin_sent&amp;lt;/code&amp;gt; guard.&lt;br /&gt;
&lt;br /&gt;
=== 16.2. Receive ===&lt;br /&gt;
&lt;br /&gt;
On arrival the receiver places the payload directly into a per-flow&lt;br /&gt;
byte-indexed receive ring of width &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; (octets) at the position&lt;br /&gt;
indicated by &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, with a two-segment &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; across the ring&lt;br /&gt;
boundary if needed.  Receipt is recorded in the FRTX reorder&lt;br /&gt;
machinery ([[#6.2. Locked main path|Section 6.2]]) augmented with the packet&#039;s &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;, and&lt;br /&gt;
FIN bit per slot.  When a packet&#039;s &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; front-overlaps&lt;br /&gt;
bytes already at or below the byte high-water mark, the overlap is&lt;br /&gt;
trimmed before placement so the same byte is never written twice.&lt;br /&gt;
After stashing, the receiver advances &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; and the byte high-water&lt;br /&gt;
mark across any newly-contiguous prefix.  Each slot advanced MUST&lt;br /&gt;
satisfy &amp;lt;code&amp;gt;start == the last-delivered slot&#039;s end&amp;lt;/code&amp;gt;; a slot whose&lt;br /&gt;
&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; does not equal that &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; is silently dropped at delivery time&lt;br /&gt;
(the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is consumed, no stream bytes contributed) and the high-&lt;br /&gt;
water mark does not advance past it.  The stream byte-stream&lt;br /&gt;
stalls at that point - there is no flow-tear-down on mismatch.&lt;br /&gt;
This filters spliced or off-path-injected slots that fall in&lt;br /&gt;
window without strong cryptographic authentication.&lt;br /&gt;
&lt;br /&gt;
A FIN slot marks end-of-stream at advance time only if its byte&lt;br /&gt;
position equals the last-delivered slot&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;; otherwise the FIN&lt;br /&gt;
is ignored and the corresponding &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; occupies a slot but&lt;br /&gt;
contributes no stream bytes.  No packet buffer is held after the&lt;br /&gt;
ring copy.&lt;br /&gt;
&lt;br /&gt;
=== 16.3. Read ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns up to &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt; octets from the contiguous prefix&lt;br /&gt;
&amp;lt;code&amp;gt;[next, high-water)&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;next&amp;lt;/code&amp;gt; is the byte the application has&lt;br /&gt;
already consumed up to and &amp;lt;code&amp;gt;high-water&amp;lt;/code&amp;gt; is the rightmost contiguous&lt;br /&gt;
byte received.  When the stream is fully drained AND end-of-stream&lt;br /&gt;
(EOS) was observed (&amp;lt;code&amp;gt;next == EOS&amp;lt;/code&amp;gt; byte position), &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns&lt;br /&gt;
0 (&amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) - the same shape POSIX &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt; uses on TCP after a peer&lt;br /&gt;
FIN.&lt;br /&gt;
&lt;br /&gt;
=== 16.4. Flow control ===&lt;br /&gt;
&lt;br /&gt;
ACK / SACK / RACK / RTO machinery is unchanged; the FRTX reorder&lt;br /&gt;
ring is reused as a per-&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; received-bitmap.  Let &amp;lt;code&amp;gt;per_pkt =&lt;br /&gt;
(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt;, the maximum stream-&lt;br /&gt;
byte payload one DATA packet can carry ([[#16.1. Send|Section 16.1]]).  The&lt;br /&gt;
receive window advertised in FC is clamped so the byte window&lt;br /&gt;
(&amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt;) cannot be overrun: the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is at most&lt;br /&gt;
&amp;lt;code&amp;gt;rcv_cr.lwe + ring_sz / per_pkt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC byte-credit flow-control model&lt;br /&gt;
(&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt;, RFC 9000 sec. 4.1 and sec. 19.10) projected onto&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; space.  With one stream per flow there is no &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; /&lt;br /&gt;
&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.  Receiver-side silly-window-syndrome&lt;br /&gt;
(SWS) avoidance (RFC 9293 sec. 3.8.6.2.2) is achieved by combining&lt;br /&gt;
the consume-time &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; bump with the global non-shrink rule from&lt;br /&gt;
[[#11. Flow control|Section 11]].&lt;br /&gt;
&lt;br /&gt;
=== 16.5. Security considerations ===&lt;br /&gt;
&lt;br /&gt;
Threat model.  An attacker that can observe (on-path passive) or&lt;br /&gt;
predict (off-path blind) the flow&#039;s seqnos and byte offsets on an&lt;br /&gt;
unencrypted stream flow can inject DATA or FIN at any in-window&lt;br /&gt;
position.  The in-line consistency checks above (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; == prior&lt;br /&gt;
&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; on advance; FIN MUST be 0-byte; FIN MUST sit at the final&lt;br /&gt;
byte position) realise the spirit of RFC 5961&#039;s &amp;quot;sequence-window&lt;br /&gt;
plus exact-position match for control bits&amp;quot; without an explicit&lt;br /&gt;
challenge-ACK probe; they make a few specific blind attack shapes&lt;br /&gt;
harder but are not cryptographic authentication. This is&lt;br /&gt;
comparable to TCP without the TCP Authentication Option (TCP-AO,&lt;br /&gt;
RFC 5925), tighter than a&lt;br /&gt;
pre-RFC-5961 TCP stack, and roughly equivalent to a modern&lt;br /&gt;
RFC 5961 stack against blind off-path injection - none of these&lt;br /&gt;
help once the attacker can sniff. TLS over TCP (RFC 8446)&lt;br /&gt;
encrypts only the TCP payload and leaves TCP seqnos, ACKs, FIN,&lt;br /&gt;
and RST in the clear, so TLS does NOT defend against TCP-header-&lt;br /&gt;
level injection; QUIC (RFC 9000) hides packet numbers under&lt;br /&gt;
header protection (RFC 9001 sec. 5.4), so this specific weakness&lt;br /&gt;
does not apply to QUIC.&lt;br /&gt;
&lt;br /&gt;
Mitigation: AEAD.  When the flow has encryption enabled the&lt;br /&gt;
recommended AEAD ciphers (AES-GCM, RFC 5288; or ChaCha20-Poly1305,&lt;br /&gt;
RFC 8439) wrap the entire FRCP packet on the wire - PCI, stream&lt;br /&gt;
extension, body, and the CRC trailer when &amp;lt;code&amp;gt;ber == 0&amp;lt;/code&amp;gt; - under a&lt;br /&gt;
per-flow symmetric key derived from the flow&#039;s own key exchange&lt;br /&gt;
([[#1.1. PCI header|Section 1.1]]).  The AEAD tag (~2^-128 forgery probability)&lt;br /&gt;
dominates the CRC (~2^-32) for integrity in this mode but the CRC&lt;br /&gt;
trailer is currently retained inside the wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
Implementations MUST NOT rely on the security properties below&lt;br /&gt;
when a non-AEAD cipher (e.g. AES-CTR alone) is negotiated; non-&lt;br /&gt;
AEAD modes provide confidentiality only and the threat-model&lt;br /&gt;
claims do not hold.&lt;br /&gt;
&lt;br /&gt;
With an AEAD cipher in use, seqnos, byte offsets, and the FIN bit&lt;br /&gt;
are both authenticated and confidential. Against an off-path or&lt;br /&gt;
on-path-passive attacker this is:&lt;br /&gt;
&lt;br /&gt;
* Stronger than TCP+TLS (TCP header in the clear).&lt;br /&gt;
* Stronger than TCP+TCP-AO (header authenticated but visible).&lt;br /&gt;
* Comparable to IPsec ESP transport mode (RFC 4303), which similarly authenticates and encrypts the upper-layer header plus payload, and to QUIC packet protection (RFC 9001 sec. 5), with the difference that QUIC must leave the destination connection ID in the clear for routing whereas FRCP relies on the IPCP below for delivery and can therefore encrypt its entire PCI.&lt;br /&gt;
&lt;br /&gt;
Keying granularity. Ouroboros flow allocation runs key exchange (&amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt;) per flow, so each &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; yields independent symmetric keys. This is finer-grained than QUIC (per-connection, RFC 9001, where one handshake covers all multiplexed streams) and finer-grained than typical IPsec deployment (per-host-pair Security Associations, SAs). Forward secrecy follows from the &amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt; when an ephemeral Diffie-Hellman exchange (DHE), or a hybrid mode (classical DH + post-quantum Key Encapsulation Mechanism / KEM), is selected.&lt;br /&gt;
&lt;br /&gt;
Replay protection. The AEAD layer itself does NOT carry an explicit anti-replay window (unlike IPsec ESP, RFC 4303 sec. 3.4.3, or DTLS, RFC 9147 sec. 4.5.1).  For FRCP-engaged flows the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space duplicate-suppression in [[#6.2. Locked main path|Section 6.2]] rejects replayed&lt;br /&gt;
DATA after the AEAD strips the wrap, because the AEAD authenticates the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a replay re-presents an old &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; that is then discarded either as a duplicate (still inside the receive window or as outside the receive window, depending on how far &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; has advanced since the original packet was delivered. RAW (&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) flows have no FRCP layer and therefore no replay protection at the AEAD layer either; deployments that need replay rejection on RAW flows SHOULD use SVC_MESSAGE.&lt;br /&gt;
&lt;br /&gt;
Layering. The AEAD wrap sits below FRCP on the data path, so RAW best-effort flows (&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;, the UDP-equivalent service of [[#2.2. Service modes (orthogonal axes)|Section 2.2]]) inherit the same per-flow integrity + confidentiality scope as FRCP-engaged flows - whatever the process and FRCP (if any) put on the wire is what the AEAD authenticates. No DTLS-equivalent layering is required for confidentiality and integrity; replay protection above AEAD is a separate concern as noted above.&lt;br /&gt;
&lt;br /&gt;
== 17. References ==&lt;br /&gt;
&lt;br /&gt;
This section lists the IETF documents, published works, and&lt;br /&gt;
source-code references cited inline elsewhere in this document.&lt;br /&gt;
IETF documents are cited inline as &amp;quot;RFC NNNN sec. X.Y&amp;quot;; books,&lt;br /&gt;
journal papers, and source-code references are cited inline by&lt;br /&gt;
author and year (or by file and function name) and are listed&lt;br /&gt;
here for convenience.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.1. IETF documents ===&lt;br /&gt;
&lt;br /&gt;
;[RFC 791]&lt;br /&gt;
:J. Postel, &amp;quot;Internet Protocol&amp;quot;, STD 5, RFC 791, September 1981.&lt;br /&gt;
;[RFC 793]&lt;br /&gt;
:J. Postel, &amp;quot;Transmission Control Protocol&amp;quot;, STD 7, RFC 793, September 1981.  Obsoleted by RFC 9293.&lt;br /&gt;
;[RFC 813]&lt;br /&gt;
:D. D. Clark, &amp;quot;Window and Acknowledgement Strategy in TCP&amp;quot;, RFC 813, July 1982.&lt;br /&gt;
;[RFC 896]&lt;br /&gt;
:J. Nagle, &amp;quot;Congestion Control in IP/TCP Internetworks&amp;quot;, RFC 896, January 1984.&lt;br /&gt;
;[RFC 1122]&lt;br /&gt;
:R. Braden (ed.), &amp;quot;Requirements for Internet Hosts -- Communication Layers&amp;quot;, STD 3, RFC 1122, October 1989.&lt;br /&gt;
;[RFC 2018]&lt;br /&gt;
:M. Mathis, J. Mahdavi, S. Floyd, A. Romanow, &amp;quot;TCP Selective Acknowledgment Options&amp;quot;, RFC 2018, October 1996.&lt;br /&gt;
;[RFC 2119]&lt;br /&gt;
:S. Bradner, &amp;quot;Key words for use in RFCs to Indicate Requirement Levels&amp;quot;, BCP 14, RFC 2119, March 1997.&lt;br /&gt;
;[RFC 2883]&lt;br /&gt;
:S. Floyd, J. Mahdavi, M. Mathis, M. Podolsky, &amp;quot;An Extension to the Selective Acknowledgement (SACK) Option for TCP&amp;quot;, RFC 2883, July 2000.&lt;br /&gt;
;[RFC 3758]&lt;br /&gt;
:R. Stewart, M. Ramalho, Q. Xie, M. Tuexen, P. Conrad, &amp;quot;Stream Control Transmission Protocol (SCTP) Partial Reliability Extension&amp;quot;, RFC 3758, May 2004.&lt;br /&gt;
;[RFC 3828]&lt;br /&gt;
:L-A. Larzon, M. Degermark, S. Pink, L-E. Jonsson (ed.), G. Fairhurst (ed.), &amp;quot;The Lightweight User Datagram Protocol (UDP-Lite)&amp;quot;, RFC 3828, July 2004.&lt;br /&gt;
;[RFC 4303]&lt;br /&gt;
:S. Kent, &amp;quot;IP Encapsulating Security Payload (ESP)&amp;quot;, RFC 4303, December 2005.&lt;br /&gt;
;[RFC 4340]&lt;br /&gt;
:E. Kohler, M. Handley, S. Floyd, &amp;quot;Datagram Congestion Control Protocol (DCCP)&amp;quot;, RFC 4340, March 2006.&lt;br /&gt;
;[RFC 5288]&lt;br /&gt;
:J. Salowey, A. Choudhury, D. McGrew, &amp;quot;AES Galois Counter Mode (GCM) Cipher Suites for TLS&amp;quot;, RFC 5288, August 2008.&lt;br /&gt;
;[RFC 5595]&lt;br /&gt;
:G. Fairhurst, &amp;quot;The Datagram Congestion Control Protocol (DCCP) Service Codes&amp;quot;, RFC 5595, September 2009.&lt;br /&gt;
;[RFC 5681]&lt;br /&gt;
:M. Allman, V. Paxson, E. Blanton, &amp;quot;TCP Congestion Control&amp;quot;, RFC 5681, September 2009.&lt;br /&gt;
;[RFC 5925]&lt;br /&gt;
:J. Touch, A. Mankin, R. Bonica, &amp;quot;The TCP Authentication Option&amp;quot;, RFC 5925, June 2010.&lt;br /&gt;
;[RFC 5961]&lt;br /&gt;
:A. Ramaiah, R. Stewart, M. Dalal, &amp;quot;Improving TCP&#039;s Robustness to Blind In-Window Attacks&amp;quot;, RFC 5961, August 2010.&lt;br /&gt;
;[RFC 6298]&lt;br /&gt;
:V. Paxson, M. Allman, J. Chu, M. Sargent, &amp;quot;Computing TCP&#039;s Retransmission Timer&amp;quot;, RFC 6298, June 2011.&lt;br /&gt;
;[RFC 6528]&lt;br /&gt;
:F. Gont, S. Bellovin, &amp;quot;Defending against Sequence Number Attacks&amp;quot;, RFC 6528, February 2012.  Obsoletes RFC 1948.&lt;br /&gt;
;[RFC 6582]&lt;br /&gt;
:T. Henderson, S. Floyd, A. Gurtov, Y. Nishida, &amp;quot;The NewReno Modification to TCP&#039;s Fast Recovery Algorithm&amp;quot;, RFC 6582, April 2012.&lt;br /&gt;
;[RFC 7323]&lt;br /&gt;
:D. Borman, B. Braden, V. Jacobson, R. Scheffenegger (ed.), &amp;quot;TCP Extensions for High Performance&amp;quot;, RFC 7323, September 2014.&lt;br /&gt;
;[RFC 7675]&lt;br /&gt;
:M. Perumal, D. Wing, R. Ravindranath, T. Reddy, M. Thomson, &amp;quot;Session Traversal Utilities for NAT (STUN) Usage for Consent Freshness&amp;quot;, RFC 7675, October 2015.&lt;br /&gt;
;[RFC 8174]&lt;br /&gt;
:B. Leiba, &amp;quot;Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words&amp;quot;, BCP 14, RFC 8174, May 2017.&lt;br /&gt;
;[RFC 8200]&lt;br /&gt;
:S. Deering, R. Hinden, &amp;quot;Internet Protocol, Version 6 (IPv6) Specification&amp;quot;, STD 86, RFC 8200, July 2017.&lt;br /&gt;
;[RFC 8439]&lt;br /&gt;
:Y. Nir, A. Langley, &amp;quot;ChaCha20 and Poly1305 for IETF Protocols&amp;quot;, RFC 8439, June 2018.&lt;br /&gt;
;[RFC 8446]&lt;br /&gt;
:E. Rescorla, &amp;quot;The Transport Layer Security (TLS) Protocol Version 1.3&amp;quot;, RFC 8446, August 2018.&lt;br /&gt;
;[RFC 8985]&lt;br /&gt;
:Y. Cheng, N. Cardwell, N. Dukkipati, P. Jha, &amp;quot;The RACK-TLP Loss Detection Algorithm for TCP&amp;quot;, RFC 8985, February 2021.&lt;br /&gt;
;[RFC 9000]&lt;br /&gt;
:J. Iyengar (ed.), M. Thomson (ed.), &amp;quot;QUIC: A UDP-Based Multiplexed and Secure Transport&amp;quot;, RFC 9000, May 2021.&lt;br /&gt;
;[RFC 9001]&lt;br /&gt;
:M. Thomson (ed.), S. Turner (ed.), &amp;quot;Using TLS to Secure QUIC&amp;quot;, RFC 9001, May 2021.&lt;br /&gt;
;[RFC 9002]&lt;br /&gt;
:J. Iyengar (ed.), I. Swett (ed.), &amp;quot;QUIC Loss Detection and Congestion Control&amp;quot;, RFC 9002, May 2021.&lt;br /&gt;
;[RFC 9147]&lt;br /&gt;
:E. Rescorla, H. Tschofenig, N. Modadugu, &amp;quot;The Datagram Transport Layer Security (DTLS) Protocol Version 1.3&amp;quot;, RFC 9147, April 2022.&lt;br /&gt;
;[RFC 9260]&lt;br /&gt;
:R. Stewart, M. Tuexen, K. Nielsen, &amp;quot;Stream Control Transmission Protocol&amp;quot;, RFC 9260, June 2022.  Obsoletes RFC 4960.&lt;br /&gt;
;[RFC 9293]&lt;br /&gt;
:W. Eddy (ed.), &amp;quot;Transmission Control Protocol (TCP)&amp;quot;, STD 7, RFC 9293, August 2022.  Obsoletes RFC 793 and several follow-ons; updates RFC 1122 and others.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.2. Books and journal papers ===&lt;br /&gt;
&lt;br /&gt;
;[Day08]&lt;br /&gt;
:J. Day, &amp;quot;Patterns in Network Architecture: A Return to Fundamentals&amp;quot;, Prentice Hall, 2008.&lt;br /&gt;
;[Grasa15]&lt;br /&gt;
:E. Grasa et al., &amp;quot;IRATI: investigating RINA as an alternative to TCP/IP&amp;quot;, Computer Networks, Vol. 92, December 2015.&lt;br /&gt;
;[KP87]&lt;br /&gt;
:P. Karn, C. Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, ACM SIGCOMM, August 1987.&lt;br /&gt;
;[Wat81]&lt;br /&gt;
:R. W. Watson, &amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection Management&amp;quot;, Computer Networks, Vol. 5, 1981.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.3. Source-code references ===&lt;br /&gt;
&lt;br /&gt;
;[Linux-RTT]&lt;br /&gt;
:&amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt; of the Linux kernel, defining the asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; variance update used as FRCP&#039;s default RTT estimator ([[#12. RTT estimation|Section 12]]).  Line-stable browseable copy at https://elixir.bootlin.com/linux/latest/source/net/ipv4/tcp_input.c.&lt;br /&gt;
&lt;br /&gt;
[[Category:Protocols]]&lt;br /&gt;
[[Category:Ouroboros internals]]&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1924</id>
		<title>Flow and Retransmission Control Protocol</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1924"/>
		<updated>2026-05-18T16:06:09Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 4. Sequence-number rotation (DRF) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:FRCP - Flow and Retransmission Control Protocol}}&lt;br /&gt;
&lt;br /&gt;
FRCP runs end-to-end between two peers over a flow.  It delivers&lt;br /&gt;
reliability, in-order delivery, flow control, and liveness.&lt;br /&gt;
Congestion Control (CC) is not in FRCP - that lives in the IPC&lt;br /&gt;
Process (IPCP) Congestion Avoidance (CA) policies, orthogonal to&lt;br /&gt;
FRCP.  Flow allocation, naming, and IPCP lifecycle are handled by&lt;br /&gt;
the IPC Resource Manager daemon (IRMd).&lt;br /&gt;
&lt;br /&gt;
FRCT (Flow and Retransmission Control Task) is the libouroboros&lt;br /&gt;
implementation of FRCP; the task lives in &amp;lt;code&amp;gt;src/lib/frct.c&amp;lt;/code&amp;gt;.  The&lt;br /&gt;
remainder of this document describes the FRCP wire protocol and the&lt;br /&gt;
behaviour FRCT realises.  Code symbols retain the &amp;lt;code&amp;gt;FRCT_&amp;lt;/code&amp;gt; prefix&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, ...) because they belong to the implementing&lt;br /&gt;
task; this document references them verbatim.&lt;br /&gt;
&lt;br /&gt;
The keywords &amp;quot;MUST&amp;quot;, &amp;quot;MUST NOT&amp;quot;, &amp;quot;REQUIRED&amp;quot;, &amp;quot;SHALL&amp;quot;, &amp;quot;SHALL NOT&amp;quot;,&lt;br /&gt;
&amp;quot;SHOULD&amp;quot;, &amp;quot;SHOULD NOT&amp;quot;, &amp;quot;RECOMMENDED&amp;quot;, &amp;quot;MAY&amp;quot;, and &amp;quot;OPTIONAL&amp;quot; in&lt;br /&gt;
this document are to be interpreted as described in &amp;lt;code&amp;gt;BCP&amp;lt;/code&amp;gt; 14 (Best&lt;br /&gt;
Current Practice; RFC 2119, RFC 8174) when, and only when, they&lt;br /&gt;
appear in all capitals.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:Unsigned 32-bit / 8-bit integers (kernel-C style).&lt;br /&gt;
;&amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:Nanoseconds.&lt;br /&gt;
&lt;br /&gt;
Modular sequence-number comparators (32-bit, modulo 2^32):&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;before(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;(int32_t)(a - b) &amp;amp;lt; 0&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;after(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;before(b, a)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Used throughout for &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; ordering checks.&lt;br /&gt;
&lt;br /&gt;
Round-Trip Time (RTT) abbreviations used throughout:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;&lt;br /&gt;
:Smoothed RTT estimate (RFC 6298).&lt;br /&gt;
;&amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt;&lt;br /&gt;
:Mean deviation of RTT (Linux variance estimator).&lt;br /&gt;
;&amp;lt;code&amp;gt;EWMA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Exponentially Weighted Moving Average.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTO&amp;lt;/code&amp;gt;&lt;br /&gt;
:Retransmission Timeout, &amp;lt;code&amp;gt;max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Timer-bound symbols &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer, ACK delay) and &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer,&lt;br /&gt;
retransmission window) are defined in [[#8. Retransmission|Section 8]]; &amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; (Maximum&lt;br /&gt;
Packet Lifetime) is introduced in [[#2.1. Per-flow state|Section 2.1]] (the &amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; field)&lt;br /&gt;
with heritage in [[#15. Heritage and adopted techniques|Section 15]].&lt;br /&gt;
&lt;br /&gt;
Wire-format diagrams follow the IETF convention: bit 0 is the&lt;br /&gt;
leftmost (most significant) bit and fields are in network byte&lt;br /&gt;
order unless stated otherwise.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1. Wire format ==&lt;br /&gt;
&lt;br /&gt;
=== 1.1. PCI header ===&lt;br /&gt;
&lt;br /&gt;
Fixed 16-octet base Protocol-Control Information (PCI) header&lt;br /&gt;
prefixed to every FRCP packet (RFC convention: bit 0 leftmost,&lt;br /&gt;
most-significant bit first).  All multi-byte fields are in network byte order. DATA packets on&lt;br /&gt;
stream-mode flows carry an additional 8-octet extension (see&lt;br /&gt;
[[#1.5. Stream PCI extension|Section 1.5]]); SACK and RTTP carry their own payloads after the&lt;br /&gt;
base PCI.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |             flags             |              hcs              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            window                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            seqno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            ackno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                     payload (variable) ...&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt;&lt;br /&gt;
:feature/type bitmap (see [[#1.2. Flag bits|Section 1.2]]).&lt;br /&gt;
;&amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;&lt;br /&gt;
:CRC-16-CCITT-FALSE Header Check Sequence (HCS) over &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; (+ stream extension when present); the two octets of the &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt; field itself are omitted from the CRC input.  Verified on receive before any flag-driven dispatch.&lt;br /&gt;
;&amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt;&lt;br /&gt;
:receiver-advertised right window edge (valid iff FC).&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-flow sequence number.&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;&lt;br /&gt;
:cumulative Acknowledgement (ACK) (valid iff ACK).&lt;br /&gt;
&lt;br /&gt;
A single packet can simultaneously carry DATA + ACK + FC (Flow&lt;br /&gt;
Control) + RXM (Retransmission) by ORing flag bits; the PCI&lt;br /&gt;
multiplexes control on the same wire frame in the spirit of SCTP&lt;br /&gt;
chunk bundling (RFC 9260 sec. 6.10) and QUIC frame multiplexing&lt;br /&gt;
(RFC 9000 sec. 12.4).  DATA-bearing packets carry the caller&#039;s&lt;br /&gt;
payload after the PCI; SACK (Selective Acknowledgement) and RTTP&lt;br /&gt;
(Round-Trip Time Probe) carry their own typed payloads after the&lt;br /&gt;
PCI.&lt;br /&gt;
&lt;br /&gt;
Optional framing (per-flow, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]]).  On the wire, the&lt;br /&gt;
order from inside out is:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Layer !! Scope&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| The FRCP packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body + CRC-32 ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| CRC-32 covers the body only (PCI is in HCS); appended iff &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; on DATA, or on every SACK packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ AEAD-wrap of above ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| Iff Authenticated Encryption with Associated Data (AEAD) is enabled.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* HCS in the PCI covers the header fields on every packet and is verified before any flag-driven dispatch.&lt;br /&gt;
* The CRC-32 trailer (IEEE 802.3 / zlib reflected polynomial &amp;lt;code&amp;gt;0xEDB88320&amp;lt;/code&amp;gt;, init &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;, xor-out &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;) covers the body on DATA when &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; and on every SACK packet. The PCI is not under the CRC (Cyclic Redundancy Check) because the HCS already protects it. It is appended before AEAD encryption and therefore rides inside the AEAD wrap when both are active; the AEAD tag (~2^-128 forgery probability) dominates the CRC (~2^-32) for integrity in that mode but the CRC trailer is currently retained.&lt;br /&gt;
* When encryption is enabled, the entire (possibly-CRC&#039;d) FRCP packet is wrapped with AEAD inside the shared-memory packet buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt;); the packet grows by the AEAD overhead, namely a leading nonce / Initialization Vector (IV) of &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_ivsz&amp;lt;/code&amp;gt;) and a trailing authentication tag of &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_tagsz&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Both CRC and AEAD are layered around the FRCP wire format and are not visible to the FRCP machinery itself.&lt;br /&gt;
&lt;br /&gt;
=== 1.2. Flag bits ===&lt;br /&gt;
&lt;br /&gt;
Flag bits are numbered most-significant-bit first to match the wire&lt;br /&gt;
diagram (bit numbering per [[#1.1. PCI header|Section 1.1]]; bit 0 is the MSB of the&lt;br /&gt;
16-bit &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; field and lands at wire-position 0 in network byte&lt;br /&gt;
order).  Bits 13..15 are reserved and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Bit !! Mask !! Name !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| 0 || &amp;lt;code&amp;gt;0x8000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt; || Carries caller payload&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &amp;lt;code&amp;gt;0x4000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DRF&amp;lt;/code&amp;gt; || Data Run Flag: start of a fresh run&lt;br /&gt;
|-&lt;br /&gt;
| 2 || &amp;lt;code&amp;gt;0x2000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt; || Acknowledgement: &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field valid&lt;br /&gt;
|-&lt;br /&gt;
| 3 || &amp;lt;code&amp;gt;0x1000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt; || Negative ACK; &amp;lt;code&amp;gt;seqno = arrival_seqno-1&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || &amp;lt;code&amp;gt;0x0800&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt; || Flow Control: &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; field valid (&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || &amp;lt;code&amp;gt;0x0400&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt; || Rendezvous probe (window-closed)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || &amp;lt;code&amp;gt;0x0200&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; || First Fragment (role bit 0; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || &amp;lt;code&amp;gt;0x0100&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; || Last Fragment (role bit 1; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 8 || &amp;lt;code&amp;gt;0x0080&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RXM&amp;lt;/code&amp;gt; || Retransmission&lt;br /&gt;
|-&lt;br /&gt;
| 9 || &amp;lt;code&amp;gt;0x0040&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt; || Selective ACK block list in payload&lt;br /&gt;
|-&lt;br /&gt;
| 10 || &amp;lt;code&amp;gt;0x0020&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt; || RTT Probe / echo (payload follows)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || &amp;lt;code&amp;gt;0x0010&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt; || Keepalive&lt;br /&gt;
|-&lt;br /&gt;
| 12 || &amp;lt;code&amp;gt;0x0008&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt; || End-of-stream marker (stream mode)&lt;br /&gt;
|-&lt;br /&gt;
| 13-15 || -- || -- || Reserved (MUST be zero)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair encodes the fragment role of a DATA-bearing&lt;br /&gt;
Service Data Unit (SDU), SCTP-style begin/end flags (RFC 9260&lt;br /&gt;
sec. 3.3.1):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FFGM !! LFGM !! Role&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || Sole / un-fragmented SDU (begin AND end)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || First fragment of a multi-fragment SDU&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Middle fragment&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Last fragment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Each fragment is carried in its own FRCP packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;;&lt;br /&gt;
FRTX (the FRCT Retransmission service mode, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]])&lt;br /&gt;
recovers individual fragments via the normal Retransmission Timeout&lt;br /&gt;
(RTO) / SACK / Recent Acknowledgement (RACK, RFC 8985) path.  The&lt;br /&gt;
receiver reassembles the SDU at consume time once the contiguous&lt;br /&gt;
[FIRST .. LAST] run has fully arrived.  On non-DATA packets the role&lt;br /&gt;
bits are unused and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
In stream mode (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;, see [[#16. Stream-mode flows|Section 16]]) there are&lt;br /&gt;
no SDU boundaries to encode, so &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST&lt;br /&gt;
be transmitted as zero.  End-of-stream uses a dedicated bit (&amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;,&lt;br /&gt;
bit 12) carried on a 0-byte DATA packet, emitted at write-half close&lt;br /&gt;
(&amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;FLOWFRDONLY&amp;lt;/code&amp;gt;), during linger drain, and at &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;;&lt;br /&gt;
emission is idempotent (first call wins).  After contiguous delivery&lt;br /&gt;
of the FIN-bearing slot, the receiver latches &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt; at the FIN&#039;s&lt;br /&gt;
start offset; &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns 0 (end-of-file, &amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) once buffered&lt;br /&gt;
bytes have been drained up to &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt;.  Per-byte position is&lt;br /&gt;
carried by the [start, end) extension ([[#1.5. Stream PCI extension|Section 1.5]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.3. SACK payload ===&lt;br /&gt;
&lt;br /&gt;
A SACK packet has the &amp;lt;code&amp;gt;FRCT_ACK | FRCT_FC | FRCT_SACK&amp;lt;/code&amp;gt; flag bits set&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI, the&lt;br /&gt;
payload is a 2-octet block count (network byte order), 2 octets of&lt;br /&gt;
padding to 4-byte align the block list, then &amp;lt;code&amp;gt;n_blocks&amp;lt;/code&amp;gt; pairs of&lt;br /&gt;
32-bit start/end seqnos describing &#039;&#039;present&#039;&#039; (received) ranges&lt;br /&gt;
above the cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |           n_blocks            |        padding (2 octets)     |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[0]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            end[0]                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[1]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
                       ... n_blocks pairs total ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;n_blocks &amp;amp;lt;= SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; (2048).  The per-flow effective cap is&lt;br /&gt;
further bounded by &amp;lt;code&amp;gt;(frag_mtu - PCI - 4) / 8&amp;lt;/code&amp;gt; blocks per packet; SACK&lt;br /&gt;
packets carry no stream extension, so PCI here is the 16-octet base&lt;br /&gt;
header even on stream-mode flows.&lt;br /&gt;
&lt;br /&gt;
Wire invariant: every block produced by the receiver, except an&lt;br /&gt;
optional leading Duplicate SACK (D-SACK) block as described below,&lt;br /&gt;
describes a range strictly above the cumulative ACK carried in the&lt;br /&gt;
PCI &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field (&amp;lt;code&amp;gt;after(start[i], ackno)&amp;lt;/code&amp;gt;).  This makes the D-SACK&lt;br /&gt;
convention below unambiguous; the receiver-side builder MUST&lt;br /&gt;
preserve it.&lt;br /&gt;
&lt;br /&gt;
Duplicate SACK (D-SACK, RFC 2883) is signalled in-band: no flag&lt;br /&gt;
bit, no extra framing.  Modular seqno arithmetic uses the&lt;br /&gt;
&amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators defined in the Notation block.&lt;br /&gt;
&lt;br /&gt;
Encoding.  When a duplicate is observed the receiver arms a&lt;br /&gt;
single-slot pending report (&amp;lt;code&amp;gt;dsack_seqno&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;dsack_valid&amp;lt;/code&amp;gt;,&lt;br /&gt;
latest-wins across multiple arms before the next emit).  On the&lt;br /&gt;
next outbound SACK the receiver prepends &amp;lt;code&amp;gt;block[0] = [dsack_seqno,&lt;br /&gt;
dsack_seqno + 1)&amp;lt;/code&amp;gt; - always a one-&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; range - and clears the&lt;br /&gt;
flag.  The three arm sites are listed in [[#10. Cumulative + selective ACK|Section 10]]; case-1 sites&lt;br /&gt;
yield &amp;lt;code&amp;gt;dsack_seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt; (the next &amp;lt;code&amp;gt;pci.ackno&amp;lt;/code&amp;gt;), and the&lt;br /&gt;
case-2 site (&amp;lt;code&amp;gt;rq_accept&amp;lt;/code&amp;gt; conflict) yields &amp;lt;code&amp;gt;dsack_seqno&amp;lt;/code&amp;gt; in&lt;br /&gt;
&amp;lt;code&amp;gt;[rcv_cr.lwe, rcv_cr.rwe)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Detection.  The sender classifies &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; by its relation to&lt;br /&gt;
&amp;lt;code&amp;gt;pci.ackno&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
;case 1 (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;before(blocks[0].start, pci.ackno)&amp;lt;/code&amp;gt; AND &amp;lt;code&amp;gt;pci.ackno - blocks[0].start &amp;amp;lt;= MAX_DSACK_LAG&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;== RQ_SIZE&amp;lt;/code&amp;gt;).  The lag bound rejects stale or spoofed reports beyond one receive window.&lt;br /&gt;
;case 2 (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;blocks[0]&amp;lt;/code&amp;gt; is a sub-range (with at least one endpoint differing) of some &amp;lt;code&amp;gt;blocks[i&amp;amp;gt;0]&amp;lt;/code&amp;gt; - i.e. the same packet&#039;s remaining SACK blocks already describe the duplicated &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; as received.&lt;br /&gt;
&lt;br /&gt;
On detect, the sender:&lt;br /&gt;
&lt;br /&gt;
* bumps &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; by 1, capped at &amp;lt;code&amp;gt;REO_WND_MULT_MAX&amp;lt;/code&amp;gt; (= 20), per RFC 8985 sec. 6.2 step 4;&lt;br /&gt;
* snapshots &amp;lt;code&amp;gt;dsack_lwe_snap = snd_cr.lwe&amp;lt;/code&amp;gt;, resetting the 16-cum-ACK halving counter so the multiplier doesn&#039;t decay while D-SACK evidence is still arriving;&lt;br /&gt;
* excludes &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; from the gap-marking loop (&amp;lt;code&amp;gt;n_real = n - 1&amp;lt;/code&amp;gt;), so a D-SACK alone never enters NewReno-careful recovery (see [[#8. Retransmission|Section 8]]); only non-D-SACK blocks count as gaps.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; halving cadence (once per 16 cumulatively-ACK&#039;d&lt;br /&gt;
seqnos since the most-recent D-SACK arrival or halve event) and&lt;br /&gt;
the reset-to-1 on a HoL RTO fire are both per the same RFC 8985&lt;br /&gt;
clause.  The clamp-and-skip path in the regular SACK-mark loop is&lt;br /&gt;
incidentally idempotent on any leftover case-1 or case-2 block&lt;br /&gt;
(&amp;lt;code&amp;gt;start &amp;amp;lt; snd_cr.lwe&amp;lt;/code&amp;gt; clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; and the inner loop&lt;br /&gt;
skips &amp;lt;code&amp;gt;k == snd_cr.lwe&amp;lt;/code&amp;gt;; case-2 re-NULLs slots already marked&lt;br /&gt;
received by later blocks), so block[0] is harmless even when fed&lt;br /&gt;
to the loop.&lt;br /&gt;
&lt;br /&gt;
=== 1.4. RTTP payload ===&lt;br /&gt;
&lt;br /&gt;
An RTTP (Round-Trip Time Probe) packet has only the &amp;lt;code&amp;gt;FRCT_RTTP&amp;lt;/code&amp;gt; flag&lt;br /&gt;
set (bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI,&lt;br /&gt;
the payload is 24 octets (packed):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          probe_id                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          echo_id                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +                  nonce (16 octets, echoed verbatim)           +&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:sender counter, 0 on reply, 0 reserved.&lt;br /&gt;
;&amp;lt;code&amp;gt;echo_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:peer&#039;s &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;, 0 on outbound probe.&lt;br /&gt;
;&amp;lt;code&amp;gt;nonce&amp;lt;/code&amp;gt;&lt;br /&gt;
:random, echoed unmodified, memcmp&#039;d to defeat spoof.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.5. Stream PCI extension ===&lt;br /&gt;
&lt;br /&gt;
A stream-mode flow (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;) carries an extra&lt;br /&gt;
8-octet extension after the 16-octet base PCI on every DATA packet&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            start                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                             end                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset of the first payload byte in the stream.&lt;br /&gt;
;&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset one past the last payload byte; &amp;lt;code&amp;gt;end - start&amp;lt;/code&amp;gt; equals the on-wire payload length.&lt;br /&gt;
&lt;br /&gt;
Total stream-mode PCI for DATA packets is 24 octets (16 base + 8&lt;br /&gt;
extension); control packets (SACK, RTTP, bare ACK, KA, etc.) retain&lt;br /&gt;
the 16-octet base PCI.  Stream mode MUST be negotiated at flow&lt;br /&gt;
allocation; the extension is present iff stream mode is in use,&lt;br /&gt;
never on a per-packet basis.  Both peers MUST treat &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; as&lt;br /&gt;
monotonic 32-bit byte offsets; when a slot reaches the head of the&lt;br /&gt;
contiguous run with &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; not equal to the prior packet&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; the&lt;br /&gt;
slot is silently dropped at delivery time ([[#16. Stream-mode flows|Section 16]]) rather&lt;br /&gt;
than rejected at stash.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC STREAM-frame reassembly model (RFC 9000 sec. 19.8):&lt;br /&gt;
each packet carries its packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (this PCI&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; field) and a&lt;br /&gt;
separate stream byte position (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;).  Separating the two&lt;br /&gt;
avoids TCP&#039;s conflation of packet identity with byte position which&lt;br /&gt;
forces Karn&#039;s algorithm for Round-Trip Time (RTT) sampling (no RTT&lt;br /&gt;
sample on retransmits, RFC 6298 sec. 3); FRCP applies the&lt;br /&gt;
Karn-equivalent gate via a combination of per-packet &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;,&lt;br /&gt;
per-slot &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; flags, and a sample-fence &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; (see [[#2.1. Per-flow state|Section 2.1]]&lt;br /&gt;
and [[#12. RTT estimation|Section 12]]).  FRCP&#039;s fixed-32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; wrap at 4 GiB of&lt;br /&gt;
wire bytes, narrower than QUIC&#039;s 62-bit varint offset (cf. RFC 9000&lt;br /&gt;
sec. 16); the on-wire wrap is handled by the same modular &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;&lt;br /&gt;
/ &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators ([[#1.3. SACK payload|Section 1.3]]) FRCP uses for seqnos, which&lt;br /&gt;
remain unambiguous as long as the in-flight byte window stays&lt;br /&gt;
strictly under 2 GiB (the half-range of the signed-int32 difference&lt;br /&gt;
in &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;).  The default per-flow ring is 1 MiB; the&lt;br /&gt;
implementation caps &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; at 128 MiB (&amp;lt;code&amp;gt;FRCT_STREAM_RING_SZ_MAX&amp;lt;/code&amp;gt;),&lt;br /&gt;
well below the 2 GiB half-range bound.  The runtime byte counters&lt;br /&gt;
exposed via FUSE (Filesystem in Userspace) in the Ouroboros&lt;br /&gt;
Resource Information Base (RIB, a virtual-filesystem introspection&lt;br /&gt;
bridge) are platform &amp;lt;code&amp;gt;size_t&amp;lt;/code&amp;gt; and do not wrap on 64-bit hosts.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2. Per-flow state and service modes ==&lt;br /&gt;
&lt;br /&gt;
=== 2.1. Per-flow state ===&lt;br /&gt;
&lt;br /&gt;
Each flow keeps a sender control record and a receiver control&lt;br /&gt;
record:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: oldest unacked &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (cumulative ACK boundary as seen by sender); rcv: next in-order &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; expected&lt;br /&gt;
;&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: peer-advertised right window edge; rcv: locally-advertised right window edge&lt;br /&gt;
;&amp;lt;code&amp;gt;cflags&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-direction feature flags: retransmission (&amp;lt;code&amp;gt;FRCTFRTX&amp;lt;/code&amp;gt;), receiver flow control (&amp;lt;code&amp;gt;FRCTFRESCNTL&amp;lt;/code&amp;gt;), linger-on-close (&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt;); see &amp;lt;code&amp;gt;&amp;amp;lt;ouroboros/fccntl.h&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: next &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; to send; rcv: force-ACK trigger - set on a stale or dup DATA so the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; emits a fresh cumulative ACK&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; counter for standalone ACK-bearing control packets (delayed ACK, SACK, final ACK on dealloc); not bumped on piggybacked ACK riding a DATA packet (which uses the DATA &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;).  Used by wire-dup ACK detection; rcv: incoming-ACK dedup tracker&lt;br /&gt;
;&amp;lt;code&amp;gt;act&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:last activity (used by inactivity / DRF)&lt;br /&gt;
;&amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:inactivity threshold; sender = &amp;lt;code&amp;gt;3*mpl + a + r + 1s&amp;lt;/code&amp;gt;, receiver = &amp;lt;code&amp;gt;2*mpl + a + r + 1s&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;mpl&amp;lt;/code&amp;gt; is the Maximum Packet Lifetime (delta-t terminology; see [[#15. Heritage and adopted techniques|Section 15]]); &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; are the FRCT a-timer and r-timer bounds (see [[#8. Retransmission|Section 8]]).  The asymmetry is load-bearing for pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
The sender holds a per-slot ring &amp;lt;code&amp;gt;snd_slots[RQ_SIZE]&amp;lt;/code&amp;gt; keyed by&lt;br /&gt;
&amp;lt;code&amp;gt;(seqno mod RQ_SIZE)&amp;lt;/code&amp;gt;.  Each slot tracks its retransmit entry (&amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;),&lt;br /&gt;
last-send timestamp, and retransmit flag bits: &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (a&lt;br /&gt;
retransmit is pending or has fired, gates the next RTT sample&lt;br /&gt;
under Karn) and &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (one-shot fast-retransmit staged for&lt;br /&gt;
this loss event).&lt;br /&gt;
&lt;br /&gt;
The receiver holds a parallel reorder ring &amp;lt;code&amp;gt;rcv_slots[RQ_SIZE]&amp;lt;/code&amp;gt;&lt;br /&gt;
(referred to as &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; in prose) holding stashed out-of-order&lt;br /&gt;
packet-buffer indexes; both FRTX and best-effort flows share this&lt;br /&gt;
path.  The invariant &amp;lt;code&amp;gt;rwe - lwe &amp;amp;lt;= RQ_SIZE&amp;lt;/code&amp;gt; holds: on each consume&lt;br /&gt;
the receiver advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; by the consumed count, capping the&lt;br /&gt;
receive window at &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; slots.&lt;br /&gt;
&lt;br /&gt;
A separate fence variable &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; is bumped on every retransmit&lt;br /&gt;
(timer-fire, SACK-driven, fast-rxm, NACK-driven) and on every&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; ([[#4. Sequence-number rotation (DRF)|Section 4]]) to mark the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; range whose RTT samples&lt;br /&gt;
MUST be discarded.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 2.2. Service modes (orthogonal axes) ===&lt;br /&gt;
&lt;br /&gt;
FRCP exposes its wire features as a vector of independent QoS&lt;br /&gt;
axes selected at flow allocation time.  All flows go through the&lt;br /&gt;
same &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive; the &amp;lt;code&amp;gt;qosspec_t&amp;lt;/code&amp;gt; passed&lt;br /&gt;
in determines which protocol machinery engages on the wire.  This&lt;br /&gt;
contrasts with the POSIX BSD socket model where TCP and UDP&lt;br /&gt;
require different socket types (&amp;lt;code&amp;gt;SOCK_STREAM&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;SOCK_DGRAM&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
The axes:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = unordered (no FRCP engagement: raw datagrams, no PCI on the wire, UDP-equivalent at this layer); 1 = message-ordered (FRCP engaged; SDU boundaries preserved across fragmentation); 2 = stream (byte-oriented, no SDU boundaries; FRTX required)&lt;br /&gt;
;&amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = lossless service requested: FRTX retransmit machinery engages ([[#8. Retransmission|Section 8]]); MUST be 0 for &amp;lt;code&amp;gt;service=2&amp;lt;/code&amp;gt;.  Non-zero = best-effort, FRTX off.&lt;br /&gt;
;&amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bit Error Rate tolerance.  0 = error-free service requested: a CRC trailer is appended after the body of DATA packets and verified on receive (added / checked outside the FRCP PCI; see [[#1.1. PCI header|Section 1.1]]).  Non-zero = peer accepts errors; trailer omitted.  SACK control packets carry a CRC32 trailer regardless of &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; gate applies to DATA only.&lt;br /&gt;
;&amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt;&lt;br /&gt;
:Peer-timeout (ms); 0 disables the keepalive timer.  Independent of FRCP engagement.&lt;br /&gt;
&lt;br /&gt;
Encryption is a separate per-flow attribute set at flow setup;&lt;br /&gt;
when enabled it wraps the FRCP packet (PCI + body, plus the CRC&lt;br /&gt;
trailer if any) under AEAD, expanding the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; by &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt;&lt;br /&gt;
octets (nonce / tag).  The CRC trailer is currently kept inside&lt;br /&gt;
the AEAD wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
&lt;br /&gt;
Reachable combinations exported by &amp;lt;code&amp;gt;include/ouroboros/qos.h&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Cube !! &amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; !! Engaged&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw&amp;lt;/code&amp;gt; || 0 || 1 || 1 || Raw passthrough&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw_safe&amp;lt;/code&amp;gt; || 0 || 1 || 0 || Raw + CRC trailer&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; || 1 || 1 || 1 || FRCP, no FRTX, no CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt_safe&amp;lt;/code&amp;gt; || 1 || 1 || 0 || FRCP, no FRTX, CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_msg&amp;lt;/code&amp;gt; || 1 || 0 || 0 || FRCP + FRTX&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_stream&amp;lt;/code&amp;gt; || 2 || 0 || 0 || FRCP + FRTX, stream&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Forced couplings actually enforced by the public API:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;service == SVC_STREAM&amp;lt;/code&amp;gt; (2) requires &amp;lt;code&amp;gt;loss == 0&amp;lt;/code&amp;gt;; &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;flow_accept&amp;lt;/code&amp;gt; reject the pair otherwise with &amp;lt;code&amp;gt;-EINVAL&amp;lt;/code&amp;gt;.&lt;br /&gt;
* FRTX requires FRCP engagement (&amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;); requesting &amp;lt;code&amp;gt;loss = 0&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;service = SVC_RAW&amp;lt;/code&amp;gt; is structurally a no-op because no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; is created.&lt;br /&gt;
* The &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; build flag globally forces &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;.  Note: this flag defaults to ON, so default builds ship with CRC disabled until &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; is set to OFF.&lt;br /&gt;
&lt;br /&gt;
Caveat: the API does NOT force &amp;lt;code&amp;gt;ber = 0&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; has &amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;, which means the PCI&lt;br /&gt;
itself is not CRC-protected on that cube; the HCS ([[#1.1. PCI header|Section 1.1]])&lt;br /&gt;
remains the only integrity check on the header.&lt;br /&gt;
&lt;br /&gt;
The FRCP-no-FRTX regime (&amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;loss &amp;amp;gt; 0&amp;lt;/code&amp;gt;) is meaningful&lt;br /&gt;
and live: sequence numbering, in-order delivery, flow-control&lt;br /&gt;
advertisement, KA, DRF rotation, and SDU fragmentation /&lt;br /&gt;
reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]) all run.  Lost packets are dropped&lt;br /&gt;
rather than retransmitted; a permanently-lost mid-fragment is&lt;br /&gt;
dropped via skip-past-gap once a later SDU is visible in the&lt;br /&gt;
reorder ring.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. Protocol parameters ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Slot ring / rcv window width&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;START_WINDOW&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Initial &amp;lt;code&amp;gt;rwe-lwe&amp;lt;/code&amp;gt; after rotate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MAX(250 us build-tunable, 1&amp;amp;lt;&amp;amp;lt;RXMQ_RES)&amp;lt;/code&amp;gt;; per-flow via &amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;FRCTSRTOMIN&amp;lt;/code&amp;gt;).  Default ~1 ms with &amp;lt;code&amp;gt;RXMQ_RES=20&amp;lt;/code&amp;gt;. || RTO floor; also floored at the retransmit-wheel resolution (~1 ms by default).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt; || 20 || Backoff shift cap&lt;br /&gt;
|-&lt;br /&gt;
| RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MIN(reo_wnd_mult * min_RTT/4, SRTT)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; scales on D-SACK, cap 20 || Reorder window; per RFC 8985 sec. 6.2; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per sec. 6.2 step 4&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; || 300 s (5 min, Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; windowed re-anchor&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_WND_MULT_MAX&amp;lt;/code&amp;gt; || 20 (RFC 8985 sec. 6.2 step 4) || &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_DECAY_PKTS&amp;lt;/code&amp;gt; || 16 (RFC 8985 sec. 6.2 step 4 / &amp;lt;code&amp;gt;RACK.reo_wnd_persist&amp;lt;/code&amp;gt;) || Fresh-ACK&#039;d seq count per halving&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_DSACK_LAG&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || D-SACK sanity cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTT_QUARANTINE&amp;lt;/code&amp;gt; || 32 (&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; steps) || NewReno gate pad&lt;br /&gt;
|-&lt;br /&gt;
| SACK rate-limit || &amp;lt;code&amp;gt;SACK_MIN_GAP_NS&amp;lt;/code&amp;gt; (250 us, fixed) || Min SACK gap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; || 2048 (wire cap; per-flow capped at &amp;lt;code&amp;gt;(frag_mtu-PCI-4)/8&amp;lt;/code&amp;gt;) || Per-SACK block cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; || 32 || Per-pass staged retransmit cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; || 3 (RFC 8985 default) || Hybrid fast-rxm trigger ([[#8. Retransmission|Section 8]])&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MDEV_MUL&amp;lt;/code&amp;gt; || 2 (build-tunable via &amp;lt;code&amp;gt;FRCT_RTO_MDEV_MULTIPLIER&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; shift in &amp;lt;code&amp;gt;RTO = srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| RTTP nonce || 16 octets || Echoed verbatim&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; || 8 || In-flight probes&lt;br /&gt;
|-&lt;br /&gt;
| RTT clamp || &amp;lt;code&amp;gt;16 * srtt&amp;lt;/code&amp;gt; || Probe-sample upper bound (ACK-derived RTT samples gated by Karn / recovery only)&lt;br /&gt;
|-&lt;br /&gt;
| Cold-probe cadence || 100 ms (rx-driven; see [[#12. RTT estimation|Section 12]]) || Pre-&amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; RTTP rate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt; || 100 ms || RDVS emit cadence&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; || 1 s || RDVS give-up&lt;br /&gt;
|-&lt;br /&gt;
| Delayed-ACK fire || &amp;lt;code&amp;gt;2 * TICTIME&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt; = FRCT tick granularity, default 5 ms; &amp;lt;code&amp;gt;2*TICTIME = 10 ms&amp;lt;/code&amp;gt; by default) || Fired after the first in-order DATA arrival; tick is build-tunable&lt;br /&gt;
|-&lt;br /&gt;
| NACK send cooldown || &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; when an &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; sample exists, else 100 ms || Pre-DRF NACK rate-limit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_SDU&amp;lt;/code&amp;gt; || 1 MiB || Max reassembled SDU; configurable per flow&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The per-flow fragment Maximum Transmission Unit (MTU) is computed&lt;br /&gt;
at flow setup from the lower IPCP&#039;s mtu minus encryption&lt;br /&gt;
&amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; and CRC trailer; there is no FRCT-level default or&lt;br /&gt;
environment-variable override.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 4. Sequence-number rotation (DRF) ==&lt;br /&gt;
&lt;br /&gt;
The DRF (Data Run Flag) bit on an outbound packet means &amp;quot;this is&lt;br /&gt;
the start of a fresh data run&amp;quot; and is set whenever the sender has&lt;br /&gt;
nothing in flight (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Independently of that, if the sender has been idle longer than&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.inact&amp;lt;/code&amp;gt; AND the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;),&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; rolls a random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; before the send and&lt;br /&gt;
resets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    snd_cr.seqno  = random()&lt;br /&gt;
    snd_cr.lwe    = snd_cr.seqno&lt;br /&gt;
    snd_cr.rwe    = snd_cr.seqno + START_WINDOW&lt;br /&gt;
    rtt_lwe       = snd_cr.seqno&lt;br /&gt;
    in_recovery   = false   (recovery state, see Section 8)&lt;br /&gt;
    recovery_high = snd_cr.seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The receiver, on observing rcv-side inactivity&lt;br /&gt;
(&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), requires a DRF on the next&lt;br /&gt;
DATA packet; otherwise it replies with a rate-limited NACK (see&lt;br /&gt;
below). Non-DATA control packets pass through without the DRF&lt;br /&gt;
requirement (no impact on receiver state). On DRF the receiver releases the &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots and&lt;br /&gt;
rebases&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    rcv_cr.lwe   = seqno&lt;br /&gt;
    rcv_cr.rwe   = seqno + RQ_SIZE&lt;br /&gt;
    rcv_cr.seqno = seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the inactive packet has DATA but no DRF, a rate-limited NACK is&lt;br /&gt;
fired back to the sender (cooldown per [[#3. Protocol parameters|Section 3]]); non-DATA stale&lt;br /&gt;
arrivals fall through to normal processing (no NACK, no drop).&lt;br /&gt;
&lt;br /&gt;
== 5. Send path ==&lt;br /&gt;
&lt;br /&gt;
# If the SDU exceeds &amp;lt;code&amp;gt;(frag_mtu - data_hdr_len)&amp;lt;/code&amp;gt;, the caller (&amp;lt;code&amp;gt;dev.c&amp;lt;/code&amp;gt;) fans it out into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - data_hdr_len))&amp;lt;/code&amp;gt; fragments, each emitted via &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; as its own DATA packet with a per-fragment role ([[#7.2. Fragmentation and reassembly|Section 7.2]]); both FRTX and best-effort flows fragment.  Raw flows (no FRCP engagement, &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) carry no PCI and return &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; for any SDU larger than one packet at the layer below.  An SDU that fits in a single packet is sent as SOLE.  &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; reserves PCI head room; sets DATA, plus DRF when the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
# &amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; if past sender inactivity and the pipe is empty ([[#4. Sequence-number rotation (DRF)|Section 4]]).&lt;br /&gt;
# Advertise FC (&amp;lt;code&amp;gt;pci.window = frcti_advert_rwe(frcti)&amp;lt;/code&amp;gt;, i.e. &amp;lt;code&amp;gt;rcv_cr.rwe&amp;lt;/code&amp;gt; clamped to &amp;lt;code&amp;gt;rcv_cr.lwe + ring_seq_cap&amp;lt;/code&amp;gt; in stream mode) when the receiver side is recent: &amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt; rcv_cr.inact&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Reliable mode (FRTX): leave &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; where it is; reset the slot at &amp;lt;code&amp;gt;RQ_SLOT(seqno)&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;snd_slots[p].time = now&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_slots[p].flags = 0&amp;lt;/code&amp;gt;); queue an &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; (saves a packet copy, arms a wheel timer at &amp;lt;code&amp;gt;now + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;).  Piggyback ACK (&amp;lt;code&amp;gt;pci.ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) while the a-timer for the most recent received DATA packet has not yet expired (&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt;= t_a&amp;lt;/code&amp;gt;); on piggyback, set &amp;lt;code&amp;gt;rcv_cr.seqno = rcv_cr.lwe&amp;lt;/code&amp;gt; so the next delayed-ACK fire is suppressed.  See [[#8. Retransmission|Section 8]] for &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; semantics.&lt;br /&gt;
# Best-effort mode (no FRTX): advance &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; immediately (&amp;lt;code&amp;gt;snd_cr.lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_cr.rwe = snd_cr.lwe + RQ_SIZE&amp;lt;/code&amp;gt;); no retransmit state.  No send-side RTT probe is armed in this mode (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt; requires an in-flight &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, which best-effort never has); the rx-driven cold seeder in &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; is the only probe path.&lt;br /&gt;
# In reliable mode, optionally arm an RTT probe ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 6. Receive path ==&lt;br /&gt;
&lt;br /&gt;
=== 6.1. Early-exit dispatch ===&lt;br /&gt;
&lt;br /&gt;
Keepalive (KA), RTT probe (RTTP), pre-DRF NACK, and rendezvous&lt;br /&gt;
(RDVS) packets short-circuit out of &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; before the locked&lt;br /&gt;
main path; each handler takes its own lock internally.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
      incoming packet&lt;br /&gt;
            |&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | KA?     |---yes--&amp;gt; ka_rcv  ; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RTTP?   |---yes--&amp;gt; rttp_rcv; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | NACK?   |---yes--&amp;gt; nack_rcv; return  (see Section 9)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RDVS?   |---yes--&amp;gt; rdv_rcv ; return  (reply bare FC, ackno=0)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       acquire wrlock; enter locked main path&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt;&lt;br /&gt;
:refresh &amp;lt;code&amp;gt;t_ka_rcv&amp;lt;/code&amp;gt;, honour piggybacked ACK.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt;&lt;br /&gt;
:probe (echo back nonce) or echo (verify nonce, sample RTT).&lt;br /&gt;
;&amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:pre-DRF, sender-side handler.  See [[#9. Pre-DRF NACK|Section 9]].&lt;br /&gt;
;&amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt;&lt;br /&gt;
:reply with a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;); &amp;lt;code&amp;gt;rdlock&amp;lt;/code&amp;gt; only.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 6.2. Locked main path ===&lt;br /&gt;
&lt;br /&gt;
Steps below run with the per-flow &amp;lt;code&amp;gt;frcti.lock&amp;lt;/code&amp;gt; held for writing&lt;br /&gt;
(&amp;lt;code&amp;gt;pthread_rwlock_wrlock&amp;lt;/code&amp;gt;) unless noted.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;&lt;br /&gt;
:Only meaningful when the receive side is stale.  On DRF (Data Run Flag): release &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots, rebase &amp;lt;code&amp;gt;rcv_cr&amp;lt;/code&amp;gt;, continue.  On stale DATA without DRF: fire a pre-DRF NACK if cooldown allows ([[#9. Pre-DRF NACK|Section 9]]), then discard the packet; on cooldown, drop without sending a NACK (a pending cumulative ACK from &amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; may still go out).  Non-DATA, non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; pure-DRF stale arrivals fall through after the DRF rebase branch.&lt;br /&gt;
&lt;br /&gt;
;DATA-only act refresh&lt;br /&gt;
:Refresh &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; only when &amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt; is set, so that non-DATA packets never block the next DRF rebase.&lt;br /&gt;
&lt;br /&gt;
;Wire-dup gate&lt;br /&gt;
:Before flag-driven dispatch, drop wire-duplicate ACKs and wire-duplicate DATA (&amp;lt;code&amp;gt;is_dup_ack&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt;).  The DATA check is bypassed for &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;-bearing arrivals so the piggybacked ACK / SACK / FC carried on a retransmitted DATA at an already-ACK&#039;d &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is still applied; the stale-in-window branch below then drops the packet.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Drop ACKs whose &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; falls outside &amp;lt;code&amp;gt;(snd_cr.lwe, snd_cr.seqno]&amp;lt;/code&amp;gt;.  If &amp;lt;code&amp;gt;ackno == snd_cr.lwe&amp;lt;/code&amp;gt; (non-advancing cumulative ACK), drive RACK fast-retransmit consideration ([[#8. Retransmission|Section 8]]).  Otherwise advance &amp;lt;code&amp;gt;snd_cr.lwe = ackno&amp;lt;/code&amp;gt;, collapse &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; to 0 (Karn-gated by &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; on the just-acknowledged slot, the old head-of-line), reset &amp;lt;code&amp;gt;dup_thresh&amp;lt;/code&amp;gt; to 0, update &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the send-time of the slot at &amp;lt;code&amp;gt;ackno-1&amp;lt;/code&amp;gt; (consumed by RACK and SACK below), decay &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per RFC 8985 sec. 6.2 step 4, exit NewReno-careful recovery (see [[#8. Retransmission|Section 8]]) on &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt;, and feed an RTT sample if eligible ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Walk the block list.  For each block (a present range above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;) NULL out &amp;lt;code&amp;gt;snd_slots[k].rxm&amp;lt;/code&amp;gt;, clear the slot&#039;s per-send flags, and advance &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the latest send-time covered (the Forward Acknowledgement / fack equivalent, Mathis &amp;amp;amp; Mahdavi 1996); the first block whose start clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; skips this fack update so that a head-of-line clamp does not falsely advance fack.  For un-SACKed gaps below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt;, stage a retransmit per slot that is (1) still owned (&amp;lt;code&amp;gt;rxm != NULL&amp;lt;/code&amp;gt;), (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (the RFC 8985 sec. 6.2 hybrid trigger).  Mark the slot &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; and NULL the &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; at stage time.  Capped at &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; staged retransmits per receive pass; what&#039;s left rides the next SACK.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bump &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; (clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt;, never shrinks) and mark window open.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bounds-check &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; against window.  On stale-dup (&amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;), set &amp;lt;code&amp;gt;rcv_cr.seqno = seqno&amp;lt;/code&amp;gt; to force a fresh ACK on the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt;, then drop.  On accept: both FRTX and best-effort stash the packet-buffer index into &amp;lt;code&amp;gt;rq[seqno mod RQ_SIZE]&amp;lt;/code&amp;gt;.  Fragments stash unchanged - the role bits are inspected only at consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]).  On out-of-order arrival, build a SACK reply if not rate-limited (per [[#3. Protocol parameters|Section 3]]) and not deduplicated against the previous &amp;lt;code&amp;gt;(rcv_cr.lwe, n_blocks)&amp;lt;/code&amp;gt; pair; D-SACK reports always bypass the dedup.  If both rate-limit and dedup suppress the reply, neither SACK nor delayed-ACK fires (the sender picks up the gap on its next ACK).  On in-order arrival, arm the delayed-ACK timer.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; exit&lt;br /&gt;
:Releases the per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;), then calls &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; synchronously after the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; release to surface any pending cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 7. Read path and reassembly ==&lt;br /&gt;
&lt;br /&gt;
=== 7.1. Read path ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns a full reassembled SDU (Service Data Unit) via&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; on every FRCP SDU-mode flow (FRTX or best-effort);&lt;br /&gt;
stream-mode is covered in [[#16. Stream-mode flows|Section 16]].  An incomplete head-of-line&lt;br /&gt;
(HoL) run yields &amp;lt;code&amp;gt;-EAGAIN&amp;lt;/code&amp;gt;; an oversized run yields &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; (the&lt;br /&gt;
run is dropped so the flow does not stall).  On best-effort flows,&lt;br /&gt;
a permanently-lost mid-fragment is dropped as soon as a later&lt;br /&gt;
complete SDU becomes visible in the ring ([[#7.2. Fragmentation and reassembly|Section 7.2]] skip-past-&lt;br /&gt;
gap).&lt;br /&gt;
&lt;br /&gt;
Raw flows carry no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt;, so &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns the next pending&lt;br /&gt;
packet-buffer index directly, with no role-bit inspection.  (Raw&lt;br /&gt;
service is selected via &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt; at flow allocation,&lt;br /&gt;
which suppresses &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; creation.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_pdu_ready&amp;lt;/code&amp;gt; is the no-advance peek used by &amp;lt;code&amp;gt;fevent&amp;lt;/code&amp;gt; (the&lt;br /&gt;
Ouroboros flow-event multiplexer, the &amp;lt;code&amp;gt;poll(2)&amp;lt;/code&amp;gt;-equivalent on&lt;br /&gt;
flows).  It returns ready only when the head-of-line run is&lt;br /&gt;
complete and the lead packet (a Protocol Data Unit, here one FRCP&lt;br /&gt;
packet) is present at &amp;lt;code&amp;gt;rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt;; any other state&lt;br /&gt;
(including the best-effort skip-past-gap case) returns not ready,&lt;br /&gt;
and &amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; is left to drop the broken prefix and re-&lt;br /&gt;
inspect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 7.2. Fragmentation and reassembly ===&lt;br /&gt;
&lt;br /&gt;
Send side (&amp;lt;code&amp;gt;flow_write_frag&amp;lt;/code&amp;gt;).  An SDU larger than&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - PCI)&amp;lt;/code&amp;gt; is split into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - PCI))&amp;lt;/code&amp;gt;&lt;br /&gt;
fragments; each fragment is its own FRCP packet with its own&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a per-fragment role flag pair ([[#1.2. Flag bits|Section 1.2]]).  Roles are&lt;br /&gt;
assigned at emit time:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! i !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=0&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=n-1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| else || &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A mid-loop allocation or transmit failure may yield a partial&lt;br /&gt;
write: the call returns the bytes already enqueued (&amp;lt;code&amp;gt;off &amp;amp;gt; 0&amp;lt;/code&amp;gt;) or&lt;br /&gt;
the underlying error (&amp;lt;code&amp;gt;off == 0&amp;lt;/code&amp;gt;).  Best-effort flows fragment&lt;br /&gt;
identically; on the receiver, a partial run with a permanently-&lt;br /&gt;
lost fragment is dropped when a later complete SDU is visible in&lt;br /&gt;
the ring (see skip-past-gap below).  Raw flows carry no PCI and&lt;br /&gt;
refuse anything larger than the layer&#039;s user MTU (&amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Wire-level recovery is fragment-agnostic on FRTX flows: each&lt;br /&gt;
fragment&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; flows through SACK / RACK / RTO / NACK exactly&lt;br /&gt;
as for a SOLE DATA packet, and reassembly does not re-enter the&lt;br /&gt;
loss-detection path.  Best-effort flows run the same &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
machinery (DRF, FC, ACK piggyback, pre-DRF NACK emit) but queue&lt;br /&gt;
no &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; state at the sender, so a lost MID is unrecoverable;&lt;br /&gt;
skip-past-gap handles it (below).&lt;br /&gt;
&lt;br /&gt;
Receive side.  Fragments stash into &amp;lt;code&amp;gt;rq[seqno]&amp;lt;/code&amp;gt; unchanged; role bits&lt;br /&gt;
are read only at consume time.  &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;, called from&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt;, walks the ring starting at the oldest still-&lt;br /&gt;
undelivered &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;base = rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt; (equal to &amp;lt;code&amp;gt;rcv_cr.lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
only when no partial run is in progress; during a partial run &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
has already advanced past &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;).  It produces one of three&lt;br /&gt;
outcomes:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Outcome !! Cause&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELIVER (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]=SOLE&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;), or &amp;lt;code&amp;gt;rq[base]=FIRST&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; follows in slots &amp;lt;code&amp;gt;[base+1..base+n-1]&amp;lt;/code&amp;gt; with all intermediate roles in &amp;lt;code&amp;gt;{MID,FIRST,LAST}&amp;lt;/code&amp;gt; contiguous.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DROP (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; without a preceding &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;); a &amp;lt;code&amp;gt;FIRST..[non-LAST]..new-FIRST&amp;lt;/code&amp;gt; or new-&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; mid-run (drop the broken prefix with &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; = run length minus 1, so the new &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; stays); or, on best-effort flows, a gap at &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; later in the ring (drop up to the new run start).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; absent or &amp;lt;code&amp;gt;FIRST..[non-LAST]&amp;lt;/code&amp;gt; with no later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; in the ring (FRTX waits for retx; best-effort waits for arrival).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DELIVER&amp;lt;/code&amp;gt; triggers &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;: a scatter-gather &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; of the &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;&lt;br /&gt;
consecutive fragments at &amp;lt;code&amp;gt;rq[base..base+n-1]&amp;lt;/code&amp;gt; directly into the&lt;br /&gt;
caller&#039;s buffer; each per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;) is&lt;br /&gt;
released and &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; advances by &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; was already advanced&lt;br /&gt;
incrementally as each contiguous fragment arrived; &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;&lt;br /&gt;
only restores the fixed-width invariant &amp;lt;code&amp;gt;rwe == lwe + RQ_SIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
No intermediate reassembly buffer is allocated.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; past the broken prefix (releasing the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;s)&lt;br /&gt;
and pulls &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; up to the new trailing edge if needed; the next&lt;br /&gt;
consume retries from the new &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;.  Oversize or arithmetically&lt;br /&gt;
overflowing delivery (sum of fragment lengths &amp;amp;gt; &amp;lt;code&amp;gt;max_rcv_sdu&amp;lt;/code&amp;gt;, sum&lt;br /&gt;
&amp;amp;gt; caller&#039;s buffer, or running-sum overflow) also drops the run&lt;br /&gt;
with &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skip-past-gap (best-effort only).  On FRTX, a gap in the run means&lt;br /&gt;
&amp;quot;waiting for retransmit&amp;quot; and &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;.&lt;br /&gt;
On best-effort flows the gap is permanent, so &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;&lt;br /&gt;
scans forward in the ring for the next &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;; if one is&lt;br /&gt;
visible within &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;, it returns &amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; for the broken prefix and&lt;br /&gt;
the consume loop retries at the new &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;.  Memory hold is bounded&lt;br /&gt;
by &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;; the partial releases on the next consume call once a&lt;br /&gt;
later complete run exists.  Voice-like flows (one &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; per SDU)&lt;br /&gt;
see no extra wait: any later &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; makes the prior gap droppable&lt;br /&gt;
immediately.&lt;br /&gt;
&lt;br /&gt;
The choice to defer reassembly to consume time keeps the receive&lt;br /&gt;
path zero-copy: fragments stay in the shared-memory ring until&lt;br /&gt;
the application pulls, and the SDU lands directly in the caller&#039;s&lt;br /&gt;
buffer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 8. Retransmission ==&lt;br /&gt;
&lt;br /&gt;
FRCP is bounded by two delta-t-derived timers (Watson 1981, see&lt;br /&gt;
[[#15. Heritage and adopted techniques|Section 15]]):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer): upper bound on ACK delay.  An ACK for a received DATA packet MUST be emitted within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; of receipt; an attempt to send an ACK after the a-timer has expired is suppressed (the sender&#039;s RTO is already in motion).&lt;br /&gt;
* &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer): upper bound on retransmission.  A given DATA packet MUST NOT be retransmitted after &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; has elapsed since its first send (&amp;lt;code&amp;gt;t0&amp;lt;/code&amp;gt;); when the bound is hit, the flow is declared down (raising the Ouroboros asynchronous flow condition &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt;, which marks the flow dead to both endpoints) rather than retransmitted again.&lt;br /&gt;
&lt;br /&gt;
Each in-flight FRTX &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; owns one &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt;, armed in a hashed&lt;br /&gt;
timing wheel; the wheel deadline is the slot&#039;s next eligible&lt;br /&gt;
retransmit time.&lt;br /&gt;
&lt;br /&gt;
;RTO timer&lt;br /&gt;
:On fire (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;), re-emit with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, mark &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (Karn-suppress next ACK&#039;s RTT sample), and (for the head-of-line (HoL) slot only) bump &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; up to &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt;.  Wheel deadline is &amp;lt;code&amp;gt;t_send + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;.  Re-armed unless consumed.  The RTO timer also clears &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (re-arming fast-retransmit eligibility), resets &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; to 1 on a HoL fire (RFC 8985 sec. 6.2 step 4 reset clause), and marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; if its &amp;lt;code&amp;gt;frct_tx&amp;lt;/code&amp;gt; call fails.&lt;br /&gt;
&lt;br /&gt;
;r-timer guard&lt;br /&gt;
:Before any retransmit attempt, check &amp;lt;code&amp;gt;(now - t0)&amp;lt;/code&amp;gt; against &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;.  If exceeded, the slot is no longer eligible for retransmit.  Only the RTO timer (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;) treats r-timer expiry as terminal: it marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; (peer unreachable).  Fast-retransmit, SACK-driven retransmit, and NACK-driven head-of-line re-emit silently skip aged-out slots and defer the flow-down decision to the next RTO fire.&lt;br /&gt;
&lt;br /&gt;
;Fast retransmit (hybrid trigger, RFC 8985 sec. 6.2)&lt;br /&gt;
:On a non-advancing cumulative ACK with the scoreboard advanced, fire one fast retransmit when EITHER (a) the head-of-line slot&#039;s latest send is older than the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; ([[#3. Protocol parameters|Section 3]]) and not yet aged out, OR (b) the SACK &amp;lt;code&amp;gt;dup-thresh&amp;lt;/code&amp;gt; count above &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; reaches &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; (= 3, RFC 8985 sec. 6.2 step 4).  Fires at most once per non-advancing cumulative-ACK value, gated by &amp;lt;code&amp;gt;rack_fired_lwe&amp;lt;/code&amp;gt; (the &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; at which fast-retransmit last fired).  Set &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; on the slot (one-shot per-slot gate) and enter NewReno-style careful recovery (see NewReno below in this section).&lt;br /&gt;
:The RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; uses the RFC 8985 sec. 6.2 form &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor.  Before the first RTT sample seeds &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; falls back to &amp;lt;code&amp;gt;MIN(reo_wnd_mult * SRTT / 4, SRTT)&amp;lt;/code&amp;gt;, still floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt; (consistent with the windowed-minimum fallback described in [[#12. RTT estimation|Section 12]]).  &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt; is a windowed minimum over the last &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; = 5 min of RTT samples (matches the Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt; default) so a route change to a longer path eventually re-anchors the reorder window without relying on &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; growth alone.&lt;br /&gt;
&lt;br /&gt;
;SACK-driven retransmit&lt;br /&gt;
:For each gap below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt; whose slot is (1) still owned, (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (same hybrid as fast-retransmit, see [[#6.2. Locked main path|Section 6.2]]), re-emit.  Each SACK-driven retransmit re-arms a fresh &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; so a lost retransmit can still be recovered by its own RTO timer.&lt;br /&gt;
&lt;br /&gt;
;NewReno&lt;br /&gt;
:On entry, &amp;lt;code&amp;gt;recovery_high = snd_cr.seqno + RTT_QUARANTINE&amp;lt;/code&amp;gt;.  Exit when &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt; (the latter means everything sent has been acknowledged).  &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; also clears recovery.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 9. Pre-DRF NACK ==&lt;br /&gt;
&lt;br /&gt;
The two sides have different inactivity thresholds (&amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), so a receiver can detect &amp;quot;stale data run&amp;quot; before the sender&#039;s own DRF logic kicks in.  NACK is the receiver-driven nudge that asks the sender to re-transmit the head of the run.&lt;br /&gt;
&lt;br /&gt;
;Send (&amp;lt;code&amp;gt;frcti_nack_snd&amp;lt;/code&amp;gt;, called by &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;FRCT_INACT_NEED_NACK&amp;lt;/code&amp;gt;)&lt;br /&gt;
:When an incoming DATA packet has no DRF and rcv-side activity is older than &amp;lt;code&amp;gt;rcv_cr.inact&amp;lt;/code&amp;gt;, the receiver emits a bare packet with &amp;lt;code&amp;gt;flags = FRCT_NACK&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;seqno = arrival_seqno - 1&amp;lt;/code&amp;gt; (informational only, not consulted by the receive handler).  The cooldown in [[#3. Protocol parameters|Section 3]] rate-limits the burst.  Non-DATA non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; non-DATA DRF still rebases via the DRF branch.&lt;br /&gt;
&lt;br /&gt;
;Receive (&amp;lt;code&amp;gt;frcti_nack_rcv&amp;lt;/code&amp;gt;)&lt;br /&gt;
:Dispatched in the early-exit branch ([[#6.1. Early-exit dispatch|Section 6.1]]), before &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;.  The sender copies the head-of-line (HoL) &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; packet, marks the slot &amp;lt;code&amp;gt;SND_RTX | SND_FAST_RXM&amp;lt;/code&amp;gt; (Karn-suppress next ACK, one-shot fast-rxm gate), sets &amp;lt;code&amp;gt;rtt_lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, and re-emits via &amp;lt;code&amp;gt;fast_rxm_send&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt; and a refreshed &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;.  The original &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed - the NACK emit is additive to the normal retransmit machinery, not a replacement.  No-op if nothing is in flight, the HoL slot has aged past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, or the HoL &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; pointer has been cleared by SACK or RACK.&lt;br /&gt;
&lt;br /&gt;
NACK has exactly one role: lost first-of-run (DRF) packet recovery. Until the DRF packet arrives, the receiver cannot rebase its window, so any subsequent in-flight packets look stale to the receiver. The NACK fires the moment a stale receiver sees DATA without DRF, telling the sender to re-emit the head-of-line (DRF) packet at NACK-cooldown latency rather than waiting for the initial RTO (which is the configured default until &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded by the first probe round-trip).  Mid-stream loss is NOT NACK-driven; it is recovered by the sender&#039;s RTO, fast retransmit, and SACK-driven retransmit paths ([[#8. Retransmission|Section 8]]) only.&lt;br /&gt;
&lt;br /&gt;
The existing &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed on a NACK re-emit, so the RTO path remains the eventual fallback.&lt;br /&gt;
&lt;br /&gt;
== 10. Cumulative + selective ACK ==&lt;br /&gt;
&lt;br /&gt;
Cumulative ACK is &amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;.  On out-of-order arrival the&lt;br /&gt;
receiver also emits a SACK packet ([[#1.3. SACK payload|Section 1.3]]) whose payload lists&lt;br /&gt;
&#039;&#039;present&#039;&#039; blocks above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; (analogous to TCP SACK / QUIC ACK&lt;br /&gt;
ranges).  SACKs are rate-limited per [[#3. Protocol parameters|Section 3]] and suppressed when&lt;br /&gt;
neither &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; nor block count has changed since the last SACK.&lt;br /&gt;
&lt;br /&gt;
D-SACK reports (RFC 2883) are emitted in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; of an&lt;br /&gt;
otherwise normal SACK frame (see [[#1.3. SACK payload|Section 1.3]] for the encoding).&lt;br /&gt;
Two receiver triggers arm a pending D-SACK report (single-slot,&lt;br /&gt;
latest-wins):&lt;br /&gt;
&lt;br /&gt;
* DATA arrival with &amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;, both wire-dup (no RXM, &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt; path) and retransmit (RXM, post-FC branch) (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
* &amp;lt;code&amp;gt;rq_accept&amp;lt;/code&amp;gt; conflict, slot already occupied in &amp;lt;code&amp;gt;[lwe, rwe)&amp;lt;/code&amp;gt; (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
&lt;br /&gt;
When a D-SACK is pending and the standard scoreboard SACK would be&lt;br /&gt;
suppressed by dedup or rate-limit, the report is emitted as a&lt;br /&gt;
stand-alone SACK frame through the normal &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; path; when a&lt;br /&gt;
D-SACK report is pending the path bypasses dedup and the &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;&lt;br /&gt;
rate-limit, but the a-timer suppression on rcv inactivity still&lt;br /&gt;
applies.&lt;br /&gt;
&lt;br /&gt;
Bare ACKs are deferred via a per-flow delayed-ACK timer (one in&lt;br /&gt;
flight at a time, atomic test-and-set dedup; fires per [[#3. Protocol parameters|Section 3]]&lt;br /&gt;
after the first in-order arrival).  Suppressed if (1) no new&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, (2) rcv side is inactive (older than &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt;), or (3) the&lt;br /&gt;
sender just sent within &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;.  A pending D-SACK ride-through&lt;br /&gt;
bypasses (1) and (3); the a-timer gate (2) is unconditional.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 11. Flow control ==&lt;br /&gt;
&lt;br /&gt;
The receiver advertises &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; in every FC field.  The sender treats&lt;br /&gt;
its &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; as the absolute right edge: when&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt;= snd_cr.rwe&amp;lt;/code&amp;gt; the window is closed and &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt;&lt;br /&gt;
yields.  While closed, the sender periodically emits RDVS&lt;br /&gt;
(rendezvous) packets (cadence &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt;); the receiver replies with&lt;br /&gt;
a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;) that reopens the window.  Once the&lt;br /&gt;
window has been closed for longer than &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; the sender stops&lt;br /&gt;
emitting RDVS but does not tear the flow down - the writer keeps&lt;br /&gt;
blocking until either a peer-driven FC arrives or the KA&lt;br /&gt;
(keepalive) / r-timer marks the flow.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt; on receipt and MUST NOT shrink:&lt;br /&gt;
a backward &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is silently clamped to the current &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt;;&lt;br /&gt;
the FC packet still reopens the window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 12. RTT estimation ==&lt;br /&gt;
&lt;br /&gt;
Active RTTP probes ([[#1.4. RTTP payload|Section 1.4]]) carry a 32-bit &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt; (0&lt;br /&gt;
reserved) and a 16-byte random nonce echoed verbatim - defends&lt;br /&gt;
against spoofed replies.  A ring of &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; in-flight probes is&lt;br /&gt;
kept; an echo whose &amp;lt;code&amp;gt;(id, nonce)&amp;lt;/code&amp;gt; doesn&#039;t match the ring slot is&lt;br /&gt;
dropped.  A single RTTP sample is clamped to &amp;lt;code&amp;gt;RTT_CLAMP_MUL * srtt&amp;lt;/code&amp;gt;&lt;br /&gt;
(compile-time &amp;lt;code&amp;gt;RTT_CLAMP_MUL = 16&amp;lt;/code&amp;gt;) once &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded; the first&lt;br /&gt;
cold-probe sample feeds &amp;lt;code&amp;gt;rtt_update&amp;lt;/code&amp;gt; raw.&lt;br /&gt;
&lt;br /&gt;
Probe arming gates:&lt;br /&gt;
&lt;br /&gt;
;Cold (no &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; yet)&lt;br /&gt;
:the receive path arms at most one probe per 100 ms via &amp;lt;code&amp;gt;frcti_rcv_probe&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;PROBE_DUE_COLD&amp;lt;/code&amp;gt;); arming requires an incoming packet.  Active send-path arming bails while &amp;lt;code&amp;gt;srtt == 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
;Warm (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt;, called from &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt;)&lt;br /&gt;
:outstanding data (&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt; snd_cr.lwe&amp;lt;/code&amp;gt;), AND at least &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_rcv_rtt&amp;lt;/code&amp;gt; (last RTT receive of any kind), AND at least &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_snd_probe&amp;lt;/code&amp;gt; (last probe emit).&lt;br /&gt;
&lt;br /&gt;
Sample feeds either Linux&#039;s asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_LINUX_RTT_ESTIMATOR&amp;lt;/code&amp;gt;, default ON) or RFC 6298 symmetric EWMA&lt;br /&gt;
(compile option).  &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is floored at 10 ms when seeded from a&lt;br /&gt;
hint, at 1 us after every update (including the first seeding&lt;br /&gt;
sample); &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; floored at 100 ns.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;RTO = max(rto_min, 2 * srtt, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(the &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; floor is an FRCT addition not in RFC 6298).&lt;br /&gt;
Effective wheel deadline capped per [[#3. Protocol parameters|Section 3]].&lt;br /&gt;
&lt;br /&gt;
ACK-derived samples (&amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;rtt_sample_eligible&amp;lt;/code&amp;gt;), beyond&lt;br /&gt;
the cum-ACK advance gate in &amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno &amp;amp;gt; lwe&amp;lt;/code&amp;gt; and&lt;br /&gt;
&amp;lt;code&amp;gt;ackno &amp;amp;lt;= seqno&amp;lt;/code&amp;gt;), require all of: not in recovery; ACK packet does&lt;br /&gt;
not carry &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;; HoL slot&#039;s &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; bit clear; slot&#039;s &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;&lt;br /&gt;
pointer non-NULL (not SACK-consumed); &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; not below the &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
fence; &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; already seeded by an RTTP probe.  There is no ACK-only&lt;br /&gt;
seeding.&lt;br /&gt;
&lt;br /&gt;
Every eligible sample also feeds &amp;lt;code&amp;gt;RACK.min_RTT&amp;lt;/code&amp;gt; (RFC 8985 sec. 6.2)&lt;br /&gt;
via a windowed minimum: replace whenever the sample is strictly&lt;br /&gt;
smaller OR more than &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; (5 min, matches Linux&lt;br /&gt;
&amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) has elapsed since the current min was set.  The&lt;br /&gt;
downward branch is immediate (faster path picked up at once); the&lt;br /&gt;
upward branch is gated on the window (a transient queue burst does&lt;br /&gt;
not poison the estimate, but a sustained route change to a longer&lt;br /&gt;
path re-anchors &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; after at most one window).  Seeded from&lt;br /&gt;
&amp;lt;code&amp;gt;rtt_hint&amp;lt;/code&amp;gt; at &amp;lt;code&amp;gt;rtt_init&amp;lt;/code&amp;gt;; 0 acts as the unset sentinel and the base&lt;br /&gt;
in &amp;lt;code&amp;gt;rack_reorder_window&amp;lt;/code&amp;gt; falls back from &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt; (so&lt;br /&gt;
&amp;lt;code&amp;gt;R = mult * SRTT/4&amp;lt;/code&amp;gt;, capped at &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;, floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt;)&lt;br /&gt;
until the first sample.  See [[#6.2. Locked main path|Section 6.2]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 13. Liveness (keepalive) ==&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;code&amp;gt;qs.timeout &amp;amp;gt; 0&amp;lt;/code&amp;gt; a per-flow KA (keepalive) timer is armed.&lt;br /&gt;
Arming uses &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; for the deadline computation:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;deadline = min(snd_act + qs.timeout/4, rcv_act + qs.timeout)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(clamped to &amp;lt;code&amp;gt;now + qs.timeout/4&amp;lt;/code&amp;gt; if already past).  The timer fires&lt;br /&gt;
either on sender idleness (to send a KA) or on receiver idleness&lt;br /&gt;
(to declare the peer dead).  On fire (&amp;lt;code&amp;gt;ka_snd&amp;lt;/code&amp;gt;) the peer-dead test&lt;br /&gt;
uses &amp;lt;code&amp;gt;max(rcv_cr.act, t_ka_rcv)&amp;lt;/code&amp;gt; so a recent KA reply counts even&lt;br /&gt;
when no DATA has arrived:&lt;br /&gt;
&lt;br /&gt;
* If &amp;lt;code&amp;gt;now - max(rcv_cr.act, t_ka_rcv) &amp;amp;gt; qs.timeout&amp;lt;/code&amp;gt;, mark the flow &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; and notify the per-process flow-event set (&amp;lt;code&amp;gt;proc.fqset&amp;lt;/code&amp;gt;) with &amp;lt;code&amp;gt;FLOW_PEER&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Else if &amp;lt;code&amp;gt;snd_idle &amp;amp;gt; qs.timeout/4&amp;lt;/code&amp;gt;, emit a bare &amp;lt;code&amp;gt;KA | ACK&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) and re-arm.&lt;br /&gt;
* Else just re-arm.&lt;br /&gt;
&lt;br /&gt;
Note: &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; are the receive and transmit shared-memory&lt;br /&gt;
ring buffers.  The r-timer raises &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; on both (route is&lt;br /&gt;
broken); keepalive raises &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; only and notifies&lt;br /&gt;
the flow-event set (peer is silent, writer keeps &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; usable) -&lt;br /&gt;
distinct ACLs.  &amp;lt;code&amp;gt;qs.timeout == 0&amp;lt;/code&amp;gt; disables keepalive entirely; a&lt;br /&gt;
silent peer crash is then undetected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 14. Linger / teardown ==&lt;br /&gt;
&lt;br /&gt;
On &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt; computes a grace timeout&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;max(rcv_cr.act + rcv_cr.inact, snd_cr.act + snd_cr.inact) - now&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(floored at 0 and converted to seconds) and returns it; &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&lt;br /&gt;
forwards this to the IRMd as the dealloc grace.  The IRMd, not FRCT,&lt;br /&gt;
performs the wait.  Before computing the timeout, FRCT may emit a&lt;br /&gt;
final ACK when &amp;lt;code&amp;gt;rcv_cr.lwe != rcv_cr.seqno&amp;lt;/code&amp;gt; (the peer has not been&lt;br /&gt;
told the most recent cumulative ACK) AND the rcv side has been&lt;br /&gt;
active within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer not aged out).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt; is honoured only when &amp;lt;code&amp;gt;snd_cr.lwe &amp;amp;lt; edge&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;edge =&lt;br /&gt;
snd_fin_seqno&amp;lt;/code&amp;gt; after FIN has been sent in stream mode and&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno&amp;lt;/code&amp;gt; otherwise (data or FIN still in flight).  The drain&lt;br /&gt;
itself runs in &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&#039;s &amp;lt;code&amp;gt;while (FRCTI_LINGERING)&amp;lt;/code&amp;gt; loop, not in&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The fd is single-reader / single-writer (documented in the&lt;br /&gt;
manpages).  &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt; pumps &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; on every call (via&lt;br /&gt;
&amp;lt;code&amp;gt;flow_wait_window&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;flow_drain_rx_nb&amp;lt;/code&amp;gt;) and additionally blocks on&lt;br /&gt;
&amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; when the send window is closed.  A pure-writer thread thus&lt;br /&gt;
consumes ACKs without a dedicated reader.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 15. Heritage and adopted techniques ==&lt;br /&gt;
&lt;br /&gt;
Delta-t (Watson, 1981) is the primary heritage; FRCP descends from&lt;br /&gt;
the delta-t protocol family via the Recursive InterNetwork&lt;br /&gt;
Architecture (RINA; Day, &amp;quot;Patterns in Network Architecture&amp;quot;, 2008,&lt;br /&gt;
ch. 9).  Timer-based connection management&lt;br /&gt;
(no SYN/FIN handshake, per-flow state born on first DATA and&lt;br /&gt;
reclaimed after &amp;lt;code&amp;gt;t_mpl + a + r&amp;lt;/code&amp;gt; of silence), the DRF marker, and the&lt;br /&gt;
&amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; timers all come from delta-t.  See Watson,&lt;br /&gt;
&amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection&lt;br /&gt;
Management&amp;quot;, Computer Networks 5 (1981).&lt;br /&gt;
&lt;br /&gt;
The unified &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive and its&lt;br /&gt;
multi-axis QoS-cube argument ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) also come from RINA&lt;br /&gt;
(Day 2008, ch. 6; Grasa et al., &amp;quot;IRATI: investigating RINA as an&lt;br /&gt;
alternative to TCP/IP&amp;quot;, Computer Networks 92 (2015)) - reliability,&lt;br /&gt;
ordering, CRC presence, and encryption are flow attributes, not&lt;br /&gt;
separate sockets or protocols.&lt;br /&gt;
&lt;br /&gt;
The table below summarises additional adopted techniques and their&lt;br /&gt;
references.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FRCP mechanism !! Heritage !! Reference / note&lt;br /&gt;
|-&lt;br /&gt;
| Random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; || TCP ISN || RFC 6528 (Gont &amp;amp;amp; Bellovin, 2012).  QUIC PN-space reset (RFC 9000 sec. 12.3) is a structural analogue.&lt;br /&gt;
|-&lt;br /&gt;
| Cumulative ACK, left-window-edge advance || TCP || RFC 793 / RFC 9293&lt;br /&gt;
|-&lt;br /&gt;
| Receive window with non-shrink rule || TCP || RFC 793 sec. 3.7 / RFC 9293 sec. 3.8.6; RFC 1122 sec. 4.2.2.16 for the explicit non-shrink prohibition&lt;br /&gt;
|-&lt;br /&gt;
| Modular &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; arithmetic (&amp;lt;code&amp;gt;before&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;after&amp;lt;/code&amp;gt; helpers) || TCP || RFC 793 sec. 3.3 / RFC 9293 sec. 3.4&lt;br /&gt;
|-&lt;br /&gt;
| Selective ACK block list || TCP || RFC 2018 (Mathis et al., 1996).  Encoded as a typed FRCP packet rather than a TCP option, so framing is closer to QUIC ACK frames.  D-SACK (RFC 2883) carried in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt;; see [[#1.3. SACK payload|Section 1.3]].&lt;br /&gt;
|-&lt;br /&gt;
| NewReno-careful recovery with &amp;lt;code&amp;gt;recovery_high&amp;lt;/code&amp;gt; gate || TCP || RFC 6582 (Henderson et al., 2012); QUIC builds on the same model in RFC 9002 sec. 7.3.2.  Cwnd half absent (CC in IPCP).&lt;br /&gt;
|-&lt;br /&gt;
| RACK reordering window for fast retransmit || TCP || RFC 8985 (Cheng et al., 2021).  FRCP &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor against &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; collapse; matches RFC 8985 sec. 6.2 and Linux &amp;lt;code&amp;gt;tcp_rack_reo_wnd&amp;lt;/code&amp;gt;.  DSACK-driven &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; (sec. 6.2 step 4) is adopted; see [[#1.3. SACK payload|Section 1.3]] for the wire encoding.  The hybrid RACK-or-&amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; trigger from RFC 8985 sec. 6.2 step 4 is adopted ([[#8. Retransmission|Section 8]]).  QUIC&#039;s analogue in RFC 9002 sec. 6.1.2 uses &amp;lt;code&amp;gt;max(srtt, latest_rtt)&amp;lt;/code&amp;gt; as the base.&lt;br /&gt;
|-&lt;br /&gt;
| Karn&#039;s algorithm: no RTT sample on retransmits, RTO-collapse freeze || TCP || Karn &amp;amp;amp; Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, SIGCOMM 1987; RFC 6298 sec. 3.&lt;br /&gt;
|-&lt;br /&gt;
| RTO formula &amp;lt;code&amp;gt;RTO = max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt; || TCP || RFC 6298 (Paxson et al., 2011).  &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; = 250 us is below RFC 6298 sec. 2.4&#039;s 1 s SHOULD-floor - a recursive-layer choice.&lt;br /&gt;
|-&lt;br /&gt;
| Linux asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator (default) || Linux kernel || &amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;if(delta&amp;amp;lt;0) m&amp;amp;gt;&amp;amp;gt;=3&amp;lt;/code&amp;gt; dampening is a kernel divergence from RFC 6298.  RFC 6298 EWMA available behind a compile flag.&lt;br /&gt;
|-&lt;br /&gt;
| Delayed ACK with rate suppression || TCP || RFC 813 (Clark, 1982); RFC 1122 sec. 4.2.3.2; RFC 5681 sec. 4.2.  Single-deadline coalescing rather than &amp;quot;ack-every-other-segment&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| Zero-window-probe / persist-timer analogue (RDVS) || TCP || RFC 1122 sec. 4.2.2.17 / RFC 9293 sec. 3.8.6.1.  RDVS solicits an FC reply, distinct from QUIC &amp;lt;code&amp;gt;DATA_BLOCKED&amp;lt;/code&amp;gt; (RFC 9000 sec. 19.12), which is one-way notification.  &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; give-up departs from TCP.&lt;br /&gt;
|-&lt;br /&gt;
| Multiplexed control on a single PCI || SCTP / QUIC || SCTP chunk bundling (RFC 9260 sec. 6.10); QUIC frame multiplexing (RFC 9000 sec. 12.4).  Cleaner fit than TCP&#039;s separate-flag-bits design.&lt;br /&gt;
|-&lt;br /&gt;
| ACK ranges as multiple discontiguous acked blocks || QUIC || QUIC ACK frame (RFC 9000 sec. 19.3).  FRCP SACK is conceptually QUIC-frame-shaped even though encoded as absolute &amp;lt;code&amp;gt;[start,end]&amp;lt;/code&amp;gt; pairs.&lt;br /&gt;
|-&lt;br /&gt;
| Nonce-authenticated active RTT / liveness probing (RTTP) || QUIC &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;PATH_RESPONSE&amp;lt;/code&amp;gt; (RFC 9000 sec. 8.2, sec. 19.17, sec. 19.18).  WebRTC ICE consent-freshness (RFC 7675) is the same pattern.  QUIC&#039;s nonce is 8 octets; FRCP chooses 16.&lt;br /&gt;
|-&lt;br /&gt;
| Probing distinct from keepalive || QUIC || KA timer answers &amp;quot;peer alive?&amp;quot;, RTTP answers &amp;quot;path measurable?&amp;quot;, as in QUIC PING (RFC 9000 sec. 19.2) vs &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| Bare KA + ACK keepalive packets || QUIC / SCTP || QUIC PING (RFC 9000 sec. 19.2); SCTP HEARTBEAT / HEARTBEAT-ACK (RFC 9260 sec. 8.3).  SCTP HEARTBEAT also carries an opaque echoed blob, structurally similar to FRCP RTTP.&lt;br /&gt;
|-&lt;br /&gt;
| (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) fragment-role bits ([[#7.2. Fragmentation and reassembly|Section 7.2]]) || SCTP || RFC 9260 sec. 3.3.1 DATA chunk B/E bits encode the same four states (&amp;lt;code&amp;gt;B+E=SOLE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;B-only=FIRST&amp;lt;/code&amp;gt;, neither=&amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E-only=LAST&amp;lt;/code&amp;gt;).  Each fragment carries its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;/TSN and is independently retransmitted.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-offset reassembly (Sections [[#1.5. Stream PCI extension|1.5]], [[#16. Stream-mode flows|16]]) || QUIC || QUIC STREAM frame (RFC 9000 sec. 19.8) uses Offset + Length varints; FRCP uses fixed 32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;.  One stream per flow vs QUIC&#039;s many streams multiplexed.&lt;br /&gt;
|-&lt;br /&gt;
| FIN end-of-stream marker (Sections [[#1.2. Flag bits|1.2]], [[#16. Stream-mode flows|16]]) || TCP / QUIC || TCP FIN flag (RFC 9293 sec. 3.1) closes one half of the byte stream; QUIC STREAM frame FIN bit (RFC 9000 sec. 19.8) does the same per stream with an immutable final-size invariance (RFC 9000 sec. 4.5: the final size is fixed once observed).  FRCP&#039;s FIN consumes one packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (not one byte of stream space) and is idempotent on the sender side.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-credit flow control ([[#16. Stream-mode flows|Section 16]]) || QUIC || &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; (RFC 9000 sec. 4.1, sec. 19.10).  FRCP projects a per-flow byte budget onto the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;.  Single stream per flow collapses QUIC&#039;s &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.&lt;br /&gt;
|-&lt;br /&gt;
| Header protection (encrypted seqnos) || QUIC || QUIC RFC 9001 sec. 5.4 applies header protection on top of AEAD to mask the packet number.  FRCP&#039;s per-flow AEAD wrap ([[#16. Stream-mode flows|Section 16]]) is wider: it encrypts the entire PCI including &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; because the IPCP below already routes, so no destination connection-ID needs to stay in clear (cf. RFC 9000 sec. 5.2).&lt;br /&gt;
|-&lt;br /&gt;
| Two-bit fragment role polarity || SCTP || The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair follows SCTP B/E (begin = 1 / end = 1) rather than IPv4 MF (RFC 791 sec. 3.2), which has the inverse polarity (MF = 1 means NOT last).&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal reliability / ordering axes ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) || SCTP || PR-SCTP (RFC 3758, per-message partial reliability) and SCTP DATA U-bit (RFC 9260 sec. 3.3.1, per-message unordered) are the closest precedents for decoupling reliability from ordering; FRCP sets them per-flow rather than per-message.&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal CRC (&amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt;) || UDP-Lite || RFC 3828 (Larzon et al., 2004) lets the sender pick a per-packet Checksum Coverage and the receiver enforce a locally configured minimum (no in-band negotiation; sec. 3.1, sec. 3.3).  FRCP gates a full CRC trailer on &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; at flow setup.  Contrast TCP / SCTP (mandatory checksum) and QUIC (AEAD subsumes CRC).&lt;br /&gt;
|-&lt;br /&gt;
| Setup-time service negotiation || DCCP / SCTP / QUIC || DCCP Service Codes (RFC 4340 sec. 8.1.2, RFC 5595); SCTP INIT parameters (RFC 9260 sec. 3.3.2); QUIC transport parameters (RFC 9000 sec. 7.4).  All negotiate service properties at connection setup; only RINA&#039;s QoS cube exposes them as an orthogonal vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.1. Original to FRCP (no clean prior art) ===&lt;br /&gt;
&lt;br /&gt;
* Pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]): receiver-driven nudge exploiting &amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;.  Closest analogues are SCTP Gap Ack Blocks (RFC 9260 sec. 3.3.4) and DCCP Ack Vector (RFC 4340 sec. 11.4) - both let the receiver describe gaps to the sender, but neither targets the cross-epoch / pre-DRF case.&lt;br /&gt;
* &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; window-probe give-up: neither TCP (persist-timer probes until application or R2 abort, RFC 9293 sec. 3.8.6.1) nor QUIC has an explicit FC-give-up counter.  A recursive-network choice: outer layers can drop the flow.&lt;br /&gt;
* Skip-past-gap reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]): SCTP fragments and reassembles every flow regardless of reliability/ordering, using its own per-stream reassembly queue; QUIC fragments via STREAM offsets.  FRCP fragments best-effort flows too, but the receiver drops the broken prefix the moment a later run-start (&amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; role) is visible inside the &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;-wide reorder ring - no IP-frag-style timeout, no SCTP-style explicit abort.  If no later run-start arrives within the ring, &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt; and the partial run keeps its slots; the next inspect retries.  The trade-off: a permanently-lost &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; in a long isolated run holds slots until either a later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; appears in the ring or the writer stops, at which point the slots are reclaimed on flow teardown.&lt;br /&gt;
* Reassembly deferred to consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]), message mode only (&amp;lt;code&amp;gt;qos.service == SVC_MESSAGE&amp;lt;/code&amp;gt;): SCTP (RFC 9260 sec. 6.9), QUIC (RFC 9000 sec. 2.2), and TCP (RFC 9293) all hold reassembly state at the receive boundary.  FRCP message-mode leaves fragments in the shared-memory ring until &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; pulls and lands the SDU directly in the caller&#039;s buffer.  Stream mode ([[#16. Stream-mode flows|Section 16]]) uses the standard QUIC-style direct ring placement on receive and does not defer.  The optimisation is enabled by the Shared-Memory Subsystem (SSM) packet-buffer ring (see &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt; at [[#1.1. PCI header|Section 1.1]]); the analogue is OS-level scatter-gather I/O (&amp;lt;code&amp;gt;recvmsg+iovec&amp;lt;/code&amp;gt;), not a transport-layer prior art.&lt;br /&gt;
* TLP-equivalent tail-loss recovery (RFC 8985 sec. 7; RFC 9002 sec. 6.2): FRCP does not emit an explicit Tail Loss Probe packet, but the same goal is met implicitly by RACK loss detection ([[#8. Retransmission|Section 8]]) firing on a non-advancing cumulative ACK once the head-of-line slot ages past the RACK reorder window &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; - well below &amp;lt;code&amp;gt;RTO = max(2 * SRTT, SRTT + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.  A receiver-driven nudge is also available via the pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.2. Not adopted ===&lt;br /&gt;
&lt;br /&gt;
* Slow start, congestion window (cwnd), Additive Increase / Multiplicative Decrease (AIMD), NewReno cwnd inflation.  Congestion control lives in the IPCP CA policies and is driven by Explicit Congestion Notification (ECN, RFC 3168).&lt;br /&gt;
* Nagle / silly-window-syndrome (SWS) avoidance (RFC 896, RFC 1122 sec. 4.2.3.4).  (Deferred work, not adopted in the current spec.)&lt;br /&gt;
* TCP Timestamps (RFC 7323) / Protection Against Wrapped Sequences (PAWS) - RTT measurement uses RTTP, not per-segment timestamps.  A peer-supplied timestamp echoed on every ACK lets a malicious peer drive the &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; estimate arbitrarily low, collapsing the RTO and triggering a self-inflicted retransmit storm.  RTTP confines RTT measurement to nonce-authenticated probe round-trips, where a forged echo is rejected before it can reach the estimator.&lt;br /&gt;
* ECN (Explicit Congestion Notification) response inside FRCP (consumed by IPCP Congestion Avoidance / CA).&lt;br /&gt;
* IP-style fragment-offset reassembly (RFC 791 sec. 3.2; RFC 8200 sec. 4.5).  Message-mode FRCP relies on the FRCT &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; reorder ring keyed by &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (shared by FRTX and best-effort flows) to put fragments back in order; no separate offset field is needed and no IP-style hole-list reassembly buffer is kept.  Stream-mode FRCP does carry &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte offsets ([[#1.5. Stream PCI extension|Section 1.5]]) for direct ring placement on receive.&lt;br /&gt;
* QUIC STREAM offset+length framing on &#039;&#039;every&#039;&#039; flow (RFC 9000 sec. 19.8).  Message-mode FRCP uses the SCTP-style B/E flag-bit encoding (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) and skips the offsets; stream-mode FRCP adopts the QUIC offset model (heritage table above).&lt;br /&gt;
&lt;br /&gt;
== 16. Stream-mode flows ==&lt;br /&gt;
&lt;br /&gt;
When a flow is allocated with &amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt; both peers&lt;br /&gt;
switch to byte-stream semantics, layered on top of the FRTX reorder&lt;br /&gt;
machinery already described in Sections [[#6. Receive path|6]]-[[#8. Retransmission|8]].&lt;br /&gt;
&lt;br /&gt;
=== 16.1. Send ===&lt;br /&gt;
&lt;br /&gt;
The sender splits the caller&#039;s octets into chunks of at most&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt; octets (Sections [[#1.1. PCI header|1.1]]&lt;br /&gt;
and [[#1.5. Stream PCI extension|1.5]]).  Each chunk is one DATA packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte range copied from a monotonic stream counter.&lt;br /&gt;
In stream mode &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST be transmitted as&lt;br /&gt;
zero; the per-byte position is carried by the &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt;&lt;br /&gt;
extension instead.&lt;br /&gt;
&lt;br /&gt;
End-of-stream is signalled with a 0-byte DATA packet that has &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;&lt;br /&gt;
(bit 12) set, emitted on the FIN triggers listed in [[#1.2. Flag bits|Section 1.2]]&lt;br /&gt;
(WR-half close, &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, and any other path that yields the&lt;br /&gt;
final byte).  The sender MUST emit at most one FIN per flow; its&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; MUST equal &amp;lt;code&amp;gt;[final-byte, final-byte)&amp;lt;/code&amp;gt; (i.e., empty&lt;br /&gt;
interval at the final byte position; final-size invariance,&lt;br /&gt;
analogous to QUIC RFC 9000 sec. 4.5).  Idempotency is enforced by&lt;br /&gt;
an &amp;lt;code&amp;gt;snd_fin_sent&amp;lt;/code&amp;gt; guard.&lt;br /&gt;
&lt;br /&gt;
=== 16.2. Receive ===&lt;br /&gt;
&lt;br /&gt;
On arrival the receiver places the payload directly into a per-flow&lt;br /&gt;
byte-indexed receive ring of width &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; (octets) at the position&lt;br /&gt;
indicated by &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, with a two-segment &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; across the ring&lt;br /&gt;
boundary if needed.  Receipt is recorded in the FRTX reorder&lt;br /&gt;
machinery ([[#6.2. Locked main path|Section 6.2]]) augmented with the packet&#039;s &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;, and&lt;br /&gt;
FIN bit per slot.  When a packet&#039;s &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; front-overlaps&lt;br /&gt;
bytes already at or below the byte high-water mark, the overlap is&lt;br /&gt;
trimmed before placement so the same byte is never written twice.&lt;br /&gt;
After stashing, the receiver advances &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; and the byte high-water&lt;br /&gt;
mark across any newly-contiguous prefix.  Each slot advanced MUST&lt;br /&gt;
satisfy &amp;lt;code&amp;gt;start == the last-delivered slot&#039;s end&amp;lt;/code&amp;gt;; a slot whose&lt;br /&gt;
&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; does not equal that &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; is silently dropped at delivery time&lt;br /&gt;
(the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is consumed, no stream bytes contributed) and the high-&lt;br /&gt;
water mark does not advance past it.  The stream byte-stream&lt;br /&gt;
stalls at that point - there is no flow-tear-down on mismatch.&lt;br /&gt;
This filters spliced or off-path-injected slots that fall in&lt;br /&gt;
window without strong cryptographic authentication.&lt;br /&gt;
&lt;br /&gt;
A FIN slot marks end-of-stream at advance time only if its byte&lt;br /&gt;
position equals the last-delivered slot&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;; otherwise the FIN&lt;br /&gt;
is ignored and the corresponding &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; occupies a slot but&lt;br /&gt;
contributes no stream bytes.  No packet buffer is held after the&lt;br /&gt;
ring copy.&lt;br /&gt;
&lt;br /&gt;
=== 16.3. Read ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns up to &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt; octets from the contiguous prefix&lt;br /&gt;
&amp;lt;code&amp;gt;[next, high-water)&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;next&amp;lt;/code&amp;gt; is the byte the application has&lt;br /&gt;
already consumed up to and &amp;lt;code&amp;gt;high-water&amp;lt;/code&amp;gt; is the rightmost contiguous&lt;br /&gt;
byte received.  When the stream is fully drained AND end-of-stream&lt;br /&gt;
(EOS) was observed (&amp;lt;code&amp;gt;next == EOS&amp;lt;/code&amp;gt; byte position), &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns&lt;br /&gt;
0 (&amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) - the same shape POSIX &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt; uses on TCP after a peer&lt;br /&gt;
FIN.&lt;br /&gt;
&lt;br /&gt;
=== 16.4. Flow control ===&lt;br /&gt;
&lt;br /&gt;
ACK / SACK / RACK / RTO machinery is unchanged; the FRTX reorder&lt;br /&gt;
ring is reused as a per-&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; received-bitmap.  Let &amp;lt;code&amp;gt;per_pkt =&lt;br /&gt;
(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt;, the maximum stream-&lt;br /&gt;
byte payload one DATA packet can carry ([[#16.1. Send|Section 16.1]]).  The&lt;br /&gt;
receive window advertised in FC is clamped so the byte window&lt;br /&gt;
(&amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt;) cannot be overrun: the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is at most&lt;br /&gt;
&amp;lt;code&amp;gt;rcv_cr.lwe + ring_sz / per_pkt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC byte-credit flow-control model&lt;br /&gt;
(&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt;, RFC 9000 sec. 4.1 and sec. 19.10) projected onto&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; space.  With one stream per flow there is no &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; /&lt;br /&gt;
&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.  Receiver-side silly-window-syndrome&lt;br /&gt;
(SWS) avoidance (RFC 9293 sec. 3.8.6.2.2) is achieved by combining&lt;br /&gt;
the consume-time &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; bump with the global non-shrink rule from&lt;br /&gt;
[[#11. Flow control|Section 11]].&lt;br /&gt;
&lt;br /&gt;
=== 16.5. Security considerations ===&lt;br /&gt;
&lt;br /&gt;
Threat model.  An attacker that can observe (on-path passive) or&lt;br /&gt;
predict (off-path blind) the flow&#039;s seqnos and byte offsets on an&lt;br /&gt;
unencrypted stream flow can inject DATA or FIN at any in-window&lt;br /&gt;
position.  The in-line consistency checks above (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; == prior&lt;br /&gt;
&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; on advance; FIN MUST be 0-byte; FIN MUST sit at the final&lt;br /&gt;
byte position) realise the spirit of RFC 5961&#039;s &amp;quot;sequence-window&lt;br /&gt;
plus exact-position match for control bits&amp;quot; without an explicit&lt;br /&gt;
challenge-ACK probe; they make a few specific blind attack shapes&lt;br /&gt;
harder but are not cryptographic authentication. This is&lt;br /&gt;
comparable to TCP without the TCP Authentication Option (TCP-AO,&lt;br /&gt;
RFC 5925), tighter than a&lt;br /&gt;
pre-RFC-5961 TCP stack, and roughly equivalent to a modern&lt;br /&gt;
RFC 5961 stack against blind off-path injection - none of these&lt;br /&gt;
help once the attacker can sniff. TLS over TCP (RFC 8446)&lt;br /&gt;
encrypts only the TCP payload and leaves TCP seqnos, ACKs, FIN,&lt;br /&gt;
and RST in the clear, so TLS does NOT defend against TCP-header-&lt;br /&gt;
level injection; QUIC (RFC 9000) hides packet numbers under&lt;br /&gt;
header protection (RFC 9001 sec. 5.4), so this specific weakness&lt;br /&gt;
does not apply to QUIC.&lt;br /&gt;
&lt;br /&gt;
Mitigation: AEAD.  When the flow has encryption enabled the&lt;br /&gt;
recommended AEAD ciphers (AES-GCM, RFC 5288; or ChaCha20-Poly1305,&lt;br /&gt;
RFC 8439) wrap the entire FRCP packet on the wire - PCI, stream&lt;br /&gt;
extension, body, and the CRC trailer when &amp;lt;code&amp;gt;ber == 0&amp;lt;/code&amp;gt; - under a&lt;br /&gt;
per-flow symmetric key derived from the flow&#039;s own key exchange&lt;br /&gt;
([[#1.1. PCI header|Section 1.1]]).  The AEAD tag (~2^-128 forgery probability)&lt;br /&gt;
dominates the CRC (~2^-32) for integrity in this mode but the CRC&lt;br /&gt;
trailer is currently retained inside the wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
Implementations MUST NOT rely on the security properties below&lt;br /&gt;
when a non-AEAD cipher (e.g. AES-CTR alone) is negotiated; non-&lt;br /&gt;
AEAD modes provide confidentiality only and the threat-model&lt;br /&gt;
claims do not hold.&lt;br /&gt;
&lt;br /&gt;
With an AEAD cipher in use, seqnos, byte offsets, and the FIN bit&lt;br /&gt;
are both authenticated and confidential. Against an off-path or&lt;br /&gt;
on-path-passive attacker this is:&lt;br /&gt;
&lt;br /&gt;
* Stronger than TCP+TLS (TCP header in the clear).&lt;br /&gt;
* Stronger than TCP+TCP-AO (header authenticated but visible).&lt;br /&gt;
* Comparable to IPsec ESP transport mode (RFC 4303), which similarly authenticates and encrypts the upper-layer header plus payload, and to QUIC packet protection (RFC 9001 sec. 5), with the difference that QUIC must leave the destination connection ID in the clear for routing whereas FRCP relies on the IPCP below for delivery and can therefore encrypt its entire PCI.&lt;br /&gt;
&lt;br /&gt;
Keying granularity. Ouroboros flow allocation runs key exchange (&amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt;) per flow, so each &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; yields independent symmetric keys. This is finer-grained than QUIC (per-connection, RFC 9001, where one handshake covers all multiplexed streams) and finer-grained than typical IPsec deployment (per-host-pair Security Associations, SAs). Forward secrecy follows from the &amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt; when an ephemeral Diffie-Hellman exchange (DHE), or a hybrid mode (classical DH + post-quantum Key Encapsulation Mechanism / KEM), is selected.&lt;br /&gt;
&lt;br /&gt;
Replay protection. The AEAD layer itself does NOT carry an explicit anti-replay window (unlike IPsec ESP, RFC 4303 sec. 3.4.3, or DTLS, RFC 9147 sec. 4.5.1).  For FRCP-engaged flows the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space duplicate-suppression in [[#6.2. Locked main path|Section 6.2]] rejects replayed&lt;br /&gt;
DATA after the AEAD strips the wrap, because the AEAD authenticates the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a replay re-presents an old &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; that is then discarded either as a duplicate (still inside the receive window or as outside the receive window, depending on how far &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; has advanced since the original packet was delivered. RAW (&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) flows have no FRCP layer and therefore no replay protection at the AEAD layer either; deployments that need replay rejection on RAW flows SHOULD use SVC_MESSAGE.&lt;br /&gt;
&lt;br /&gt;
Layering. The AEAD wrap sits below FRCP on the data path, so RAW best-effort flows (&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;, the UDP-equivalent service of [[#2.2. Service modes (orthogonal axes)|Section 2.2]]) inherit the same per-flow integrity + confidentiality scope as FRCP-engaged flows - whatever the process and FRCP (if any) put on the wire is what the AEAD authenticates. No DTLS-equivalent layering is required for confidentiality and integrity; replay protection above AEAD is a separate concern as noted above.&lt;br /&gt;
&lt;br /&gt;
== 17. References ==&lt;br /&gt;
&lt;br /&gt;
This section lists the IETF documents, published works, and&lt;br /&gt;
source-code references cited inline elsewhere in this document.&lt;br /&gt;
IETF documents are cited inline as &amp;quot;RFC NNNN sec. X.Y&amp;quot;; books,&lt;br /&gt;
journal papers, and source-code references are cited inline by&lt;br /&gt;
author and year (or by file and function name) and are listed&lt;br /&gt;
here for convenience.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.1. IETF documents ===&lt;br /&gt;
&lt;br /&gt;
;[RFC 791]&lt;br /&gt;
:J. Postel, &amp;quot;Internet Protocol&amp;quot;, STD 5, RFC 791, September 1981.&lt;br /&gt;
;[RFC 793]&lt;br /&gt;
:J. Postel, &amp;quot;Transmission Control Protocol&amp;quot;, STD 7, RFC 793, September 1981.  Obsoleted by RFC 9293.&lt;br /&gt;
;[RFC 813]&lt;br /&gt;
:D. D. Clark, &amp;quot;Window and Acknowledgement Strategy in TCP&amp;quot;, RFC 813, July 1982.&lt;br /&gt;
;[RFC 896]&lt;br /&gt;
:J. Nagle, &amp;quot;Congestion Control in IP/TCP Internetworks&amp;quot;, RFC 896, January 1984.&lt;br /&gt;
;[RFC 1122]&lt;br /&gt;
:R. Braden (ed.), &amp;quot;Requirements for Internet Hosts -- Communication Layers&amp;quot;, STD 3, RFC 1122, October 1989.&lt;br /&gt;
;[RFC 2018]&lt;br /&gt;
:M. Mathis, J. Mahdavi, S. Floyd, A. Romanow, &amp;quot;TCP Selective Acknowledgment Options&amp;quot;, RFC 2018, October 1996.&lt;br /&gt;
;[RFC 2119]&lt;br /&gt;
:S. Bradner, &amp;quot;Key words for use in RFCs to Indicate Requirement Levels&amp;quot;, BCP 14, RFC 2119, March 1997.&lt;br /&gt;
;[RFC 2883]&lt;br /&gt;
:S. Floyd, J. Mahdavi, M. Mathis, M. Podolsky, &amp;quot;An Extension to the Selective Acknowledgement (SACK) Option for TCP&amp;quot;, RFC 2883, July 2000.&lt;br /&gt;
;[RFC 3758]&lt;br /&gt;
:R. Stewart, M. Ramalho, Q. Xie, M. Tuexen, P. Conrad, &amp;quot;Stream Control Transmission Protocol (SCTP) Partial Reliability Extension&amp;quot;, RFC 3758, May 2004.&lt;br /&gt;
;[RFC 3828]&lt;br /&gt;
:L-A. Larzon, M. Degermark, S. Pink, L-E. Jonsson (ed.), G. Fairhurst (ed.), &amp;quot;The Lightweight User Datagram Protocol (UDP-Lite)&amp;quot;, RFC 3828, July 2004.&lt;br /&gt;
;[RFC 4303]&lt;br /&gt;
:S. Kent, &amp;quot;IP Encapsulating Security Payload (ESP)&amp;quot;, RFC 4303, December 2005.&lt;br /&gt;
;[RFC 4340]&lt;br /&gt;
:E. Kohler, M. Handley, S. Floyd, &amp;quot;Datagram Congestion Control Protocol (DCCP)&amp;quot;, RFC 4340, March 2006.&lt;br /&gt;
;[RFC 5288]&lt;br /&gt;
:J. Salowey, A. Choudhury, D. McGrew, &amp;quot;AES Galois Counter Mode (GCM) Cipher Suites for TLS&amp;quot;, RFC 5288, August 2008.&lt;br /&gt;
;[RFC 5595]&lt;br /&gt;
:G. Fairhurst, &amp;quot;The Datagram Congestion Control Protocol (DCCP) Service Codes&amp;quot;, RFC 5595, September 2009.&lt;br /&gt;
;[RFC 5681]&lt;br /&gt;
:M. Allman, V. Paxson, E. Blanton, &amp;quot;TCP Congestion Control&amp;quot;, RFC 5681, September 2009.&lt;br /&gt;
;[RFC 5925]&lt;br /&gt;
:J. Touch, A. Mankin, R. Bonica, &amp;quot;The TCP Authentication Option&amp;quot;, RFC 5925, June 2010.&lt;br /&gt;
;[RFC 5961]&lt;br /&gt;
:A. Ramaiah, R. Stewart, M. Dalal, &amp;quot;Improving TCP&#039;s Robustness to Blind In-Window Attacks&amp;quot;, RFC 5961, August 2010.&lt;br /&gt;
;[RFC 6298]&lt;br /&gt;
:V. Paxson, M. Allman, J. Chu, M. Sargent, &amp;quot;Computing TCP&#039;s Retransmission Timer&amp;quot;, RFC 6298, June 2011.&lt;br /&gt;
;[RFC 6528]&lt;br /&gt;
:F. Gont, S. Bellovin, &amp;quot;Defending against Sequence Number Attacks&amp;quot;, RFC 6528, February 2012.  Obsoletes RFC 1948.&lt;br /&gt;
;[RFC 6582]&lt;br /&gt;
:T. Henderson, S. Floyd, A. Gurtov, Y. Nishida, &amp;quot;The NewReno Modification to TCP&#039;s Fast Recovery Algorithm&amp;quot;, RFC 6582, April 2012.&lt;br /&gt;
;[RFC 7323]&lt;br /&gt;
:D. Borman, B. Braden, V. Jacobson, R. Scheffenegger (ed.), &amp;quot;TCP Extensions for High Performance&amp;quot;, RFC 7323, September 2014.&lt;br /&gt;
;[RFC 7675]&lt;br /&gt;
:M. Perumal, D. Wing, R. Ravindranath, T. Reddy, M. Thomson, &amp;quot;Session Traversal Utilities for NAT (STUN) Usage for Consent Freshness&amp;quot;, RFC 7675, October 2015.&lt;br /&gt;
;[RFC 8174]&lt;br /&gt;
:B. Leiba, &amp;quot;Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words&amp;quot;, BCP 14, RFC 8174, May 2017.&lt;br /&gt;
;[RFC 8200]&lt;br /&gt;
:S. Deering, R. Hinden, &amp;quot;Internet Protocol, Version 6 (IPv6) Specification&amp;quot;, STD 86, RFC 8200, July 2017.&lt;br /&gt;
;[RFC 8439]&lt;br /&gt;
:Y. Nir, A. Langley, &amp;quot;ChaCha20 and Poly1305 for IETF Protocols&amp;quot;, RFC 8439, June 2018.&lt;br /&gt;
;[RFC 8446]&lt;br /&gt;
:E. Rescorla, &amp;quot;The Transport Layer Security (TLS) Protocol Version 1.3&amp;quot;, RFC 8446, August 2018.&lt;br /&gt;
;[RFC 8985]&lt;br /&gt;
:Y. Cheng, N. Cardwell, N. Dukkipati, P. Jha, &amp;quot;The RACK-TLP Loss Detection Algorithm for TCP&amp;quot;, RFC 8985, February 2021.&lt;br /&gt;
;[RFC 9000]&lt;br /&gt;
:J. Iyengar (ed.), M. Thomson (ed.), &amp;quot;QUIC: A UDP-Based Multiplexed and Secure Transport&amp;quot;, RFC 9000, May 2021.&lt;br /&gt;
;[RFC 9001]&lt;br /&gt;
:M. Thomson (ed.), S. Turner (ed.), &amp;quot;Using TLS to Secure QUIC&amp;quot;, RFC 9001, May 2021.&lt;br /&gt;
;[RFC 9002]&lt;br /&gt;
:J. Iyengar (ed.), I. Swett (ed.), &amp;quot;QUIC Loss Detection and Congestion Control&amp;quot;, RFC 9002, May 2021.&lt;br /&gt;
;[RFC 9147]&lt;br /&gt;
:E. Rescorla, H. Tschofenig, N. Modadugu, &amp;quot;The Datagram Transport Layer Security (DTLS) Protocol Version 1.3&amp;quot;, RFC 9147, April 2022.&lt;br /&gt;
;[RFC 9260]&lt;br /&gt;
:R. Stewart, M. Tuexen, K. Nielsen, &amp;quot;Stream Control Transmission Protocol&amp;quot;, RFC 9260, June 2022.  Obsoletes RFC 4960.&lt;br /&gt;
;[RFC 9293]&lt;br /&gt;
:W. Eddy (ed.), &amp;quot;Transmission Control Protocol (TCP)&amp;quot;, STD 7, RFC 9293, August 2022.  Obsoletes RFC 793 and several follow-ons; updates RFC 1122 and others.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.2. Books and journal papers ===&lt;br /&gt;
&lt;br /&gt;
;[Day08]&lt;br /&gt;
:J. Day, &amp;quot;Patterns in Network Architecture: A Return to Fundamentals&amp;quot;, Prentice Hall, 2008.&lt;br /&gt;
;[Grasa15]&lt;br /&gt;
:E. Grasa et al., &amp;quot;IRATI: investigating RINA as an alternative to TCP/IP&amp;quot;, Computer Networks, Vol. 92, December 2015.&lt;br /&gt;
;[KP87]&lt;br /&gt;
:P. Karn, C. Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, ACM SIGCOMM, August 1987.&lt;br /&gt;
;[Wat81]&lt;br /&gt;
:R. W. Watson, &amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection Management&amp;quot;, Computer Networks, Vol. 5, 1981.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.3. Source-code references ===&lt;br /&gt;
&lt;br /&gt;
;[Linux-RTT]&lt;br /&gt;
:&amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt; of the Linux kernel, defining the asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; variance update used as FRCP&#039;s default RTT estimator ([[#12. RTT estimation|Section 12]]).  Line-stable browseable copy at https://elixir.bootlin.com/linux/latest/source/net/ipv4/tcp_input.c.&lt;br /&gt;
&lt;br /&gt;
[[Category:Protocols]]&lt;br /&gt;
[[Category:Ouroboros internals]]&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1923</id>
		<title>Flow and Retransmission Control Protocol</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1923"/>
		<updated>2026-05-18T01:06:47Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 1.1. PCI header */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:FRCP - Flow and Retransmission Control Protocol}}&lt;br /&gt;
&lt;br /&gt;
FRCP runs end-to-end between two peers over a flow.  It delivers&lt;br /&gt;
reliability, in-order delivery, flow control, and liveness.&lt;br /&gt;
Congestion Control (CC) is not in FRCP - that lives in the IPC&lt;br /&gt;
Process (IPCP) Congestion Avoidance (CA) policies, orthogonal to&lt;br /&gt;
FRCP.  Flow allocation, naming, and IPCP lifecycle are handled by&lt;br /&gt;
the IPC Resource Manager daemon (IRMd).&lt;br /&gt;
&lt;br /&gt;
FRCT (Flow and Retransmission Control Task) is the libouroboros&lt;br /&gt;
implementation of FRCP; the task lives in &amp;lt;code&amp;gt;src/lib/frct.c&amp;lt;/code&amp;gt;.  The&lt;br /&gt;
remainder of this document describes the FRCP wire protocol and the&lt;br /&gt;
behaviour FRCT realises.  Code symbols retain the &amp;lt;code&amp;gt;FRCT_&amp;lt;/code&amp;gt; prefix&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, ...) because they belong to the implementing&lt;br /&gt;
task; this document references them verbatim.&lt;br /&gt;
&lt;br /&gt;
The keywords &amp;quot;MUST&amp;quot;, &amp;quot;MUST NOT&amp;quot;, &amp;quot;REQUIRED&amp;quot;, &amp;quot;SHALL&amp;quot;, &amp;quot;SHALL NOT&amp;quot;,&lt;br /&gt;
&amp;quot;SHOULD&amp;quot;, &amp;quot;SHOULD NOT&amp;quot;, &amp;quot;RECOMMENDED&amp;quot;, &amp;quot;MAY&amp;quot;, and &amp;quot;OPTIONAL&amp;quot; in&lt;br /&gt;
this document are to be interpreted as described in &amp;lt;code&amp;gt;BCP&amp;lt;/code&amp;gt; 14 (Best&lt;br /&gt;
Current Practice; RFC 2119, RFC 8174) when, and only when, they&lt;br /&gt;
appear in all capitals.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:Unsigned 32-bit / 8-bit integers (kernel-C style).&lt;br /&gt;
;&amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:Nanoseconds.&lt;br /&gt;
&lt;br /&gt;
Modular sequence-number comparators (32-bit, modulo 2^32):&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;before(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;(int32_t)(a - b) &amp;amp;lt; 0&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;after(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;before(b, a)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Used throughout for &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; ordering checks.&lt;br /&gt;
&lt;br /&gt;
Round-Trip Time (RTT) abbreviations used throughout:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;&lt;br /&gt;
:Smoothed RTT estimate (RFC 6298).&lt;br /&gt;
;&amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt;&lt;br /&gt;
:Mean deviation of RTT (Linux variance estimator).&lt;br /&gt;
;&amp;lt;code&amp;gt;EWMA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Exponentially Weighted Moving Average.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTO&amp;lt;/code&amp;gt;&lt;br /&gt;
:Retransmission Timeout, &amp;lt;code&amp;gt;max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Timer-bound symbols &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer, ACK delay) and &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer,&lt;br /&gt;
retransmission window) are defined in [[#8. Retransmission|Section 8]]; &amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; (Maximum&lt;br /&gt;
Packet Lifetime) is introduced in [[#2.1. Per-flow state|Section 2.1]] (the &amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; field)&lt;br /&gt;
with heritage in [[#15. Heritage and adopted techniques|Section 15]].&lt;br /&gt;
&lt;br /&gt;
Wire-format diagrams follow the IETF convention: bit 0 is the&lt;br /&gt;
leftmost (most significant) bit and fields are in network byte&lt;br /&gt;
order unless stated otherwise.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1. Wire format ==&lt;br /&gt;
&lt;br /&gt;
=== 1.1. PCI header ===&lt;br /&gt;
&lt;br /&gt;
Fixed 16-octet base Protocol-Control Information (PCI) header&lt;br /&gt;
prefixed to every FRCP packet (RFC convention: bit 0 leftmost,&lt;br /&gt;
most-significant bit first).  All multi-byte fields are in network byte order. DATA packets on&lt;br /&gt;
stream-mode flows carry an additional 8-octet extension (see&lt;br /&gt;
[[#1.5. Stream PCI extension|Section 1.5]]); SACK and RTTP carry their own payloads after the&lt;br /&gt;
base PCI.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |             flags             |              hcs              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            window                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            seqno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            ackno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                     payload (variable) ...&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt;&lt;br /&gt;
:feature/type bitmap (see [[#1.2. Flag bits|Section 1.2]]).&lt;br /&gt;
;&amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;&lt;br /&gt;
:CRC-16-CCITT-FALSE Header Check Sequence (HCS) over &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; (+ stream extension when present); the two octets of the &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt; field itself are omitted from the CRC input.  Verified on receive before any flag-driven dispatch.&lt;br /&gt;
;&amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt;&lt;br /&gt;
:receiver-advertised right window edge (valid iff FC).&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-flow sequence number.&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;&lt;br /&gt;
:cumulative Acknowledgement (ACK) (valid iff ACK).&lt;br /&gt;
&lt;br /&gt;
A single packet can simultaneously carry DATA + ACK + FC (Flow&lt;br /&gt;
Control) + RXM (Retransmission) by ORing flag bits; the PCI&lt;br /&gt;
multiplexes control on the same wire frame in the spirit of SCTP&lt;br /&gt;
chunk bundling (RFC 9260 sec. 6.10) and QUIC frame multiplexing&lt;br /&gt;
(RFC 9000 sec. 12.4).  DATA-bearing packets carry the caller&#039;s&lt;br /&gt;
payload after the PCI; SACK (Selective Acknowledgement) and RTTP&lt;br /&gt;
(Round-Trip Time Probe) carry their own typed payloads after the&lt;br /&gt;
PCI.&lt;br /&gt;
&lt;br /&gt;
Optional framing (per-flow, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]]).  On the wire, the&lt;br /&gt;
order from inside out is:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Layer !! Scope&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| The FRCP packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body + CRC-32 ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| CRC-32 covers the body only (PCI is in HCS); appended iff &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; on DATA, or on every SACK packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ AEAD-wrap of above ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| Iff Authenticated Encryption with Associated Data (AEAD) is enabled.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* HCS in the PCI covers the header fields on every packet and is verified before any flag-driven dispatch.&lt;br /&gt;
* The CRC-32 trailer (IEEE 802.3 / zlib reflected polynomial &amp;lt;code&amp;gt;0xEDB88320&amp;lt;/code&amp;gt;, init &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;, xor-out &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;) covers the body on DATA when &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; and on every SACK packet. The PCI is not under the CRC (Cyclic Redundancy Check) because the HCS already protects it. It is appended before AEAD encryption and therefore rides inside the AEAD wrap when both are active; the AEAD tag (~2^-128 forgery probability) dominates the CRC (~2^-32) for integrity in that mode but the CRC trailer is currently retained.&lt;br /&gt;
* When encryption is enabled, the entire (possibly-CRC&#039;d) FRCP packet is wrapped with AEAD inside the shared-memory packet buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt;); the packet grows by the AEAD overhead, namely a leading nonce / Initialization Vector (IV) of &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_ivsz&amp;lt;/code&amp;gt;) and a trailing authentication tag of &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_tagsz&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Both CRC and AEAD are layered around the FRCP wire format and are not visible to the FRCP machinery itself.&lt;br /&gt;
&lt;br /&gt;
=== 1.2. Flag bits ===&lt;br /&gt;
&lt;br /&gt;
Flag bits are numbered most-significant-bit first to match the wire&lt;br /&gt;
diagram (bit numbering per [[#1.1. PCI header|Section 1.1]]; bit 0 is the MSB of the&lt;br /&gt;
16-bit &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; field and lands at wire-position 0 in network byte&lt;br /&gt;
order).  Bits 13..15 are reserved and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Bit !! Mask !! Name !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| 0 || &amp;lt;code&amp;gt;0x8000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt; || Carries caller payload&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &amp;lt;code&amp;gt;0x4000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DRF&amp;lt;/code&amp;gt; || Data Run Flag: start of a fresh run&lt;br /&gt;
|-&lt;br /&gt;
| 2 || &amp;lt;code&amp;gt;0x2000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt; || Acknowledgement: &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field valid&lt;br /&gt;
|-&lt;br /&gt;
| 3 || &amp;lt;code&amp;gt;0x1000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt; || Negative ACK; &amp;lt;code&amp;gt;seqno = arrival_seqno-1&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || &amp;lt;code&amp;gt;0x0800&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt; || Flow Control: &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; field valid (&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || &amp;lt;code&amp;gt;0x0400&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt; || Rendezvous probe (window-closed)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || &amp;lt;code&amp;gt;0x0200&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; || First Fragment (role bit 0; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || &amp;lt;code&amp;gt;0x0100&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; || Last Fragment (role bit 1; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 8 || &amp;lt;code&amp;gt;0x0080&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RXM&amp;lt;/code&amp;gt; || Retransmission&lt;br /&gt;
|-&lt;br /&gt;
| 9 || &amp;lt;code&amp;gt;0x0040&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt; || Selective ACK block list in payload&lt;br /&gt;
|-&lt;br /&gt;
| 10 || &amp;lt;code&amp;gt;0x0020&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt; || RTT Probe / echo (payload follows)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || &amp;lt;code&amp;gt;0x0010&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt; || Keepalive&lt;br /&gt;
|-&lt;br /&gt;
| 12 || &amp;lt;code&amp;gt;0x0008&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt; || End-of-stream marker (stream mode)&lt;br /&gt;
|-&lt;br /&gt;
| 13-15 || -- || -- || Reserved (MUST be zero)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair encodes the fragment role of a DATA-bearing&lt;br /&gt;
Service Data Unit (SDU), SCTP-style begin/end flags (RFC 9260&lt;br /&gt;
sec. 3.3.1):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FFGM !! LFGM !! Role&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || Sole / un-fragmented SDU (begin AND end)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || First fragment of a multi-fragment SDU&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Middle fragment&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Last fragment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Each fragment is carried in its own FRCP packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;;&lt;br /&gt;
FRTX (the FRCT Retransmission service mode, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]])&lt;br /&gt;
recovers individual fragments via the normal Retransmission Timeout&lt;br /&gt;
(RTO) / SACK / Recent Acknowledgement (RACK, RFC 8985) path.  The&lt;br /&gt;
receiver reassembles the SDU at consume time once the contiguous&lt;br /&gt;
[FIRST .. LAST] run has fully arrived.  On non-DATA packets the role&lt;br /&gt;
bits are unused and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
In stream mode (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;, see [[#16. Stream-mode flows|Section 16]]) there are&lt;br /&gt;
no SDU boundaries to encode, so &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST&lt;br /&gt;
be transmitted as zero.  End-of-stream uses a dedicated bit (&amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;,&lt;br /&gt;
bit 12) carried on a 0-byte DATA packet, emitted at write-half close&lt;br /&gt;
(&amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;FLOWFRDONLY&amp;lt;/code&amp;gt;), during linger drain, and at &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;;&lt;br /&gt;
emission is idempotent (first call wins).  After contiguous delivery&lt;br /&gt;
of the FIN-bearing slot, the receiver latches &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt; at the FIN&#039;s&lt;br /&gt;
start offset; &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns 0 (end-of-file, &amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) once buffered&lt;br /&gt;
bytes have been drained up to &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt;.  Per-byte position is&lt;br /&gt;
carried by the [start, end) extension ([[#1.5. Stream PCI extension|Section 1.5]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.3. SACK payload ===&lt;br /&gt;
&lt;br /&gt;
A SACK packet has the &amp;lt;code&amp;gt;FRCT_ACK | FRCT_FC | FRCT_SACK&amp;lt;/code&amp;gt; flag bits set&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI, the&lt;br /&gt;
payload is a 2-octet block count (network byte order), 2 octets of&lt;br /&gt;
padding to 4-byte align the block list, then &amp;lt;code&amp;gt;n_blocks&amp;lt;/code&amp;gt; pairs of&lt;br /&gt;
32-bit start/end seqnos describing &#039;&#039;present&#039;&#039; (received) ranges&lt;br /&gt;
above the cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |           n_blocks            |        padding (2 octets)     |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[0]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            end[0]                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[1]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
                       ... n_blocks pairs total ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;n_blocks &amp;amp;lt;= SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; (2048).  The per-flow effective cap is&lt;br /&gt;
further bounded by &amp;lt;code&amp;gt;(frag_mtu - PCI - 4) / 8&amp;lt;/code&amp;gt; blocks per packet; SACK&lt;br /&gt;
packets carry no stream extension, so PCI here is the 16-octet base&lt;br /&gt;
header even on stream-mode flows.&lt;br /&gt;
&lt;br /&gt;
Wire invariant: every block produced by the receiver, except an&lt;br /&gt;
optional leading Duplicate SACK (D-SACK) block as described below,&lt;br /&gt;
describes a range strictly above the cumulative ACK carried in the&lt;br /&gt;
PCI &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field (&amp;lt;code&amp;gt;after(start[i], ackno)&amp;lt;/code&amp;gt;).  This makes the D-SACK&lt;br /&gt;
convention below unambiguous; the receiver-side builder MUST&lt;br /&gt;
preserve it.&lt;br /&gt;
&lt;br /&gt;
Duplicate SACK (D-SACK, RFC 2883) is signalled in-band: no flag&lt;br /&gt;
bit, no extra framing.  Modular seqno arithmetic uses the&lt;br /&gt;
&amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators defined in the Notation block.&lt;br /&gt;
&lt;br /&gt;
Encoding.  When a duplicate is observed the receiver arms a&lt;br /&gt;
single-slot pending report (&amp;lt;code&amp;gt;dsack_seqno&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;dsack_valid&amp;lt;/code&amp;gt;,&lt;br /&gt;
latest-wins across multiple arms before the next emit).  On the&lt;br /&gt;
next outbound SACK the receiver prepends &amp;lt;code&amp;gt;block[0] = [dsack_seqno,&lt;br /&gt;
dsack_seqno + 1)&amp;lt;/code&amp;gt; - always a one-&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; range - and clears the&lt;br /&gt;
flag.  The three arm sites are listed in [[#10. Cumulative + selective ACK|Section 10]]; case-1 sites&lt;br /&gt;
yield &amp;lt;code&amp;gt;dsack_seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt; (the next &amp;lt;code&amp;gt;pci.ackno&amp;lt;/code&amp;gt;), and the&lt;br /&gt;
case-2 site (&amp;lt;code&amp;gt;rq_accept&amp;lt;/code&amp;gt; conflict) yields &amp;lt;code&amp;gt;dsack_seqno&amp;lt;/code&amp;gt; in&lt;br /&gt;
&amp;lt;code&amp;gt;[rcv_cr.lwe, rcv_cr.rwe)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Detection.  The sender classifies &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; by its relation to&lt;br /&gt;
&amp;lt;code&amp;gt;pci.ackno&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
;case 1 (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;before(blocks[0].start, pci.ackno)&amp;lt;/code&amp;gt; AND &amp;lt;code&amp;gt;pci.ackno - blocks[0].start &amp;amp;lt;= MAX_DSACK_LAG&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;== RQ_SIZE&amp;lt;/code&amp;gt;).  The lag bound rejects stale or spoofed reports beyond one receive window.&lt;br /&gt;
;case 2 (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;blocks[0]&amp;lt;/code&amp;gt; is a sub-range (with at least one endpoint differing) of some &amp;lt;code&amp;gt;blocks[i&amp;amp;gt;0]&amp;lt;/code&amp;gt; - i.e. the same packet&#039;s remaining SACK blocks already describe the duplicated &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; as received.&lt;br /&gt;
&lt;br /&gt;
On detect, the sender:&lt;br /&gt;
&lt;br /&gt;
* bumps &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; by 1, capped at &amp;lt;code&amp;gt;REO_WND_MULT_MAX&amp;lt;/code&amp;gt; (= 20), per RFC 8985 sec. 6.2 step 4;&lt;br /&gt;
* snapshots &amp;lt;code&amp;gt;dsack_lwe_snap = snd_cr.lwe&amp;lt;/code&amp;gt;, resetting the 16-cum-ACK halving counter so the multiplier doesn&#039;t decay while D-SACK evidence is still arriving;&lt;br /&gt;
* excludes &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; from the gap-marking loop (&amp;lt;code&amp;gt;n_real = n - 1&amp;lt;/code&amp;gt;), so a D-SACK alone never enters NewReno-careful recovery (see [[#8. Retransmission|Section 8]]); only non-D-SACK blocks count as gaps.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; halving cadence (once per 16 cumulatively-ACK&#039;d&lt;br /&gt;
seqnos since the most-recent D-SACK arrival or halve event) and&lt;br /&gt;
the reset-to-1 on a HoL RTO fire are both per the same RFC 8985&lt;br /&gt;
clause.  The clamp-and-skip path in the regular SACK-mark loop is&lt;br /&gt;
incidentally idempotent on any leftover case-1 or case-2 block&lt;br /&gt;
(&amp;lt;code&amp;gt;start &amp;amp;lt; snd_cr.lwe&amp;lt;/code&amp;gt; clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; and the inner loop&lt;br /&gt;
skips &amp;lt;code&amp;gt;k == snd_cr.lwe&amp;lt;/code&amp;gt;; case-2 re-NULLs slots already marked&lt;br /&gt;
received by later blocks), so block[0] is harmless even when fed&lt;br /&gt;
to the loop.&lt;br /&gt;
&lt;br /&gt;
=== 1.4. RTTP payload ===&lt;br /&gt;
&lt;br /&gt;
An RTTP (Round-Trip Time Probe) packet has only the &amp;lt;code&amp;gt;FRCT_RTTP&amp;lt;/code&amp;gt; flag&lt;br /&gt;
set (bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI,&lt;br /&gt;
the payload is 24 octets (packed):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          probe_id                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          echo_id                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +                  nonce (16 octets, echoed verbatim)           +&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:sender counter, 0 on reply, 0 reserved.&lt;br /&gt;
;&amp;lt;code&amp;gt;echo_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:peer&#039;s &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;, 0 on outbound probe.&lt;br /&gt;
;&amp;lt;code&amp;gt;nonce&amp;lt;/code&amp;gt;&lt;br /&gt;
:random, echoed unmodified, memcmp&#039;d to defeat spoof.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.5. Stream PCI extension ===&lt;br /&gt;
&lt;br /&gt;
A stream-mode flow (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;) carries an extra&lt;br /&gt;
8-octet extension after the 16-octet base PCI on every DATA packet&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            start                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                             end                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset of the first payload byte in the stream.&lt;br /&gt;
;&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset one past the last payload byte; &amp;lt;code&amp;gt;end - start&amp;lt;/code&amp;gt; equals the on-wire payload length.&lt;br /&gt;
&lt;br /&gt;
Total stream-mode PCI for DATA packets is 24 octets (16 base + 8&lt;br /&gt;
extension); control packets (SACK, RTTP, bare ACK, KA, etc.) retain&lt;br /&gt;
the 16-octet base PCI.  Stream mode MUST be negotiated at flow&lt;br /&gt;
allocation; the extension is present iff stream mode is in use,&lt;br /&gt;
never on a per-packet basis.  Both peers MUST treat &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; as&lt;br /&gt;
monotonic 32-bit byte offsets; when a slot reaches the head of the&lt;br /&gt;
contiguous run with &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; not equal to the prior packet&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; the&lt;br /&gt;
slot is silently dropped at delivery time ([[#16. Stream-mode flows|Section 16]]) rather&lt;br /&gt;
than rejected at stash.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC STREAM-frame reassembly model (RFC 9000 sec. 19.8):&lt;br /&gt;
each packet carries its packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (this PCI&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; field) and a&lt;br /&gt;
separate stream byte position (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;).  Separating the two&lt;br /&gt;
avoids TCP&#039;s conflation of packet identity with byte position which&lt;br /&gt;
forces Karn&#039;s algorithm for Round-Trip Time (RTT) sampling (no RTT&lt;br /&gt;
sample on retransmits, RFC 6298 sec. 3); FRCP applies the&lt;br /&gt;
Karn-equivalent gate via a combination of per-packet &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;,&lt;br /&gt;
per-slot &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; flags, and a sample-fence &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; (see [[#2.1. Per-flow state|Section 2.1]]&lt;br /&gt;
and [[#12. RTT estimation|Section 12]]).  FRCP&#039;s fixed-32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; wrap at 4 GiB of&lt;br /&gt;
wire bytes, narrower than QUIC&#039;s 62-bit varint offset (cf. RFC 9000&lt;br /&gt;
sec. 16); the on-wire wrap is handled by the same modular &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;&lt;br /&gt;
/ &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators ([[#1.3. SACK payload|Section 1.3]]) FRCP uses for seqnos, which&lt;br /&gt;
remain unambiguous as long as the in-flight byte window stays&lt;br /&gt;
strictly under 2 GiB (the half-range of the signed-int32 difference&lt;br /&gt;
in &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;).  The default per-flow ring is 1 MiB; the&lt;br /&gt;
implementation caps &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; at 128 MiB (&amp;lt;code&amp;gt;FRCT_STREAM_RING_SZ_MAX&amp;lt;/code&amp;gt;),&lt;br /&gt;
well below the 2 GiB half-range bound.  The runtime byte counters&lt;br /&gt;
exposed via FUSE (Filesystem in Userspace) in the Ouroboros&lt;br /&gt;
Resource Information Base (RIB, a virtual-filesystem introspection&lt;br /&gt;
bridge) are platform &amp;lt;code&amp;gt;size_t&amp;lt;/code&amp;gt; and do not wrap on 64-bit hosts.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2. Per-flow state and service modes ==&lt;br /&gt;
&lt;br /&gt;
=== 2.1. Per-flow state ===&lt;br /&gt;
&lt;br /&gt;
Each flow keeps a sender control record and a receiver control&lt;br /&gt;
record:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: oldest unacked &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (cumulative ACK boundary as seen by sender); rcv: next in-order &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; expected&lt;br /&gt;
;&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: peer-advertised right window edge; rcv: locally-advertised right window edge&lt;br /&gt;
;&amp;lt;code&amp;gt;cflags&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-direction feature flags: retransmission (&amp;lt;code&amp;gt;FRCTFRTX&amp;lt;/code&amp;gt;), receiver flow control (&amp;lt;code&amp;gt;FRCTFRESCNTL&amp;lt;/code&amp;gt;), linger-on-close (&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt;); see &amp;lt;code&amp;gt;&amp;amp;lt;ouroboros/fccntl.h&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: next &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; to send; rcv: force-ACK trigger - set on a stale or dup DATA so the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; emits a fresh cumulative ACK&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; counter for standalone ACK-bearing control packets (delayed ACK, SACK, final ACK on dealloc); not bumped on piggybacked ACK riding a DATA packet (which uses the DATA &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;).  Used by wire-dup ACK detection; rcv: incoming-ACK dedup tracker&lt;br /&gt;
;&amp;lt;code&amp;gt;act&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:last activity (used by inactivity / DRF)&lt;br /&gt;
;&amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:inactivity threshold; sender = &amp;lt;code&amp;gt;3*mpl + a + r + 1s&amp;lt;/code&amp;gt;, receiver = &amp;lt;code&amp;gt;2*mpl + a + r + 1s&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;mpl&amp;lt;/code&amp;gt; is the Maximum Packet Lifetime (delta-t terminology; see [[#15. Heritage and adopted techniques|Section 15]]); &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; are the FRCT a-timer and r-timer bounds (see [[#8. Retransmission|Section 8]]).  The asymmetry is load-bearing for pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
The sender holds a per-slot ring &amp;lt;code&amp;gt;snd_slots[RQ_SIZE]&amp;lt;/code&amp;gt; keyed by&lt;br /&gt;
&amp;lt;code&amp;gt;(seqno mod RQ_SIZE)&amp;lt;/code&amp;gt;.  Each slot tracks its retransmit entry (&amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;),&lt;br /&gt;
last-send timestamp, and retransmit flag bits: &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (a&lt;br /&gt;
retransmit is pending or has fired, gates the next RTT sample&lt;br /&gt;
under Karn) and &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (one-shot fast-retransmit staged for&lt;br /&gt;
this loss event).&lt;br /&gt;
&lt;br /&gt;
The receiver holds a parallel reorder ring &amp;lt;code&amp;gt;rcv_slots[RQ_SIZE]&amp;lt;/code&amp;gt;&lt;br /&gt;
(referred to as &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; in prose) holding stashed out-of-order&lt;br /&gt;
packet-buffer indexes; both FRTX and best-effort flows share this&lt;br /&gt;
path.  The invariant &amp;lt;code&amp;gt;rwe - lwe &amp;amp;lt;= RQ_SIZE&amp;lt;/code&amp;gt; holds: on each consume&lt;br /&gt;
the receiver advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; by the consumed count, capping the&lt;br /&gt;
receive window at &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; slots.&lt;br /&gt;
&lt;br /&gt;
A separate fence variable &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; is bumped on every retransmit&lt;br /&gt;
(timer-fire, SACK-driven, fast-rxm, NACK-driven) and on every&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; ([[#4. Sequence-number rotation (DRF)|Section 4]]) to mark the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; range whose RTT samples&lt;br /&gt;
MUST be discarded.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 2.2. Service modes (orthogonal axes) ===&lt;br /&gt;
&lt;br /&gt;
FRCP exposes its wire features as a vector of independent QoS&lt;br /&gt;
axes selected at flow allocation time.  All flows go through the&lt;br /&gt;
same &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive; the &amp;lt;code&amp;gt;qosspec_t&amp;lt;/code&amp;gt; passed&lt;br /&gt;
in determines which protocol machinery engages on the wire.  This&lt;br /&gt;
contrasts with the POSIX BSD socket model where TCP and UDP&lt;br /&gt;
require different socket types (&amp;lt;code&amp;gt;SOCK_STREAM&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;SOCK_DGRAM&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
The axes:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = unordered (no FRCP engagement: raw datagrams, no PCI on the wire, UDP-equivalent at this layer); 1 = message-ordered (FRCP engaged; SDU boundaries preserved across fragmentation); 2 = stream (byte-oriented, no SDU boundaries; FRTX required)&lt;br /&gt;
;&amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = lossless service requested: FRTX retransmit machinery engages ([[#8. Retransmission|Section 8]]); MUST be 0 for &amp;lt;code&amp;gt;service=2&amp;lt;/code&amp;gt;.  Non-zero = best-effort, FRTX off.&lt;br /&gt;
;&amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bit Error Rate tolerance.  0 = error-free service requested: a CRC trailer is appended after the body of DATA packets and verified on receive (added / checked outside the FRCP PCI; see [[#1.1. PCI header|Section 1.1]]).  Non-zero = peer accepts errors; trailer omitted.  SACK control packets carry a CRC32 trailer regardless of &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; gate applies to DATA only.&lt;br /&gt;
;&amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt;&lt;br /&gt;
:Peer-timeout (ms); 0 disables the keepalive timer.  Independent of FRCP engagement.&lt;br /&gt;
&lt;br /&gt;
Encryption is a separate per-flow attribute set at flow setup;&lt;br /&gt;
when enabled it wraps the FRCP packet (PCI + body, plus the CRC&lt;br /&gt;
trailer if any) under AEAD, expanding the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; by &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt;&lt;br /&gt;
octets (nonce / tag).  The CRC trailer is currently kept inside&lt;br /&gt;
the AEAD wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
&lt;br /&gt;
Reachable combinations exported by &amp;lt;code&amp;gt;include/ouroboros/qos.h&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Cube !! &amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; !! Engaged&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw&amp;lt;/code&amp;gt; || 0 || 1 || 1 || Raw passthrough&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw_safe&amp;lt;/code&amp;gt; || 0 || 1 || 0 || Raw + CRC trailer&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; || 1 || 1 || 1 || FRCP, no FRTX, no CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt_safe&amp;lt;/code&amp;gt; || 1 || 1 || 0 || FRCP, no FRTX, CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_msg&amp;lt;/code&amp;gt; || 1 || 0 || 0 || FRCP + FRTX&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_stream&amp;lt;/code&amp;gt; || 2 || 0 || 0 || FRCP + FRTX, stream&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Forced couplings actually enforced by the public API:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;service == SVC_STREAM&amp;lt;/code&amp;gt; (2) requires &amp;lt;code&amp;gt;loss == 0&amp;lt;/code&amp;gt;; &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;flow_accept&amp;lt;/code&amp;gt; reject the pair otherwise with &amp;lt;code&amp;gt;-EINVAL&amp;lt;/code&amp;gt;.&lt;br /&gt;
* FRTX requires FRCP engagement (&amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;); requesting &amp;lt;code&amp;gt;loss = 0&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;service = SVC_RAW&amp;lt;/code&amp;gt; is structurally a no-op because no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; is created.&lt;br /&gt;
* The &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; build flag globally forces &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;.  Note: this flag defaults to ON, so default builds ship with CRC disabled until &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; is set to OFF.&lt;br /&gt;
&lt;br /&gt;
Caveat: the API does NOT force &amp;lt;code&amp;gt;ber = 0&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; has &amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;, which means the PCI&lt;br /&gt;
itself is not CRC-protected on that cube; the HCS ([[#1.1. PCI header|Section 1.1]])&lt;br /&gt;
remains the only integrity check on the header.&lt;br /&gt;
&lt;br /&gt;
The FRCP-no-FRTX regime (&amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;loss &amp;amp;gt; 0&amp;lt;/code&amp;gt;) is meaningful&lt;br /&gt;
and live: sequence numbering, in-order delivery, flow-control&lt;br /&gt;
advertisement, KA, DRF rotation, and SDU fragmentation /&lt;br /&gt;
reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]) all run.  Lost packets are dropped&lt;br /&gt;
rather than retransmitted; a permanently-lost mid-fragment is&lt;br /&gt;
dropped via skip-past-gap once a later SDU is visible in the&lt;br /&gt;
reorder ring.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. Protocol parameters ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Slot ring / rcv window width&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;START_WINDOW&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Initial &amp;lt;code&amp;gt;rwe-lwe&amp;lt;/code&amp;gt; after rotate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MAX(250 us build-tunable, 1&amp;amp;lt;&amp;amp;lt;RXMQ_RES)&amp;lt;/code&amp;gt;; per-flow via &amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;FRCTSRTOMIN&amp;lt;/code&amp;gt;).  Default ~1 ms with &amp;lt;code&amp;gt;RXMQ_RES=20&amp;lt;/code&amp;gt;. || RTO floor; also floored at the retransmit-wheel resolution (~1 ms by default).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt; || 20 || Backoff shift cap&lt;br /&gt;
|-&lt;br /&gt;
| RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MIN(reo_wnd_mult * min_RTT/4, SRTT)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; scales on D-SACK, cap 20 || Reorder window; per RFC 8985 sec. 6.2; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per sec. 6.2 step 4&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; || 300 s (5 min, Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; windowed re-anchor&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_WND_MULT_MAX&amp;lt;/code&amp;gt; || 20 (RFC 8985 sec. 6.2 step 4) || &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_DECAY_PKTS&amp;lt;/code&amp;gt; || 16 (RFC 8985 sec. 6.2 step 4 / &amp;lt;code&amp;gt;RACK.reo_wnd_persist&amp;lt;/code&amp;gt;) || Fresh-ACK&#039;d seq count per halving&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_DSACK_LAG&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || D-SACK sanity cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTT_QUARANTINE&amp;lt;/code&amp;gt; || 32 (&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; steps) || NewReno gate pad&lt;br /&gt;
|-&lt;br /&gt;
| SACK rate-limit || &amp;lt;code&amp;gt;SACK_MIN_GAP_NS&amp;lt;/code&amp;gt; (250 us, fixed) || Min SACK gap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; || 2048 (wire cap; per-flow capped at &amp;lt;code&amp;gt;(frag_mtu-PCI-4)/8&amp;lt;/code&amp;gt;) || Per-SACK block cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; || 32 || Per-pass staged retransmit cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; || 3 (RFC 8985 default) || Hybrid fast-rxm trigger ([[#8. Retransmission|Section 8]])&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MDEV_MUL&amp;lt;/code&amp;gt; || 2 (build-tunable via &amp;lt;code&amp;gt;FRCT_RTO_MDEV_MULTIPLIER&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; shift in &amp;lt;code&amp;gt;RTO = srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| RTTP nonce || 16 octets || Echoed verbatim&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; || 8 || In-flight probes&lt;br /&gt;
|-&lt;br /&gt;
| RTT clamp || &amp;lt;code&amp;gt;16 * srtt&amp;lt;/code&amp;gt; || Probe-sample upper bound (ACK-derived RTT samples gated by Karn / recovery only)&lt;br /&gt;
|-&lt;br /&gt;
| Cold-probe cadence || 100 ms (rx-driven; see [[#12. RTT estimation|Section 12]]) || Pre-&amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; RTTP rate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt; || 100 ms || RDVS emit cadence&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; || 1 s || RDVS give-up&lt;br /&gt;
|-&lt;br /&gt;
| Delayed-ACK fire || &amp;lt;code&amp;gt;2 * TICTIME&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt; = FRCT tick granularity, default 5 ms; &amp;lt;code&amp;gt;2*TICTIME = 10 ms&amp;lt;/code&amp;gt; by default) || Fired after the first in-order DATA arrival; tick is build-tunable&lt;br /&gt;
|-&lt;br /&gt;
| NACK send cooldown || &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; when an &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; sample exists, else 100 ms || Pre-DRF NACK rate-limit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_SDU&amp;lt;/code&amp;gt; || 1 MiB || Max reassembled SDU; configurable per flow&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The per-flow fragment Maximum Transmission Unit (MTU) is computed&lt;br /&gt;
at flow setup from the lower IPCP&#039;s mtu minus encryption&lt;br /&gt;
&amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; and CRC trailer; there is no FRCT-level default or&lt;br /&gt;
environment-variable override.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 4. Sequence-number rotation (DRF) ==&lt;br /&gt;
&lt;br /&gt;
The DRF (Data Run Flag) bit on an outbound packet means &amp;quot;this is&lt;br /&gt;
the start of a fresh data run&amp;quot; and is set whenever the sender has&lt;br /&gt;
nothing in flight (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Independently of that, if the sender has been idle longer than&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.inact&amp;lt;/code&amp;gt; AND the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;),&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; rolls a random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; before the send and&lt;br /&gt;
resets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    snd_cr.seqno  = random()&lt;br /&gt;
    snd_cr.lwe    = snd_cr.seqno&lt;br /&gt;
    snd_cr.rwe    = snd_cr.seqno + START_WINDOW&lt;br /&gt;
    rtt_lwe       = snd_cr.seqno&lt;br /&gt;
    in_recovery   = false   (recovery state, see Section 8)&lt;br /&gt;
    recovery_high = snd_cr.seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The receiver, on observing rcv-side inactivity&lt;br /&gt;
(&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), requires a DRF on the next&lt;br /&gt;
DATA packet; otherwise it replies with a rate-limited NACK (see&lt;br /&gt;
below).  Non-DATA control packets pass through without the DRF&lt;br /&gt;
requirement.  On DRF the receiver releases the &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots and&lt;br /&gt;
rebases&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    rcv_cr.lwe   = seqno&lt;br /&gt;
    rcv_cr.rwe   = seqno + RQ_SIZE&lt;br /&gt;
    rcv_cr.seqno = seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the inactive packet has DATA but no DRF, a rate-limited NACK is&lt;br /&gt;
fired back to the sender (cooldown per [[#3. Protocol parameters|Section 3]]); non-DATA stale&lt;br /&gt;
arrivals fall through to normal processing (no NACK, no drop).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 5. Send path ==&lt;br /&gt;
&lt;br /&gt;
# If the SDU exceeds &amp;lt;code&amp;gt;(frag_mtu - data_hdr_len)&amp;lt;/code&amp;gt;, the caller (&amp;lt;code&amp;gt;dev.c&amp;lt;/code&amp;gt;) fans it out into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - data_hdr_len))&amp;lt;/code&amp;gt; fragments, each emitted via &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; as its own DATA packet with a per-fragment role ([[#7.2. Fragmentation and reassembly|Section 7.2]]); both FRTX and best-effort flows fragment.  Raw flows (no FRCP engagement, &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) carry no PCI and return &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; for any SDU larger than one packet at the layer below.  An SDU that fits in a single packet is sent as SOLE.  &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; reserves PCI head room; sets DATA, plus DRF when the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
# &amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; if past sender inactivity and the pipe is empty ([[#4. Sequence-number rotation (DRF)|Section 4]]).&lt;br /&gt;
# Advertise FC (&amp;lt;code&amp;gt;pci.window = frcti_advert_rwe(frcti)&amp;lt;/code&amp;gt;, i.e. &amp;lt;code&amp;gt;rcv_cr.rwe&amp;lt;/code&amp;gt; clamped to &amp;lt;code&amp;gt;rcv_cr.lwe + ring_seq_cap&amp;lt;/code&amp;gt; in stream mode) when the receiver side is recent: &amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt; rcv_cr.inact&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Reliable mode (FRTX): leave &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; where it is; reset the slot at &amp;lt;code&amp;gt;RQ_SLOT(seqno)&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;snd_slots[p].time = now&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_slots[p].flags = 0&amp;lt;/code&amp;gt;); queue an &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; (saves a packet copy, arms a wheel timer at &amp;lt;code&amp;gt;now + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;).  Piggyback ACK (&amp;lt;code&amp;gt;pci.ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) while the a-timer for the most recent received DATA packet has not yet expired (&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt;= t_a&amp;lt;/code&amp;gt;); on piggyback, set &amp;lt;code&amp;gt;rcv_cr.seqno = rcv_cr.lwe&amp;lt;/code&amp;gt; so the next delayed-ACK fire is suppressed.  See [[#8. Retransmission|Section 8]] for &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; semantics.&lt;br /&gt;
# Best-effort mode (no FRTX): advance &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; immediately (&amp;lt;code&amp;gt;snd_cr.lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_cr.rwe = snd_cr.lwe + RQ_SIZE&amp;lt;/code&amp;gt;); no retransmit state.  No send-side RTT probe is armed in this mode (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt; requires an in-flight &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, which best-effort never has); the rx-driven cold seeder in &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; is the only probe path.&lt;br /&gt;
# In reliable mode, optionally arm an RTT probe ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 6. Receive path ==&lt;br /&gt;
&lt;br /&gt;
=== 6.1. Early-exit dispatch ===&lt;br /&gt;
&lt;br /&gt;
Keepalive (KA), RTT probe (RTTP), pre-DRF NACK, and rendezvous&lt;br /&gt;
(RDVS) packets short-circuit out of &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; before the locked&lt;br /&gt;
main path; each handler takes its own lock internally.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
      incoming packet&lt;br /&gt;
            |&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | KA?     |---yes--&amp;gt; ka_rcv  ; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RTTP?   |---yes--&amp;gt; rttp_rcv; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | NACK?   |---yes--&amp;gt; nack_rcv; return  (see Section 9)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RDVS?   |---yes--&amp;gt; rdv_rcv ; return  (reply bare FC, ackno=0)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       acquire wrlock; enter locked main path&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt;&lt;br /&gt;
:refresh &amp;lt;code&amp;gt;t_ka_rcv&amp;lt;/code&amp;gt;, honour piggybacked ACK.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt;&lt;br /&gt;
:probe (echo back nonce) or echo (verify nonce, sample RTT).&lt;br /&gt;
;&amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:pre-DRF, sender-side handler.  See [[#9. Pre-DRF NACK|Section 9]].&lt;br /&gt;
;&amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt;&lt;br /&gt;
:reply with a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;); &amp;lt;code&amp;gt;rdlock&amp;lt;/code&amp;gt; only.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 6.2. Locked main path ===&lt;br /&gt;
&lt;br /&gt;
Steps below run with the per-flow &amp;lt;code&amp;gt;frcti.lock&amp;lt;/code&amp;gt; held for writing&lt;br /&gt;
(&amp;lt;code&amp;gt;pthread_rwlock_wrlock&amp;lt;/code&amp;gt;) unless noted.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;&lt;br /&gt;
:Only meaningful when the receive side is stale.  On DRF (Data Run Flag): release &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots, rebase &amp;lt;code&amp;gt;rcv_cr&amp;lt;/code&amp;gt;, continue.  On stale DATA without DRF: fire a pre-DRF NACK if cooldown allows ([[#9. Pre-DRF NACK|Section 9]]), then discard the packet; on cooldown, drop without sending a NACK (a pending cumulative ACK from &amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; may still go out).  Non-DATA, non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; pure-DRF stale arrivals fall through after the DRF rebase branch.&lt;br /&gt;
&lt;br /&gt;
;DATA-only act refresh&lt;br /&gt;
:Refresh &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; only when &amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt; is set, so that non-DATA packets never block the next DRF rebase.&lt;br /&gt;
&lt;br /&gt;
;Wire-dup gate&lt;br /&gt;
:Before flag-driven dispatch, drop wire-duplicate ACKs and wire-duplicate DATA (&amp;lt;code&amp;gt;is_dup_ack&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt;).  The DATA check is bypassed for &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;-bearing arrivals so the piggybacked ACK / SACK / FC carried on a retransmitted DATA at an already-ACK&#039;d &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is still applied; the stale-in-window branch below then drops the packet.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Drop ACKs whose &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; falls outside &amp;lt;code&amp;gt;(snd_cr.lwe, snd_cr.seqno]&amp;lt;/code&amp;gt;.  If &amp;lt;code&amp;gt;ackno == snd_cr.lwe&amp;lt;/code&amp;gt; (non-advancing cumulative ACK), drive RACK fast-retransmit consideration ([[#8. Retransmission|Section 8]]).  Otherwise advance &amp;lt;code&amp;gt;snd_cr.lwe = ackno&amp;lt;/code&amp;gt;, collapse &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; to 0 (Karn-gated by &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; on the just-acknowledged slot, the old head-of-line), reset &amp;lt;code&amp;gt;dup_thresh&amp;lt;/code&amp;gt; to 0, update &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the send-time of the slot at &amp;lt;code&amp;gt;ackno-1&amp;lt;/code&amp;gt; (consumed by RACK and SACK below), decay &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per RFC 8985 sec. 6.2 step 4, exit NewReno-careful recovery (see [[#8. Retransmission|Section 8]]) on &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt;, and feed an RTT sample if eligible ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Walk the block list.  For each block (a present range above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;) NULL out &amp;lt;code&amp;gt;snd_slots[k].rxm&amp;lt;/code&amp;gt;, clear the slot&#039;s per-send flags, and advance &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the latest send-time covered (the Forward Acknowledgement / fack equivalent, Mathis &amp;amp;amp; Mahdavi 1996); the first block whose start clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; skips this fack update so that a head-of-line clamp does not falsely advance fack.  For un-SACKed gaps below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt;, stage a retransmit per slot that is (1) still owned (&amp;lt;code&amp;gt;rxm != NULL&amp;lt;/code&amp;gt;), (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (the RFC 8985 sec. 6.2 hybrid trigger).  Mark the slot &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; and NULL the &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; at stage time.  Capped at &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; staged retransmits per receive pass; what&#039;s left rides the next SACK.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bump &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; (clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt;, never shrinks) and mark window open.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bounds-check &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; against window.  On stale-dup (&amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;), set &amp;lt;code&amp;gt;rcv_cr.seqno = seqno&amp;lt;/code&amp;gt; to force a fresh ACK on the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt;, then drop.  On accept: both FRTX and best-effort stash the packet-buffer index into &amp;lt;code&amp;gt;rq[seqno mod RQ_SIZE]&amp;lt;/code&amp;gt;.  Fragments stash unchanged - the role bits are inspected only at consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]).  On out-of-order arrival, build a SACK reply if not rate-limited (per [[#3. Protocol parameters|Section 3]]) and not deduplicated against the previous &amp;lt;code&amp;gt;(rcv_cr.lwe, n_blocks)&amp;lt;/code&amp;gt; pair; D-SACK reports always bypass the dedup.  If both rate-limit and dedup suppress the reply, neither SACK nor delayed-ACK fires (the sender picks up the gap on its next ACK).  On in-order arrival, arm the delayed-ACK timer.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; exit&lt;br /&gt;
:Releases the per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;), then calls &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; synchronously after the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; release to surface any pending cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 7. Read path and reassembly ==&lt;br /&gt;
&lt;br /&gt;
=== 7.1. Read path ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns a full reassembled SDU (Service Data Unit) via&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; on every FRCP SDU-mode flow (FRTX or best-effort);&lt;br /&gt;
stream-mode is covered in [[#16. Stream-mode flows|Section 16]].  An incomplete head-of-line&lt;br /&gt;
(HoL) run yields &amp;lt;code&amp;gt;-EAGAIN&amp;lt;/code&amp;gt;; an oversized run yields &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; (the&lt;br /&gt;
run is dropped so the flow does not stall).  On best-effort flows,&lt;br /&gt;
a permanently-lost mid-fragment is dropped as soon as a later&lt;br /&gt;
complete SDU becomes visible in the ring ([[#7.2. Fragmentation and reassembly|Section 7.2]] skip-past-&lt;br /&gt;
gap).&lt;br /&gt;
&lt;br /&gt;
Raw flows carry no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt;, so &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns the next pending&lt;br /&gt;
packet-buffer index directly, with no role-bit inspection.  (Raw&lt;br /&gt;
service is selected via &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt; at flow allocation,&lt;br /&gt;
which suppresses &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; creation.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_pdu_ready&amp;lt;/code&amp;gt; is the no-advance peek used by &amp;lt;code&amp;gt;fevent&amp;lt;/code&amp;gt; (the&lt;br /&gt;
Ouroboros flow-event multiplexer, the &amp;lt;code&amp;gt;poll(2)&amp;lt;/code&amp;gt;-equivalent on&lt;br /&gt;
flows).  It returns ready only when the head-of-line run is&lt;br /&gt;
complete and the lead packet (a Protocol Data Unit, here one FRCP&lt;br /&gt;
packet) is present at &amp;lt;code&amp;gt;rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt;; any other state&lt;br /&gt;
(including the best-effort skip-past-gap case) returns not ready,&lt;br /&gt;
and &amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; is left to drop the broken prefix and re-&lt;br /&gt;
inspect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 7.2. Fragmentation and reassembly ===&lt;br /&gt;
&lt;br /&gt;
Send side (&amp;lt;code&amp;gt;flow_write_frag&amp;lt;/code&amp;gt;).  An SDU larger than&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - PCI)&amp;lt;/code&amp;gt; is split into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - PCI))&amp;lt;/code&amp;gt;&lt;br /&gt;
fragments; each fragment is its own FRCP packet with its own&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a per-fragment role flag pair ([[#1.2. Flag bits|Section 1.2]]).  Roles are&lt;br /&gt;
assigned at emit time:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! i !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=0&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=n-1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| else || &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A mid-loop allocation or transmit failure may yield a partial&lt;br /&gt;
write: the call returns the bytes already enqueued (&amp;lt;code&amp;gt;off &amp;amp;gt; 0&amp;lt;/code&amp;gt;) or&lt;br /&gt;
the underlying error (&amp;lt;code&amp;gt;off == 0&amp;lt;/code&amp;gt;).  Best-effort flows fragment&lt;br /&gt;
identically; on the receiver, a partial run with a permanently-&lt;br /&gt;
lost fragment is dropped when a later complete SDU is visible in&lt;br /&gt;
the ring (see skip-past-gap below).  Raw flows carry no PCI and&lt;br /&gt;
refuse anything larger than the layer&#039;s user MTU (&amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Wire-level recovery is fragment-agnostic on FRTX flows: each&lt;br /&gt;
fragment&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; flows through SACK / RACK / RTO / NACK exactly&lt;br /&gt;
as for a SOLE DATA packet, and reassembly does not re-enter the&lt;br /&gt;
loss-detection path.  Best-effort flows run the same &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
machinery (DRF, FC, ACK piggyback, pre-DRF NACK emit) but queue&lt;br /&gt;
no &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; state at the sender, so a lost MID is unrecoverable;&lt;br /&gt;
skip-past-gap handles it (below).&lt;br /&gt;
&lt;br /&gt;
Receive side.  Fragments stash into &amp;lt;code&amp;gt;rq[seqno]&amp;lt;/code&amp;gt; unchanged; role bits&lt;br /&gt;
are read only at consume time.  &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;, called from&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt;, walks the ring starting at the oldest still-&lt;br /&gt;
undelivered &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;base = rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt; (equal to &amp;lt;code&amp;gt;rcv_cr.lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
only when no partial run is in progress; during a partial run &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
has already advanced past &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;).  It produces one of three&lt;br /&gt;
outcomes:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Outcome !! Cause&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELIVER (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]=SOLE&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;), or &amp;lt;code&amp;gt;rq[base]=FIRST&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; follows in slots &amp;lt;code&amp;gt;[base+1..base+n-1]&amp;lt;/code&amp;gt; with all intermediate roles in &amp;lt;code&amp;gt;{MID,FIRST,LAST}&amp;lt;/code&amp;gt; contiguous.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DROP (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; without a preceding &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;); a &amp;lt;code&amp;gt;FIRST..[non-LAST]..new-FIRST&amp;lt;/code&amp;gt; or new-&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; mid-run (drop the broken prefix with &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; = run length minus 1, so the new &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; stays); or, on best-effort flows, a gap at &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; later in the ring (drop up to the new run start).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; absent or &amp;lt;code&amp;gt;FIRST..[non-LAST]&amp;lt;/code&amp;gt; with no later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; in the ring (FRTX waits for retx; best-effort waits for arrival).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DELIVER&amp;lt;/code&amp;gt; triggers &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;: a scatter-gather &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; of the &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;&lt;br /&gt;
consecutive fragments at &amp;lt;code&amp;gt;rq[base..base+n-1]&amp;lt;/code&amp;gt; directly into the&lt;br /&gt;
caller&#039;s buffer; each per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;) is&lt;br /&gt;
released and &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; advances by &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; was already advanced&lt;br /&gt;
incrementally as each contiguous fragment arrived; &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;&lt;br /&gt;
only restores the fixed-width invariant &amp;lt;code&amp;gt;rwe == lwe + RQ_SIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
No intermediate reassembly buffer is allocated.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; past the broken prefix (releasing the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;s)&lt;br /&gt;
and pulls &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; up to the new trailing edge if needed; the next&lt;br /&gt;
consume retries from the new &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;.  Oversize or arithmetically&lt;br /&gt;
overflowing delivery (sum of fragment lengths &amp;amp;gt; &amp;lt;code&amp;gt;max_rcv_sdu&amp;lt;/code&amp;gt;, sum&lt;br /&gt;
&amp;amp;gt; caller&#039;s buffer, or running-sum overflow) also drops the run&lt;br /&gt;
with &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skip-past-gap (best-effort only).  On FRTX, a gap in the run means&lt;br /&gt;
&amp;quot;waiting for retransmit&amp;quot; and &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;.&lt;br /&gt;
On best-effort flows the gap is permanent, so &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;&lt;br /&gt;
scans forward in the ring for the next &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;; if one is&lt;br /&gt;
visible within &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;, it returns &amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; for the broken prefix and&lt;br /&gt;
the consume loop retries at the new &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;.  Memory hold is bounded&lt;br /&gt;
by &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;; the partial releases on the next consume call once a&lt;br /&gt;
later complete run exists.  Voice-like flows (one &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; per SDU)&lt;br /&gt;
see no extra wait: any later &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; makes the prior gap droppable&lt;br /&gt;
immediately.&lt;br /&gt;
&lt;br /&gt;
The choice to defer reassembly to consume time keeps the receive&lt;br /&gt;
path zero-copy: fragments stay in the shared-memory ring until&lt;br /&gt;
the application pulls, and the SDU lands directly in the caller&#039;s&lt;br /&gt;
buffer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 8. Retransmission ==&lt;br /&gt;
&lt;br /&gt;
FRCP is bounded by two delta-t-derived timers (Watson 1981, see&lt;br /&gt;
[[#15. Heritage and adopted techniques|Section 15]]):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer): upper bound on ACK delay.  An ACK for a received DATA packet MUST be emitted within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; of receipt; an attempt to send an ACK after the a-timer has expired is suppressed (the sender&#039;s RTO is already in motion).&lt;br /&gt;
* &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer): upper bound on retransmission.  A given DATA packet MUST NOT be retransmitted after &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; has elapsed since its first send (&amp;lt;code&amp;gt;t0&amp;lt;/code&amp;gt;); when the bound is hit, the flow is declared down (raising the Ouroboros asynchronous flow condition &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt;, which marks the flow dead to both endpoints) rather than retransmitted again.&lt;br /&gt;
&lt;br /&gt;
Each in-flight FRTX &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; owns one &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt;, armed in a hashed&lt;br /&gt;
timing wheel; the wheel deadline is the slot&#039;s next eligible&lt;br /&gt;
retransmit time.&lt;br /&gt;
&lt;br /&gt;
;RTO timer&lt;br /&gt;
:On fire (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;), re-emit with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, mark &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (Karn-suppress next ACK&#039;s RTT sample), and (for the head-of-line (HoL) slot only) bump &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; up to &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt;.  Wheel deadline is &amp;lt;code&amp;gt;t_send + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;.  Re-armed unless consumed.  The RTO timer also clears &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (re-arming fast-retransmit eligibility), resets &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; to 1 on a HoL fire (RFC 8985 sec. 6.2 step 4 reset clause), and marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; if its &amp;lt;code&amp;gt;frct_tx&amp;lt;/code&amp;gt; call fails.&lt;br /&gt;
&lt;br /&gt;
;r-timer guard&lt;br /&gt;
:Before any retransmit attempt, check &amp;lt;code&amp;gt;(now - t0)&amp;lt;/code&amp;gt; against &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;.  If exceeded, the slot is no longer eligible for retransmit.  Only the RTO timer (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;) treats r-timer expiry as terminal: it marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; (peer unreachable).  Fast-retransmit, SACK-driven retransmit, and NACK-driven head-of-line re-emit silently skip aged-out slots and defer the flow-down decision to the next RTO fire.&lt;br /&gt;
&lt;br /&gt;
;Fast retransmit (hybrid trigger, RFC 8985 sec. 6.2)&lt;br /&gt;
:On a non-advancing cumulative ACK with the scoreboard advanced, fire one fast retransmit when EITHER (a) the head-of-line slot&#039;s latest send is older than the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; ([[#3. Protocol parameters|Section 3]]) and not yet aged out, OR (b) the SACK &amp;lt;code&amp;gt;dup-thresh&amp;lt;/code&amp;gt; count above &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; reaches &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; (= 3, RFC 8985 sec. 6.2 step 4).  Fires at most once per non-advancing cumulative-ACK value, gated by &amp;lt;code&amp;gt;rack_fired_lwe&amp;lt;/code&amp;gt; (the &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; at which fast-retransmit last fired).  Set &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; on the slot (one-shot per-slot gate) and enter NewReno-style careful recovery (see NewReno below in this section).&lt;br /&gt;
:The RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; uses the RFC 8985 sec. 6.2 form &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor.  Before the first RTT sample seeds &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; falls back to &amp;lt;code&amp;gt;MIN(reo_wnd_mult * SRTT / 4, SRTT)&amp;lt;/code&amp;gt;, still floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt; (consistent with the windowed-minimum fallback described in [[#12. RTT estimation|Section 12]]).  &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt; is a windowed minimum over the last &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; = 5 min of RTT samples (matches the Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt; default) so a route change to a longer path eventually re-anchors the reorder window without relying on &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; growth alone.&lt;br /&gt;
&lt;br /&gt;
;SACK-driven retransmit&lt;br /&gt;
:For each gap below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt; whose slot is (1) still owned, (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (same hybrid as fast-retransmit, see [[#6.2. Locked main path|Section 6.2]]), re-emit.  Each SACK-driven retransmit re-arms a fresh &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; so a lost retransmit can still be recovered by its own RTO timer.&lt;br /&gt;
&lt;br /&gt;
;NewReno&lt;br /&gt;
:On entry, &amp;lt;code&amp;gt;recovery_high = snd_cr.seqno + RTT_QUARANTINE&amp;lt;/code&amp;gt;.  Exit when &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt; (the latter means everything sent has been acknowledged).  &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; also clears recovery.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 9. Pre-DRF NACK ==&lt;br /&gt;
&lt;br /&gt;
The two sides have different inactivity thresholds (&amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), so a receiver can detect &amp;quot;stale data run&amp;quot; before the sender&#039;s own DRF logic kicks in.  NACK is the receiver-driven nudge that asks the sender to re-transmit the head of the run.&lt;br /&gt;
&lt;br /&gt;
;Send (&amp;lt;code&amp;gt;frcti_nack_snd&amp;lt;/code&amp;gt;, called by &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;FRCT_INACT_NEED_NACK&amp;lt;/code&amp;gt;)&lt;br /&gt;
:When an incoming DATA packet has no DRF and rcv-side activity is older than &amp;lt;code&amp;gt;rcv_cr.inact&amp;lt;/code&amp;gt;, the receiver emits a bare packet with &amp;lt;code&amp;gt;flags = FRCT_NACK&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;seqno = arrival_seqno - 1&amp;lt;/code&amp;gt; (informational only, not consulted by the receive handler).  The cooldown in [[#3. Protocol parameters|Section 3]] rate-limits the burst.  Non-DATA non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; non-DATA DRF still rebases via the DRF branch.&lt;br /&gt;
&lt;br /&gt;
;Receive (&amp;lt;code&amp;gt;frcti_nack_rcv&amp;lt;/code&amp;gt;)&lt;br /&gt;
:Dispatched in the early-exit branch ([[#6.1. Early-exit dispatch|Section 6.1]]), before &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;.  The sender copies the head-of-line (HoL) &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; packet, marks the slot &amp;lt;code&amp;gt;SND_RTX | SND_FAST_RXM&amp;lt;/code&amp;gt; (Karn-suppress next ACK, one-shot fast-rxm gate), sets &amp;lt;code&amp;gt;rtt_lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, and re-emits via &amp;lt;code&amp;gt;fast_rxm_send&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt; and a refreshed &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;.  The original &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed - the NACK emit is additive to the normal retransmit machinery, not a replacement.  No-op if nothing is in flight, the HoL slot has aged past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, or the HoL &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; pointer has been cleared by SACK or RACK.&lt;br /&gt;
&lt;br /&gt;
NACK has exactly one role: lost first-of-run (DRF) packet recovery. Until the DRF packet arrives, the receiver cannot rebase its window, so any subsequent in-flight packets look stale to the receiver. The NACK fires the moment a stale receiver sees DATA without DRF, telling the sender to re-emit the head-of-line (DRF) packet at NACK-cooldown latency rather than waiting for the initial RTO (which is the configured default until &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded by the first probe round-trip).  Mid-stream loss is NOT NACK-driven; it is recovered by the sender&#039;s RTO, fast retransmit, and SACK-driven retransmit paths ([[#8. Retransmission|Section 8]]) only.&lt;br /&gt;
&lt;br /&gt;
The existing &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed on a NACK re-emit, so the RTO path remains the eventual fallback.&lt;br /&gt;
&lt;br /&gt;
== 10. Cumulative + selective ACK ==&lt;br /&gt;
&lt;br /&gt;
Cumulative ACK is &amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;.  On out-of-order arrival the&lt;br /&gt;
receiver also emits a SACK packet ([[#1.3. SACK payload|Section 1.3]]) whose payload lists&lt;br /&gt;
&#039;&#039;present&#039;&#039; blocks above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; (analogous to TCP SACK / QUIC ACK&lt;br /&gt;
ranges).  SACKs are rate-limited per [[#3. Protocol parameters|Section 3]] and suppressed when&lt;br /&gt;
neither &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; nor block count has changed since the last SACK.&lt;br /&gt;
&lt;br /&gt;
D-SACK reports (RFC 2883) are emitted in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; of an&lt;br /&gt;
otherwise normal SACK frame (see [[#1.3. SACK payload|Section 1.3]] for the encoding).&lt;br /&gt;
Two receiver triggers arm a pending D-SACK report (single-slot,&lt;br /&gt;
latest-wins):&lt;br /&gt;
&lt;br /&gt;
* DATA arrival with &amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;, both wire-dup (no RXM, &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt; path) and retransmit (RXM, post-FC branch) (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
* &amp;lt;code&amp;gt;rq_accept&amp;lt;/code&amp;gt; conflict, slot already occupied in &amp;lt;code&amp;gt;[lwe, rwe)&amp;lt;/code&amp;gt; (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
&lt;br /&gt;
When a D-SACK is pending and the standard scoreboard SACK would be&lt;br /&gt;
suppressed by dedup or rate-limit, the report is emitted as a&lt;br /&gt;
stand-alone SACK frame through the normal &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; path; when a&lt;br /&gt;
D-SACK report is pending the path bypasses dedup and the &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;&lt;br /&gt;
rate-limit, but the a-timer suppression on rcv inactivity still&lt;br /&gt;
applies.&lt;br /&gt;
&lt;br /&gt;
Bare ACKs are deferred via a per-flow delayed-ACK timer (one in&lt;br /&gt;
flight at a time, atomic test-and-set dedup; fires per [[#3. Protocol parameters|Section 3]]&lt;br /&gt;
after the first in-order arrival).  Suppressed if (1) no new&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, (2) rcv side is inactive (older than &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt;), or (3) the&lt;br /&gt;
sender just sent within &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;.  A pending D-SACK ride-through&lt;br /&gt;
bypasses (1) and (3); the a-timer gate (2) is unconditional.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 11. Flow control ==&lt;br /&gt;
&lt;br /&gt;
The receiver advertises &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; in every FC field.  The sender treats&lt;br /&gt;
its &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; as the absolute right edge: when&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt;= snd_cr.rwe&amp;lt;/code&amp;gt; the window is closed and &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt;&lt;br /&gt;
yields.  While closed, the sender periodically emits RDVS&lt;br /&gt;
(rendezvous) packets (cadence &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt;); the receiver replies with&lt;br /&gt;
a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;) that reopens the window.  Once the&lt;br /&gt;
window has been closed for longer than &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; the sender stops&lt;br /&gt;
emitting RDVS but does not tear the flow down - the writer keeps&lt;br /&gt;
blocking until either a peer-driven FC arrives or the KA&lt;br /&gt;
(keepalive) / r-timer marks the flow.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt; on receipt and MUST NOT shrink:&lt;br /&gt;
a backward &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is silently clamped to the current &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt;;&lt;br /&gt;
the FC packet still reopens the window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 12. RTT estimation ==&lt;br /&gt;
&lt;br /&gt;
Active RTTP probes ([[#1.4. RTTP payload|Section 1.4]]) carry a 32-bit &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt; (0&lt;br /&gt;
reserved) and a 16-byte random nonce echoed verbatim - defends&lt;br /&gt;
against spoofed replies.  A ring of &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; in-flight probes is&lt;br /&gt;
kept; an echo whose &amp;lt;code&amp;gt;(id, nonce)&amp;lt;/code&amp;gt; doesn&#039;t match the ring slot is&lt;br /&gt;
dropped.  A single RTTP sample is clamped to &amp;lt;code&amp;gt;RTT_CLAMP_MUL * srtt&amp;lt;/code&amp;gt;&lt;br /&gt;
(compile-time &amp;lt;code&amp;gt;RTT_CLAMP_MUL = 16&amp;lt;/code&amp;gt;) once &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded; the first&lt;br /&gt;
cold-probe sample feeds &amp;lt;code&amp;gt;rtt_update&amp;lt;/code&amp;gt; raw.&lt;br /&gt;
&lt;br /&gt;
Probe arming gates:&lt;br /&gt;
&lt;br /&gt;
;Cold (no &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; yet)&lt;br /&gt;
:the receive path arms at most one probe per 100 ms via &amp;lt;code&amp;gt;frcti_rcv_probe&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;PROBE_DUE_COLD&amp;lt;/code&amp;gt;); arming requires an incoming packet.  Active send-path arming bails while &amp;lt;code&amp;gt;srtt == 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
;Warm (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt;, called from &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt;)&lt;br /&gt;
:outstanding data (&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt; snd_cr.lwe&amp;lt;/code&amp;gt;), AND at least &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_rcv_rtt&amp;lt;/code&amp;gt; (last RTT receive of any kind), AND at least &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_snd_probe&amp;lt;/code&amp;gt; (last probe emit).&lt;br /&gt;
&lt;br /&gt;
Sample feeds either Linux&#039;s asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_LINUX_RTT_ESTIMATOR&amp;lt;/code&amp;gt;, default ON) or RFC 6298 symmetric EWMA&lt;br /&gt;
(compile option).  &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is floored at 10 ms when seeded from a&lt;br /&gt;
hint, at 1 us after every update (including the first seeding&lt;br /&gt;
sample); &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; floored at 100 ns.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;RTO = max(rto_min, 2 * srtt, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(the &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; floor is an FRCT addition not in RFC 6298).&lt;br /&gt;
Effective wheel deadline capped per [[#3. Protocol parameters|Section 3]].&lt;br /&gt;
&lt;br /&gt;
ACK-derived samples (&amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;rtt_sample_eligible&amp;lt;/code&amp;gt;), beyond&lt;br /&gt;
the cum-ACK advance gate in &amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno &amp;amp;gt; lwe&amp;lt;/code&amp;gt; and&lt;br /&gt;
&amp;lt;code&amp;gt;ackno &amp;amp;lt;= seqno&amp;lt;/code&amp;gt;), require all of: not in recovery; ACK packet does&lt;br /&gt;
not carry &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;; HoL slot&#039;s &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; bit clear; slot&#039;s &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;&lt;br /&gt;
pointer non-NULL (not SACK-consumed); &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; not below the &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
fence; &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; already seeded by an RTTP probe.  There is no ACK-only&lt;br /&gt;
seeding.&lt;br /&gt;
&lt;br /&gt;
Every eligible sample also feeds &amp;lt;code&amp;gt;RACK.min_RTT&amp;lt;/code&amp;gt; (RFC 8985 sec. 6.2)&lt;br /&gt;
via a windowed minimum: replace whenever the sample is strictly&lt;br /&gt;
smaller OR more than &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; (5 min, matches Linux&lt;br /&gt;
&amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) has elapsed since the current min was set.  The&lt;br /&gt;
downward branch is immediate (faster path picked up at once); the&lt;br /&gt;
upward branch is gated on the window (a transient queue burst does&lt;br /&gt;
not poison the estimate, but a sustained route change to a longer&lt;br /&gt;
path re-anchors &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; after at most one window).  Seeded from&lt;br /&gt;
&amp;lt;code&amp;gt;rtt_hint&amp;lt;/code&amp;gt; at &amp;lt;code&amp;gt;rtt_init&amp;lt;/code&amp;gt;; 0 acts as the unset sentinel and the base&lt;br /&gt;
in &amp;lt;code&amp;gt;rack_reorder_window&amp;lt;/code&amp;gt; falls back from &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt; (so&lt;br /&gt;
&amp;lt;code&amp;gt;R = mult * SRTT/4&amp;lt;/code&amp;gt;, capped at &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;, floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt;)&lt;br /&gt;
until the first sample.  See [[#6.2. Locked main path|Section 6.2]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 13. Liveness (keepalive) ==&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;code&amp;gt;qs.timeout &amp;amp;gt; 0&amp;lt;/code&amp;gt; a per-flow KA (keepalive) timer is armed.&lt;br /&gt;
Arming uses &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; for the deadline computation:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;deadline = min(snd_act + qs.timeout/4, rcv_act + qs.timeout)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(clamped to &amp;lt;code&amp;gt;now + qs.timeout/4&amp;lt;/code&amp;gt; if already past).  The timer fires&lt;br /&gt;
either on sender idleness (to send a KA) or on receiver idleness&lt;br /&gt;
(to declare the peer dead).  On fire (&amp;lt;code&amp;gt;ka_snd&amp;lt;/code&amp;gt;) the peer-dead test&lt;br /&gt;
uses &amp;lt;code&amp;gt;max(rcv_cr.act, t_ka_rcv)&amp;lt;/code&amp;gt; so a recent KA reply counts even&lt;br /&gt;
when no DATA has arrived:&lt;br /&gt;
&lt;br /&gt;
* If &amp;lt;code&amp;gt;now - max(rcv_cr.act, t_ka_rcv) &amp;amp;gt; qs.timeout&amp;lt;/code&amp;gt;, mark the flow &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; and notify the per-process flow-event set (&amp;lt;code&amp;gt;proc.fqset&amp;lt;/code&amp;gt;) with &amp;lt;code&amp;gt;FLOW_PEER&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Else if &amp;lt;code&amp;gt;snd_idle &amp;amp;gt; qs.timeout/4&amp;lt;/code&amp;gt;, emit a bare &amp;lt;code&amp;gt;KA | ACK&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) and re-arm.&lt;br /&gt;
* Else just re-arm.&lt;br /&gt;
&lt;br /&gt;
Note: &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; are the receive and transmit shared-memory&lt;br /&gt;
ring buffers.  The r-timer raises &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; on both (route is&lt;br /&gt;
broken); keepalive raises &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; only and notifies&lt;br /&gt;
the flow-event set (peer is silent, writer keeps &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; usable) -&lt;br /&gt;
distinct ACLs.  &amp;lt;code&amp;gt;qs.timeout == 0&amp;lt;/code&amp;gt; disables keepalive entirely; a&lt;br /&gt;
silent peer crash is then undetected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 14. Linger / teardown ==&lt;br /&gt;
&lt;br /&gt;
On &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt; computes a grace timeout&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;max(rcv_cr.act + rcv_cr.inact, snd_cr.act + snd_cr.inact) - now&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(floored at 0 and converted to seconds) and returns it; &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&lt;br /&gt;
forwards this to the IRMd as the dealloc grace.  The IRMd, not FRCT,&lt;br /&gt;
performs the wait.  Before computing the timeout, FRCT may emit a&lt;br /&gt;
final ACK when &amp;lt;code&amp;gt;rcv_cr.lwe != rcv_cr.seqno&amp;lt;/code&amp;gt; (the peer has not been&lt;br /&gt;
told the most recent cumulative ACK) AND the rcv side has been&lt;br /&gt;
active within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer not aged out).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt; is honoured only when &amp;lt;code&amp;gt;snd_cr.lwe &amp;amp;lt; edge&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;edge =&lt;br /&gt;
snd_fin_seqno&amp;lt;/code&amp;gt; after FIN has been sent in stream mode and&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno&amp;lt;/code&amp;gt; otherwise (data or FIN still in flight).  The drain&lt;br /&gt;
itself runs in &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&#039;s &amp;lt;code&amp;gt;while (FRCTI_LINGERING)&amp;lt;/code&amp;gt; loop, not in&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The fd is single-reader / single-writer (documented in the&lt;br /&gt;
manpages).  &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt; pumps &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; on every call (via&lt;br /&gt;
&amp;lt;code&amp;gt;flow_wait_window&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;flow_drain_rx_nb&amp;lt;/code&amp;gt;) and additionally blocks on&lt;br /&gt;
&amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; when the send window is closed.  A pure-writer thread thus&lt;br /&gt;
consumes ACKs without a dedicated reader.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 15. Heritage and adopted techniques ==&lt;br /&gt;
&lt;br /&gt;
Delta-t (Watson, 1981) is the primary heritage; FRCP descends from&lt;br /&gt;
the delta-t protocol family via the Recursive InterNetwork&lt;br /&gt;
Architecture (RINA; Day, &amp;quot;Patterns in Network Architecture&amp;quot;, 2008,&lt;br /&gt;
ch. 9).  Timer-based connection management&lt;br /&gt;
(no SYN/FIN handshake, per-flow state born on first DATA and&lt;br /&gt;
reclaimed after &amp;lt;code&amp;gt;t_mpl + a + r&amp;lt;/code&amp;gt; of silence), the DRF marker, and the&lt;br /&gt;
&amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; timers all come from delta-t.  See Watson,&lt;br /&gt;
&amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection&lt;br /&gt;
Management&amp;quot;, Computer Networks 5 (1981).&lt;br /&gt;
&lt;br /&gt;
The unified &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive and its&lt;br /&gt;
multi-axis QoS-cube argument ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) also come from RINA&lt;br /&gt;
(Day 2008, ch. 6; Grasa et al., &amp;quot;IRATI: investigating RINA as an&lt;br /&gt;
alternative to TCP/IP&amp;quot;, Computer Networks 92 (2015)) - reliability,&lt;br /&gt;
ordering, CRC presence, and encryption are flow attributes, not&lt;br /&gt;
separate sockets or protocols.&lt;br /&gt;
&lt;br /&gt;
The table below summarises additional adopted techniques and their&lt;br /&gt;
references.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FRCP mechanism !! Heritage !! Reference / note&lt;br /&gt;
|-&lt;br /&gt;
| Random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; || TCP ISN || RFC 6528 (Gont &amp;amp;amp; Bellovin, 2012).  QUIC PN-space reset (RFC 9000 sec. 12.3) is a structural analogue.&lt;br /&gt;
|-&lt;br /&gt;
| Cumulative ACK, left-window-edge advance || TCP || RFC 793 / RFC 9293&lt;br /&gt;
|-&lt;br /&gt;
| Receive window with non-shrink rule || TCP || RFC 793 sec. 3.7 / RFC 9293 sec. 3.8.6; RFC 1122 sec. 4.2.2.16 for the explicit non-shrink prohibition&lt;br /&gt;
|-&lt;br /&gt;
| Modular &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; arithmetic (&amp;lt;code&amp;gt;before&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;after&amp;lt;/code&amp;gt; helpers) || TCP || RFC 793 sec. 3.3 / RFC 9293 sec. 3.4&lt;br /&gt;
|-&lt;br /&gt;
| Selective ACK block list || TCP || RFC 2018 (Mathis et al., 1996).  Encoded as a typed FRCP packet rather than a TCP option, so framing is closer to QUIC ACK frames.  D-SACK (RFC 2883) carried in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt;; see [[#1.3. SACK payload|Section 1.3]].&lt;br /&gt;
|-&lt;br /&gt;
| NewReno-careful recovery with &amp;lt;code&amp;gt;recovery_high&amp;lt;/code&amp;gt; gate || TCP || RFC 6582 (Henderson et al., 2012); QUIC builds on the same model in RFC 9002 sec. 7.3.2.  Cwnd half absent (CC in IPCP).&lt;br /&gt;
|-&lt;br /&gt;
| RACK reordering window for fast retransmit || TCP || RFC 8985 (Cheng et al., 2021).  FRCP &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor against &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; collapse; matches RFC 8985 sec. 6.2 and Linux &amp;lt;code&amp;gt;tcp_rack_reo_wnd&amp;lt;/code&amp;gt;.  DSACK-driven &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; (sec. 6.2 step 4) is adopted; see [[#1.3. SACK payload|Section 1.3]] for the wire encoding.  The hybrid RACK-or-&amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; trigger from RFC 8985 sec. 6.2 step 4 is adopted ([[#8. Retransmission|Section 8]]).  QUIC&#039;s analogue in RFC 9002 sec. 6.1.2 uses &amp;lt;code&amp;gt;max(srtt, latest_rtt)&amp;lt;/code&amp;gt; as the base.&lt;br /&gt;
|-&lt;br /&gt;
| Karn&#039;s algorithm: no RTT sample on retransmits, RTO-collapse freeze || TCP || Karn &amp;amp;amp; Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, SIGCOMM 1987; RFC 6298 sec. 3.&lt;br /&gt;
|-&lt;br /&gt;
| RTO formula &amp;lt;code&amp;gt;RTO = max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt; || TCP || RFC 6298 (Paxson et al., 2011).  &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; = 250 us is below RFC 6298 sec. 2.4&#039;s 1 s SHOULD-floor - a recursive-layer choice.&lt;br /&gt;
|-&lt;br /&gt;
| Linux asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator (default) || Linux kernel || &amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;if(delta&amp;amp;lt;0) m&amp;amp;gt;&amp;amp;gt;=3&amp;lt;/code&amp;gt; dampening is a kernel divergence from RFC 6298.  RFC 6298 EWMA available behind a compile flag.&lt;br /&gt;
|-&lt;br /&gt;
| Delayed ACK with rate suppression || TCP || RFC 813 (Clark, 1982); RFC 1122 sec. 4.2.3.2; RFC 5681 sec. 4.2.  Single-deadline coalescing rather than &amp;quot;ack-every-other-segment&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| Zero-window-probe / persist-timer analogue (RDVS) || TCP || RFC 1122 sec. 4.2.2.17 / RFC 9293 sec. 3.8.6.1.  RDVS solicits an FC reply, distinct from QUIC &amp;lt;code&amp;gt;DATA_BLOCKED&amp;lt;/code&amp;gt; (RFC 9000 sec. 19.12), which is one-way notification.  &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; give-up departs from TCP.&lt;br /&gt;
|-&lt;br /&gt;
| Multiplexed control on a single PCI || SCTP / QUIC || SCTP chunk bundling (RFC 9260 sec. 6.10); QUIC frame multiplexing (RFC 9000 sec. 12.4).  Cleaner fit than TCP&#039;s separate-flag-bits design.&lt;br /&gt;
|-&lt;br /&gt;
| ACK ranges as multiple discontiguous acked blocks || QUIC || QUIC ACK frame (RFC 9000 sec. 19.3).  FRCP SACK is conceptually QUIC-frame-shaped even though encoded as absolute &amp;lt;code&amp;gt;[start,end]&amp;lt;/code&amp;gt; pairs.&lt;br /&gt;
|-&lt;br /&gt;
| Nonce-authenticated active RTT / liveness probing (RTTP) || QUIC &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;PATH_RESPONSE&amp;lt;/code&amp;gt; (RFC 9000 sec. 8.2, sec. 19.17, sec. 19.18).  WebRTC ICE consent-freshness (RFC 7675) is the same pattern.  QUIC&#039;s nonce is 8 octets; FRCP chooses 16.&lt;br /&gt;
|-&lt;br /&gt;
| Probing distinct from keepalive || QUIC || KA timer answers &amp;quot;peer alive?&amp;quot;, RTTP answers &amp;quot;path measurable?&amp;quot;, as in QUIC PING (RFC 9000 sec. 19.2) vs &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| Bare KA + ACK keepalive packets || QUIC / SCTP || QUIC PING (RFC 9000 sec. 19.2); SCTP HEARTBEAT / HEARTBEAT-ACK (RFC 9260 sec. 8.3).  SCTP HEARTBEAT also carries an opaque echoed blob, structurally similar to FRCP RTTP.&lt;br /&gt;
|-&lt;br /&gt;
| (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) fragment-role bits ([[#7.2. Fragmentation and reassembly|Section 7.2]]) || SCTP || RFC 9260 sec. 3.3.1 DATA chunk B/E bits encode the same four states (&amp;lt;code&amp;gt;B+E=SOLE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;B-only=FIRST&amp;lt;/code&amp;gt;, neither=&amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E-only=LAST&amp;lt;/code&amp;gt;).  Each fragment carries its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;/TSN and is independently retransmitted.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-offset reassembly (Sections [[#1.5. Stream PCI extension|1.5]], [[#16. Stream-mode flows|16]]) || QUIC || QUIC STREAM frame (RFC 9000 sec. 19.8) uses Offset + Length varints; FRCP uses fixed 32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;.  One stream per flow vs QUIC&#039;s many streams multiplexed.&lt;br /&gt;
|-&lt;br /&gt;
| FIN end-of-stream marker (Sections [[#1.2. Flag bits|1.2]], [[#16. Stream-mode flows|16]]) || TCP / QUIC || TCP FIN flag (RFC 9293 sec. 3.1) closes one half of the byte stream; QUIC STREAM frame FIN bit (RFC 9000 sec. 19.8) does the same per stream with an immutable final-size invariance (RFC 9000 sec. 4.5: the final size is fixed once observed).  FRCP&#039;s FIN consumes one packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (not one byte of stream space) and is idempotent on the sender side.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-credit flow control ([[#16. Stream-mode flows|Section 16]]) || QUIC || &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; (RFC 9000 sec. 4.1, sec. 19.10).  FRCP projects a per-flow byte budget onto the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;.  Single stream per flow collapses QUIC&#039;s &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.&lt;br /&gt;
|-&lt;br /&gt;
| Header protection (encrypted seqnos) || QUIC || QUIC RFC 9001 sec. 5.4 applies header protection on top of AEAD to mask the packet number.  FRCP&#039;s per-flow AEAD wrap ([[#16. Stream-mode flows|Section 16]]) is wider: it encrypts the entire PCI including &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; because the IPCP below already routes, so no destination connection-ID needs to stay in clear (cf. RFC 9000 sec. 5.2).&lt;br /&gt;
|-&lt;br /&gt;
| Two-bit fragment role polarity || SCTP || The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair follows SCTP B/E (begin = 1 / end = 1) rather than IPv4 MF (RFC 791 sec. 3.2), which has the inverse polarity (MF = 1 means NOT last).&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal reliability / ordering axes ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) || SCTP || PR-SCTP (RFC 3758, per-message partial reliability) and SCTP DATA U-bit (RFC 9260 sec. 3.3.1, per-message unordered) are the closest precedents for decoupling reliability from ordering; FRCP sets them per-flow rather than per-message.&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal CRC (&amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt;) || UDP-Lite || RFC 3828 (Larzon et al., 2004) lets the sender pick a per-packet Checksum Coverage and the receiver enforce a locally configured minimum (no in-band negotiation; sec. 3.1, sec. 3.3).  FRCP gates a full CRC trailer on &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; at flow setup.  Contrast TCP / SCTP (mandatory checksum) and QUIC (AEAD subsumes CRC).&lt;br /&gt;
|-&lt;br /&gt;
| Setup-time service negotiation || DCCP / SCTP / QUIC || DCCP Service Codes (RFC 4340 sec. 8.1.2, RFC 5595); SCTP INIT parameters (RFC 9260 sec. 3.3.2); QUIC transport parameters (RFC 9000 sec. 7.4).  All negotiate service properties at connection setup; only RINA&#039;s QoS cube exposes them as an orthogonal vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.1. Original to FRCP (no clean prior art) ===&lt;br /&gt;
&lt;br /&gt;
* Pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]): receiver-driven nudge exploiting &amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;.  Closest analogues are SCTP Gap Ack Blocks (RFC 9260 sec. 3.3.4) and DCCP Ack Vector (RFC 4340 sec. 11.4) - both let the receiver describe gaps to the sender, but neither targets the cross-epoch / pre-DRF case.&lt;br /&gt;
* &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; window-probe give-up: neither TCP (persist-timer probes until application or R2 abort, RFC 9293 sec. 3.8.6.1) nor QUIC has an explicit FC-give-up counter.  A recursive-network choice: outer layers can drop the flow.&lt;br /&gt;
* Skip-past-gap reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]): SCTP fragments and reassembles every flow regardless of reliability/ordering, using its own per-stream reassembly queue; QUIC fragments via STREAM offsets.  FRCP fragments best-effort flows too, but the receiver drops the broken prefix the moment a later run-start (&amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; role) is visible inside the &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;-wide reorder ring - no IP-frag-style timeout, no SCTP-style explicit abort.  If no later run-start arrives within the ring, &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt; and the partial run keeps its slots; the next inspect retries.  The trade-off: a permanently-lost &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; in a long isolated run holds slots until either a later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; appears in the ring or the writer stops, at which point the slots are reclaimed on flow teardown.&lt;br /&gt;
* Reassembly deferred to consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]), message mode only (&amp;lt;code&amp;gt;qos.service == SVC_MESSAGE&amp;lt;/code&amp;gt;): SCTP (RFC 9260 sec. 6.9), QUIC (RFC 9000 sec. 2.2), and TCP (RFC 9293) all hold reassembly state at the receive boundary.  FRCP message-mode leaves fragments in the shared-memory ring until &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; pulls and lands the SDU directly in the caller&#039;s buffer.  Stream mode ([[#16. Stream-mode flows|Section 16]]) uses the standard QUIC-style direct ring placement on receive and does not defer.  The optimisation is enabled by the Shared-Memory Subsystem (SSM) packet-buffer ring (see &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt; at [[#1.1. PCI header|Section 1.1]]); the analogue is OS-level scatter-gather I/O (&amp;lt;code&amp;gt;recvmsg+iovec&amp;lt;/code&amp;gt;), not a transport-layer prior art.&lt;br /&gt;
* TLP-equivalent tail-loss recovery (RFC 8985 sec. 7; RFC 9002 sec. 6.2): FRCP does not emit an explicit Tail Loss Probe packet, but the same goal is met implicitly by RACK loss detection ([[#8. Retransmission|Section 8]]) firing on a non-advancing cumulative ACK once the head-of-line slot ages past the RACK reorder window &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; - well below &amp;lt;code&amp;gt;RTO = max(2 * SRTT, SRTT + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.  A receiver-driven nudge is also available via the pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.2. Not adopted ===&lt;br /&gt;
&lt;br /&gt;
* Slow start, congestion window (cwnd), Additive Increase / Multiplicative Decrease (AIMD), NewReno cwnd inflation.  Congestion control lives in the IPCP CA policies and is driven by Explicit Congestion Notification (ECN, RFC 3168).&lt;br /&gt;
* Nagle / silly-window-syndrome (SWS) avoidance (RFC 896, RFC 1122 sec. 4.2.3.4).  (Deferred work, not adopted in the current spec.)&lt;br /&gt;
* TCP Timestamps (RFC 7323) / Protection Against Wrapped Sequences (PAWS) - RTT measurement uses RTTP, not per-segment timestamps.  A peer-supplied timestamp echoed on every ACK lets a malicious peer drive the &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; estimate arbitrarily low, collapsing the RTO and triggering a self-inflicted retransmit storm.  RTTP confines RTT measurement to nonce-authenticated probe round-trips, where a forged echo is rejected before it can reach the estimator.&lt;br /&gt;
* ECN (Explicit Congestion Notification) response inside FRCP (consumed by IPCP Congestion Avoidance / CA).&lt;br /&gt;
* IP-style fragment-offset reassembly (RFC 791 sec. 3.2; RFC 8200 sec. 4.5).  Message-mode FRCP relies on the FRCT &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; reorder ring keyed by &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (shared by FRTX and best-effort flows) to put fragments back in order; no separate offset field is needed and no IP-style hole-list reassembly buffer is kept.  Stream-mode FRCP does carry &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte offsets ([[#1.5. Stream PCI extension|Section 1.5]]) for direct ring placement on receive.&lt;br /&gt;
* QUIC STREAM offset+length framing on &#039;&#039;every&#039;&#039; flow (RFC 9000 sec. 19.8).  Message-mode FRCP uses the SCTP-style B/E flag-bit encoding (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) and skips the offsets; stream-mode FRCP adopts the QUIC offset model (heritage table above).&lt;br /&gt;
&lt;br /&gt;
== 16. Stream-mode flows ==&lt;br /&gt;
&lt;br /&gt;
When a flow is allocated with &amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt; both peers&lt;br /&gt;
switch to byte-stream semantics, layered on top of the FRTX reorder&lt;br /&gt;
machinery already described in Sections [[#6. Receive path|6]]-[[#8. Retransmission|8]].&lt;br /&gt;
&lt;br /&gt;
=== 16.1. Send ===&lt;br /&gt;
&lt;br /&gt;
The sender splits the caller&#039;s octets into chunks of at most&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt; octets (Sections [[#1.1. PCI header|1.1]]&lt;br /&gt;
and [[#1.5. Stream PCI extension|1.5]]).  Each chunk is one DATA packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte range copied from a monotonic stream counter.&lt;br /&gt;
In stream mode &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST be transmitted as&lt;br /&gt;
zero; the per-byte position is carried by the &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt;&lt;br /&gt;
extension instead.&lt;br /&gt;
&lt;br /&gt;
End-of-stream is signalled with a 0-byte DATA packet that has &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;&lt;br /&gt;
(bit 12) set, emitted on the FIN triggers listed in [[#1.2. Flag bits|Section 1.2]]&lt;br /&gt;
(WR-half close, &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, and any other path that yields the&lt;br /&gt;
final byte).  The sender MUST emit at most one FIN per flow; its&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; MUST equal &amp;lt;code&amp;gt;[final-byte, final-byte)&amp;lt;/code&amp;gt; (i.e., empty&lt;br /&gt;
interval at the final byte position; final-size invariance,&lt;br /&gt;
analogous to QUIC RFC 9000 sec. 4.5).  Idempotency is enforced by&lt;br /&gt;
an &amp;lt;code&amp;gt;snd_fin_sent&amp;lt;/code&amp;gt; guard.&lt;br /&gt;
&lt;br /&gt;
=== 16.2. Receive ===&lt;br /&gt;
&lt;br /&gt;
On arrival the receiver places the payload directly into a per-flow&lt;br /&gt;
byte-indexed receive ring of width &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; (octets) at the position&lt;br /&gt;
indicated by &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, with a two-segment &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; across the ring&lt;br /&gt;
boundary if needed.  Receipt is recorded in the FRTX reorder&lt;br /&gt;
machinery ([[#6.2. Locked main path|Section 6.2]]) augmented with the packet&#039;s &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;, and&lt;br /&gt;
FIN bit per slot.  When a packet&#039;s &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; front-overlaps&lt;br /&gt;
bytes already at or below the byte high-water mark, the overlap is&lt;br /&gt;
trimmed before placement so the same byte is never written twice.&lt;br /&gt;
After stashing, the receiver advances &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; and the byte high-water&lt;br /&gt;
mark across any newly-contiguous prefix.  Each slot advanced MUST&lt;br /&gt;
satisfy &amp;lt;code&amp;gt;start == the last-delivered slot&#039;s end&amp;lt;/code&amp;gt;; a slot whose&lt;br /&gt;
&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; does not equal that &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; is silently dropped at delivery time&lt;br /&gt;
(the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is consumed, no stream bytes contributed) and the high-&lt;br /&gt;
water mark does not advance past it.  The stream byte-stream&lt;br /&gt;
stalls at that point - there is no flow-tear-down on mismatch.&lt;br /&gt;
This filters spliced or off-path-injected slots that fall in&lt;br /&gt;
window without strong cryptographic authentication.&lt;br /&gt;
&lt;br /&gt;
A FIN slot marks end-of-stream at advance time only if its byte&lt;br /&gt;
position equals the last-delivered slot&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;; otherwise the FIN&lt;br /&gt;
is ignored and the corresponding &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; occupies a slot but&lt;br /&gt;
contributes no stream bytes.  No packet buffer is held after the&lt;br /&gt;
ring copy.&lt;br /&gt;
&lt;br /&gt;
=== 16.3. Read ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns up to &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt; octets from the contiguous prefix&lt;br /&gt;
&amp;lt;code&amp;gt;[next, high-water)&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;next&amp;lt;/code&amp;gt; is the byte the application has&lt;br /&gt;
already consumed up to and &amp;lt;code&amp;gt;high-water&amp;lt;/code&amp;gt; is the rightmost contiguous&lt;br /&gt;
byte received.  When the stream is fully drained AND end-of-stream&lt;br /&gt;
(EOS) was observed (&amp;lt;code&amp;gt;next == EOS&amp;lt;/code&amp;gt; byte position), &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns&lt;br /&gt;
0 (&amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) - the same shape POSIX &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt; uses on TCP after a peer&lt;br /&gt;
FIN.&lt;br /&gt;
&lt;br /&gt;
=== 16.4. Flow control ===&lt;br /&gt;
&lt;br /&gt;
ACK / SACK / RACK / RTO machinery is unchanged; the FRTX reorder&lt;br /&gt;
ring is reused as a per-&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; received-bitmap.  Let &amp;lt;code&amp;gt;per_pkt =&lt;br /&gt;
(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt;, the maximum stream-&lt;br /&gt;
byte payload one DATA packet can carry ([[#16.1. Send|Section 16.1]]).  The&lt;br /&gt;
receive window advertised in FC is clamped so the byte window&lt;br /&gt;
(&amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt;) cannot be overrun: the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is at most&lt;br /&gt;
&amp;lt;code&amp;gt;rcv_cr.lwe + ring_sz / per_pkt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC byte-credit flow-control model&lt;br /&gt;
(&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt;, RFC 9000 sec. 4.1 and sec. 19.10) projected onto&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; space.  With one stream per flow there is no &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; /&lt;br /&gt;
&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.  Receiver-side silly-window-syndrome&lt;br /&gt;
(SWS) avoidance (RFC 9293 sec. 3.8.6.2.2) is achieved by combining&lt;br /&gt;
the consume-time &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; bump with the global non-shrink rule from&lt;br /&gt;
[[#11. Flow control|Section 11]].&lt;br /&gt;
&lt;br /&gt;
=== 16.5. Security considerations ===&lt;br /&gt;
&lt;br /&gt;
Threat model.  An attacker that can observe (on-path passive) or&lt;br /&gt;
predict (off-path blind) the flow&#039;s seqnos and byte offsets on an&lt;br /&gt;
unencrypted stream flow can inject DATA or FIN at any in-window&lt;br /&gt;
position.  The in-line consistency checks above (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; == prior&lt;br /&gt;
&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; on advance; FIN MUST be 0-byte; FIN MUST sit at the final&lt;br /&gt;
byte position) realise the spirit of RFC 5961&#039;s &amp;quot;sequence-window&lt;br /&gt;
plus exact-position match for control bits&amp;quot; without an explicit&lt;br /&gt;
challenge-ACK probe; they make a few specific blind attack shapes&lt;br /&gt;
harder but are not cryptographic authentication. This is&lt;br /&gt;
comparable to TCP without the TCP Authentication Option (TCP-AO,&lt;br /&gt;
RFC 5925), tighter than a&lt;br /&gt;
pre-RFC-5961 TCP stack, and roughly equivalent to a modern&lt;br /&gt;
RFC 5961 stack against blind off-path injection - none of these&lt;br /&gt;
help once the attacker can sniff. TLS over TCP (RFC 8446)&lt;br /&gt;
encrypts only the TCP payload and leaves TCP seqnos, ACKs, FIN,&lt;br /&gt;
and RST in the clear, so TLS does NOT defend against TCP-header-&lt;br /&gt;
level injection; QUIC (RFC 9000) hides packet numbers under&lt;br /&gt;
header protection (RFC 9001 sec. 5.4), so this specific weakness&lt;br /&gt;
does not apply to QUIC.&lt;br /&gt;
&lt;br /&gt;
Mitigation: AEAD.  When the flow has encryption enabled the&lt;br /&gt;
recommended AEAD ciphers (AES-GCM, RFC 5288; or ChaCha20-Poly1305,&lt;br /&gt;
RFC 8439) wrap the entire FRCP packet on the wire - PCI, stream&lt;br /&gt;
extension, body, and the CRC trailer when &amp;lt;code&amp;gt;ber == 0&amp;lt;/code&amp;gt; - under a&lt;br /&gt;
per-flow symmetric key derived from the flow&#039;s own key exchange&lt;br /&gt;
([[#1.1. PCI header|Section 1.1]]).  The AEAD tag (~2^-128 forgery probability)&lt;br /&gt;
dominates the CRC (~2^-32) for integrity in this mode but the CRC&lt;br /&gt;
trailer is currently retained inside the wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
Implementations MUST NOT rely on the security properties below&lt;br /&gt;
when a non-AEAD cipher (e.g. AES-CTR alone) is negotiated; non-&lt;br /&gt;
AEAD modes provide confidentiality only and the threat-model&lt;br /&gt;
claims do not hold.&lt;br /&gt;
&lt;br /&gt;
With an AEAD cipher in use, seqnos, byte offsets, and the FIN bit&lt;br /&gt;
are both authenticated and confidential. Against an off-path or&lt;br /&gt;
on-path-passive attacker this is:&lt;br /&gt;
&lt;br /&gt;
* Stronger than TCP+TLS (TCP header in the clear).&lt;br /&gt;
* Stronger than TCP+TCP-AO (header authenticated but visible).&lt;br /&gt;
* Comparable to IPsec ESP transport mode (RFC 4303), which similarly authenticates and encrypts the upper-layer header plus payload, and to QUIC packet protection (RFC 9001 sec. 5), with the difference that QUIC must leave the destination connection ID in the clear for routing whereas FRCP relies on the IPCP below for delivery and can therefore encrypt its entire PCI.&lt;br /&gt;
&lt;br /&gt;
Keying granularity. Ouroboros flow allocation runs key exchange (&amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt;) per flow, so each &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; yields independent symmetric keys. This is finer-grained than QUIC (per-connection, RFC 9001, where one handshake covers all multiplexed streams) and finer-grained than typical IPsec deployment (per-host-pair Security Associations, SAs). Forward secrecy follows from the &amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt; when an ephemeral Diffie-Hellman exchange (DHE), or a hybrid mode (classical DH + post-quantum Key Encapsulation Mechanism / KEM), is selected.&lt;br /&gt;
&lt;br /&gt;
Replay protection. The AEAD layer itself does NOT carry an explicit anti-replay window (unlike IPsec ESP, RFC 4303 sec. 3.4.3, or DTLS, RFC 9147 sec. 4.5.1).  For FRCP-engaged flows the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space duplicate-suppression in [[#6.2. Locked main path|Section 6.2]] rejects replayed&lt;br /&gt;
DATA after the AEAD strips the wrap, because the AEAD authenticates the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a replay re-presents an old &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; that is then discarded either as a duplicate (still inside the receive window or as outside the receive window, depending on how far &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; has advanced since the original packet was delivered. RAW (&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) flows have no FRCP layer and therefore no replay protection at the AEAD layer either; deployments that need replay rejection on RAW flows SHOULD use SVC_MESSAGE.&lt;br /&gt;
&lt;br /&gt;
Layering. The AEAD wrap sits below FRCP on the data path, so RAW best-effort flows (&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;, the UDP-equivalent service of [[#2.2. Service modes (orthogonal axes)|Section 2.2]]) inherit the same per-flow integrity + confidentiality scope as FRCP-engaged flows - whatever the process and FRCP (if any) put on the wire is what the AEAD authenticates. No DTLS-equivalent layering is required for confidentiality and integrity; replay protection above AEAD is a separate concern as noted above.&lt;br /&gt;
&lt;br /&gt;
== 17. References ==&lt;br /&gt;
&lt;br /&gt;
This section lists the IETF documents, published works, and&lt;br /&gt;
source-code references cited inline elsewhere in this document.&lt;br /&gt;
IETF documents are cited inline as &amp;quot;RFC NNNN sec. X.Y&amp;quot;; books,&lt;br /&gt;
journal papers, and source-code references are cited inline by&lt;br /&gt;
author and year (or by file and function name) and are listed&lt;br /&gt;
here for convenience.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.1. IETF documents ===&lt;br /&gt;
&lt;br /&gt;
;[RFC 791]&lt;br /&gt;
:J. Postel, &amp;quot;Internet Protocol&amp;quot;, STD 5, RFC 791, September 1981.&lt;br /&gt;
;[RFC 793]&lt;br /&gt;
:J. Postel, &amp;quot;Transmission Control Protocol&amp;quot;, STD 7, RFC 793, September 1981.  Obsoleted by RFC 9293.&lt;br /&gt;
;[RFC 813]&lt;br /&gt;
:D. D. Clark, &amp;quot;Window and Acknowledgement Strategy in TCP&amp;quot;, RFC 813, July 1982.&lt;br /&gt;
;[RFC 896]&lt;br /&gt;
:J. Nagle, &amp;quot;Congestion Control in IP/TCP Internetworks&amp;quot;, RFC 896, January 1984.&lt;br /&gt;
;[RFC 1122]&lt;br /&gt;
:R. Braden (ed.), &amp;quot;Requirements for Internet Hosts -- Communication Layers&amp;quot;, STD 3, RFC 1122, October 1989.&lt;br /&gt;
;[RFC 2018]&lt;br /&gt;
:M. Mathis, J. Mahdavi, S. Floyd, A. Romanow, &amp;quot;TCP Selective Acknowledgment Options&amp;quot;, RFC 2018, October 1996.&lt;br /&gt;
;[RFC 2119]&lt;br /&gt;
:S. Bradner, &amp;quot;Key words for use in RFCs to Indicate Requirement Levels&amp;quot;, BCP 14, RFC 2119, March 1997.&lt;br /&gt;
;[RFC 2883]&lt;br /&gt;
:S. Floyd, J. Mahdavi, M. Mathis, M. Podolsky, &amp;quot;An Extension to the Selective Acknowledgement (SACK) Option for TCP&amp;quot;, RFC 2883, July 2000.&lt;br /&gt;
;[RFC 3758]&lt;br /&gt;
:R. Stewart, M. Ramalho, Q. Xie, M. Tuexen, P. Conrad, &amp;quot;Stream Control Transmission Protocol (SCTP) Partial Reliability Extension&amp;quot;, RFC 3758, May 2004.&lt;br /&gt;
;[RFC 3828]&lt;br /&gt;
:L-A. Larzon, M. Degermark, S. Pink, L-E. Jonsson (ed.), G. Fairhurst (ed.), &amp;quot;The Lightweight User Datagram Protocol (UDP-Lite)&amp;quot;, RFC 3828, July 2004.&lt;br /&gt;
;[RFC 4303]&lt;br /&gt;
:S. Kent, &amp;quot;IP Encapsulating Security Payload (ESP)&amp;quot;, RFC 4303, December 2005.&lt;br /&gt;
;[RFC 4340]&lt;br /&gt;
:E. Kohler, M. Handley, S. Floyd, &amp;quot;Datagram Congestion Control Protocol (DCCP)&amp;quot;, RFC 4340, March 2006.&lt;br /&gt;
;[RFC 5288]&lt;br /&gt;
:J. Salowey, A. Choudhury, D. McGrew, &amp;quot;AES Galois Counter Mode (GCM) Cipher Suites for TLS&amp;quot;, RFC 5288, August 2008.&lt;br /&gt;
;[RFC 5595]&lt;br /&gt;
:G. Fairhurst, &amp;quot;The Datagram Congestion Control Protocol (DCCP) Service Codes&amp;quot;, RFC 5595, September 2009.&lt;br /&gt;
;[RFC 5681]&lt;br /&gt;
:M. Allman, V. Paxson, E. Blanton, &amp;quot;TCP Congestion Control&amp;quot;, RFC 5681, September 2009.&lt;br /&gt;
;[RFC 5925]&lt;br /&gt;
:J. Touch, A. Mankin, R. Bonica, &amp;quot;The TCP Authentication Option&amp;quot;, RFC 5925, June 2010.&lt;br /&gt;
;[RFC 5961]&lt;br /&gt;
:A. Ramaiah, R. Stewart, M. Dalal, &amp;quot;Improving TCP&#039;s Robustness to Blind In-Window Attacks&amp;quot;, RFC 5961, August 2010.&lt;br /&gt;
;[RFC 6298]&lt;br /&gt;
:V. Paxson, M. Allman, J. Chu, M. Sargent, &amp;quot;Computing TCP&#039;s Retransmission Timer&amp;quot;, RFC 6298, June 2011.&lt;br /&gt;
;[RFC 6528]&lt;br /&gt;
:F. Gont, S. Bellovin, &amp;quot;Defending against Sequence Number Attacks&amp;quot;, RFC 6528, February 2012.  Obsoletes RFC 1948.&lt;br /&gt;
;[RFC 6582]&lt;br /&gt;
:T. Henderson, S. Floyd, A. Gurtov, Y. Nishida, &amp;quot;The NewReno Modification to TCP&#039;s Fast Recovery Algorithm&amp;quot;, RFC 6582, April 2012.&lt;br /&gt;
;[RFC 7323]&lt;br /&gt;
:D. Borman, B. Braden, V. Jacobson, R. Scheffenegger (ed.), &amp;quot;TCP Extensions for High Performance&amp;quot;, RFC 7323, September 2014.&lt;br /&gt;
;[RFC 7675]&lt;br /&gt;
:M. Perumal, D. Wing, R. Ravindranath, T. Reddy, M. Thomson, &amp;quot;Session Traversal Utilities for NAT (STUN) Usage for Consent Freshness&amp;quot;, RFC 7675, October 2015.&lt;br /&gt;
;[RFC 8174]&lt;br /&gt;
:B. Leiba, &amp;quot;Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words&amp;quot;, BCP 14, RFC 8174, May 2017.&lt;br /&gt;
;[RFC 8200]&lt;br /&gt;
:S. Deering, R. Hinden, &amp;quot;Internet Protocol, Version 6 (IPv6) Specification&amp;quot;, STD 86, RFC 8200, July 2017.&lt;br /&gt;
;[RFC 8439]&lt;br /&gt;
:Y. Nir, A. Langley, &amp;quot;ChaCha20 and Poly1305 for IETF Protocols&amp;quot;, RFC 8439, June 2018.&lt;br /&gt;
;[RFC 8446]&lt;br /&gt;
:E. Rescorla, &amp;quot;The Transport Layer Security (TLS) Protocol Version 1.3&amp;quot;, RFC 8446, August 2018.&lt;br /&gt;
;[RFC 8985]&lt;br /&gt;
:Y. Cheng, N. Cardwell, N. Dukkipati, P. Jha, &amp;quot;The RACK-TLP Loss Detection Algorithm for TCP&amp;quot;, RFC 8985, February 2021.&lt;br /&gt;
;[RFC 9000]&lt;br /&gt;
:J. Iyengar (ed.), M. Thomson (ed.), &amp;quot;QUIC: A UDP-Based Multiplexed and Secure Transport&amp;quot;, RFC 9000, May 2021.&lt;br /&gt;
;[RFC 9001]&lt;br /&gt;
:M. Thomson (ed.), S. Turner (ed.), &amp;quot;Using TLS to Secure QUIC&amp;quot;, RFC 9001, May 2021.&lt;br /&gt;
;[RFC 9002]&lt;br /&gt;
:J. Iyengar (ed.), I. Swett (ed.), &amp;quot;QUIC Loss Detection and Congestion Control&amp;quot;, RFC 9002, May 2021.&lt;br /&gt;
;[RFC 9147]&lt;br /&gt;
:E. Rescorla, H. Tschofenig, N. Modadugu, &amp;quot;The Datagram Transport Layer Security (DTLS) Protocol Version 1.3&amp;quot;, RFC 9147, April 2022.&lt;br /&gt;
;[RFC 9260]&lt;br /&gt;
:R. Stewart, M. Tuexen, K. Nielsen, &amp;quot;Stream Control Transmission Protocol&amp;quot;, RFC 9260, June 2022.  Obsoletes RFC 4960.&lt;br /&gt;
;[RFC 9293]&lt;br /&gt;
:W. Eddy (ed.), &amp;quot;Transmission Control Protocol (TCP)&amp;quot;, STD 7, RFC 9293, August 2022.  Obsoletes RFC 793 and several follow-ons; updates RFC 1122 and others.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.2. Books and journal papers ===&lt;br /&gt;
&lt;br /&gt;
;[Day08]&lt;br /&gt;
:J. Day, &amp;quot;Patterns in Network Architecture: A Return to Fundamentals&amp;quot;, Prentice Hall, 2008.&lt;br /&gt;
;[Grasa15]&lt;br /&gt;
:E. Grasa et al., &amp;quot;IRATI: investigating RINA as an alternative to TCP/IP&amp;quot;, Computer Networks, Vol. 92, December 2015.&lt;br /&gt;
;[KP87]&lt;br /&gt;
:P. Karn, C. Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, ACM SIGCOMM, August 1987.&lt;br /&gt;
;[Wat81]&lt;br /&gt;
:R. W. Watson, &amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection Management&amp;quot;, Computer Networks, Vol. 5, 1981.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.3. Source-code references ===&lt;br /&gt;
&lt;br /&gt;
;[Linux-RTT]&lt;br /&gt;
:&amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt; of the Linux kernel, defining the asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; variance update used as FRCP&#039;s default RTT estimator ([[#12. RTT estimation|Section 12]]).  Line-stable browseable copy at https://elixir.bootlin.com/linux/latest/source/net/ipv4/tcp_input.c.&lt;br /&gt;
&lt;br /&gt;
[[Category:Protocols]]&lt;br /&gt;
[[Category:Ouroboros internals]]&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1922</id>
		<title>Flow and Retransmission Control Protocol</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1922"/>
		<updated>2026-05-18T01:04:52Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 1.1. PCI header */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:FRCP - Flow and Retransmission Control Protocol}}&lt;br /&gt;
&lt;br /&gt;
FRCP runs end-to-end between two peers over a flow.  It delivers&lt;br /&gt;
reliability, in-order delivery, flow control, and liveness.&lt;br /&gt;
Congestion Control (CC) is not in FRCP - that lives in the IPC&lt;br /&gt;
Process (IPCP) Congestion Avoidance (CA) policies, orthogonal to&lt;br /&gt;
FRCP.  Flow allocation, naming, and IPCP lifecycle are handled by&lt;br /&gt;
the IPC Resource Manager daemon (IRMd).&lt;br /&gt;
&lt;br /&gt;
FRCT (Flow and Retransmission Control Task) is the libouroboros&lt;br /&gt;
implementation of FRCP; the task lives in &amp;lt;code&amp;gt;src/lib/frct.c&amp;lt;/code&amp;gt;.  The&lt;br /&gt;
remainder of this document describes the FRCP wire protocol and the&lt;br /&gt;
behaviour FRCT realises.  Code symbols retain the &amp;lt;code&amp;gt;FRCT_&amp;lt;/code&amp;gt; prefix&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, ...) because they belong to the implementing&lt;br /&gt;
task; this document references them verbatim.&lt;br /&gt;
&lt;br /&gt;
The keywords &amp;quot;MUST&amp;quot;, &amp;quot;MUST NOT&amp;quot;, &amp;quot;REQUIRED&amp;quot;, &amp;quot;SHALL&amp;quot;, &amp;quot;SHALL NOT&amp;quot;,&lt;br /&gt;
&amp;quot;SHOULD&amp;quot;, &amp;quot;SHOULD NOT&amp;quot;, &amp;quot;RECOMMENDED&amp;quot;, &amp;quot;MAY&amp;quot;, and &amp;quot;OPTIONAL&amp;quot; in&lt;br /&gt;
this document are to be interpreted as described in &amp;lt;code&amp;gt;BCP&amp;lt;/code&amp;gt; 14 (Best&lt;br /&gt;
Current Practice; RFC 2119, RFC 8174) when, and only when, they&lt;br /&gt;
appear in all capitals.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:Unsigned 32-bit / 8-bit integers (kernel-C style).&lt;br /&gt;
;&amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:Nanoseconds.&lt;br /&gt;
&lt;br /&gt;
Modular sequence-number comparators (32-bit, modulo 2^32):&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;before(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;(int32_t)(a - b) &amp;amp;lt; 0&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;after(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;before(b, a)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Used throughout for &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; ordering checks.&lt;br /&gt;
&lt;br /&gt;
Round-Trip Time (RTT) abbreviations used throughout:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;&lt;br /&gt;
:Smoothed RTT estimate (RFC 6298).&lt;br /&gt;
;&amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt;&lt;br /&gt;
:Mean deviation of RTT (Linux variance estimator).&lt;br /&gt;
;&amp;lt;code&amp;gt;EWMA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Exponentially Weighted Moving Average.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTO&amp;lt;/code&amp;gt;&lt;br /&gt;
:Retransmission Timeout, &amp;lt;code&amp;gt;max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Timer-bound symbols &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer, ACK delay) and &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer,&lt;br /&gt;
retransmission window) are defined in [[#8. Retransmission|Section 8]]; &amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; (Maximum&lt;br /&gt;
Packet Lifetime) is introduced in [[#2.1. Per-flow state|Section 2.1]] (the &amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; field)&lt;br /&gt;
with heritage in [[#15. Heritage and adopted techniques|Section 15]].&lt;br /&gt;
&lt;br /&gt;
Wire-format diagrams follow the IETF convention: bit 0 is the&lt;br /&gt;
leftmost (most significant) bit and fields are in network byte&lt;br /&gt;
order unless stated otherwise.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1. Wire format ==&lt;br /&gt;
&lt;br /&gt;
=== 1.1. PCI header ===&lt;br /&gt;
&lt;br /&gt;
Fixed 16-octet base Protocol-Control Information (PCI) header&lt;br /&gt;
prefixed to every FRCP packet (RFC convention: bit 0 leftmost,&lt;br /&gt;
most-significant bit first).  All multi-byte fields are in network byte order. DATA packets on&lt;br /&gt;
stream-mode flows carry an additional 8-octet extension (see&lt;br /&gt;
[[#1.5. Stream PCI extension|Section 1.5]]); SACK and RTTP carry their own payloads after the&lt;br /&gt;
base PCI.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |             flags             |              hcs              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            window                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            seqno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            ackno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                     payload (variable) ...&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt;&lt;br /&gt;
:feature/type bitmap (see [[#1.2. Flag bits|Section 1.2]]).&lt;br /&gt;
;&amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;&lt;br /&gt;
:CRC-16-CCITT-FALSE Header Check Sequence (HCS) over &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; (+ stream extension when present); the two octets of the &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt; field itself are omitted from the CRC input.  Verified on receive before any flag-driven dispatch.&lt;br /&gt;
;&amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt;&lt;br /&gt;
:receiver-advertised right window edge (valid iff FC).&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-flow sequence number.&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;&lt;br /&gt;
:cumulative Acknowledgement (ACK) (valid iff ACK).&lt;br /&gt;
&lt;br /&gt;
A single packet can simultaneously carry DATA + ACK + FC (Flow&lt;br /&gt;
Control) + RXM (Retransmission) by ORing flag bits; the PCI&lt;br /&gt;
multiplexes control on the same wire frame in the spirit of SCTP&lt;br /&gt;
chunk bundling (RFC 9260 sec. 6.10) and QUIC frame multiplexing&lt;br /&gt;
(RFC 9000 sec. 12.4).  DATA-bearing packets carry the caller&#039;s&lt;br /&gt;
payload after the PCI; SACK (Selective Acknowledgement) and RTTP&lt;br /&gt;
(Round-Trip Time Probe) carry their own typed payloads after the&lt;br /&gt;
PCI.&lt;br /&gt;
&lt;br /&gt;
Optional framing (per-flow, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]]).  On the wire, the&lt;br /&gt;
order from inside out is:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Layer !! Scope&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| The FRCP packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body + CRC-32 ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| CRC-32 covers the body only (PCI is in HCS); appended iff &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; on DATA, or on every SACK packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ AEAD-wrap of above ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| Iff Authenticated Encryption with Associated Data (AEAD) is enabled.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* HCS in the PCI covers the header fields on every packet and is verified before any flag-driven dispatch.&lt;br /&gt;
* The CRC-32 trailer (IEEE 802.3 / zlib reflected polynomial &amp;lt;code&amp;gt;0xEDB88320&amp;lt;/code&amp;gt;, init &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;, xor-out &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;) covers the body on DATA when &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; and on every SACK packet; the trailer is written as a raw &amp;lt;code&amp;gt;uint32_t&amp;lt;/code&amp;gt; (the same convention as &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;: opaque on the wire as long as both peers run compatible builds).  The PCI is not under the CRC (Cyclic Redundancy Check) because the HCS already protects it.  It is appended before AEAD encryption and therefore rides inside the AEAD wrap when both are active; the AEAD tag (~2^-128 forgery probability) dominates the CRC (~2^-32) for integrity in that mode but the CRC trailer is currently retained.&lt;br /&gt;
* When encryption is enabled, the entire (possibly-CRC&#039;d) FRCP packet is wrapped with AEAD inside the shared-memory packet buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt;); the packet grows by the AEAD overhead, namely a leading nonce / Initialization Vector (IV) of &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_ivsz&amp;lt;/code&amp;gt;) and a trailing authentication tag of &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_tagsz&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Both CRC and AEAD are layered around the FRCP wire format and are not visible to the FRCP machinery itself.&lt;br /&gt;
&lt;br /&gt;
=== 1.2. Flag bits ===&lt;br /&gt;
&lt;br /&gt;
Flag bits are numbered most-significant-bit first to match the wire&lt;br /&gt;
diagram (bit numbering per [[#1.1. PCI header|Section 1.1]]; bit 0 is the MSB of the&lt;br /&gt;
16-bit &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; field and lands at wire-position 0 in network byte&lt;br /&gt;
order).  Bits 13..15 are reserved and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Bit !! Mask !! Name !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| 0 || &amp;lt;code&amp;gt;0x8000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt; || Carries caller payload&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &amp;lt;code&amp;gt;0x4000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DRF&amp;lt;/code&amp;gt; || Data Run Flag: start of a fresh run&lt;br /&gt;
|-&lt;br /&gt;
| 2 || &amp;lt;code&amp;gt;0x2000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt; || Acknowledgement: &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field valid&lt;br /&gt;
|-&lt;br /&gt;
| 3 || &amp;lt;code&amp;gt;0x1000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt; || Negative ACK; &amp;lt;code&amp;gt;seqno = arrival_seqno-1&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || &amp;lt;code&amp;gt;0x0800&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt; || Flow Control: &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; field valid (&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || &amp;lt;code&amp;gt;0x0400&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt; || Rendezvous probe (window-closed)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || &amp;lt;code&amp;gt;0x0200&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; || First Fragment (role bit 0; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || &amp;lt;code&amp;gt;0x0100&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; || Last Fragment (role bit 1; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 8 || &amp;lt;code&amp;gt;0x0080&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RXM&amp;lt;/code&amp;gt; || Retransmission&lt;br /&gt;
|-&lt;br /&gt;
| 9 || &amp;lt;code&amp;gt;0x0040&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt; || Selective ACK block list in payload&lt;br /&gt;
|-&lt;br /&gt;
| 10 || &amp;lt;code&amp;gt;0x0020&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt; || RTT Probe / echo (payload follows)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || &amp;lt;code&amp;gt;0x0010&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt; || Keepalive&lt;br /&gt;
|-&lt;br /&gt;
| 12 || &amp;lt;code&amp;gt;0x0008&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt; || End-of-stream marker (stream mode)&lt;br /&gt;
|-&lt;br /&gt;
| 13-15 || -- || -- || Reserved (MUST be zero)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair encodes the fragment role of a DATA-bearing&lt;br /&gt;
Service Data Unit (SDU), SCTP-style begin/end flags (RFC 9260&lt;br /&gt;
sec. 3.3.1):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FFGM !! LFGM !! Role&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || Sole / un-fragmented SDU (begin AND end)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || First fragment of a multi-fragment SDU&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Middle fragment&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Last fragment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Each fragment is carried in its own FRCP packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;;&lt;br /&gt;
FRTX (the FRCT Retransmission service mode, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]])&lt;br /&gt;
recovers individual fragments via the normal Retransmission Timeout&lt;br /&gt;
(RTO) / SACK / Recent Acknowledgement (RACK, RFC 8985) path.  The&lt;br /&gt;
receiver reassembles the SDU at consume time once the contiguous&lt;br /&gt;
[FIRST .. LAST] run has fully arrived.  On non-DATA packets the role&lt;br /&gt;
bits are unused and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
In stream mode (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;, see [[#16. Stream-mode flows|Section 16]]) there are&lt;br /&gt;
no SDU boundaries to encode, so &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST&lt;br /&gt;
be transmitted as zero.  End-of-stream uses a dedicated bit (&amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;,&lt;br /&gt;
bit 12) carried on a 0-byte DATA packet, emitted at write-half close&lt;br /&gt;
(&amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;FLOWFRDONLY&amp;lt;/code&amp;gt;), during linger drain, and at &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;;&lt;br /&gt;
emission is idempotent (first call wins).  After contiguous delivery&lt;br /&gt;
of the FIN-bearing slot, the receiver latches &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt; at the FIN&#039;s&lt;br /&gt;
start offset; &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns 0 (end-of-file, &amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) once buffered&lt;br /&gt;
bytes have been drained up to &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt;.  Per-byte position is&lt;br /&gt;
carried by the [start, end) extension ([[#1.5. Stream PCI extension|Section 1.5]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.3. SACK payload ===&lt;br /&gt;
&lt;br /&gt;
A SACK packet has the &amp;lt;code&amp;gt;FRCT_ACK | FRCT_FC | FRCT_SACK&amp;lt;/code&amp;gt; flag bits set&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI, the&lt;br /&gt;
payload is a 2-octet block count (network byte order), 2 octets of&lt;br /&gt;
padding to 4-byte align the block list, then &amp;lt;code&amp;gt;n_blocks&amp;lt;/code&amp;gt; pairs of&lt;br /&gt;
32-bit start/end seqnos describing &#039;&#039;present&#039;&#039; (received) ranges&lt;br /&gt;
above the cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |           n_blocks            |        padding (2 octets)     |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[0]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            end[0]                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[1]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
                       ... n_blocks pairs total ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;n_blocks &amp;amp;lt;= SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; (2048).  The per-flow effective cap is&lt;br /&gt;
further bounded by &amp;lt;code&amp;gt;(frag_mtu - PCI - 4) / 8&amp;lt;/code&amp;gt; blocks per packet; SACK&lt;br /&gt;
packets carry no stream extension, so PCI here is the 16-octet base&lt;br /&gt;
header even on stream-mode flows.&lt;br /&gt;
&lt;br /&gt;
Wire invariant: every block produced by the receiver, except an&lt;br /&gt;
optional leading Duplicate SACK (D-SACK) block as described below,&lt;br /&gt;
describes a range strictly above the cumulative ACK carried in the&lt;br /&gt;
PCI &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field (&amp;lt;code&amp;gt;after(start[i], ackno)&amp;lt;/code&amp;gt;).  This makes the D-SACK&lt;br /&gt;
convention below unambiguous; the receiver-side builder MUST&lt;br /&gt;
preserve it.&lt;br /&gt;
&lt;br /&gt;
Duplicate SACK (D-SACK, RFC 2883) is signalled in-band: no flag&lt;br /&gt;
bit, no extra framing.  Modular seqno arithmetic uses the&lt;br /&gt;
&amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators defined in the Notation block.&lt;br /&gt;
&lt;br /&gt;
Encoding.  When a duplicate is observed the receiver arms a&lt;br /&gt;
single-slot pending report (&amp;lt;code&amp;gt;dsack_seqno&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;dsack_valid&amp;lt;/code&amp;gt;,&lt;br /&gt;
latest-wins across multiple arms before the next emit).  On the&lt;br /&gt;
next outbound SACK the receiver prepends &amp;lt;code&amp;gt;block[0] = [dsack_seqno,&lt;br /&gt;
dsack_seqno + 1)&amp;lt;/code&amp;gt; - always a one-&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; range - and clears the&lt;br /&gt;
flag.  The three arm sites are listed in [[#10. Cumulative + selective ACK|Section 10]]; case-1 sites&lt;br /&gt;
yield &amp;lt;code&amp;gt;dsack_seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt; (the next &amp;lt;code&amp;gt;pci.ackno&amp;lt;/code&amp;gt;), and the&lt;br /&gt;
case-2 site (&amp;lt;code&amp;gt;rq_accept&amp;lt;/code&amp;gt; conflict) yields &amp;lt;code&amp;gt;dsack_seqno&amp;lt;/code&amp;gt; in&lt;br /&gt;
&amp;lt;code&amp;gt;[rcv_cr.lwe, rcv_cr.rwe)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Detection.  The sender classifies &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; by its relation to&lt;br /&gt;
&amp;lt;code&amp;gt;pci.ackno&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
;case 1 (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;before(blocks[0].start, pci.ackno)&amp;lt;/code&amp;gt; AND &amp;lt;code&amp;gt;pci.ackno - blocks[0].start &amp;amp;lt;= MAX_DSACK_LAG&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;== RQ_SIZE&amp;lt;/code&amp;gt;).  The lag bound rejects stale or spoofed reports beyond one receive window.&lt;br /&gt;
;case 2 (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;blocks[0]&amp;lt;/code&amp;gt; is a sub-range (with at least one endpoint differing) of some &amp;lt;code&amp;gt;blocks[i&amp;amp;gt;0]&amp;lt;/code&amp;gt; - i.e. the same packet&#039;s remaining SACK blocks already describe the duplicated &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; as received.&lt;br /&gt;
&lt;br /&gt;
On detect, the sender:&lt;br /&gt;
&lt;br /&gt;
* bumps &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; by 1, capped at &amp;lt;code&amp;gt;REO_WND_MULT_MAX&amp;lt;/code&amp;gt; (= 20), per RFC 8985 sec. 6.2 step 4;&lt;br /&gt;
* snapshots &amp;lt;code&amp;gt;dsack_lwe_snap = snd_cr.lwe&amp;lt;/code&amp;gt;, resetting the 16-cum-ACK halving counter so the multiplier doesn&#039;t decay while D-SACK evidence is still arriving;&lt;br /&gt;
* excludes &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; from the gap-marking loop (&amp;lt;code&amp;gt;n_real = n - 1&amp;lt;/code&amp;gt;), so a D-SACK alone never enters NewReno-careful recovery (see [[#8. Retransmission|Section 8]]); only non-D-SACK blocks count as gaps.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; halving cadence (once per 16 cumulatively-ACK&#039;d&lt;br /&gt;
seqnos since the most-recent D-SACK arrival or halve event) and&lt;br /&gt;
the reset-to-1 on a HoL RTO fire are both per the same RFC 8985&lt;br /&gt;
clause.  The clamp-and-skip path in the regular SACK-mark loop is&lt;br /&gt;
incidentally idempotent on any leftover case-1 or case-2 block&lt;br /&gt;
(&amp;lt;code&amp;gt;start &amp;amp;lt; snd_cr.lwe&amp;lt;/code&amp;gt; clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; and the inner loop&lt;br /&gt;
skips &amp;lt;code&amp;gt;k == snd_cr.lwe&amp;lt;/code&amp;gt;; case-2 re-NULLs slots already marked&lt;br /&gt;
received by later blocks), so block[0] is harmless even when fed&lt;br /&gt;
to the loop.&lt;br /&gt;
&lt;br /&gt;
=== 1.4. RTTP payload ===&lt;br /&gt;
&lt;br /&gt;
An RTTP (Round-Trip Time Probe) packet has only the &amp;lt;code&amp;gt;FRCT_RTTP&amp;lt;/code&amp;gt; flag&lt;br /&gt;
set (bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI,&lt;br /&gt;
the payload is 24 octets (packed):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          probe_id                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          echo_id                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +                  nonce (16 octets, echoed verbatim)           +&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:sender counter, 0 on reply, 0 reserved.&lt;br /&gt;
;&amp;lt;code&amp;gt;echo_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:peer&#039;s &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;, 0 on outbound probe.&lt;br /&gt;
;&amp;lt;code&amp;gt;nonce&amp;lt;/code&amp;gt;&lt;br /&gt;
:random, echoed unmodified, memcmp&#039;d to defeat spoof.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.5. Stream PCI extension ===&lt;br /&gt;
&lt;br /&gt;
A stream-mode flow (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;) carries an extra&lt;br /&gt;
8-octet extension after the 16-octet base PCI on every DATA packet&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            start                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                             end                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset of the first payload byte in the stream.&lt;br /&gt;
;&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset one past the last payload byte; &amp;lt;code&amp;gt;end - start&amp;lt;/code&amp;gt; equals the on-wire payload length.&lt;br /&gt;
&lt;br /&gt;
Total stream-mode PCI for DATA packets is 24 octets (16 base + 8&lt;br /&gt;
extension); control packets (SACK, RTTP, bare ACK, KA, etc.) retain&lt;br /&gt;
the 16-octet base PCI.  Stream mode MUST be negotiated at flow&lt;br /&gt;
allocation; the extension is present iff stream mode is in use,&lt;br /&gt;
never on a per-packet basis.  Both peers MUST treat &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; as&lt;br /&gt;
monotonic 32-bit byte offsets; when a slot reaches the head of the&lt;br /&gt;
contiguous run with &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; not equal to the prior packet&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; the&lt;br /&gt;
slot is silently dropped at delivery time ([[#16. Stream-mode flows|Section 16]]) rather&lt;br /&gt;
than rejected at stash.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC STREAM-frame reassembly model (RFC 9000 sec. 19.8):&lt;br /&gt;
each packet carries its packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (this PCI&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; field) and a&lt;br /&gt;
separate stream byte position (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;).  Separating the two&lt;br /&gt;
avoids TCP&#039;s conflation of packet identity with byte position which&lt;br /&gt;
forces Karn&#039;s algorithm for Round-Trip Time (RTT) sampling (no RTT&lt;br /&gt;
sample on retransmits, RFC 6298 sec. 3); FRCP applies the&lt;br /&gt;
Karn-equivalent gate via a combination of per-packet &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;,&lt;br /&gt;
per-slot &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; flags, and a sample-fence &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; (see [[#2.1. Per-flow state|Section 2.1]]&lt;br /&gt;
and [[#12. RTT estimation|Section 12]]).  FRCP&#039;s fixed-32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; wrap at 4 GiB of&lt;br /&gt;
wire bytes, narrower than QUIC&#039;s 62-bit varint offset (cf. RFC 9000&lt;br /&gt;
sec. 16); the on-wire wrap is handled by the same modular &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;&lt;br /&gt;
/ &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators ([[#1.3. SACK payload|Section 1.3]]) FRCP uses for seqnos, which&lt;br /&gt;
remain unambiguous as long as the in-flight byte window stays&lt;br /&gt;
strictly under 2 GiB (the half-range of the signed-int32 difference&lt;br /&gt;
in &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;).  The default per-flow ring is 1 MiB; the&lt;br /&gt;
implementation caps &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; at 128 MiB (&amp;lt;code&amp;gt;FRCT_STREAM_RING_SZ_MAX&amp;lt;/code&amp;gt;),&lt;br /&gt;
well below the 2 GiB half-range bound.  The runtime byte counters&lt;br /&gt;
exposed via FUSE (Filesystem in Userspace) in the Ouroboros&lt;br /&gt;
Resource Information Base (RIB, a virtual-filesystem introspection&lt;br /&gt;
bridge) are platform &amp;lt;code&amp;gt;size_t&amp;lt;/code&amp;gt; and do not wrap on 64-bit hosts.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2. Per-flow state and service modes ==&lt;br /&gt;
&lt;br /&gt;
=== 2.1. Per-flow state ===&lt;br /&gt;
&lt;br /&gt;
Each flow keeps a sender control record and a receiver control&lt;br /&gt;
record:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: oldest unacked &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (cumulative ACK boundary as seen by sender); rcv: next in-order &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; expected&lt;br /&gt;
;&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: peer-advertised right window edge; rcv: locally-advertised right window edge&lt;br /&gt;
;&amp;lt;code&amp;gt;cflags&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-direction feature flags: retransmission (&amp;lt;code&amp;gt;FRCTFRTX&amp;lt;/code&amp;gt;), receiver flow control (&amp;lt;code&amp;gt;FRCTFRESCNTL&amp;lt;/code&amp;gt;), linger-on-close (&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt;); see &amp;lt;code&amp;gt;&amp;amp;lt;ouroboros/fccntl.h&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: next &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; to send; rcv: force-ACK trigger - set on a stale or dup DATA so the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; emits a fresh cumulative ACK&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; counter for standalone ACK-bearing control packets (delayed ACK, SACK, final ACK on dealloc); not bumped on piggybacked ACK riding a DATA packet (which uses the DATA &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;).  Used by wire-dup ACK detection; rcv: incoming-ACK dedup tracker&lt;br /&gt;
;&amp;lt;code&amp;gt;act&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:last activity (used by inactivity / DRF)&lt;br /&gt;
;&amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:inactivity threshold; sender = &amp;lt;code&amp;gt;3*mpl + a + r + 1s&amp;lt;/code&amp;gt;, receiver = &amp;lt;code&amp;gt;2*mpl + a + r + 1s&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;mpl&amp;lt;/code&amp;gt; is the Maximum Packet Lifetime (delta-t terminology; see [[#15. Heritage and adopted techniques|Section 15]]); &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; are the FRCT a-timer and r-timer bounds (see [[#8. Retransmission|Section 8]]).  The asymmetry is load-bearing for pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
The sender holds a per-slot ring &amp;lt;code&amp;gt;snd_slots[RQ_SIZE]&amp;lt;/code&amp;gt; keyed by&lt;br /&gt;
&amp;lt;code&amp;gt;(seqno mod RQ_SIZE)&amp;lt;/code&amp;gt;.  Each slot tracks its retransmit entry (&amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;),&lt;br /&gt;
last-send timestamp, and retransmit flag bits: &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (a&lt;br /&gt;
retransmit is pending or has fired, gates the next RTT sample&lt;br /&gt;
under Karn) and &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (one-shot fast-retransmit staged for&lt;br /&gt;
this loss event).&lt;br /&gt;
&lt;br /&gt;
The receiver holds a parallel reorder ring &amp;lt;code&amp;gt;rcv_slots[RQ_SIZE]&amp;lt;/code&amp;gt;&lt;br /&gt;
(referred to as &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; in prose) holding stashed out-of-order&lt;br /&gt;
packet-buffer indexes; both FRTX and best-effort flows share this&lt;br /&gt;
path.  The invariant &amp;lt;code&amp;gt;rwe - lwe &amp;amp;lt;= RQ_SIZE&amp;lt;/code&amp;gt; holds: on each consume&lt;br /&gt;
the receiver advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; by the consumed count, capping the&lt;br /&gt;
receive window at &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; slots.&lt;br /&gt;
&lt;br /&gt;
A separate fence variable &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; is bumped on every retransmit&lt;br /&gt;
(timer-fire, SACK-driven, fast-rxm, NACK-driven) and on every&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; ([[#4. Sequence-number rotation (DRF)|Section 4]]) to mark the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; range whose RTT samples&lt;br /&gt;
MUST be discarded.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 2.2. Service modes (orthogonal axes) ===&lt;br /&gt;
&lt;br /&gt;
FRCP exposes its wire features as a vector of independent QoS&lt;br /&gt;
axes selected at flow allocation time.  All flows go through the&lt;br /&gt;
same &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive; the &amp;lt;code&amp;gt;qosspec_t&amp;lt;/code&amp;gt; passed&lt;br /&gt;
in determines which protocol machinery engages on the wire.  This&lt;br /&gt;
contrasts with the POSIX BSD socket model where TCP and UDP&lt;br /&gt;
require different socket types (&amp;lt;code&amp;gt;SOCK_STREAM&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;SOCK_DGRAM&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
The axes:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = unordered (no FRCP engagement: raw datagrams, no PCI on the wire, UDP-equivalent at this layer); 1 = message-ordered (FRCP engaged; SDU boundaries preserved across fragmentation); 2 = stream (byte-oriented, no SDU boundaries; FRTX required)&lt;br /&gt;
;&amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = lossless service requested: FRTX retransmit machinery engages ([[#8. Retransmission|Section 8]]); MUST be 0 for &amp;lt;code&amp;gt;service=2&amp;lt;/code&amp;gt;.  Non-zero = best-effort, FRTX off.&lt;br /&gt;
;&amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bit Error Rate tolerance.  0 = error-free service requested: a CRC trailer is appended after the body of DATA packets and verified on receive (added / checked outside the FRCP PCI; see [[#1.1. PCI header|Section 1.1]]).  Non-zero = peer accepts errors; trailer omitted.  SACK control packets carry a CRC32 trailer regardless of &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; gate applies to DATA only.&lt;br /&gt;
;&amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt;&lt;br /&gt;
:Peer-timeout (ms); 0 disables the keepalive timer.  Independent of FRCP engagement.&lt;br /&gt;
&lt;br /&gt;
Encryption is a separate per-flow attribute set at flow setup;&lt;br /&gt;
when enabled it wraps the FRCP packet (PCI + body, plus the CRC&lt;br /&gt;
trailer if any) under AEAD, expanding the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; by &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt;&lt;br /&gt;
octets (nonce / tag).  The CRC trailer is currently kept inside&lt;br /&gt;
the AEAD wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
&lt;br /&gt;
Reachable combinations exported by &amp;lt;code&amp;gt;include/ouroboros/qos.h&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Cube !! &amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; !! Engaged&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw&amp;lt;/code&amp;gt; || 0 || 1 || 1 || Raw passthrough&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw_safe&amp;lt;/code&amp;gt; || 0 || 1 || 0 || Raw + CRC trailer&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; || 1 || 1 || 1 || FRCP, no FRTX, no CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt_safe&amp;lt;/code&amp;gt; || 1 || 1 || 0 || FRCP, no FRTX, CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_msg&amp;lt;/code&amp;gt; || 1 || 0 || 0 || FRCP + FRTX&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_stream&amp;lt;/code&amp;gt; || 2 || 0 || 0 || FRCP + FRTX, stream&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Forced couplings actually enforced by the public API:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;service == SVC_STREAM&amp;lt;/code&amp;gt; (2) requires &amp;lt;code&amp;gt;loss == 0&amp;lt;/code&amp;gt;; &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;flow_accept&amp;lt;/code&amp;gt; reject the pair otherwise with &amp;lt;code&amp;gt;-EINVAL&amp;lt;/code&amp;gt;.&lt;br /&gt;
* FRTX requires FRCP engagement (&amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;); requesting &amp;lt;code&amp;gt;loss = 0&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;service = SVC_RAW&amp;lt;/code&amp;gt; is structurally a no-op because no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; is created.&lt;br /&gt;
* The &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; build flag globally forces &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;.  Note: this flag defaults to ON, so default builds ship with CRC disabled until &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; is set to OFF.&lt;br /&gt;
&lt;br /&gt;
Caveat: the API does NOT force &amp;lt;code&amp;gt;ber = 0&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; has &amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;, which means the PCI&lt;br /&gt;
itself is not CRC-protected on that cube; the HCS ([[#1.1. PCI header|Section 1.1]])&lt;br /&gt;
remains the only integrity check on the header.&lt;br /&gt;
&lt;br /&gt;
The FRCP-no-FRTX regime (&amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;loss &amp;amp;gt; 0&amp;lt;/code&amp;gt;) is meaningful&lt;br /&gt;
and live: sequence numbering, in-order delivery, flow-control&lt;br /&gt;
advertisement, KA, DRF rotation, and SDU fragmentation /&lt;br /&gt;
reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]) all run.  Lost packets are dropped&lt;br /&gt;
rather than retransmitted; a permanently-lost mid-fragment is&lt;br /&gt;
dropped via skip-past-gap once a later SDU is visible in the&lt;br /&gt;
reorder ring.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. Protocol parameters ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Slot ring / rcv window width&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;START_WINDOW&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Initial &amp;lt;code&amp;gt;rwe-lwe&amp;lt;/code&amp;gt; after rotate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MAX(250 us build-tunable, 1&amp;amp;lt;&amp;amp;lt;RXMQ_RES)&amp;lt;/code&amp;gt;; per-flow via &amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;FRCTSRTOMIN&amp;lt;/code&amp;gt;).  Default ~1 ms with &amp;lt;code&amp;gt;RXMQ_RES=20&amp;lt;/code&amp;gt;. || RTO floor; also floored at the retransmit-wheel resolution (~1 ms by default).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt; || 20 || Backoff shift cap&lt;br /&gt;
|-&lt;br /&gt;
| RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MIN(reo_wnd_mult * min_RTT/4, SRTT)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; scales on D-SACK, cap 20 || Reorder window; per RFC 8985 sec. 6.2; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per sec. 6.2 step 4&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; || 300 s (5 min, Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; windowed re-anchor&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_WND_MULT_MAX&amp;lt;/code&amp;gt; || 20 (RFC 8985 sec. 6.2 step 4) || &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_DECAY_PKTS&amp;lt;/code&amp;gt; || 16 (RFC 8985 sec. 6.2 step 4 / &amp;lt;code&amp;gt;RACK.reo_wnd_persist&amp;lt;/code&amp;gt;) || Fresh-ACK&#039;d seq count per halving&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_DSACK_LAG&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || D-SACK sanity cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTT_QUARANTINE&amp;lt;/code&amp;gt; || 32 (&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; steps) || NewReno gate pad&lt;br /&gt;
|-&lt;br /&gt;
| SACK rate-limit || &amp;lt;code&amp;gt;SACK_MIN_GAP_NS&amp;lt;/code&amp;gt; (250 us, fixed) || Min SACK gap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; || 2048 (wire cap; per-flow capped at &amp;lt;code&amp;gt;(frag_mtu-PCI-4)/8&amp;lt;/code&amp;gt;) || Per-SACK block cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; || 32 || Per-pass staged retransmit cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; || 3 (RFC 8985 default) || Hybrid fast-rxm trigger ([[#8. Retransmission|Section 8]])&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MDEV_MUL&amp;lt;/code&amp;gt; || 2 (build-tunable via &amp;lt;code&amp;gt;FRCT_RTO_MDEV_MULTIPLIER&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; shift in &amp;lt;code&amp;gt;RTO = srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| RTTP nonce || 16 octets || Echoed verbatim&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; || 8 || In-flight probes&lt;br /&gt;
|-&lt;br /&gt;
| RTT clamp || &amp;lt;code&amp;gt;16 * srtt&amp;lt;/code&amp;gt; || Probe-sample upper bound (ACK-derived RTT samples gated by Karn / recovery only)&lt;br /&gt;
|-&lt;br /&gt;
| Cold-probe cadence || 100 ms (rx-driven; see [[#12. RTT estimation|Section 12]]) || Pre-&amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; RTTP rate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt; || 100 ms || RDVS emit cadence&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; || 1 s || RDVS give-up&lt;br /&gt;
|-&lt;br /&gt;
| Delayed-ACK fire || &amp;lt;code&amp;gt;2 * TICTIME&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt; = FRCT tick granularity, default 5 ms; &amp;lt;code&amp;gt;2*TICTIME = 10 ms&amp;lt;/code&amp;gt; by default) || Fired after the first in-order DATA arrival; tick is build-tunable&lt;br /&gt;
|-&lt;br /&gt;
| NACK send cooldown || &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; when an &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; sample exists, else 100 ms || Pre-DRF NACK rate-limit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_SDU&amp;lt;/code&amp;gt; || 1 MiB || Max reassembled SDU; configurable per flow&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The per-flow fragment Maximum Transmission Unit (MTU) is computed&lt;br /&gt;
at flow setup from the lower IPCP&#039;s mtu minus encryption&lt;br /&gt;
&amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; and CRC trailer; there is no FRCT-level default or&lt;br /&gt;
environment-variable override.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 4. Sequence-number rotation (DRF) ==&lt;br /&gt;
&lt;br /&gt;
The DRF (Data Run Flag) bit on an outbound packet means &amp;quot;this is&lt;br /&gt;
the start of a fresh data run&amp;quot; and is set whenever the sender has&lt;br /&gt;
nothing in flight (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Independently of that, if the sender has been idle longer than&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.inact&amp;lt;/code&amp;gt; AND the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;),&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; rolls a random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; before the send and&lt;br /&gt;
resets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    snd_cr.seqno  = random()&lt;br /&gt;
    snd_cr.lwe    = snd_cr.seqno&lt;br /&gt;
    snd_cr.rwe    = snd_cr.seqno + START_WINDOW&lt;br /&gt;
    rtt_lwe       = snd_cr.seqno&lt;br /&gt;
    in_recovery   = false   (recovery state, see Section 8)&lt;br /&gt;
    recovery_high = snd_cr.seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The receiver, on observing rcv-side inactivity&lt;br /&gt;
(&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), requires a DRF on the next&lt;br /&gt;
DATA packet; otherwise it replies with a rate-limited NACK (see&lt;br /&gt;
below).  Non-DATA control packets pass through without the DRF&lt;br /&gt;
requirement.  On DRF the receiver releases the &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots and&lt;br /&gt;
rebases&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    rcv_cr.lwe   = seqno&lt;br /&gt;
    rcv_cr.rwe   = seqno + RQ_SIZE&lt;br /&gt;
    rcv_cr.seqno = seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the inactive packet has DATA but no DRF, a rate-limited NACK is&lt;br /&gt;
fired back to the sender (cooldown per [[#3. Protocol parameters|Section 3]]); non-DATA stale&lt;br /&gt;
arrivals fall through to normal processing (no NACK, no drop).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 5. Send path ==&lt;br /&gt;
&lt;br /&gt;
# If the SDU exceeds &amp;lt;code&amp;gt;(frag_mtu - data_hdr_len)&amp;lt;/code&amp;gt;, the caller (&amp;lt;code&amp;gt;dev.c&amp;lt;/code&amp;gt;) fans it out into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - data_hdr_len))&amp;lt;/code&amp;gt; fragments, each emitted via &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; as its own DATA packet with a per-fragment role ([[#7.2. Fragmentation and reassembly|Section 7.2]]); both FRTX and best-effort flows fragment.  Raw flows (no FRCP engagement, &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) carry no PCI and return &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; for any SDU larger than one packet at the layer below.  An SDU that fits in a single packet is sent as SOLE.  &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; reserves PCI head room; sets DATA, plus DRF when the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
# &amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; if past sender inactivity and the pipe is empty ([[#4. Sequence-number rotation (DRF)|Section 4]]).&lt;br /&gt;
# Advertise FC (&amp;lt;code&amp;gt;pci.window = frcti_advert_rwe(frcti)&amp;lt;/code&amp;gt;, i.e. &amp;lt;code&amp;gt;rcv_cr.rwe&amp;lt;/code&amp;gt; clamped to &amp;lt;code&amp;gt;rcv_cr.lwe + ring_seq_cap&amp;lt;/code&amp;gt; in stream mode) when the receiver side is recent: &amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt; rcv_cr.inact&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Reliable mode (FRTX): leave &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; where it is; reset the slot at &amp;lt;code&amp;gt;RQ_SLOT(seqno)&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;snd_slots[p].time = now&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_slots[p].flags = 0&amp;lt;/code&amp;gt;); queue an &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; (saves a packet copy, arms a wheel timer at &amp;lt;code&amp;gt;now + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;).  Piggyback ACK (&amp;lt;code&amp;gt;pci.ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) while the a-timer for the most recent received DATA packet has not yet expired (&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt;= t_a&amp;lt;/code&amp;gt;); on piggyback, set &amp;lt;code&amp;gt;rcv_cr.seqno = rcv_cr.lwe&amp;lt;/code&amp;gt; so the next delayed-ACK fire is suppressed.  See [[#8. Retransmission|Section 8]] for &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; semantics.&lt;br /&gt;
# Best-effort mode (no FRTX): advance &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; immediately (&amp;lt;code&amp;gt;snd_cr.lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_cr.rwe = snd_cr.lwe + RQ_SIZE&amp;lt;/code&amp;gt;); no retransmit state.  No send-side RTT probe is armed in this mode (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt; requires an in-flight &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, which best-effort never has); the rx-driven cold seeder in &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; is the only probe path.&lt;br /&gt;
# In reliable mode, optionally arm an RTT probe ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 6. Receive path ==&lt;br /&gt;
&lt;br /&gt;
=== 6.1. Early-exit dispatch ===&lt;br /&gt;
&lt;br /&gt;
Keepalive (KA), RTT probe (RTTP), pre-DRF NACK, and rendezvous&lt;br /&gt;
(RDVS) packets short-circuit out of &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; before the locked&lt;br /&gt;
main path; each handler takes its own lock internally.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
      incoming packet&lt;br /&gt;
            |&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | KA?     |---yes--&amp;gt; ka_rcv  ; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RTTP?   |---yes--&amp;gt; rttp_rcv; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | NACK?   |---yes--&amp;gt; nack_rcv; return  (see Section 9)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RDVS?   |---yes--&amp;gt; rdv_rcv ; return  (reply bare FC, ackno=0)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       acquire wrlock; enter locked main path&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt;&lt;br /&gt;
:refresh &amp;lt;code&amp;gt;t_ka_rcv&amp;lt;/code&amp;gt;, honour piggybacked ACK.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt;&lt;br /&gt;
:probe (echo back nonce) or echo (verify nonce, sample RTT).&lt;br /&gt;
;&amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:pre-DRF, sender-side handler.  See [[#9. Pre-DRF NACK|Section 9]].&lt;br /&gt;
;&amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt;&lt;br /&gt;
:reply with a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;); &amp;lt;code&amp;gt;rdlock&amp;lt;/code&amp;gt; only.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 6.2. Locked main path ===&lt;br /&gt;
&lt;br /&gt;
Steps below run with the per-flow &amp;lt;code&amp;gt;frcti.lock&amp;lt;/code&amp;gt; held for writing&lt;br /&gt;
(&amp;lt;code&amp;gt;pthread_rwlock_wrlock&amp;lt;/code&amp;gt;) unless noted.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;&lt;br /&gt;
:Only meaningful when the receive side is stale.  On DRF (Data Run Flag): release &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots, rebase &amp;lt;code&amp;gt;rcv_cr&amp;lt;/code&amp;gt;, continue.  On stale DATA without DRF: fire a pre-DRF NACK if cooldown allows ([[#9. Pre-DRF NACK|Section 9]]), then discard the packet; on cooldown, drop without sending a NACK (a pending cumulative ACK from &amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; may still go out).  Non-DATA, non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; pure-DRF stale arrivals fall through after the DRF rebase branch.&lt;br /&gt;
&lt;br /&gt;
;DATA-only act refresh&lt;br /&gt;
:Refresh &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; only when &amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt; is set, so that non-DATA packets never block the next DRF rebase.&lt;br /&gt;
&lt;br /&gt;
;Wire-dup gate&lt;br /&gt;
:Before flag-driven dispatch, drop wire-duplicate ACKs and wire-duplicate DATA (&amp;lt;code&amp;gt;is_dup_ack&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt;).  The DATA check is bypassed for &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;-bearing arrivals so the piggybacked ACK / SACK / FC carried on a retransmitted DATA at an already-ACK&#039;d &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is still applied; the stale-in-window branch below then drops the packet.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Drop ACKs whose &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; falls outside &amp;lt;code&amp;gt;(snd_cr.lwe, snd_cr.seqno]&amp;lt;/code&amp;gt;.  If &amp;lt;code&amp;gt;ackno == snd_cr.lwe&amp;lt;/code&amp;gt; (non-advancing cumulative ACK), drive RACK fast-retransmit consideration ([[#8. Retransmission|Section 8]]).  Otherwise advance &amp;lt;code&amp;gt;snd_cr.lwe = ackno&amp;lt;/code&amp;gt;, collapse &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; to 0 (Karn-gated by &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; on the just-acknowledged slot, the old head-of-line), reset &amp;lt;code&amp;gt;dup_thresh&amp;lt;/code&amp;gt; to 0, update &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the send-time of the slot at &amp;lt;code&amp;gt;ackno-1&amp;lt;/code&amp;gt; (consumed by RACK and SACK below), decay &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per RFC 8985 sec. 6.2 step 4, exit NewReno-careful recovery (see [[#8. Retransmission|Section 8]]) on &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt;, and feed an RTT sample if eligible ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Walk the block list.  For each block (a present range above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;) NULL out &amp;lt;code&amp;gt;snd_slots[k].rxm&amp;lt;/code&amp;gt;, clear the slot&#039;s per-send flags, and advance &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the latest send-time covered (the Forward Acknowledgement / fack equivalent, Mathis &amp;amp;amp; Mahdavi 1996); the first block whose start clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; skips this fack update so that a head-of-line clamp does not falsely advance fack.  For un-SACKed gaps below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt;, stage a retransmit per slot that is (1) still owned (&amp;lt;code&amp;gt;rxm != NULL&amp;lt;/code&amp;gt;), (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (the RFC 8985 sec. 6.2 hybrid trigger).  Mark the slot &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; and NULL the &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; at stage time.  Capped at &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; staged retransmits per receive pass; what&#039;s left rides the next SACK.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bump &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; (clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt;, never shrinks) and mark window open.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bounds-check &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; against window.  On stale-dup (&amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;), set &amp;lt;code&amp;gt;rcv_cr.seqno = seqno&amp;lt;/code&amp;gt; to force a fresh ACK on the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt;, then drop.  On accept: both FRTX and best-effort stash the packet-buffer index into &amp;lt;code&amp;gt;rq[seqno mod RQ_SIZE]&amp;lt;/code&amp;gt;.  Fragments stash unchanged - the role bits are inspected only at consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]).  On out-of-order arrival, build a SACK reply if not rate-limited (per [[#3. Protocol parameters|Section 3]]) and not deduplicated against the previous &amp;lt;code&amp;gt;(rcv_cr.lwe, n_blocks)&amp;lt;/code&amp;gt; pair; D-SACK reports always bypass the dedup.  If both rate-limit and dedup suppress the reply, neither SACK nor delayed-ACK fires (the sender picks up the gap on its next ACK).  On in-order arrival, arm the delayed-ACK timer.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; exit&lt;br /&gt;
:Releases the per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;), then calls &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; synchronously after the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; release to surface any pending cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 7. Read path and reassembly ==&lt;br /&gt;
&lt;br /&gt;
=== 7.1. Read path ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns a full reassembled SDU (Service Data Unit) via&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; on every FRCP SDU-mode flow (FRTX or best-effort);&lt;br /&gt;
stream-mode is covered in [[#16. Stream-mode flows|Section 16]].  An incomplete head-of-line&lt;br /&gt;
(HoL) run yields &amp;lt;code&amp;gt;-EAGAIN&amp;lt;/code&amp;gt;; an oversized run yields &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; (the&lt;br /&gt;
run is dropped so the flow does not stall).  On best-effort flows,&lt;br /&gt;
a permanently-lost mid-fragment is dropped as soon as a later&lt;br /&gt;
complete SDU becomes visible in the ring ([[#7.2. Fragmentation and reassembly|Section 7.2]] skip-past-&lt;br /&gt;
gap).&lt;br /&gt;
&lt;br /&gt;
Raw flows carry no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt;, so &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns the next pending&lt;br /&gt;
packet-buffer index directly, with no role-bit inspection.  (Raw&lt;br /&gt;
service is selected via &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt; at flow allocation,&lt;br /&gt;
which suppresses &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; creation.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_pdu_ready&amp;lt;/code&amp;gt; is the no-advance peek used by &amp;lt;code&amp;gt;fevent&amp;lt;/code&amp;gt; (the&lt;br /&gt;
Ouroboros flow-event multiplexer, the &amp;lt;code&amp;gt;poll(2)&amp;lt;/code&amp;gt;-equivalent on&lt;br /&gt;
flows).  It returns ready only when the head-of-line run is&lt;br /&gt;
complete and the lead packet (a Protocol Data Unit, here one FRCP&lt;br /&gt;
packet) is present at &amp;lt;code&amp;gt;rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt;; any other state&lt;br /&gt;
(including the best-effort skip-past-gap case) returns not ready,&lt;br /&gt;
and &amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; is left to drop the broken prefix and re-&lt;br /&gt;
inspect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 7.2. Fragmentation and reassembly ===&lt;br /&gt;
&lt;br /&gt;
Send side (&amp;lt;code&amp;gt;flow_write_frag&amp;lt;/code&amp;gt;).  An SDU larger than&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - PCI)&amp;lt;/code&amp;gt; is split into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - PCI))&amp;lt;/code&amp;gt;&lt;br /&gt;
fragments; each fragment is its own FRCP packet with its own&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a per-fragment role flag pair ([[#1.2. Flag bits|Section 1.2]]).  Roles are&lt;br /&gt;
assigned at emit time:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! i !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=0&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=n-1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| else || &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A mid-loop allocation or transmit failure may yield a partial&lt;br /&gt;
write: the call returns the bytes already enqueued (&amp;lt;code&amp;gt;off &amp;amp;gt; 0&amp;lt;/code&amp;gt;) or&lt;br /&gt;
the underlying error (&amp;lt;code&amp;gt;off == 0&amp;lt;/code&amp;gt;).  Best-effort flows fragment&lt;br /&gt;
identically; on the receiver, a partial run with a permanently-&lt;br /&gt;
lost fragment is dropped when a later complete SDU is visible in&lt;br /&gt;
the ring (see skip-past-gap below).  Raw flows carry no PCI and&lt;br /&gt;
refuse anything larger than the layer&#039;s user MTU (&amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Wire-level recovery is fragment-agnostic on FRTX flows: each&lt;br /&gt;
fragment&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; flows through SACK / RACK / RTO / NACK exactly&lt;br /&gt;
as for a SOLE DATA packet, and reassembly does not re-enter the&lt;br /&gt;
loss-detection path.  Best-effort flows run the same &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
machinery (DRF, FC, ACK piggyback, pre-DRF NACK emit) but queue&lt;br /&gt;
no &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; state at the sender, so a lost MID is unrecoverable;&lt;br /&gt;
skip-past-gap handles it (below).&lt;br /&gt;
&lt;br /&gt;
Receive side.  Fragments stash into &amp;lt;code&amp;gt;rq[seqno]&amp;lt;/code&amp;gt; unchanged; role bits&lt;br /&gt;
are read only at consume time.  &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;, called from&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt;, walks the ring starting at the oldest still-&lt;br /&gt;
undelivered &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;base = rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt; (equal to &amp;lt;code&amp;gt;rcv_cr.lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
only when no partial run is in progress; during a partial run &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
has already advanced past &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;).  It produces one of three&lt;br /&gt;
outcomes:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Outcome !! Cause&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELIVER (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]=SOLE&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;), or &amp;lt;code&amp;gt;rq[base]=FIRST&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; follows in slots &amp;lt;code&amp;gt;[base+1..base+n-1]&amp;lt;/code&amp;gt; with all intermediate roles in &amp;lt;code&amp;gt;{MID,FIRST,LAST}&amp;lt;/code&amp;gt; contiguous.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DROP (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; without a preceding &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;); a &amp;lt;code&amp;gt;FIRST..[non-LAST]..new-FIRST&amp;lt;/code&amp;gt; or new-&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; mid-run (drop the broken prefix with &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; = run length minus 1, so the new &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; stays); or, on best-effort flows, a gap at &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; later in the ring (drop up to the new run start).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; absent or &amp;lt;code&amp;gt;FIRST..[non-LAST]&amp;lt;/code&amp;gt; with no later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; in the ring (FRTX waits for retx; best-effort waits for arrival).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DELIVER&amp;lt;/code&amp;gt; triggers &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;: a scatter-gather &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; of the &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;&lt;br /&gt;
consecutive fragments at &amp;lt;code&amp;gt;rq[base..base+n-1]&amp;lt;/code&amp;gt; directly into the&lt;br /&gt;
caller&#039;s buffer; each per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;) is&lt;br /&gt;
released and &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; advances by &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; was already advanced&lt;br /&gt;
incrementally as each contiguous fragment arrived; &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;&lt;br /&gt;
only restores the fixed-width invariant &amp;lt;code&amp;gt;rwe == lwe + RQ_SIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
No intermediate reassembly buffer is allocated.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; past the broken prefix (releasing the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;s)&lt;br /&gt;
and pulls &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; up to the new trailing edge if needed; the next&lt;br /&gt;
consume retries from the new &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;.  Oversize or arithmetically&lt;br /&gt;
overflowing delivery (sum of fragment lengths &amp;amp;gt; &amp;lt;code&amp;gt;max_rcv_sdu&amp;lt;/code&amp;gt;, sum&lt;br /&gt;
&amp;amp;gt; caller&#039;s buffer, or running-sum overflow) also drops the run&lt;br /&gt;
with &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skip-past-gap (best-effort only).  On FRTX, a gap in the run means&lt;br /&gt;
&amp;quot;waiting for retransmit&amp;quot; and &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;.&lt;br /&gt;
On best-effort flows the gap is permanent, so &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;&lt;br /&gt;
scans forward in the ring for the next &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;; if one is&lt;br /&gt;
visible within &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;, it returns &amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; for the broken prefix and&lt;br /&gt;
the consume loop retries at the new &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;.  Memory hold is bounded&lt;br /&gt;
by &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;; the partial releases on the next consume call once a&lt;br /&gt;
later complete run exists.  Voice-like flows (one &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; per SDU)&lt;br /&gt;
see no extra wait: any later &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; makes the prior gap droppable&lt;br /&gt;
immediately.&lt;br /&gt;
&lt;br /&gt;
The choice to defer reassembly to consume time keeps the receive&lt;br /&gt;
path zero-copy: fragments stay in the shared-memory ring until&lt;br /&gt;
the application pulls, and the SDU lands directly in the caller&#039;s&lt;br /&gt;
buffer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 8. Retransmission ==&lt;br /&gt;
&lt;br /&gt;
FRCP is bounded by two delta-t-derived timers (Watson 1981, see&lt;br /&gt;
[[#15. Heritage and adopted techniques|Section 15]]):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer): upper bound on ACK delay.  An ACK for a received DATA packet MUST be emitted within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; of receipt; an attempt to send an ACK after the a-timer has expired is suppressed (the sender&#039;s RTO is already in motion).&lt;br /&gt;
* &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer): upper bound on retransmission.  A given DATA packet MUST NOT be retransmitted after &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; has elapsed since its first send (&amp;lt;code&amp;gt;t0&amp;lt;/code&amp;gt;); when the bound is hit, the flow is declared down (raising the Ouroboros asynchronous flow condition &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt;, which marks the flow dead to both endpoints) rather than retransmitted again.&lt;br /&gt;
&lt;br /&gt;
Each in-flight FRTX &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; owns one &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt;, armed in a hashed&lt;br /&gt;
timing wheel; the wheel deadline is the slot&#039;s next eligible&lt;br /&gt;
retransmit time.&lt;br /&gt;
&lt;br /&gt;
;RTO timer&lt;br /&gt;
:On fire (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;), re-emit with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, mark &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (Karn-suppress next ACK&#039;s RTT sample), and (for the head-of-line (HoL) slot only) bump &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; up to &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt;.  Wheel deadline is &amp;lt;code&amp;gt;t_send + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;.  Re-armed unless consumed.  The RTO timer also clears &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (re-arming fast-retransmit eligibility), resets &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; to 1 on a HoL fire (RFC 8985 sec. 6.2 step 4 reset clause), and marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; if its &amp;lt;code&amp;gt;frct_tx&amp;lt;/code&amp;gt; call fails.&lt;br /&gt;
&lt;br /&gt;
;r-timer guard&lt;br /&gt;
:Before any retransmit attempt, check &amp;lt;code&amp;gt;(now - t0)&amp;lt;/code&amp;gt; against &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;.  If exceeded, the slot is no longer eligible for retransmit.  Only the RTO timer (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;) treats r-timer expiry as terminal: it marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; (peer unreachable).  Fast-retransmit, SACK-driven retransmit, and NACK-driven head-of-line re-emit silently skip aged-out slots and defer the flow-down decision to the next RTO fire.&lt;br /&gt;
&lt;br /&gt;
;Fast retransmit (hybrid trigger, RFC 8985 sec. 6.2)&lt;br /&gt;
:On a non-advancing cumulative ACK with the scoreboard advanced, fire one fast retransmit when EITHER (a) the head-of-line slot&#039;s latest send is older than the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; ([[#3. Protocol parameters|Section 3]]) and not yet aged out, OR (b) the SACK &amp;lt;code&amp;gt;dup-thresh&amp;lt;/code&amp;gt; count above &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; reaches &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; (= 3, RFC 8985 sec. 6.2 step 4).  Fires at most once per non-advancing cumulative-ACK value, gated by &amp;lt;code&amp;gt;rack_fired_lwe&amp;lt;/code&amp;gt; (the &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; at which fast-retransmit last fired).  Set &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; on the slot (one-shot per-slot gate) and enter NewReno-style careful recovery (see NewReno below in this section).&lt;br /&gt;
:The RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; uses the RFC 8985 sec. 6.2 form &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor.  Before the first RTT sample seeds &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; falls back to &amp;lt;code&amp;gt;MIN(reo_wnd_mult * SRTT / 4, SRTT)&amp;lt;/code&amp;gt;, still floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt; (consistent with the windowed-minimum fallback described in [[#12. RTT estimation|Section 12]]).  &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt; is a windowed minimum over the last &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; = 5 min of RTT samples (matches the Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt; default) so a route change to a longer path eventually re-anchors the reorder window without relying on &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; growth alone.&lt;br /&gt;
&lt;br /&gt;
;SACK-driven retransmit&lt;br /&gt;
:For each gap below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt; whose slot is (1) still owned, (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (same hybrid as fast-retransmit, see [[#6.2. Locked main path|Section 6.2]]), re-emit.  Each SACK-driven retransmit re-arms a fresh &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; so a lost retransmit can still be recovered by its own RTO timer.&lt;br /&gt;
&lt;br /&gt;
;NewReno&lt;br /&gt;
:On entry, &amp;lt;code&amp;gt;recovery_high = snd_cr.seqno + RTT_QUARANTINE&amp;lt;/code&amp;gt;.  Exit when &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt; (the latter means everything sent has been acknowledged).  &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; also clears recovery.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 9. Pre-DRF NACK ==&lt;br /&gt;
&lt;br /&gt;
The two sides have different inactivity thresholds (&amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), so a receiver can detect &amp;quot;stale data run&amp;quot; before the sender&#039;s own DRF logic kicks in.  NACK is the receiver-driven nudge that asks the sender to re-transmit the head of the run.&lt;br /&gt;
&lt;br /&gt;
;Send (&amp;lt;code&amp;gt;frcti_nack_snd&amp;lt;/code&amp;gt;, called by &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;FRCT_INACT_NEED_NACK&amp;lt;/code&amp;gt;)&lt;br /&gt;
:When an incoming DATA packet has no DRF and rcv-side activity is older than &amp;lt;code&amp;gt;rcv_cr.inact&amp;lt;/code&amp;gt;, the receiver emits a bare packet with &amp;lt;code&amp;gt;flags = FRCT_NACK&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;seqno = arrival_seqno - 1&amp;lt;/code&amp;gt; (informational only, not consulted by the receive handler).  The cooldown in [[#3. Protocol parameters|Section 3]] rate-limits the burst.  Non-DATA non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; non-DATA DRF still rebases via the DRF branch.&lt;br /&gt;
&lt;br /&gt;
;Receive (&amp;lt;code&amp;gt;frcti_nack_rcv&amp;lt;/code&amp;gt;)&lt;br /&gt;
:Dispatched in the early-exit branch ([[#6.1. Early-exit dispatch|Section 6.1]]), before &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;.  The sender copies the head-of-line (HoL) &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; packet, marks the slot &amp;lt;code&amp;gt;SND_RTX | SND_FAST_RXM&amp;lt;/code&amp;gt; (Karn-suppress next ACK, one-shot fast-rxm gate), sets &amp;lt;code&amp;gt;rtt_lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, and re-emits via &amp;lt;code&amp;gt;fast_rxm_send&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt; and a refreshed &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;.  The original &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed - the NACK emit is additive to the normal retransmit machinery, not a replacement.  No-op if nothing is in flight, the HoL slot has aged past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, or the HoL &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; pointer has been cleared by SACK or RACK.&lt;br /&gt;
&lt;br /&gt;
NACK has exactly one role: lost first-of-run (DRF) packet recovery. Until the DRF packet arrives, the receiver cannot rebase its window, so any subsequent in-flight packets look stale to the receiver. The NACK fires the moment a stale receiver sees DATA without DRF, telling the sender to re-emit the head-of-line (DRF) packet at NACK-cooldown latency rather than waiting for the initial RTO (which is the configured default until &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded by the first probe round-trip).  Mid-stream loss is NOT NACK-driven; it is recovered by the sender&#039;s RTO, fast retransmit, and SACK-driven retransmit paths ([[#8. Retransmission|Section 8]]) only.&lt;br /&gt;
&lt;br /&gt;
The existing &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed on a NACK re-emit, so the RTO path remains the eventual fallback.&lt;br /&gt;
&lt;br /&gt;
== 10. Cumulative + selective ACK ==&lt;br /&gt;
&lt;br /&gt;
Cumulative ACK is &amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;.  On out-of-order arrival the&lt;br /&gt;
receiver also emits a SACK packet ([[#1.3. SACK payload|Section 1.3]]) whose payload lists&lt;br /&gt;
&#039;&#039;present&#039;&#039; blocks above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; (analogous to TCP SACK / QUIC ACK&lt;br /&gt;
ranges).  SACKs are rate-limited per [[#3. Protocol parameters|Section 3]] and suppressed when&lt;br /&gt;
neither &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; nor block count has changed since the last SACK.&lt;br /&gt;
&lt;br /&gt;
D-SACK reports (RFC 2883) are emitted in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; of an&lt;br /&gt;
otherwise normal SACK frame (see [[#1.3. SACK payload|Section 1.3]] for the encoding).&lt;br /&gt;
Two receiver triggers arm a pending D-SACK report (single-slot,&lt;br /&gt;
latest-wins):&lt;br /&gt;
&lt;br /&gt;
* DATA arrival with &amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;, both wire-dup (no RXM, &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt; path) and retransmit (RXM, post-FC branch) (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
* &amp;lt;code&amp;gt;rq_accept&amp;lt;/code&amp;gt; conflict, slot already occupied in &amp;lt;code&amp;gt;[lwe, rwe)&amp;lt;/code&amp;gt; (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
&lt;br /&gt;
When a D-SACK is pending and the standard scoreboard SACK would be&lt;br /&gt;
suppressed by dedup or rate-limit, the report is emitted as a&lt;br /&gt;
stand-alone SACK frame through the normal &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; path; when a&lt;br /&gt;
D-SACK report is pending the path bypasses dedup and the &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;&lt;br /&gt;
rate-limit, but the a-timer suppression on rcv inactivity still&lt;br /&gt;
applies.&lt;br /&gt;
&lt;br /&gt;
Bare ACKs are deferred via a per-flow delayed-ACK timer (one in&lt;br /&gt;
flight at a time, atomic test-and-set dedup; fires per [[#3. Protocol parameters|Section 3]]&lt;br /&gt;
after the first in-order arrival).  Suppressed if (1) no new&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, (2) rcv side is inactive (older than &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt;), or (3) the&lt;br /&gt;
sender just sent within &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;.  A pending D-SACK ride-through&lt;br /&gt;
bypasses (1) and (3); the a-timer gate (2) is unconditional.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 11. Flow control ==&lt;br /&gt;
&lt;br /&gt;
The receiver advertises &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; in every FC field.  The sender treats&lt;br /&gt;
its &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; as the absolute right edge: when&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt;= snd_cr.rwe&amp;lt;/code&amp;gt; the window is closed and &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt;&lt;br /&gt;
yields.  While closed, the sender periodically emits RDVS&lt;br /&gt;
(rendezvous) packets (cadence &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt;); the receiver replies with&lt;br /&gt;
a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;) that reopens the window.  Once the&lt;br /&gt;
window has been closed for longer than &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; the sender stops&lt;br /&gt;
emitting RDVS but does not tear the flow down - the writer keeps&lt;br /&gt;
blocking until either a peer-driven FC arrives or the KA&lt;br /&gt;
(keepalive) / r-timer marks the flow.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt; on receipt and MUST NOT shrink:&lt;br /&gt;
a backward &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is silently clamped to the current &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt;;&lt;br /&gt;
the FC packet still reopens the window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 12. RTT estimation ==&lt;br /&gt;
&lt;br /&gt;
Active RTTP probes ([[#1.4. RTTP payload|Section 1.4]]) carry a 32-bit &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt; (0&lt;br /&gt;
reserved) and a 16-byte random nonce echoed verbatim - defends&lt;br /&gt;
against spoofed replies.  A ring of &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; in-flight probes is&lt;br /&gt;
kept; an echo whose &amp;lt;code&amp;gt;(id, nonce)&amp;lt;/code&amp;gt; doesn&#039;t match the ring slot is&lt;br /&gt;
dropped.  A single RTTP sample is clamped to &amp;lt;code&amp;gt;RTT_CLAMP_MUL * srtt&amp;lt;/code&amp;gt;&lt;br /&gt;
(compile-time &amp;lt;code&amp;gt;RTT_CLAMP_MUL = 16&amp;lt;/code&amp;gt;) once &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded; the first&lt;br /&gt;
cold-probe sample feeds &amp;lt;code&amp;gt;rtt_update&amp;lt;/code&amp;gt; raw.&lt;br /&gt;
&lt;br /&gt;
Probe arming gates:&lt;br /&gt;
&lt;br /&gt;
;Cold (no &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; yet)&lt;br /&gt;
:the receive path arms at most one probe per 100 ms via &amp;lt;code&amp;gt;frcti_rcv_probe&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;PROBE_DUE_COLD&amp;lt;/code&amp;gt;); arming requires an incoming packet.  Active send-path arming bails while &amp;lt;code&amp;gt;srtt == 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
;Warm (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt;, called from &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt;)&lt;br /&gt;
:outstanding data (&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt; snd_cr.lwe&amp;lt;/code&amp;gt;), AND at least &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_rcv_rtt&amp;lt;/code&amp;gt; (last RTT receive of any kind), AND at least &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_snd_probe&amp;lt;/code&amp;gt; (last probe emit).&lt;br /&gt;
&lt;br /&gt;
Sample feeds either Linux&#039;s asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_LINUX_RTT_ESTIMATOR&amp;lt;/code&amp;gt;, default ON) or RFC 6298 symmetric EWMA&lt;br /&gt;
(compile option).  &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is floored at 10 ms when seeded from a&lt;br /&gt;
hint, at 1 us after every update (including the first seeding&lt;br /&gt;
sample); &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; floored at 100 ns.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;RTO = max(rto_min, 2 * srtt, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(the &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; floor is an FRCT addition not in RFC 6298).&lt;br /&gt;
Effective wheel deadline capped per [[#3. Protocol parameters|Section 3]].&lt;br /&gt;
&lt;br /&gt;
ACK-derived samples (&amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;rtt_sample_eligible&amp;lt;/code&amp;gt;), beyond&lt;br /&gt;
the cum-ACK advance gate in &amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno &amp;amp;gt; lwe&amp;lt;/code&amp;gt; and&lt;br /&gt;
&amp;lt;code&amp;gt;ackno &amp;amp;lt;= seqno&amp;lt;/code&amp;gt;), require all of: not in recovery; ACK packet does&lt;br /&gt;
not carry &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;; HoL slot&#039;s &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; bit clear; slot&#039;s &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;&lt;br /&gt;
pointer non-NULL (not SACK-consumed); &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; not below the &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
fence; &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; already seeded by an RTTP probe.  There is no ACK-only&lt;br /&gt;
seeding.&lt;br /&gt;
&lt;br /&gt;
Every eligible sample also feeds &amp;lt;code&amp;gt;RACK.min_RTT&amp;lt;/code&amp;gt; (RFC 8985 sec. 6.2)&lt;br /&gt;
via a windowed minimum: replace whenever the sample is strictly&lt;br /&gt;
smaller OR more than &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; (5 min, matches Linux&lt;br /&gt;
&amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) has elapsed since the current min was set.  The&lt;br /&gt;
downward branch is immediate (faster path picked up at once); the&lt;br /&gt;
upward branch is gated on the window (a transient queue burst does&lt;br /&gt;
not poison the estimate, but a sustained route change to a longer&lt;br /&gt;
path re-anchors &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; after at most one window).  Seeded from&lt;br /&gt;
&amp;lt;code&amp;gt;rtt_hint&amp;lt;/code&amp;gt; at &amp;lt;code&amp;gt;rtt_init&amp;lt;/code&amp;gt;; 0 acts as the unset sentinel and the base&lt;br /&gt;
in &amp;lt;code&amp;gt;rack_reorder_window&amp;lt;/code&amp;gt; falls back from &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt; (so&lt;br /&gt;
&amp;lt;code&amp;gt;R = mult * SRTT/4&amp;lt;/code&amp;gt;, capped at &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;, floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt;)&lt;br /&gt;
until the first sample.  See [[#6.2. Locked main path|Section 6.2]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 13. Liveness (keepalive) ==&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;code&amp;gt;qs.timeout &amp;amp;gt; 0&amp;lt;/code&amp;gt; a per-flow KA (keepalive) timer is armed.&lt;br /&gt;
Arming uses &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; for the deadline computation:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;deadline = min(snd_act + qs.timeout/4, rcv_act + qs.timeout)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(clamped to &amp;lt;code&amp;gt;now + qs.timeout/4&amp;lt;/code&amp;gt; if already past).  The timer fires&lt;br /&gt;
either on sender idleness (to send a KA) or on receiver idleness&lt;br /&gt;
(to declare the peer dead).  On fire (&amp;lt;code&amp;gt;ka_snd&amp;lt;/code&amp;gt;) the peer-dead test&lt;br /&gt;
uses &amp;lt;code&amp;gt;max(rcv_cr.act, t_ka_rcv)&amp;lt;/code&amp;gt; so a recent KA reply counts even&lt;br /&gt;
when no DATA has arrived:&lt;br /&gt;
&lt;br /&gt;
* If &amp;lt;code&amp;gt;now - max(rcv_cr.act, t_ka_rcv) &amp;amp;gt; qs.timeout&amp;lt;/code&amp;gt;, mark the flow &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; and notify the per-process flow-event set (&amp;lt;code&amp;gt;proc.fqset&amp;lt;/code&amp;gt;) with &amp;lt;code&amp;gt;FLOW_PEER&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Else if &amp;lt;code&amp;gt;snd_idle &amp;amp;gt; qs.timeout/4&amp;lt;/code&amp;gt;, emit a bare &amp;lt;code&amp;gt;KA | ACK&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) and re-arm.&lt;br /&gt;
* Else just re-arm.&lt;br /&gt;
&lt;br /&gt;
Note: &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; are the receive and transmit shared-memory&lt;br /&gt;
ring buffers.  The r-timer raises &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; on both (route is&lt;br /&gt;
broken); keepalive raises &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; only and notifies&lt;br /&gt;
the flow-event set (peer is silent, writer keeps &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; usable) -&lt;br /&gt;
distinct ACLs.  &amp;lt;code&amp;gt;qs.timeout == 0&amp;lt;/code&amp;gt; disables keepalive entirely; a&lt;br /&gt;
silent peer crash is then undetected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 14. Linger / teardown ==&lt;br /&gt;
&lt;br /&gt;
On &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt; computes a grace timeout&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;max(rcv_cr.act + rcv_cr.inact, snd_cr.act + snd_cr.inact) - now&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(floored at 0 and converted to seconds) and returns it; &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&lt;br /&gt;
forwards this to the IRMd as the dealloc grace.  The IRMd, not FRCT,&lt;br /&gt;
performs the wait.  Before computing the timeout, FRCT may emit a&lt;br /&gt;
final ACK when &amp;lt;code&amp;gt;rcv_cr.lwe != rcv_cr.seqno&amp;lt;/code&amp;gt; (the peer has not been&lt;br /&gt;
told the most recent cumulative ACK) AND the rcv side has been&lt;br /&gt;
active within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer not aged out).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt; is honoured only when &amp;lt;code&amp;gt;snd_cr.lwe &amp;amp;lt; edge&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;edge =&lt;br /&gt;
snd_fin_seqno&amp;lt;/code&amp;gt; after FIN has been sent in stream mode and&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno&amp;lt;/code&amp;gt; otherwise (data or FIN still in flight).  The drain&lt;br /&gt;
itself runs in &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&#039;s &amp;lt;code&amp;gt;while (FRCTI_LINGERING)&amp;lt;/code&amp;gt; loop, not in&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The fd is single-reader / single-writer (documented in the&lt;br /&gt;
manpages).  &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt; pumps &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; on every call (via&lt;br /&gt;
&amp;lt;code&amp;gt;flow_wait_window&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;flow_drain_rx_nb&amp;lt;/code&amp;gt;) and additionally blocks on&lt;br /&gt;
&amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; when the send window is closed.  A pure-writer thread thus&lt;br /&gt;
consumes ACKs without a dedicated reader.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 15. Heritage and adopted techniques ==&lt;br /&gt;
&lt;br /&gt;
Delta-t (Watson, 1981) is the primary heritage; FRCP descends from&lt;br /&gt;
the delta-t protocol family via the Recursive InterNetwork&lt;br /&gt;
Architecture (RINA; Day, &amp;quot;Patterns in Network Architecture&amp;quot;, 2008,&lt;br /&gt;
ch. 9).  Timer-based connection management&lt;br /&gt;
(no SYN/FIN handshake, per-flow state born on first DATA and&lt;br /&gt;
reclaimed after &amp;lt;code&amp;gt;t_mpl + a + r&amp;lt;/code&amp;gt; of silence), the DRF marker, and the&lt;br /&gt;
&amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; timers all come from delta-t.  See Watson,&lt;br /&gt;
&amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection&lt;br /&gt;
Management&amp;quot;, Computer Networks 5 (1981).&lt;br /&gt;
&lt;br /&gt;
The unified &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive and its&lt;br /&gt;
multi-axis QoS-cube argument ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) also come from RINA&lt;br /&gt;
(Day 2008, ch. 6; Grasa et al., &amp;quot;IRATI: investigating RINA as an&lt;br /&gt;
alternative to TCP/IP&amp;quot;, Computer Networks 92 (2015)) - reliability,&lt;br /&gt;
ordering, CRC presence, and encryption are flow attributes, not&lt;br /&gt;
separate sockets or protocols.&lt;br /&gt;
&lt;br /&gt;
The table below summarises additional adopted techniques and their&lt;br /&gt;
references.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FRCP mechanism !! Heritage !! Reference / note&lt;br /&gt;
|-&lt;br /&gt;
| Random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; || TCP ISN || RFC 6528 (Gont &amp;amp;amp; Bellovin, 2012).  QUIC PN-space reset (RFC 9000 sec. 12.3) is a structural analogue.&lt;br /&gt;
|-&lt;br /&gt;
| Cumulative ACK, left-window-edge advance || TCP || RFC 793 / RFC 9293&lt;br /&gt;
|-&lt;br /&gt;
| Receive window with non-shrink rule || TCP || RFC 793 sec. 3.7 / RFC 9293 sec. 3.8.6; RFC 1122 sec. 4.2.2.16 for the explicit non-shrink prohibition&lt;br /&gt;
|-&lt;br /&gt;
| Modular &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; arithmetic (&amp;lt;code&amp;gt;before&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;after&amp;lt;/code&amp;gt; helpers) || TCP || RFC 793 sec. 3.3 / RFC 9293 sec. 3.4&lt;br /&gt;
|-&lt;br /&gt;
| Selective ACK block list || TCP || RFC 2018 (Mathis et al., 1996).  Encoded as a typed FRCP packet rather than a TCP option, so framing is closer to QUIC ACK frames.  D-SACK (RFC 2883) carried in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt;; see [[#1.3. SACK payload|Section 1.3]].&lt;br /&gt;
|-&lt;br /&gt;
| NewReno-careful recovery with &amp;lt;code&amp;gt;recovery_high&amp;lt;/code&amp;gt; gate || TCP || RFC 6582 (Henderson et al., 2012); QUIC builds on the same model in RFC 9002 sec. 7.3.2.  Cwnd half absent (CC in IPCP).&lt;br /&gt;
|-&lt;br /&gt;
| RACK reordering window for fast retransmit || TCP || RFC 8985 (Cheng et al., 2021).  FRCP &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor against &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; collapse; matches RFC 8985 sec. 6.2 and Linux &amp;lt;code&amp;gt;tcp_rack_reo_wnd&amp;lt;/code&amp;gt;.  DSACK-driven &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; (sec. 6.2 step 4) is adopted; see [[#1.3. SACK payload|Section 1.3]] for the wire encoding.  The hybrid RACK-or-&amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; trigger from RFC 8985 sec. 6.2 step 4 is adopted ([[#8. Retransmission|Section 8]]).  QUIC&#039;s analogue in RFC 9002 sec. 6.1.2 uses &amp;lt;code&amp;gt;max(srtt, latest_rtt)&amp;lt;/code&amp;gt; as the base.&lt;br /&gt;
|-&lt;br /&gt;
| Karn&#039;s algorithm: no RTT sample on retransmits, RTO-collapse freeze || TCP || Karn &amp;amp;amp; Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, SIGCOMM 1987; RFC 6298 sec. 3.&lt;br /&gt;
|-&lt;br /&gt;
| RTO formula &amp;lt;code&amp;gt;RTO = max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt; || TCP || RFC 6298 (Paxson et al., 2011).  &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; = 250 us is below RFC 6298 sec. 2.4&#039;s 1 s SHOULD-floor - a recursive-layer choice.&lt;br /&gt;
|-&lt;br /&gt;
| Linux asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator (default) || Linux kernel || &amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;if(delta&amp;amp;lt;0) m&amp;amp;gt;&amp;amp;gt;=3&amp;lt;/code&amp;gt; dampening is a kernel divergence from RFC 6298.  RFC 6298 EWMA available behind a compile flag.&lt;br /&gt;
|-&lt;br /&gt;
| Delayed ACK with rate suppression || TCP || RFC 813 (Clark, 1982); RFC 1122 sec. 4.2.3.2; RFC 5681 sec. 4.2.  Single-deadline coalescing rather than &amp;quot;ack-every-other-segment&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| Zero-window-probe / persist-timer analogue (RDVS) || TCP || RFC 1122 sec. 4.2.2.17 / RFC 9293 sec. 3.8.6.1.  RDVS solicits an FC reply, distinct from QUIC &amp;lt;code&amp;gt;DATA_BLOCKED&amp;lt;/code&amp;gt; (RFC 9000 sec. 19.12), which is one-way notification.  &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; give-up departs from TCP.&lt;br /&gt;
|-&lt;br /&gt;
| Multiplexed control on a single PCI || SCTP / QUIC || SCTP chunk bundling (RFC 9260 sec. 6.10); QUIC frame multiplexing (RFC 9000 sec. 12.4).  Cleaner fit than TCP&#039;s separate-flag-bits design.&lt;br /&gt;
|-&lt;br /&gt;
| ACK ranges as multiple discontiguous acked blocks || QUIC || QUIC ACK frame (RFC 9000 sec. 19.3).  FRCP SACK is conceptually QUIC-frame-shaped even though encoded as absolute &amp;lt;code&amp;gt;[start,end]&amp;lt;/code&amp;gt; pairs.&lt;br /&gt;
|-&lt;br /&gt;
| Nonce-authenticated active RTT / liveness probing (RTTP) || QUIC &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;PATH_RESPONSE&amp;lt;/code&amp;gt; (RFC 9000 sec. 8.2, sec. 19.17, sec. 19.18).  WebRTC ICE consent-freshness (RFC 7675) is the same pattern.  QUIC&#039;s nonce is 8 octets; FRCP chooses 16.&lt;br /&gt;
|-&lt;br /&gt;
| Probing distinct from keepalive || QUIC || KA timer answers &amp;quot;peer alive?&amp;quot;, RTTP answers &amp;quot;path measurable?&amp;quot;, as in QUIC PING (RFC 9000 sec. 19.2) vs &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| Bare KA + ACK keepalive packets || QUIC / SCTP || QUIC PING (RFC 9000 sec. 19.2); SCTP HEARTBEAT / HEARTBEAT-ACK (RFC 9260 sec. 8.3).  SCTP HEARTBEAT also carries an opaque echoed blob, structurally similar to FRCP RTTP.&lt;br /&gt;
|-&lt;br /&gt;
| (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) fragment-role bits ([[#7.2. Fragmentation and reassembly|Section 7.2]]) || SCTP || RFC 9260 sec. 3.3.1 DATA chunk B/E bits encode the same four states (&amp;lt;code&amp;gt;B+E=SOLE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;B-only=FIRST&amp;lt;/code&amp;gt;, neither=&amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E-only=LAST&amp;lt;/code&amp;gt;).  Each fragment carries its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;/TSN and is independently retransmitted.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-offset reassembly (Sections [[#1.5. Stream PCI extension|1.5]], [[#16. Stream-mode flows|16]]) || QUIC || QUIC STREAM frame (RFC 9000 sec. 19.8) uses Offset + Length varints; FRCP uses fixed 32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;.  One stream per flow vs QUIC&#039;s many streams multiplexed.&lt;br /&gt;
|-&lt;br /&gt;
| FIN end-of-stream marker (Sections [[#1.2. Flag bits|1.2]], [[#16. Stream-mode flows|16]]) || TCP / QUIC || TCP FIN flag (RFC 9293 sec. 3.1) closes one half of the byte stream; QUIC STREAM frame FIN bit (RFC 9000 sec. 19.8) does the same per stream with an immutable final-size invariance (RFC 9000 sec. 4.5: the final size is fixed once observed).  FRCP&#039;s FIN consumes one packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (not one byte of stream space) and is idempotent on the sender side.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-credit flow control ([[#16. Stream-mode flows|Section 16]]) || QUIC || &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; (RFC 9000 sec. 4.1, sec. 19.10).  FRCP projects a per-flow byte budget onto the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;.  Single stream per flow collapses QUIC&#039;s &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.&lt;br /&gt;
|-&lt;br /&gt;
| Header protection (encrypted seqnos) || QUIC || QUIC RFC 9001 sec. 5.4 applies header protection on top of AEAD to mask the packet number.  FRCP&#039;s per-flow AEAD wrap ([[#16. Stream-mode flows|Section 16]]) is wider: it encrypts the entire PCI including &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; because the IPCP below already routes, so no destination connection-ID needs to stay in clear (cf. RFC 9000 sec. 5.2).&lt;br /&gt;
|-&lt;br /&gt;
| Two-bit fragment role polarity || SCTP || The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair follows SCTP B/E (begin = 1 / end = 1) rather than IPv4 MF (RFC 791 sec. 3.2), which has the inverse polarity (MF = 1 means NOT last).&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal reliability / ordering axes ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) || SCTP || PR-SCTP (RFC 3758, per-message partial reliability) and SCTP DATA U-bit (RFC 9260 sec. 3.3.1, per-message unordered) are the closest precedents for decoupling reliability from ordering; FRCP sets them per-flow rather than per-message.&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal CRC (&amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt;) || UDP-Lite || RFC 3828 (Larzon et al., 2004) lets the sender pick a per-packet Checksum Coverage and the receiver enforce a locally configured minimum (no in-band negotiation; sec. 3.1, sec. 3.3).  FRCP gates a full CRC trailer on &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; at flow setup.  Contrast TCP / SCTP (mandatory checksum) and QUIC (AEAD subsumes CRC).&lt;br /&gt;
|-&lt;br /&gt;
| Setup-time service negotiation || DCCP / SCTP / QUIC || DCCP Service Codes (RFC 4340 sec. 8.1.2, RFC 5595); SCTP INIT parameters (RFC 9260 sec. 3.3.2); QUIC transport parameters (RFC 9000 sec. 7.4).  All negotiate service properties at connection setup; only RINA&#039;s QoS cube exposes them as an orthogonal vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.1. Original to FRCP (no clean prior art) ===&lt;br /&gt;
&lt;br /&gt;
* Pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]): receiver-driven nudge exploiting &amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;.  Closest analogues are SCTP Gap Ack Blocks (RFC 9260 sec. 3.3.4) and DCCP Ack Vector (RFC 4340 sec. 11.4) - both let the receiver describe gaps to the sender, but neither targets the cross-epoch / pre-DRF case.&lt;br /&gt;
* &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; window-probe give-up: neither TCP (persist-timer probes until application or R2 abort, RFC 9293 sec. 3.8.6.1) nor QUIC has an explicit FC-give-up counter.  A recursive-network choice: outer layers can drop the flow.&lt;br /&gt;
* Skip-past-gap reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]): SCTP fragments and reassembles every flow regardless of reliability/ordering, using its own per-stream reassembly queue; QUIC fragments via STREAM offsets.  FRCP fragments best-effort flows too, but the receiver drops the broken prefix the moment a later run-start (&amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; role) is visible inside the &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;-wide reorder ring - no IP-frag-style timeout, no SCTP-style explicit abort.  If no later run-start arrives within the ring, &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt; and the partial run keeps its slots; the next inspect retries.  The trade-off: a permanently-lost &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; in a long isolated run holds slots until either a later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; appears in the ring or the writer stops, at which point the slots are reclaimed on flow teardown.&lt;br /&gt;
* Reassembly deferred to consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]), message mode only (&amp;lt;code&amp;gt;qos.service == SVC_MESSAGE&amp;lt;/code&amp;gt;): SCTP (RFC 9260 sec. 6.9), QUIC (RFC 9000 sec. 2.2), and TCP (RFC 9293) all hold reassembly state at the receive boundary.  FRCP message-mode leaves fragments in the shared-memory ring until &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; pulls and lands the SDU directly in the caller&#039;s buffer.  Stream mode ([[#16. Stream-mode flows|Section 16]]) uses the standard QUIC-style direct ring placement on receive and does not defer.  The optimisation is enabled by the Shared-Memory Subsystem (SSM) packet-buffer ring (see &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt; at [[#1.1. PCI header|Section 1.1]]); the analogue is OS-level scatter-gather I/O (&amp;lt;code&amp;gt;recvmsg+iovec&amp;lt;/code&amp;gt;), not a transport-layer prior art.&lt;br /&gt;
* TLP-equivalent tail-loss recovery (RFC 8985 sec. 7; RFC 9002 sec. 6.2): FRCP does not emit an explicit Tail Loss Probe packet, but the same goal is met implicitly by RACK loss detection ([[#8. Retransmission|Section 8]]) firing on a non-advancing cumulative ACK once the head-of-line slot ages past the RACK reorder window &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; - well below &amp;lt;code&amp;gt;RTO = max(2 * SRTT, SRTT + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.  A receiver-driven nudge is also available via the pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.2. Not adopted ===&lt;br /&gt;
&lt;br /&gt;
* Slow start, congestion window (cwnd), Additive Increase / Multiplicative Decrease (AIMD), NewReno cwnd inflation.  Congestion control lives in the IPCP CA policies and is driven by Explicit Congestion Notification (ECN, RFC 3168).&lt;br /&gt;
* Nagle / silly-window-syndrome (SWS) avoidance (RFC 896, RFC 1122 sec. 4.2.3.4).  (Deferred work, not adopted in the current spec.)&lt;br /&gt;
* TCP Timestamps (RFC 7323) / Protection Against Wrapped Sequences (PAWS) - RTT measurement uses RTTP, not per-segment timestamps.  A peer-supplied timestamp echoed on every ACK lets a malicious peer drive the &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; estimate arbitrarily low, collapsing the RTO and triggering a self-inflicted retransmit storm.  RTTP confines RTT measurement to nonce-authenticated probe round-trips, where a forged echo is rejected before it can reach the estimator.&lt;br /&gt;
* ECN (Explicit Congestion Notification) response inside FRCP (consumed by IPCP Congestion Avoidance / CA).&lt;br /&gt;
* IP-style fragment-offset reassembly (RFC 791 sec. 3.2; RFC 8200 sec. 4.5).  Message-mode FRCP relies on the FRCT &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; reorder ring keyed by &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (shared by FRTX and best-effort flows) to put fragments back in order; no separate offset field is needed and no IP-style hole-list reassembly buffer is kept.  Stream-mode FRCP does carry &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte offsets ([[#1.5. Stream PCI extension|Section 1.5]]) for direct ring placement on receive.&lt;br /&gt;
* QUIC STREAM offset+length framing on &#039;&#039;every&#039;&#039; flow (RFC 9000 sec. 19.8).  Message-mode FRCP uses the SCTP-style B/E flag-bit encoding (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) and skips the offsets; stream-mode FRCP adopts the QUIC offset model (heritage table above).&lt;br /&gt;
&lt;br /&gt;
== 16. Stream-mode flows ==&lt;br /&gt;
&lt;br /&gt;
When a flow is allocated with &amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt; both peers&lt;br /&gt;
switch to byte-stream semantics, layered on top of the FRTX reorder&lt;br /&gt;
machinery already described in Sections [[#6. Receive path|6]]-[[#8. Retransmission|8]].&lt;br /&gt;
&lt;br /&gt;
=== 16.1. Send ===&lt;br /&gt;
&lt;br /&gt;
The sender splits the caller&#039;s octets into chunks of at most&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt; octets (Sections [[#1.1. PCI header|1.1]]&lt;br /&gt;
and [[#1.5. Stream PCI extension|1.5]]).  Each chunk is one DATA packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte range copied from a monotonic stream counter.&lt;br /&gt;
In stream mode &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST be transmitted as&lt;br /&gt;
zero; the per-byte position is carried by the &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt;&lt;br /&gt;
extension instead.&lt;br /&gt;
&lt;br /&gt;
End-of-stream is signalled with a 0-byte DATA packet that has &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;&lt;br /&gt;
(bit 12) set, emitted on the FIN triggers listed in [[#1.2. Flag bits|Section 1.2]]&lt;br /&gt;
(WR-half close, &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, and any other path that yields the&lt;br /&gt;
final byte).  The sender MUST emit at most one FIN per flow; its&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; MUST equal &amp;lt;code&amp;gt;[final-byte, final-byte)&amp;lt;/code&amp;gt; (i.e., empty&lt;br /&gt;
interval at the final byte position; final-size invariance,&lt;br /&gt;
analogous to QUIC RFC 9000 sec. 4.5).  Idempotency is enforced by&lt;br /&gt;
an &amp;lt;code&amp;gt;snd_fin_sent&amp;lt;/code&amp;gt; guard.&lt;br /&gt;
&lt;br /&gt;
=== 16.2. Receive ===&lt;br /&gt;
&lt;br /&gt;
On arrival the receiver places the payload directly into a per-flow&lt;br /&gt;
byte-indexed receive ring of width &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; (octets) at the position&lt;br /&gt;
indicated by &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, with a two-segment &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; across the ring&lt;br /&gt;
boundary if needed.  Receipt is recorded in the FRTX reorder&lt;br /&gt;
machinery ([[#6.2. Locked main path|Section 6.2]]) augmented with the packet&#039;s &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;, and&lt;br /&gt;
FIN bit per slot.  When a packet&#039;s &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; front-overlaps&lt;br /&gt;
bytes already at or below the byte high-water mark, the overlap is&lt;br /&gt;
trimmed before placement so the same byte is never written twice.&lt;br /&gt;
After stashing, the receiver advances &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; and the byte high-water&lt;br /&gt;
mark across any newly-contiguous prefix.  Each slot advanced MUST&lt;br /&gt;
satisfy &amp;lt;code&amp;gt;start == the last-delivered slot&#039;s end&amp;lt;/code&amp;gt;; a slot whose&lt;br /&gt;
&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; does not equal that &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; is silently dropped at delivery time&lt;br /&gt;
(the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is consumed, no stream bytes contributed) and the high-&lt;br /&gt;
water mark does not advance past it.  The stream byte-stream&lt;br /&gt;
stalls at that point - there is no flow-tear-down on mismatch.&lt;br /&gt;
This filters spliced or off-path-injected slots that fall in&lt;br /&gt;
window without strong cryptographic authentication.&lt;br /&gt;
&lt;br /&gt;
A FIN slot marks end-of-stream at advance time only if its byte&lt;br /&gt;
position equals the last-delivered slot&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;; otherwise the FIN&lt;br /&gt;
is ignored and the corresponding &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; occupies a slot but&lt;br /&gt;
contributes no stream bytes.  No packet buffer is held after the&lt;br /&gt;
ring copy.&lt;br /&gt;
&lt;br /&gt;
=== 16.3. Read ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns up to &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt; octets from the contiguous prefix&lt;br /&gt;
&amp;lt;code&amp;gt;[next, high-water)&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;next&amp;lt;/code&amp;gt; is the byte the application has&lt;br /&gt;
already consumed up to and &amp;lt;code&amp;gt;high-water&amp;lt;/code&amp;gt; is the rightmost contiguous&lt;br /&gt;
byte received.  When the stream is fully drained AND end-of-stream&lt;br /&gt;
(EOS) was observed (&amp;lt;code&amp;gt;next == EOS&amp;lt;/code&amp;gt; byte position), &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns&lt;br /&gt;
0 (&amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) - the same shape POSIX &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt; uses on TCP after a peer&lt;br /&gt;
FIN.&lt;br /&gt;
&lt;br /&gt;
=== 16.4. Flow control ===&lt;br /&gt;
&lt;br /&gt;
ACK / SACK / RACK / RTO machinery is unchanged; the FRTX reorder&lt;br /&gt;
ring is reused as a per-&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; received-bitmap.  Let &amp;lt;code&amp;gt;per_pkt =&lt;br /&gt;
(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt;, the maximum stream-&lt;br /&gt;
byte payload one DATA packet can carry ([[#16.1. Send|Section 16.1]]).  The&lt;br /&gt;
receive window advertised in FC is clamped so the byte window&lt;br /&gt;
(&amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt;) cannot be overrun: the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is at most&lt;br /&gt;
&amp;lt;code&amp;gt;rcv_cr.lwe + ring_sz / per_pkt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC byte-credit flow-control model&lt;br /&gt;
(&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt;, RFC 9000 sec. 4.1 and sec. 19.10) projected onto&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; space.  With one stream per flow there is no &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; /&lt;br /&gt;
&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.  Receiver-side silly-window-syndrome&lt;br /&gt;
(SWS) avoidance (RFC 9293 sec. 3.8.6.2.2) is achieved by combining&lt;br /&gt;
the consume-time &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; bump with the global non-shrink rule from&lt;br /&gt;
[[#11. Flow control|Section 11]].&lt;br /&gt;
&lt;br /&gt;
=== 16.5. Security considerations ===&lt;br /&gt;
&lt;br /&gt;
Threat model.  An attacker that can observe (on-path passive) or&lt;br /&gt;
predict (off-path blind) the flow&#039;s seqnos and byte offsets on an&lt;br /&gt;
unencrypted stream flow can inject DATA or FIN at any in-window&lt;br /&gt;
position.  The in-line consistency checks above (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; == prior&lt;br /&gt;
&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; on advance; FIN MUST be 0-byte; FIN MUST sit at the final&lt;br /&gt;
byte position) realise the spirit of RFC 5961&#039;s &amp;quot;sequence-window&lt;br /&gt;
plus exact-position match for control bits&amp;quot; without an explicit&lt;br /&gt;
challenge-ACK probe; they make a few specific blind attack shapes&lt;br /&gt;
harder but are not cryptographic authentication. This is&lt;br /&gt;
comparable to TCP without the TCP Authentication Option (TCP-AO,&lt;br /&gt;
RFC 5925), tighter than a&lt;br /&gt;
pre-RFC-5961 TCP stack, and roughly equivalent to a modern&lt;br /&gt;
RFC 5961 stack against blind off-path injection - none of these&lt;br /&gt;
help once the attacker can sniff. TLS over TCP (RFC 8446)&lt;br /&gt;
encrypts only the TCP payload and leaves TCP seqnos, ACKs, FIN,&lt;br /&gt;
and RST in the clear, so TLS does NOT defend against TCP-header-&lt;br /&gt;
level injection; QUIC (RFC 9000) hides packet numbers under&lt;br /&gt;
header protection (RFC 9001 sec. 5.4), so this specific weakness&lt;br /&gt;
does not apply to QUIC.&lt;br /&gt;
&lt;br /&gt;
Mitigation: AEAD.  When the flow has encryption enabled the&lt;br /&gt;
recommended AEAD ciphers (AES-GCM, RFC 5288; or ChaCha20-Poly1305,&lt;br /&gt;
RFC 8439) wrap the entire FRCP packet on the wire - PCI, stream&lt;br /&gt;
extension, body, and the CRC trailer when &amp;lt;code&amp;gt;ber == 0&amp;lt;/code&amp;gt; - under a&lt;br /&gt;
per-flow symmetric key derived from the flow&#039;s own key exchange&lt;br /&gt;
([[#1.1. PCI header|Section 1.1]]).  The AEAD tag (~2^-128 forgery probability)&lt;br /&gt;
dominates the CRC (~2^-32) for integrity in this mode but the CRC&lt;br /&gt;
trailer is currently retained inside the wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
Implementations MUST NOT rely on the security properties below&lt;br /&gt;
when a non-AEAD cipher (e.g. AES-CTR alone) is negotiated; non-&lt;br /&gt;
AEAD modes provide confidentiality only and the threat-model&lt;br /&gt;
claims do not hold.&lt;br /&gt;
&lt;br /&gt;
With an AEAD cipher in use, seqnos, byte offsets, and the FIN bit&lt;br /&gt;
are both authenticated and confidential. Against an off-path or&lt;br /&gt;
on-path-passive attacker this is:&lt;br /&gt;
&lt;br /&gt;
* Stronger than TCP+TLS (TCP header in the clear).&lt;br /&gt;
* Stronger than TCP+TCP-AO (header authenticated but visible).&lt;br /&gt;
* Comparable to IPsec ESP transport mode (RFC 4303), which similarly authenticates and encrypts the upper-layer header plus payload, and to QUIC packet protection (RFC 9001 sec. 5), with the difference that QUIC must leave the destination connection ID in the clear for routing whereas FRCP relies on the IPCP below for delivery and can therefore encrypt its entire PCI.&lt;br /&gt;
&lt;br /&gt;
Keying granularity. Ouroboros flow allocation runs key exchange (&amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt;) per flow, so each &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; yields independent symmetric keys. This is finer-grained than QUIC (per-connection, RFC 9001, where one handshake covers all multiplexed streams) and finer-grained than typical IPsec deployment (per-host-pair Security Associations, SAs). Forward secrecy follows from the &amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt; when an ephemeral Diffie-Hellman exchange (DHE), or a hybrid mode (classical DH + post-quantum Key Encapsulation Mechanism / KEM), is selected.&lt;br /&gt;
&lt;br /&gt;
Replay protection. The AEAD layer itself does NOT carry an explicit anti-replay window (unlike IPsec ESP, RFC 4303 sec. 3.4.3, or DTLS, RFC 9147 sec. 4.5.1).  For FRCP-engaged flows the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space duplicate-suppression in [[#6.2. Locked main path|Section 6.2]] rejects replayed&lt;br /&gt;
DATA after the AEAD strips the wrap, because the AEAD authenticates the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a replay re-presents an old &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; that is then discarded either as a duplicate (still inside the receive window or as outside the receive window, depending on how far &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; has advanced since the original packet was delivered. RAW (&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) flows have no FRCP layer and therefore no replay protection at the AEAD layer either; deployments that need replay rejection on RAW flows SHOULD use SVC_MESSAGE.&lt;br /&gt;
&lt;br /&gt;
Layering. The AEAD wrap sits below FRCP on the data path, so RAW best-effort flows (&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;, the UDP-equivalent service of [[#2.2. Service modes (orthogonal axes)|Section 2.2]]) inherit the same per-flow integrity + confidentiality scope as FRCP-engaged flows - whatever the process and FRCP (if any) put on the wire is what the AEAD authenticates. No DTLS-equivalent layering is required for confidentiality and integrity; replay protection above AEAD is a separate concern as noted above.&lt;br /&gt;
&lt;br /&gt;
== 17. References ==&lt;br /&gt;
&lt;br /&gt;
This section lists the IETF documents, published works, and&lt;br /&gt;
source-code references cited inline elsewhere in this document.&lt;br /&gt;
IETF documents are cited inline as &amp;quot;RFC NNNN sec. X.Y&amp;quot;; books,&lt;br /&gt;
journal papers, and source-code references are cited inline by&lt;br /&gt;
author and year (or by file and function name) and are listed&lt;br /&gt;
here for convenience.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.1. IETF documents ===&lt;br /&gt;
&lt;br /&gt;
;[RFC 791]&lt;br /&gt;
:J. Postel, &amp;quot;Internet Protocol&amp;quot;, STD 5, RFC 791, September 1981.&lt;br /&gt;
;[RFC 793]&lt;br /&gt;
:J. Postel, &amp;quot;Transmission Control Protocol&amp;quot;, STD 7, RFC 793, September 1981.  Obsoleted by RFC 9293.&lt;br /&gt;
;[RFC 813]&lt;br /&gt;
:D. D. Clark, &amp;quot;Window and Acknowledgement Strategy in TCP&amp;quot;, RFC 813, July 1982.&lt;br /&gt;
;[RFC 896]&lt;br /&gt;
:J. Nagle, &amp;quot;Congestion Control in IP/TCP Internetworks&amp;quot;, RFC 896, January 1984.&lt;br /&gt;
;[RFC 1122]&lt;br /&gt;
:R. Braden (ed.), &amp;quot;Requirements for Internet Hosts -- Communication Layers&amp;quot;, STD 3, RFC 1122, October 1989.&lt;br /&gt;
;[RFC 2018]&lt;br /&gt;
:M. Mathis, J. Mahdavi, S. Floyd, A. Romanow, &amp;quot;TCP Selective Acknowledgment Options&amp;quot;, RFC 2018, October 1996.&lt;br /&gt;
;[RFC 2119]&lt;br /&gt;
:S. Bradner, &amp;quot;Key words for use in RFCs to Indicate Requirement Levels&amp;quot;, BCP 14, RFC 2119, March 1997.&lt;br /&gt;
;[RFC 2883]&lt;br /&gt;
:S. Floyd, J. Mahdavi, M. Mathis, M. Podolsky, &amp;quot;An Extension to the Selective Acknowledgement (SACK) Option for TCP&amp;quot;, RFC 2883, July 2000.&lt;br /&gt;
;[RFC 3758]&lt;br /&gt;
:R. Stewart, M. Ramalho, Q. Xie, M. Tuexen, P. Conrad, &amp;quot;Stream Control Transmission Protocol (SCTP) Partial Reliability Extension&amp;quot;, RFC 3758, May 2004.&lt;br /&gt;
;[RFC 3828]&lt;br /&gt;
:L-A. Larzon, M. Degermark, S. Pink, L-E. Jonsson (ed.), G. Fairhurst (ed.), &amp;quot;The Lightweight User Datagram Protocol (UDP-Lite)&amp;quot;, RFC 3828, July 2004.&lt;br /&gt;
;[RFC 4303]&lt;br /&gt;
:S. Kent, &amp;quot;IP Encapsulating Security Payload (ESP)&amp;quot;, RFC 4303, December 2005.&lt;br /&gt;
;[RFC 4340]&lt;br /&gt;
:E. Kohler, M. Handley, S. Floyd, &amp;quot;Datagram Congestion Control Protocol (DCCP)&amp;quot;, RFC 4340, March 2006.&lt;br /&gt;
;[RFC 5288]&lt;br /&gt;
:J. Salowey, A. Choudhury, D. McGrew, &amp;quot;AES Galois Counter Mode (GCM) Cipher Suites for TLS&amp;quot;, RFC 5288, August 2008.&lt;br /&gt;
;[RFC 5595]&lt;br /&gt;
:G. Fairhurst, &amp;quot;The Datagram Congestion Control Protocol (DCCP) Service Codes&amp;quot;, RFC 5595, September 2009.&lt;br /&gt;
;[RFC 5681]&lt;br /&gt;
:M. Allman, V. Paxson, E. Blanton, &amp;quot;TCP Congestion Control&amp;quot;, RFC 5681, September 2009.&lt;br /&gt;
;[RFC 5925]&lt;br /&gt;
:J. Touch, A. Mankin, R. Bonica, &amp;quot;The TCP Authentication Option&amp;quot;, RFC 5925, June 2010.&lt;br /&gt;
;[RFC 5961]&lt;br /&gt;
:A. Ramaiah, R. Stewart, M. Dalal, &amp;quot;Improving TCP&#039;s Robustness to Blind In-Window Attacks&amp;quot;, RFC 5961, August 2010.&lt;br /&gt;
;[RFC 6298]&lt;br /&gt;
:V. Paxson, M. Allman, J. Chu, M. Sargent, &amp;quot;Computing TCP&#039;s Retransmission Timer&amp;quot;, RFC 6298, June 2011.&lt;br /&gt;
;[RFC 6528]&lt;br /&gt;
:F. Gont, S. Bellovin, &amp;quot;Defending against Sequence Number Attacks&amp;quot;, RFC 6528, February 2012.  Obsoletes RFC 1948.&lt;br /&gt;
;[RFC 6582]&lt;br /&gt;
:T. Henderson, S. Floyd, A. Gurtov, Y. Nishida, &amp;quot;The NewReno Modification to TCP&#039;s Fast Recovery Algorithm&amp;quot;, RFC 6582, April 2012.&lt;br /&gt;
;[RFC 7323]&lt;br /&gt;
:D. Borman, B. Braden, V. Jacobson, R. Scheffenegger (ed.), &amp;quot;TCP Extensions for High Performance&amp;quot;, RFC 7323, September 2014.&lt;br /&gt;
;[RFC 7675]&lt;br /&gt;
:M. Perumal, D. Wing, R. Ravindranath, T. Reddy, M. Thomson, &amp;quot;Session Traversal Utilities for NAT (STUN) Usage for Consent Freshness&amp;quot;, RFC 7675, October 2015.&lt;br /&gt;
;[RFC 8174]&lt;br /&gt;
:B. Leiba, &amp;quot;Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words&amp;quot;, BCP 14, RFC 8174, May 2017.&lt;br /&gt;
;[RFC 8200]&lt;br /&gt;
:S. Deering, R. Hinden, &amp;quot;Internet Protocol, Version 6 (IPv6) Specification&amp;quot;, STD 86, RFC 8200, July 2017.&lt;br /&gt;
;[RFC 8439]&lt;br /&gt;
:Y. Nir, A. Langley, &amp;quot;ChaCha20 and Poly1305 for IETF Protocols&amp;quot;, RFC 8439, June 2018.&lt;br /&gt;
;[RFC 8446]&lt;br /&gt;
:E. Rescorla, &amp;quot;The Transport Layer Security (TLS) Protocol Version 1.3&amp;quot;, RFC 8446, August 2018.&lt;br /&gt;
;[RFC 8985]&lt;br /&gt;
:Y. Cheng, N. Cardwell, N. Dukkipati, P. Jha, &amp;quot;The RACK-TLP Loss Detection Algorithm for TCP&amp;quot;, RFC 8985, February 2021.&lt;br /&gt;
;[RFC 9000]&lt;br /&gt;
:J. Iyengar (ed.), M. Thomson (ed.), &amp;quot;QUIC: A UDP-Based Multiplexed and Secure Transport&amp;quot;, RFC 9000, May 2021.&lt;br /&gt;
;[RFC 9001]&lt;br /&gt;
:M. Thomson (ed.), S. Turner (ed.), &amp;quot;Using TLS to Secure QUIC&amp;quot;, RFC 9001, May 2021.&lt;br /&gt;
;[RFC 9002]&lt;br /&gt;
:J. Iyengar (ed.), I. Swett (ed.), &amp;quot;QUIC Loss Detection and Congestion Control&amp;quot;, RFC 9002, May 2021.&lt;br /&gt;
;[RFC 9147]&lt;br /&gt;
:E. Rescorla, H. Tschofenig, N. Modadugu, &amp;quot;The Datagram Transport Layer Security (DTLS) Protocol Version 1.3&amp;quot;, RFC 9147, April 2022.&lt;br /&gt;
;[RFC 9260]&lt;br /&gt;
:R. Stewart, M. Tuexen, K. Nielsen, &amp;quot;Stream Control Transmission Protocol&amp;quot;, RFC 9260, June 2022.  Obsoletes RFC 4960.&lt;br /&gt;
;[RFC 9293]&lt;br /&gt;
:W. Eddy (ed.), &amp;quot;Transmission Control Protocol (TCP)&amp;quot;, STD 7, RFC 9293, August 2022.  Obsoletes RFC 793 and several follow-ons; updates RFC 1122 and others.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.2. Books and journal papers ===&lt;br /&gt;
&lt;br /&gt;
;[Day08]&lt;br /&gt;
:J. Day, &amp;quot;Patterns in Network Architecture: A Return to Fundamentals&amp;quot;, Prentice Hall, 2008.&lt;br /&gt;
;[Grasa15]&lt;br /&gt;
:E. Grasa et al., &amp;quot;IRATI: investigating RINA as an alternative to TCP/IP&amp;quot;, Computer Networks, Vol. 92, December 2015.&lt;br /&gt;
;[KP87]&lt;br /&gt;
:P. Karn, C. Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, ACM SIGCOMM, August 1987.&lt;br /&gt;
;[Wat81]&lt;br /&gt;
:R. W. Watson, &amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection Management&amp;quot;, Computer Networks, Vol. 5, 1981.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.3. Source-code references ===&lt;br /&gt;
&lt;br /&gt;
;[Linux-RTT]&lt;br /&gt;
:&amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt; of the Linux kernel, defining the asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; variance update used as FRCP&#039;s default RTT estimator ([[#12. RTT estimation|Section 12]]).  Line-stable browseable copy at https://elixir.bootlin.com/linux/latest/source/net/ipv4/tcp_input.c.&lt;br /&gt;
&lt;br /&gt;
[[Category:Protocols]]&lt;br /&gt;
[[Category:Ouroboros internals]]&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1921</id>
		<title>Flow and Retransmission Control Protocol</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1921"/>
		<updated>2026-05-17T15:24:25Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 1.3. SACK payload */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:FRCP - Flow and Retransmission Control Protocol}}&lt;br /&gt;
&lt;br /&gt;
FRCP runs end-to-end between two peers over a flow.  It delivers&lt;br /&gt;
reliability, in-order delivery, flow control, and liveness.&lt;br /&gt;
Congestion Control (CC) is not in FRCP - that lives in the IPC&lt;br /&gt;
Process (IPCP) Congestion Avoidance (CA) policies, orthogonal to&lt;br /&gt;
FRCP.  Flow allocation, naming, and IPCP lifecycle are handled by&lt;br /&gt;
the IPC Resource Manager daemon (IRMd).&lt;br /&gt;
&lt;br /&gt;
FRCT (Flow and Retransmission Control Task) is the libouroboros&lt;br /&gt;
implementation of FRCP; the task lives in &amp;lt;code&amp;gt;src/lib/frct.c&amp;lt;/code&amp;gt;.  The&lt;br /&gt;
remainder of this document describes the FRCP wire protocol and the&lt;br /&gt;
behaviour FRCT realises.  Code symbols retain the &amp;lt;code&amp;gt;FRCT_&amp;lt;/code&amp;gt; prefix&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, ...) because they belong to the implementing&lt;br /&gt;
task; this document references them verbatim.&lt;br /&gt;
&lt;br /&gt;
The keywords &amp;quot;MUST&amp;quot;, &amp;quot;MUST NOT&amp;quot;, &amp;quot;REQUIRED&amp;quot;, &amp;quot;SHALL&amp;quot;, &amp;quot;SHALL NOT&amp;quot;,&lt;br /&gt;
&amp;quot;SHOULD&amp;quot;, &amp;quot;SHOULD NOT&amp;quot;, &amp;quot;RECOMMENDED&amp;quot;, &amp;quot;MAY&amp;quot;, and &amp;quot;OPTIONAL&amp;quot; in&lt;br /&gt;
this document are to be interpreted as described in &amp;lt;code&amp;gt;BCP&amp;lt;/code&amp;gt; 14 (Best&lt;br /&gt;
Current Practice; RFC 2119, RFC 8174) when, and only when, they&lt;br /&gt;
appear in all capitals.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:Unsigned 32-bit / 8-bit integers (kernel-C style).&lt;br /&gt;
;&amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:Nanoseconds.&lt;br /&gt;
&lt;br /&gt;
Modular sequence-number comparators (32-bit, modulo 2^32):&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;before(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;(int32_t)(a - b) &amp;amp;lt; 0&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;after(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;before(b, a)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Used throughout for &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; ordering checks.&lt;br /&gt;
&lt;br /&gt;
Round-Trip Time (RTT) abbreviations used throughout:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;&lt;br /&gt;
:Smoothed RTT estimate (RFC 6298).&lt;br /&gt;
;&amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt;&lt;br /&gt;
:Mean deviation of RTT (Linux variance estimator).&lt;br /&gt;
;&amp;lt;code&amp;gt;EWMA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Exponentially Weighted Moving Average.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTO&amp;lt;/code&amp;gt;&lt;br /&gt;
:Retransmission Timeout, &amp;lt;code&amp;gt;max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Timer-bound symbols &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer, ACK delay) and &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer,&lt;br /&gt;
retransmission window) are defined in [[#8. Retransmission|Section 8]]; &amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; (Maximum&lt;br /&gt;
Packet Lifetime) is introduced in [[#2.1. Per-flow state|Section 2.1]] (the &amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; field)&lt;br /&gt;
with heritage in [[#15. Heritage and adopted techniques|Section 15]].&lt;br /&gt;
&lt;br /&gt;
Wire-format diagrams follow the IETF convention: bit 0 is the&lt;br /&gt;
leftmost (most significant) bit and fields are in network byte&lt;br /&gt;
order unless stated otherwise.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1. Wire format ==&lt;br /&gt;
&lt;br /&gt;
=== 1.1. PCI header ===&lt;br /&gt;
&lt;br /&gt;
Fixed 16-octet base Protocol-Control Information (PCI) header&lt;br /&gt;
prefixed to every FRCP packet (RFC convention: bit 0 leftmost,&lt;br /&gt;
most-significant bit first).  All multi-byte fields except &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;&lt;br /&gt;
are in network byte order; &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt; is an opaque 16-bit value that&lt;br /&gt;
the receiver recomputes from the wire bytes and compares to the&lt;br /&gt;
in-place &amp;lt;code&amp;gt;pci-&amp;gt;hcs&amp;lt;/code&amp;gt; read, so its on-wire byte order need only&lt;br /&gt;
match between peers running compatible builds.  DATA packets on&lt;br /&gt;
stream-mode flows carry an additional 8-octet extension (see&lt;br /&gt;
[[#1.5. Stream PCI extension|Section 1.5]]); SACK and RTTP carry their own payloads after the&lt;br /&gt;
base PCI.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |             flags             |              hcs              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            window                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            seqno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            ackno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                     payload (variable) ...&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt;&lt;br /&gt;
:feature/type bitmap (see [[#1.2. Flag bits|Section 1.2]]).&lt;br /&gt;
;&amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;&lt;br /&gt;
:CRC-16-CCITT-FALSE Header Check Sequence (HCS) over &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; (+ stream extension when present); the two octets of the &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt; field itself are omitted from the CRC input.  Verified on receive before any flag-driven dispatch.&lt;br /&gt;
;&amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt;&lt;br /&gt;
:receiver-advertised right window edge (valid iff FC).&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-flow sequence number.&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;&lt;br /&gt;
:cumulative Acknowledgement (ACK) (valid iff ACK).&lt;br /&gt;
&lt;br /&gt;
A single packet can simultaneously carry DATA + ACK + FC (Flow&lt;br /&gt;
Control) + RXM (Retransmission) by ORing flag bits; the PCI&lt;br /&gt;
multiplexes control on the same wire frame in the spirit of SCTP&lt;br /&gt;
chunk bundling (RFC 9260 sec. 6.10) and QUIC frame multiplexing&lt;br /&gt;
(RFC 9000 sec. 12.4).  DATA-bearing packets carry the caller&#039;s&lt;br /&gt;
payload after the PCI; SACK (Selective Acknowledgement) and RTTP&lt;br /&gt;
(Round-Trip Time Probe) carry their own typed payloads after the&lt;br /&gt;
PCI.&lt;br /&gt;
&lt;br /&gt;
Optional framing (per-flow, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]]).  On the wire, the&lt;br /&gt;
order from inside out is:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Layer !! Scope&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| The FRCP packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body + CRC-32 ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| CRC-32 covers the body only (PCI is in HCS); appended iff &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; on DATA, or on every SACK packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ AEAD-wrap of above ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| Iff Authenticated Encryption with Associated Data (AEAD) is enabled.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* HCS in the PCI covers the header fields on every packet and is verified before any flag-driven dispatch.&lt;br /&gt;
* The CRC-32 trailer (IEEE 802.3 / zlib reflected polynomial &amp;lt;code&amp;gt;0xEDB88320&amp;lt;/code&amp;gt;, init &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;, xor-out &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;) covers the body on DATA when &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; and on every SACK packet; the trailer is written as a raw &amp;lt;code&amp;gt;uint32_t&amp;lt;/code&amp;gt; (the same convention as &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;: opaque on the wire as long as both peers run compatible builds).  The PCI is not under the CRC (Cyclic Redundancy Check) because the HCS already protects it.  It is appended before AEAD encryption and therefore rides inside the AEAD wrap when both are active; the AEAD tag (~2^-128 forgery probability) dominates the CRC (~2^-32) for integrity in that mode but the CRC trailer is currently retained.&lt;br /&gt;
* When encryption is enabled, the entire (possibly-CRC&#039;d) FRCP packet is wrapped with AEAD inside the shared-memory packet buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt;); the packet grows by the AEAD overhead, namely a leading nonce / Initialization Vector (IV) of &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_ivsz&amp;lt;/code&amp;gt;) and a trailing authentication tag of &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_tagsz&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Both CRC and AEAD are layered around the FRCP wire format and are not visible to the FRCP machinery itself.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.2. Flag bits ===&lt;br /&gt;
&lt;br /&gt;
Flag bits are numbered most-significant-bit first to match the wire&lt;br /&gt;
diagram (bit numbering per [[#1.1. PCI header|Section 1.1]]; bit 0 is the MSB of the&lt;br /&gt;
16-bit &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; field and lands at wire-position 0 in network byte&lt;br /&gt;
order).  Bits 13..15 are reserved and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Bit !! Mask !! Name !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| 0 || &amp;lt;code&amp;gt;0x8000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt; || Carries caller payload&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &amp;lt;code&amp;gt;0x4000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DRF&amp;lt;/code&amp;gt; || Data Run Flag: start of a fresh run&lt;br /&gt;
|-&lt;br /&gt;
| 2 || &amp;lt;code&amp;gt;0x2000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt; || Acknowledgement: &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field valid&lt;br /&gt;
|-&lt;br /&gt;
| 3 || &amp;lt;code&amp;gt;0x1000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt; || Negative ACK; &amp;lt;code&amp;gt;seqno = arrival_seqno-1&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || &amp;lt;code&amp;gt;0x0800&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt; || Flow Control: &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; field valid (&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || &amp;lt;code&amp;gt;0x0400&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt; || Rendezvous probe (window-closed)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || &amp;lt;code&amp;gt;0x0200&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; || First Fragment (role bit 0; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || &amp;lt;code&amp;gt;0x0100&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; || Last Fragment (role bit 1; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 8 || &amp;lt;code&amp;gt;0x0080&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RXM&amp;lt;/code&amp;gt; || Retransmission&lt;br /&gt;
|-&lt;br /&gt;
| 9 || &amp;lt;code&amp;gt;0x0040&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt; || Selective ACK block list in payload&lt;br /&gt;
|-&lt;br /&gt;
| 10 || &amp;lt;code&amp;gt;0x0020&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt; || RTT Probe / echo (payload follows)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || &amp;lt;code&amp;gt;0x0010&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt; || Keepalive&lt;br /&gt;
|-&lt;br /&gt;
| 12 || &amp;lt;code&amp;gt;0x0008&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt; || End-of-stream marker (stream mode)&lt;br /&gt;
|-&lt;br /&gt;
| 13-15 || -- || -- || Reserved (MUST be zero)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair encodes the fragment role of a DATA-bearing&lt;br /&gt;
Service Data Unit (SDU), SCTP-style begin/end flags (RFC 9260&lt;br /&gt;
sec. 3.3.1):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FFGM !! LFGM !! Role&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || Sole / un-fragmented SDU (begin AND end)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || First fragment of a multi-fragment SDU&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Middle fragment&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Last fragment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Each fragment is carried in its own FRCP packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;;&lt;br /&gt;
FRTX (the FRCT Retransmission service mode, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]])&lt;br /&gt;
recovers individual fragments via the normal Retransmission Timeout&lt;br /&gt;
(RTO) / SACK / Recent Acknowledgement (RACK, RFC 8985) path.  The&lt;br /&gt;
receiver reassembles the SDU at consume time once the contiguous&lt;br /&gt;
[FIRST .. LAST] run has fully arrived.  On non-DATA packets the role&lt;br /&gt;
bits are unused and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
In stream mode (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;, see [[#16. Stream-mode flows|Section 16]]) there are&lt;br /&gt;
no SDU boundaries to encode, so &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST&lt;br /&gt;
be transmitted as zero.  End-of-stream uses a dedicated bit (&amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;,&lt;br /&gt;
bit 12) carried on a 0-byte DATA packet, emitted at write-half close&lt;br /&gt;
(&amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;FLOWFRDONLY&amp;lt;/code&amp;gt;), during linger drain, and at &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;;&lt;br /&gt;
emission is idempotent (first call wins).  After contiguous delivery&lt;br /&gt;
of the FIN-bearing slot, the receiver latches &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt; at the FIN&#039;s&lt;br /&gt;
start offset; &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns 0 (end-of-file, &amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) once buffered&lt;br /&gt;
bytes have been drained up to &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt;.  Per-byte position is&lt;br /&gt;
carried by the [start, end) extension ([[#1.5. Stream PCI extension|Section 1.5]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.3. SACK payload ===&lt;br /&gt;
&lt;br /&gt;
A SACK packet has the &amp;lt;code&amp;gt;FRCT_ACK | FRCT_FC | FRCT_SACK&amp;lt;/code&amp;gt; flag bits set&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI, the&lt;br /&gt;
payload is a 2-octet block count (network byte order), 2 octets of&lt;br /&gt;
padding to 4-byte align the block list, then &amp;lt;code&amp;gt;n_blocks&amp;lt;/code&amp;gt; pairs of&lt;br /&gt;
32-bit start/end seqnos describing &#039;&#039;present&#039;&#039; (received) ranges&lt;br /&gt;
above the cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |           n_blocks            |        padding (2 octets)     |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[0]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            end[0]                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[1]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
                       ... n_blocks pairs total ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;n_blocks &amp;amp;lt;= SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; (2048).  The per-flow effective cap is&lt;br /&gt;
further bounded by &amp;lt;code&amp;gt;(frag_mtu - PCI - 4) / 8&amp;lt;/code&amp;gt; blocks per packet; SACK&lt;br /&gt;
packets carry no stream extension, so PCI here is the 16-octet base&lt;br /&gt;
header even on stream-mode flows.&lt;br /&gt;
&lt;br /&gt;
Wire invariant: every block produced by the receiver, except an&lt;br /&gt;
optional leading Duplicate SACK (D-SACK) block as described below,&lt;br /&gt;
describes a range strictly above the cumulative ACK carried in the&lt;br /&gt;
PCI &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field (&amp;lt;code&amp;gt;after(start[i], ackno)&amp;lt;/code&amp;gt;).  This makes the D-SACK&lt;br /&gt;
convention below unambiguous; the receiver-side builder MUST&lt;br /&gt;
preserve it.&lt;br /&gt;
&lt;br /&gt;
Duplicate SACK (D-SACK, RFC 2883) is signalled in-band: no flag&lt;br /&gt;
bit, no extra framing.  Modular seqno arithmetic uses the&lt;br /&gt;
&amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators defined in the Notation block.&lt;br /&gt;
&lt;br /&gt;
Encoding.  When a duplicate is observed the receiver arms a&lt;br /&gt;
single-slot pending report (&amp;lt;code&amp;gt;dsack_seqno&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;dsack_valid&amp;lt;/code&amp;gt;,&lt;br /&gt;
latest-wins across multiple arms before the next emit).  On the&lt;br /&gt;
next outbound SACK the receiver prepends &amp;lt;code&amp;gt;block[0] = [dsack_seqno,&lt;br /&gt;
dsack_seqno + 1)&amp;lt;/code&amp;gt; - always a one-&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; range - and clears the&lt;br /&gt;
flag.  The three arm sites are listed in [[#10. Cumulative + selective ACK|Section 10]]; case-1 sites&lt;br /&gt;
yield &amp;lt;code&amp;gt;dsack_seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt; (the next &amp;lt;code&amp;gt;pci.ackno&amp;lt;/code&amp;gt;), and the&lt;br /&gt;
case-2 site (&amp;lt;code&amp;gt;rq_accept&amp;lt;/code&amp;gt; conflict) yields &amp;lt;code&amp;gt;dsack_seqno&amp;lt;/code&amp;gt; in&lt;br /&gt;
&amp;lt;code&amp;gt;[rcv_cr.lwe, rcv_cr.rwe)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Detection.  The sender classifies &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; by its relation to&lt;br /&gt;
&amp;lt;code&amp;gt;pci.ackno&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
;case 1 (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;before(blocks[0].start, pci.ackno)&amp;lt;/code&amp;gt; AND &amp;lt;code&amp;gt;pci.ackno - blocks[0].start &amp;amp;lt;= MAX_DSACK_LAG&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;== RQ_SIZE&amp;lt;/code&amp;gt;).  The lag bound rejects stale or spoofed reports beyond one receive window.&lt;br /&gt;
;case 2 (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;blocks[0]&amp;lt;/code&amp;gt; is a sub-range (with at least one endpoint differing) of some &amp;lt;code&amp;gt;blocks[i&amp;amp;gt;0]&amp;lt;/code&amp;gt; - i.e. the same packet&#039;s remaining SACK blocks already describe the duplicated &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; as received.&lt;br /&gt;
&lt;br /&gt;
On detect, the sender:&lt;br /&gt;
&lt;br /&gt;
* bumps &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; by 1, capped at &amp;lt;code&amp;gt;REO_WND_MULT_MAX&amp;lt;/code&amp;gt; (= 20), per RFC 8985 sec. 6.2 step 4;&lt;br /&gt;
* snapshots &amp;lt;code&amp;gt;dsack_lwe_snap = snd_cr.lwe&amp;lt;/code&amp;gt;, resetting the 16-cum-ACK halving counter so the multiplier doesn&#039;t decay while D-SACK evidence is still arriving;&lt;br /&gt;
* excludes &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; from the gap-marking loop (&amp;lt;code&amp;gt;n_real = n - 1&amp;lt;/code&amp;gt;), so a D-SACK alone never enters NewReno-careful recovery (see [[#8. Retransmission|Section 8]]); only non-D-SACK blocks count as gaps.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; halving cadence (once per 16 cumulatively-ACK&#039;d&lt;br /&gt;
seqnos since the most-recent D-SACK arrival or halve event) and&lt;br /&gt;
the reset-to-1 on a HoL RTO fire are both per the same RFC 8985&lt;br /&gt;
clause.  The clamp-and-skip path in the regular SACK-mark loop is&lt;br /&gt;
incidentally idempotent on any leftover case-1 or case-2 block&lt;br /&gt;
(&amp;lt;code&amp;gt;start &amp;amp;lt; snd_cr.lwe&amp;lt;/code&amp;gt; clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; and the inner loop&lt;br /&gt;
skips &amp;lt;code&amp;gt;k == snd_cr.lwe&amp;lt;/code&amp;gt;; case-2 re-NULLs slots already marked&lt;br /&gt;
received by later blocks), so block[0] is harmless even when fed&lt;br /&gt;
to the loop.&lt;br /&gt;
&lt;br /&gt;
=== 1.4. RTTP payload ===&lt;br /&gt;
&lt;br /&gt;
An RTTP (Round-Trip Time Probe) packet has only the &amp;lt;code&amp;gt;FRCT_RTTP&amp;lt;/code&amp;gt; flag&lt;br /&gt;
set (bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI,&lt;br /&gt;
the payload is 24 octets (packed):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          probe_id                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          echo_id                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +                  nonce (16 octets, echoed verbatim)           +&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:sender counter, 0 on reply, 0 reserved.&lt;br /&gt;
;&amp;lt;code&amp;gt;echo_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:peer&#039;s &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;, 0 on outbound probe.&lt;br /&gt;
;&amp;lt;code&amp;gt;nonce&amp;lt;/code&amp;gt;&lt;br /&gt;
:random, echoed unmodified, memcmp&#039;d to defeat spoof.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.5. Stream PCI extension ===&lt;br /&gt;
&lt;br /&gt;
A stream-mode flow (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;) carries an extra&lt;br /&gt;
8-octet extension after the 16-octet base PCI on every DATA packet&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            start                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                             end                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset of the first payload byte in the stream.&lt;br /&gt;
;&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset one past the last payload byte; &amp;lt;code&amp;gt;end - start&amp;lt;/code&amp;gt; equals the on-wire payload length.&lt;br /&gt;
&lt;br /&gt;
Total stream-mode PCI for DATA packets is 24 octets (16 base + 8&lt;br /&gt;
extension); control packets (SACK, RTTP, bare ACK, KA, etc.) retain&lt;br /&gt;
the 16-octet base PCI.  Stream mode MUST be negotiated at flow&lt;br /&gt;
allocation; the extension is present iff stream mode is in use,&lt;br /&gt;
never on a per-packet basis.  Both peers MUST treat &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; as&lt;br /&gt;
monotonic 32-bit byte offsets; when a slot reaches the head of the&lt;br /&gt;
contiguous run with &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; not equal to the prior packet&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; the&lt;br /&gt;
slot is silently dropped at delivery time ([[#16. Stream-mode flows|Section 16]]) rather&lt;br /&gt;
than rejected at stash.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC STREAM-frame reassembly model (RFC 9000 sec. 19.8):&lt;br /&gt;
each packet carries its packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (this PCI&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; field) and a&lt;br /&gt;
separate stream byte position (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;).  Separating the two&lt;br /&gt;
avoids TCP&#039;s conflation of packet identity with byte position which&lt;br /&gt;
forces Karn&#039;s algorithm for Round-Trip Time (RTT) sampling (no RTT&lt;br /&gt;
sample on retransmits, RFC 6298 sec. 3); FRCP applies the&lt;br /&gt;
Karn-equivalent gate via a combination of per-packet &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;,&lt;br /&gt;
per-slot &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; flags, and a sample-fence &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; (see [[#2.1. Per-flow state|Section 2.1]]&lt;br /&gt;
and [[#12. RTT estimation|Section 12]]).  FRCP&#039;s fixed-32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; wrap at 4 GiB of&lt;br /&gt;
wire bytes, narrower than QUIC&#039;s 62-bit varint offset (cf. RFC 9000&lt;br /&gt;
sec. 16); the on-wire wrap is handled by the same modular &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;&lt;br /&gt;
/ &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators ([[#1.3. SACK payload|Section 1.3]]) FRCP uses for seqnos, which&lt;br /&gt;
remain unambiguous as long as the in-flight byte window stays&lt;br /&gt;
strictly under 2 GiB (the half-range of the signed-int32 difference&lt;br /&gt;
in &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;).  The default per-flow ring is 1 MiB; the&lt;br /&gt;
implementation caps &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; at 128 MiB (&amp;lt;code&amp;gt;FRCT_STREAM_RING_SZ_MAX&amp;lt;/code&amp;gt;),&lt;br /&gt;
well below the 2 GiB half-range bound.  The runtime byte counters&lt;br /&gt;
exposed via FUSE (Filesystem in Userspace) in the Ouroboros&lt;br /&gt;
Resource Information Base (RIB, a virtual-filesystem introspection&lt;br /&gt;
bridge) are platform &amp;lt;code&amp;gt;size_t&amp;lt;/code&amp;gt; and do not wrap on 64-bit hosts.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2. Per-flow state and service modes ==&lt;br /&gt;
&lt;br /&gt;
=== 2.1. Per-flow state ===&lt;br /&gt;
&lt;br /&gt;
Each flow keeps a sender control record and a receiver control&lt;br /&gt;
record:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: oldest unacked &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (cumulative ACK boundary as seen by sender); rcv: next in-order &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; expected&lt;br /&gt;
;&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: peer-advertised right window edge; rcv: locally-advertised right window edge&lt;br /&gt;
;&amp;lt;code&amp;gt;cflags&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-direction feature flags: retransmission (&amp;lt;code&amp;gt;FRCTFRTX&amp;lt;/code&amp;gt;), receiver flow control (&amp;lt;code&amp;gt;FRCTFRESCNTL&amp;lt;/code&amp;gt;), linger-on-close (&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt;); see &amp;lt;code&amp;gt;&amp;amp;lt;ouroboros/fccntl.h&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: next &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; to send; rcv: force-ACK trigger - set on a stale or dup DATA so the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; emits a fresh cumulative ACK&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; counter for standalone ACK-bearing control packets (delayed ACK, SACK, final ACK on dealloc); not bumped on piggybacked ACK riding a DATA packet (which uses the DATA &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;).  Used by wire-dup ACK detection; rcv: incoming-ACK dedup tracker&lt;br /&gt;
;&amp;lt;code&amp;gt;act&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:last activity (used by inactivity / DRF)&lt;br /&gt;
;&amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:inactivity threshold; sender = &amp;lt;code&amp;gt;3*mpl + a + r + 1s&amp;lt;/code&amp;gt;, receiver = &amp;lt;code&amp;gt;2*mpl + a + r + 1s&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;mpl&amp;lt;/code&amp;gt; is the Maximum Packet Lifetime (delta-t terminology; see [[#15. Heritage and adopted techniques|Section 15]]); &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; are the FRCT a-timer and r-timer bounds (see [[#8. Retransmission|Section 8]]).  The asymmetry is load-bearing for pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
The sender holds a per-slot ring &amp;lt;code&amp;gt;snd_slots[RQ_SIZE]&amp;lt;/code&amp;gt; keyed by&lt;br /&gt;
&amp;lt;code&amp;gt;(seqno mod RQ_SIZE)&amp;lt;/code&amp;gt;.  Each slot tracks its retransmit entry (&amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;),&lt;br /&gt;
last-send timestamp, and retransmit flag bits: &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (a&lt;br /&gt;
retransmit is pending or has fired, gates the next RTT sample&lt;br /&gt;
under Karn) and &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (one-shot fast-retransmit staged for&lt;br /&gt;
this loss event).&lt;br /&gt;
&lt;br /&gt;
The receiver holds a parallel reorder ring &amp;lt;code&amp;gt;rcv_slots[RQ_SIZE]&amp;lt;/code&amp;gt;&lt;br /&gt;
(referred to as &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; in prose) holding stashed out-of-order&lt;br /&gt;
packet-buffer indexes; both FRTX and best-effort flows share this&lt;br /&gt;
path.  The invariant &amp;lt;code&amp;gt;rwe - lwe &amp;amp;lt;= RQ_SIZE&amp;lt;/code&amp;gt; holds: on each consume&lt;br /&gt;
the receiver advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; by the consumed count, capping the&lt;br /&gt;
receive window at &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; slots.&lt;br /&gt;
&lt;br /&gt;
A separate fence variable &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; is bumped on every retransmit&lt;br /&gt;
(timer-fire, SACK-driven, fast-rxm, NACK-driven) and on every&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; ([[#4. Sequence-number rotation (DRF)|Section 4]]) to mark the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; range whose RTT samples&lt;br /&gt;
MUST be discarded.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 2.2. Service modes (orthogonal axes) ===&lt;br /&gt;
&lt;br /&gt;
FRCP exposes its wire features as a vector of independent QoS&lt;br /&gt;
axes selected at flow allocation time.  All flows go through the&lt;br /&gt;
same &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive; the &amp;lt;code&amp;gt;qosspec_t&amp;lt;/code&amp;gt; passed&lt;br /&gt;
in determines which protocol machinery engages on the wire.  This&lt;br /&gt;
contrasts with the POSIX BSD socket model where TCP and UDP&lt;br /&gt;
require different socket types (&amp;lt;code&amp;gt;SOCK_STREAM&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;SOCK_DGRAM&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
The axes:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = unordered (no FRCP engagement: raw datagrams, no PCI on the wire, UDP-equivalent at this layer); 1 = message-ordered (FRCP engaged; SDU boundaries preserved across fragmentation); 2 = stream (byte-oriented, no SDU boundaries; FRTX required)&lt;br /&gt;
;&amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = lossless service requested: FRTX retransmit machinery engages ([[#8. Retransmission|Section 8]]); MUST be 0 for &amp;lt;code&amp;gt;service=2&amp;lt;/code&amp;gt;.  Non-zero = best-effort, FRTX off.&lt;br /&gt;
;&amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bit Error Rate tolerance.  0 = error-free service requested: a CRC trailer is appended after the body of DATA packets and verified on receive (added / checked outside the FRCP PCI; see [[#1.1. PCI header|Section 1.1]]).  Non-zero = peer accepts errors; trailer omitted.  SACK control packets carry a CRC32 trailer regardless of &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; gate applies to DATA only.&lt;br /&gt;
;&amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt;&lt;br /&gt;
:Peer-timeout (ms); 0 disables the keepalive timer.  Independent of FRCP engagement.&lt;br /&gt;
&lt;br /&gt;
Encryption is a separate per-flow attribute set at flow setup;&lt;br /&gt;
when enabled it wraps the FRCP packet (PCI + body, plus the CRC&lt;br /&gt;
trailer if any) under AEAD, expanding the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; by &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt;&lt;br /&gt;
octets (nonce / tag).  The CRC trailer is currently kept inside&lt;br /&gt;
the AEAD wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
&lt;br /&gt;
Reachable combinations exported by &amp;lt;code&amp;gt;include/ouroboros/qos.h&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Cube !! &amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; !! Engaged&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw&amp;lt;/code&amp;gt; || 0 || 1 || 1 || Raw passthrough&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw_safe&amp;lt;/code&amp;gt; || 0 || 1 || 0 || Raw + CRC trailer&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; || 1 || 1 || 1 || FRCP, no FRTX, no CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt_safe&amp;lt;/code&amp;gt; || 1 || 1 || 0 || FRCP, no FRTX, CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_msg&amp;lt;/code&amp;gt; || 1 || 0 || 0 || FRCP + FRTX&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_stream&amp;lt;/code&amp;gt; || 2 || 0 || 0 || FRCP + FRTX, stream&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Forced couplings actually enforced by the public API:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;service == SVC_STREAM&amp;lt;/code&amp;gt; (2) requires &amp;lt;code&amp;gt;loss == 0&amp;lt;/code&amp;gt;; &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;flow_accept&amp;lt;/code&amp;gt; reject the pair otherwise with &amp;lt;code&amp;gt;-EINVAL&amp;lt;/code&amp;gt;.&lt;br /&gt;
* FRTX requires FRCP engagement (&amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;); requesting &amp;lt;code&amp;gt;loss = 0&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;service = SVC_RAW&amp;lt;/code&amp;gt; is structurally a no-op because no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; is created.&lt;br /&gt;
* The &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; build flag globally forces &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;.  Note: this flag defaults to ON, so default builds ship with CRC disabled until &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; is set to OFF.&lt;br /&gt;
&lt;br /&gt;
Caveat: the API does NOT force &amp;lt;code&amp;gt;ber = 0&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; has &amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;, which means the PCI&lt;br /&gt;
itself is not CRC-protected on that cube; the HCS ([[#1.1. PCI header|Section 1.1]])&lt;br /&gt;
remains the only integrity check on the header.&lt;br /&gt;
&lt;br /&gt;
The FRCP-no-FRTX regime (&amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;loss &amp;amp;gt; 0&amp;lt;/code&amp;gt;) is meaningful&lt;br /&gt;
and live: sequence numbering, in-order delivery, flow-control&lt;br /&gt;
advertisement, KA, DRF rotation, and SDU fragmentation /&lt;br /&gt;
reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]) all run.  Lost packets are dropped&lt;br /&gt;
rather than retransmitted; a permanently-lost mid-fragment is&lt;br /&gt;
dropped via skip-past-gap once a later SDU is visible in the&lt;br /&gt;
reorder ring.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. Protocol parameters ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Slot ring / rcv window width&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;START_WINDOW&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Initial &amp;lt;code&amp;gt;rwe-lwe&amp;lt;/code&amp;gt; after rotate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MAX(250 us build-tunable, 1&amp;amp;lt;&amp;amp;lt;RXMQ_RES)&amp;lt;/code&amp;gt;; per-flow via &amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;FRCTSRTOMIN&amp;lt;/code&amp;gt;).  Default ~1 ms with &amp;lt;code&amp;gt;RXMQ_RES=20&amp;lt;/code&amp;gt;. || RTO floor; also floored at the retransmit-wheel resolution (~1 ms by default).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt; || 20 || Backoff shift cap&lt;br /&gt;
|-&lt;br /&gt;
| RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MIN(reo_wnd_mult * min_RTT/4, SRTT)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; scales on D-SACK, cap 20 || Reorder window; per RFC 8985 sec. 6.2; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per sec. 6.2 step 4&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; || 300 s (5 min, Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; windowed re-anchor&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_WND_MULT_MAX&amp;lt;/code&amp;gt; || 20 (RFC 8985 sec. 6.2 step 4) || &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_DECAY_PKTS&amp;lt;/code&amp;gt; || 16 (RFC 8985 sec. 6.2 step 4 / &amp;lt;code&amp;gt;RACK.reo_wnd_persist&amp;lt;/code&amp;gt;) || Fresh-ACK&#039;d seq count per halving&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_DSACK_LAG&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || D-SACK sanity cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTT_QUARANTINE&amp;lt;/code&amp;gt; || 32 (&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; steps) || NewReno gate pad&lt;br /&gt;
|-&lt;br /&gt;
| SACK rate-limit || &amp;lt;code&amp;gt;SACK_MIN_GAP_NS&amp;lt;/code&amp;gt; (250 us, fixed) || Min SACK gap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; || 2048 (wire cap; per-flow capped at &amp;lt;code&amp;gt;(frag_mtu-PCI-4)/8&amp;lt;/code&amp;gt;) || Per-SACK block cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; || 32 || Per-pass staged retransmit cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; || 3 (RFC 8985 default) || Hybrid fast-rxm trigger ([[#8. Retransmission|Section 8]])&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MDEV_MUL&amp;lt;/code&amp;gt; || 2 (build-tunable via &amp;lt;code&amp;gt;FRCT_RTO_MDEV_MULTIPLIER&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; shift in &amp;lt;code&amp;gt;RTO = srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| RTTP nonce || 16 octets || Echoed verbatim&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; || 8 || In-flight probes&lt;br /&gt;
|-&lt;br /&gt;
| RTT clamp || &amp;lt;code&amp;gt;16 * srtt&amp;lt;/code&amp;gt; || Probe-sample upper bound (ACK-derived RTT samples gated by Karn / recovery only)&lt;br /&gt;
|-&lt;br /&gt;
| Cold-probe cadence || 100 ms (rx-driven; see [[#12. RTT estimation|Section 12]]) || Pre-&amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; RTTP rate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt; || 100 ms || RDVS emit cadence&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; || 1 s || RDVS give-up&lt;br /&gt;
|-&lt;br /&gt;
| Delayed-ACK fire || &amp;lt;code&amp;gt;2 * TICTIME&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt; = FRCT tick granularity, default 5 ms; &amp;lt;code&amp;gt;2*TICTIME = 10 ms&amp;lt;/code&amp;gt; by default) || Fired after the first in-order DATA arrival; tick is build-tunable&lt;br /&gt;
|-&lt;br /&gt;
| NACK send cooldown || &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; when an &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; sample exists, else 100 ms || Pre-DRF NACK rate-limit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_SDU&amp;lt;/code&amp;gt; || 1 MiB || Max reassembled SDU; configurable per flow&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The per-flow fragment Maximum Transmission Unit (MTU) is computed&lt;br /&gt;
at flow setup from the lower IPCP&#039;s mtu minus encryption&lt;br /&gt;
&amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; and CRC trailer; there is no FRCT-level default or&lt;br /&gt;
environment-variable override.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 4. Sequence-number rotation (DRF) ==&lt;br /&gt;
&lt;br /&gt;
The DRF (Data Run Flag) bit on an outbound packet means &amp;quot;this is&lt;br /&gt;
the start of a fresh data run&amp;quot; and is set whenever the sender has&lt;br /&gt;
nothing in flight (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Independently of that, if the sender has been idle longer than&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.inact&amp;lt;/code&amp;gt; AND the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;),&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; rolls a random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; before the send and&lt;br /&gt;
resets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    snd_cr.seqno  = random()&lt;br /&gt;
    snd_cr.lwe    = snd_cr.seqno&lt;br /&gt;
    snd_cr.rwe    = snd_cr.seqno + START_WINDOW&lt;br /&gt;
    rtt_lwe       = snd_cr.seqno&lt;br /&gt;
    in_recovery   = false   (recovery state, see Section 8)&lt;br /&gt;
    recovery_high = snd_cr.seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The receiver, on observing rcv-side inactivity&lt;br /&gt;
(&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), requires a DRF on the next&lt;br /&gt;
DATA packet; otherwise it replies with a rate-limited NACK (see&lt;br /&gt;
below).  Non-DATA control packets pass through without the DRF&lt;br /&gt;
requirement.  On DRF the receiver releases the &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots and&lt;br /&gt;
rebases&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    rcv_cr.lwe   = seqno&lt;br /&gt;
    rcv_cr.rwe   = seqno + RQ_SIZE&lt;br /&gt;
    rcv_cr.seqno = seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the inactive packet has DATA but no DRF, a rate-limited NACK is&lt;br /&gt;
fired back to the sender (cooldown per [[#3. Protocol parameters|Section 3]]); non-DATA stale&lt;br /&gt;
arrivals fall through to normal processing (no NACK, no drop).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 5. Send path ==&lt;br /&gt;
&lt;br /&gt;
# If the SDU exceeds &amp;lt;code&amp;gt;(frag_mtu - data_hdr_len)&amp;lt;/code&amp;gt;, the caller (&amp;lt;code&amp;gt;dev.c&amp;lt;/code&amp;gt;) fans it out into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - data_hdr_len))&amp;lt;/code&amp;gt; fragments, each emitted via &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; as its own DATA packet with a per-fragment role ([[#7.2. Fragmentation and reassembly|Section 7.2]]); both FRTX and best-effort flows fragment.  Raw flows (no FRCP engagement, &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) carry no PCI and return &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; for any SDU larger than one packet at the layer below.  An SDU that fits in a single packet is sent as SOLE.  &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; reserves PCI head room; sets DATA, plus DRF when the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
# &amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; if past sender inactivity and the pipe is empty ([[#4. Sequence-number rotation (DRF)|Section 4]]).&lt;br /&gt;
# Advertise FC (&amp;lt;code&amp;gt;pci.window = frcti_advert_rwe(frcti)&amp;lt;/code&amp;gt;, i.e. &amp;lt;code&amp;gt;rcv_cr.rwe&amp;lt;/code&amp;gt; clamped to &amp;lt;code&amp;gt;rcv_cr.lwe + ring_seq_cap&amp;lt;/code&amp;gt; in stream mode) when the receiver side is recent: &amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt; rcv_cr.inact&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Reliable mode (FRTX): leave &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; where it is; reset the slot at &amp;lt;code&amp;gt;RQ_SLOT(seqno)&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;snd_slots[p].time = now&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_slots[p].flags = 0&amp;lt;/code&amp;gt;); queue an &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; (saves a packet copy, arms a wheel timer at &amp;lt;code&amp;gt;now + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;).  Piggyback ACK (&amp;lt;code&amp;gt;pci.ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) while the a-timer for the most recent received DATA packet has not yet expired (&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt;= t_a&amp;lt;/code&amp;gt;); on piggyback, set &amp;lt;code&amp;gt;rcv_cr.seqno = rcv_cr.lwe&amp;lt;/code&amp;gt; so the next delayed-ACK fire is suppressed.  See [[#8. Retransmission|Section 8]] for &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; semantics.&lt;br /&gt;
# Best-effort mode (no FRTX): advance &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; immediately (&amp;lt;code&amp;gt;snd_cr.lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_cr.rwe = snd_cr.lwe + RQ_SIZE&amp;lt;/code&amp;gt;); no retransmit state.  No send-side RTT probe is armed in this mode (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt; requires an in-flight &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, which best-effort never has); the rx-driven cold seeder in &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; is the only probe path.&lt;br /&gt;
# In reliable mode, optionally arm an RTT probe ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 6. Receive path ==&lt;br /&gt;
&lt;br /&gt;
=== 6.1. Early-exit dispatch ===&lt;br /&gt;
&lt;br /&gt;
Keepalive (KA), RTT probe (RTTP), pre-DRF NACK, and rendezvous&lt;br /&gt;
(RDVS) packets short-circuit out of &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; before the locked&lt;br /&gt;
main path; each handler takes its own lock internally.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
      incoming packet&lt;br /&gt;
            |&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | KA?     |---yes--&amp;gt; ka_rcv  ; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RTTP?   |---yes--&amp;gt; rttp_rcv; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | NACK?   |---yes--&amp;gt; nack_rcv; return  (see Section 9)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RDVS?   |---yes--&amp;gt; rdv_rcv ; return  (reply bare FC, ackno=0)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       acquire wrlock; enter locked main path&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt;&lt;br /&gt;
:refresh &amp;lt;code&amp;gt;t_ka_rcv&amp;lt;/code&amp;gt;, honour piggybacked ACK.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt;&lt;br /&gt;
:probe (echo back nonce) or echo (verify nonce, sample RTT).&lt;br /&gt;
;&amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:pre-DRF, sender-side handler.  See [[#9. Pre-DRF NACK|Section 9]].&lt;br /&gt;
;&amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt;&lt;br /&gt;
:reply with a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;); &amp;lt;code&amp;gt;rdlock&amp;lt;/code&amp;gt; only.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 6.2. Locked main path ===&lt;br /&gt;
&lt;br /&gt;
Steps below run with the per-flow &amp;lt;code&amp;gt;frcti.lock&amp;lt;/code&amp;gt; held for writing&lt;br /&gt;
(&amp;lt;code&amp;gt;pthread_rwlock_wrlock&amp;lt;/code&amp;gt;) unless noted.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;&lt;br /&gt;
:Only meaningful when the receive side is stale.  On DRF (Data Run Flag): release &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots, rebase &amp;lt;code&amp;gt;rcv_cr&amp;lt;/code&amp;gt;, continue.  On stale DATA without DRF: fire a pre-DRF NACK if cooldown allows ([[#9. Pre-DRF NACK|Section 9]]), then discard the packet; on cooldown, drop without sending a NACK (a pending cumulative ACK from &amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; may still go out).  Non-DATA, non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; pure-DRF stale arrivals fall through after the DRF rebase branch.&lt;br /&gt;
&lt;br /&gt;
;DATA-only act refresh&lt;br /&gt;
:Refresh &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; only when &amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt; is set, so that non-DATA packets never block the next DRF rebase.&lt;br /&gt;
&lt;br /&gt;
;Wire-dup gate&lt;br /&gt;
:Before flag-driven dispatch, drop wire-duplicate ACKs and wire-duplicate DATA (&amp;lt;code&amp;gt;is_dup_ack&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt;).  The DATA check is bypassed for &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;-bearing arrivals so the piggybacked ACK / SACK / FC carried on a retransmitted DATA at an already-ACK&#039;d &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is still applied; the stale-in-window branch below then drops the packet.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Drop ACKs whose &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; falls outside &amp;lt;code&amp;gt;(snd_cr.lwe, snd_cr.seqno]&amp;lt;/code&amp;gt;.  If &amp;lt;code&amp;gt;ackno == snd_cr.lwe&amp;lt;/code&amp;gt; (non-advancing cumulative ACK), drive RACK fast-retransmit consideration ([[#8. Retransmission|Section 8]]).  Otherwise advance &amp;lt;code&amp;gt;snd_cr.lwe = ackno&amp;lt;/code&amp;gt;, collapse &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; to 0 (Karn-gated by &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; on the just-acknowledged slot, the old head-of-line), reset &amp;lt;code&amp;gt;dup_thresh&amp;lt;/code&amp;gt; to 0, update &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the send-time of the slot at &amp;lt;code&amp;gt;ackno-1&amp;lt;/code&amp;gt; (consumed by RACK and SACK below), decay &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per RFC 8985 sec. 6.2 step 4, exit NewReno-careful recovery (see [[#8. Retransmission|Section 8]]) on &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt;, and feed an RTT sample if eligible ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Walk the block list.  For each block (a present range above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;) NULL out &amp;lt;code&amp;gt;snd_slots[k].rxm&amp;lt;/code&amp;gt;, clear the slot&#039;s per-send flags, and advance &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the latest send-time covered (the Forward Acknowledgement / fack equivalent, Mathis &amp;amp;amp; Mahdavi 1996); the first block whose start clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; skips this fack update so that a head-of-line clamp does not falsely advance fack.  For un-SACKed gaps below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt;, stage a retransmit per slot that is (1) still owned (&amp;lt;code&amp;gt;rxm != NULL&amp;lt;/code&amp;gt;), (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (the RFC 8985 sec. 6.2 hybrid trigger).  Mark the slot &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; and NULL the &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; at stage time.  Capped at &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; staged retransmits per receive pass; what&#039;s left rides the next SACK.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bump &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; (clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt;, never shrinks) and mark window open.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bounds-check &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; against window.  On stale-dup (&amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;), set &amp;lt;code&amp;gt;rcv_cr.seqno = seqno&amp;lt;/code&amp;gt; to force a fresh ACK on the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt;, then drop.  On accept: both FRTX and best-effort stash the packet-buffer index into &amp;lt;code&amp;gt;rq[seqno mod RQ_SIZE]&amp;lt;/code&amp;gt;.  Fragments stash unchanged - the role bits are inspected only at consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]).  On out-of-order arrival, build a SACK reply if not rate-limited (per [[#3. Protocol parameters|Section 3]]) and not deduplicated against the previous &amp;lt;code&amp;gt;(rcv_cr.lwe, n_blocks)&amp;lt;/code&amp;gt; pair; D-SACK reports always bypass the dedup.  If both rate-limit and dedup suppress the reply, neither SACK nor delayed-ACK fires (the sender picks up the gap on its next ACK).  On in-order arrival, arm the delayed-ACK timer.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; exit&lt;br /&gt;
:Releases the per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;), then calls &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; synchronously after the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; release to surface any pending cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 7. Read path and reassembly ==&lt;br /&gt;
&lt;br /&gt;
=== 7.1. Read path ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns a full reassembled SDU (Service Data Unit) via&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; on every FRCP SDU-mode flow (FRTX or best-effort);&lt;br /&gt;
stream-mode is covered in [[#16. Stream-mode flows|Section 16]].  An incomplete head-of-line&lt;br /&gt;
(HoL) run yields &amp;lt;code&amp;gt;-EAGAIN&amp;lt;/code&amp;gt;; an oversized run yields &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; (the&lt;br /&gt;
run is dropped so the flow does not stall).  On best-effort flows,&lt;br /&gt;
a permanently-lost mid-fragment is dropped as soon as a later&lt;br /&gt;
complete SDU becomes visible in the ring ([[#7.2. Fragmentation and reassembly|Section 7.2]] skip-past-&lt;br /&gt;
gap).&lt;br /&gt;
&lt;br /&gt;
Raw flows carry no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt;, so &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns the next pending&lt;br /&gt;
packet-buffer index directly, with no role-bit inspection.  (Raw&lt;br /&gt;
service is selected via &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt; at flow allocation,&lt;br /&gt;
which suppresses &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; creation.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_pdu_ready&amp;lt;/code&amp;gt; is the no-advance peek used by &amp;lt;code&amp;gt;fevent&amp;lt;/code&amp;gt; (the&lt;br /&gt;
Ouroboros flow-event multiplexer, the &amp;lt;code&amp;gt;poll(2)&amp;lt;/code&amp;gt;-equivalent on&lt;br /&gt;
flows).  It returns ready only when the head-of-line run is&lt;br /&gt;
complete and the lead packet (a Protocol Data Unit, here one FRCP&lt;br /&gt;
packet) is present at &amp;lt;code&amp;gt;rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt;; any other state&lt;br /&gt;
(including the best-effort skip-past-gap case) returns not ready,&lt;br /&gt;
and &amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; is left to drop the broken prefix and re-&lt;br /&gt;
inspect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 7.2. Fragmentation and reassembly ===&lt;br /&gt;
&lt;br /&gt;
Send side (&amp;lt;code&amp;gt;flow_write_frag&amp;lt;/code&amp;gt;).  An SDU larger than&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - PCI)&amp;lt;/code&amp;gt; is split into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - PCI))&amp;lt;/code&amp;gt;&lt;br /&gt;
fragments; each fragment is its own FRCP packet with its own&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a per-fragment role flag pair ([[#1.2. Flag bits|Section 1.2]]).  Roles are&lt;br /&gt;
assigned at emit time:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! i !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=0&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=n-1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| else || &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A mid-loop allocation or transmit failure may yield a partial&lt;br /&gt;
write: the call returns the bytes already enqueued (&amp;lt;code&amp;gt;off &amp;amp;gt; 0&amp;lt;/code&amp;gt;) or&lt;br /&gt;
the underlying error (&amp;lt;code&amp;gt;off == 0&amp;lt;/code&amp;gt;).  Best-effort flows fragment&lt;br /&gt;
identically; on the receiver, a partial run with a permanently-&lt;br /&gt;
lost fragment is dropped when a later complete SDU is visible in&lt;br /&gt;
the ring (see skip-past-gap below).  Raw flows carry no PCI and&lt;br /&gt;
refuse anything larger than the layer&#039;s user MTU (&amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Wire-level recovery is fragment-agnostic on FRTX flows: each&lt;br /&gt;
fragment&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; flows through SACK / RACK / RTO / NACK exactly&lt;br /&gt;
as for a SOLE DATA packet, and reassembly does not re-enter the&lt;br /&gt;
loss-detection path.  Best-effort flows run the same &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
machinery (DRF, FC, ACK piggyback, pre-DRF NACK emit) but queue&lt;br /&gt;
no &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; state at the sender, so a lost MID is unrecoverable;&lt;br /&gt;
skip-past-gap handles it (below).&lt;br /&gt;
&lt;br /&gt;
Receive side.  Fragments stash into &amp;lt;code&amp;gt;rq[seqno]&amp;lt;/code&amp;gt; unchanged; role bits&lt;br /&gt;
are read only at consume time.  &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;, called from&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt;, walks the ring starting at the oldest still-&lt;br /&gt;
undelivered &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;base = rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt; (equal to &amp;lt;code&amp;gt;rcv_cr.lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
only when no partial run is in progress; during a partial run &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
has already advanced past &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;).  It produces one of three&lt;br /&gt;
outcomes:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Outcome !! Cause&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELIVER (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]=SOLE&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;), or &amp;lt;code&amp;gt;rq[base]=FIRST&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; follows in slots &amp;lt;code&amp;gt;[base+1..base+n-1]&amp;lt;/code&amp;gt; with all intermediate roles in &amp;lt;code&amp;gt;{MID,FIRST,LAST}&amp;lt;/code&amp;gt; contiguous.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DROP (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; without a preceding &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;); a &amp;lt;code&amp;gt;FIRST..[non-LAST]..new-FIRST&amp;lt;/code&amp;gt; or new-&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; mid-run (drop the broken prefix with &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; = run length minus 1, so the new &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; stays); or, on best-effort flows, a gap at &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; later in the ring (drop up to the new run start).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; absent or &amp;lt;code&amp;gt;FIRST..[non-LAST]&amp;lt;/code&amp;gt; with no later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; in the ring (FRTX waits for retx; best-effort waits for arrival).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DELIVER&amp;lt;/code&amp;gt; triggers &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;: a scatter-gather &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; of the &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;&lt;br /&gt;
consecutive fragments at &amp;lt;code&amp;gt;rq[base..base+n-1]&amp;lt;/code&amp;gt; directly into the&lt;br /&gt;
caller&#039;s buffer; each per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;) is&lt;br /&gt;
released and &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; advances by &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; was already advanced&lt;br /&gt;
incrementally as each contiguous fragment arrived; &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;&lt;br /&gt;
only restores the fixed-width invariant &amp;lt;code&amp;gt;rwe == lwe + RQ_SIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
No intermediate reassembly buffer is allocated.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; past the broken prefix (releasing the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;s)&lt;br /&gt;
and pulls &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; up to the new trailing edge if needed; the next&lt;br /&gt;
consume retries from the new &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;.  Oversize or arithmetically&lt;br /&gt;
overflowing delivery (sum of fragment lengths &amp;amp;gt; &amp;lt;code&amp;gt;max_rcv_sdu&amp;lt;/code&amp;gt;, sum&lt;br /&gt;
&amp;amp;gt; caller&#039;s buffer, or running-sum overflow) also drops the run&lt;br /&gt;
with &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skip-past-gap (best-effort only).  On FRTX, a gap in the run means&lt;br /&gt;
&amp;quot;waiting for retransmit&amp;quot; and &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;.&lt;br /&gt;
On best-effort flows the gap is permanent, so &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;&lt;br /&gt;
scans forward in the ring for the next &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;; if one is&lt;br /&gt;
visible within &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;, it returns &amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; for the broken prefix and&lt;br /&gt;
the consume loop retries at the new &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;.  Memory hold is bounded&lt;br /&gt;
by &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;; the partial releases on the next consume call once a&lt;br /&gt;
later complete run exists.  Voice-like flows (one &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; per SDU)&lt;br /&gt;
see no extra wait: any later &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; makes the prior gap droppable&lt;br /&gt;
immediately.&lt;br /&gt;
&lt;br /&gt;
The choice to defer reassembly to consume time keeps the receive&lt;br /&gt;
path zero-copy: fragments stay in the shared-memory ring until&lt;br /&gt;
the application pulls, and the SDU lands directly in the caller&#039;s&lt;br /&gt;
buffer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 8. Retransmission ==&lt;br /&gt;
&lt;br /&gt;
FRCP is bounded by two delta-t-derived timers (Watson 1981, see&lt;br /&gt;
[[#15. Heritage and adopted techniques|Section 15]]):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer): upper bound on ACK delay.  An ACK for a received DATA packet MUST be emitted within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; of receipt; an attempt to send an ACK after the a-timer has expired is suppressed (the sender&#039;s RTO is already in motion).&lt;br /&gt;
* &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer): upper bound on retransmission.  A given DATA packet MUST NOT be retransmitted after &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; has elapsed since its first send (&amp;lt;code&amp;gt;t0&amp;lt;/code&amp;gt;); when the bound is hit, the flow is declared down (raising the Ouroboros asynchronous flow condition &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt;, which marks the flow dead to both endpoints) rather than retransmitted again.&lt;br /&gt;
&lt;br /&gt;
Each in-flight FRTX &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; owns one &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt;, armed in a hashed&lt;br /&gt;
timing wheel; the wheel deadline is the slot&#039;s next eligible&lt;br /&gt;
retransmit time.&lt;br /&gt;
&lt;br /&gt;
;RTO timer&lt;br /&gt;
:On fire (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;), re-emit with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, mark &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (Karn-suppress next ACK&#039;s RTT sample), and (for the head-of-line (HoL) slot only) bump &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; up to &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt;.  Wheel deadline is &amp;lt;code&amp;gt;t_send + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;.  Re-armed unless consumed.  The RTO timer also clears &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (re-arming fast-retransmit eligibility), resets &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; to 1 on a HoL fire (RFC 8985 sec. 6.2 step 4 reset clause), and marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; if its &amp;lt;code&amp;gt;frct_tx&amp;lt;/code&amp;gt; call fails.&lt;br /&gt;
&lt;br /&gt;
;r-timer guard&lt;br /&gt;
:Before any retransmit attempt, check &amp;lt;code&amp;gt;(now - t0)&amp;lt;/code&amp;gt; against &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;.  If exceeded, the slot is no longer eligible for retransmit.  Only the RTO timer (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;) treats r-timer expiry as terminal: it marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; (peer unreachable).  Fast-retransmit, SACK-driven retransmit, and NACK-driven head-of-line re-emit silently skip aged-out slots and defer the flow-down decision to the next RTO fire.&lt;br /&gt;
&lt;br /&gt;
;Fast retransmit (hybrid trigger, RFC 8985 sec. 6.2)&lt;br /&gt;
:On a non-advancing cumulative ACK with the scoreboard advanced, fire one fast retransmit when EITHER (a) the head-of-line slot&#039;s latest send is older than the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; ([[#3. Protocol parameters|Section 3]]) and not yet aged out, OR (b) the SACK &amp;lt;code&amp;gt;dup-thresh&amp;lt;/code&amp;gt; count above &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; reaches &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; (= 3, RFC 8985 sec. 6.2 step 4).  Fires at most once per non-advancing cumulative-ACK value, gated by &amp;lt;code&amp;gt;rack_fired_lwe&amp;lt;/code&amp;gt; (the &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; at which fast-retransmit last fired).  Set &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; on the slot (one-shot per-slot gate) and enter NewReno-style careful recovery (see NewReno below in this section).&lt;br /&gt;
:The RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; uses the RFC 8985 sec. 6.2 form &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor.  Before the first RTT sample seeds &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; falls back to &amp;lt;code&amp;gt;MIN(reo_wnd_mult * SRTT / 4, SRTT)&amp;lt;/code&amp;gt;, still floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt; (consistent with the windowed-minimum fallback described in [[#12. RTT estimation|Section 12]]).  &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt; is a windowed minimum over the last &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; = 5 min of RTT samples (matches the Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt; default) so a route change to a longer path eventually re-anchors the reorder window without relying on &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; growth alone.&lt;br /&gt;
&lt;br /&gt;
;SACK-driven retransmit&lt;br /&gt;
:For each gap below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt; whose slot is (1) still owned, (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (same hybrid as fast-retransmit, see [[#6.2. Locked main path|Section 6.2]]), re-emit.  Each SACK-driven retransmit re-arms a fresh &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; so a lost retransmit can still be recovered by its own RTO timer.&lt;br /&gt;
&lt;br /&gt;
;NewReno&lt;br /&gt;
:On entry, &amp;lt;code&amp;gt;recovery_high = snd_cr.seqno + RTT_QUARANTINE&amp;lt;/code&amp;gt;.  Exit when &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt; (the latter means everything sent has been acknowledged).  &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; also clears recovery.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 9. Pre-DRF NACK ==&lt;br /&gt;
&lt;br /&gt;
The two sides have different inactivity thresholds (&amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), so a receiver can detect &amp;quot;stale data run&amp;quot; before the sender&#039;s own DRF logic kicks in.  NACK is the receiver-driven nudge that asks the sender to re-transmit the head of the run.&lt;br /&gt;
&lt;br /&gt;
;Send (&amp;lt;code&amp;gt;frcti_nack_snd&amp;lt;/code&amp;gt;, called by &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;FRCT_INACT_NEED_NACK&amp;lt;/code&amp;gt;)&lt;br /&gt;
:When an incoming DATA packet has no DRF and rcv-side activity is older than &amp;lt;code&amp;gt;rcv_cr.inact&amp;lt;/code&amp;gt;, the receiver emits a bare packet with &amp;lt;code&amp;gt;flags = FRCT_NACK&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;seqno = arrival_seqno - 1&amp;lt;/code&amp;gt; (informational only, not consulted by the receive handler).  The cooldown in [[#3. Protocol parameters|Section 3]] rate-limits the burst.  Non-DATA non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; non-DATA DRF still rebases via the DRF branch.&lt;br /&gt;
&lt;br /&gt;
;Receive (&amp;lt;code&amp;gt;frcti_nack_rcv&amp;lt;/code&amp;gt;)&lt;br /&gt;
:Dispatched in the early-exit branch ([[#6.1. Early-exit dispatch|Section 6.1]]), before &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;.  The sender copies the head-of-line (HoL) &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; packet, marks the slot &amp;lt;code&amp;gt;SND_RTX | SND_FAST_RXM&amp;lt;/code&amp;gt; (Karn-suppress next ACK, one-shot fast-rxm gate), sets &amp;lt;code&amp;gt;rtt_lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, and re-emits via &amp;lt;code&amp;gt;fast_rxm_send&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt; and a refreshed &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;.  The original &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed - the NACK emit is additive to the normal retransmit machinery, not a replacement.  No-op if nothing is in flight, the HoL slot has aged past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, or the HoL &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; pointer has been cleared by SACK or RACK.&lt;br /&gt;
&lt;br /&gt;
NACK has exactly one role: lost first-of-run (DRF) packet recovery. Until the DRF packet arrives, the receiver cannot rebase its window, so any subsequent in-flight packets look stale to the receiver. The NACK fires the moment a stale receiver sees DATA without DRF, telling the sender to re-emit the head-of-line (DRF) packet at NACK-cooldown latency rather than waiting for the initial RTO (which is the configured default until &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded by the first probe round-trip).  Mid-stream loss is NOT NACK-driven; it is recovered by the sender&#039;s RTO, fast retransmit, and SACK-driven retransmit paths ([[#8. Retransmission|Section 8]]) only.&lt;br /&gt;
&lt;br /&gt;
The existing &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed on a NACK re-emit, so the RTO path remains the eventual fallback.&lt;br /&gt;
&lt;br /&gt;
== 10. Cumulative + selective ACK ==&lt;br /&gt;
&lt;br /&gt;
Cumulative ACK is &amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;.  On out-of-order arrival the&lt;br /&gt;
receiver also emits a SACK packet ([[#1.3. SACK payload|Section 1.3]]) whose payload lists&lt;br /&gt;
&#039;&#039;present&#039;&#039; blocks above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; (analogous to TCP SACK / QUIC ACK&lt;br /&gt;
ranges).  SACKs are rate-limited per [[#3. Protocol parameters|Section 3]] and suppressed when&lt;br /&gt;
neither &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; nor block count has changed since the last SACK.&lt;br /&gt;
&lt;br /&gt;
D-SACK reports (RFC 2883) are emitted in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; of an&lt;br /&gt;
otherwise normal SACK frame (see [[#1.3. SACK payload|Section 1.3]] for the encoding).&lt;br /&gt;
Two receiver triggers arm a pending D-SACK report (single-slot,&lt;br /&gt;
latest-wins):&lt;br /&gt;
&lt;br /&gt;
* DATA arrival with &amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;, both wire-dup (no RXM, &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt; path) and retransmit (RXM, post-FC branch) (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
* &amp;lt;code&amp;gt;rq_accept&amp;lt;/code&amp;gt; conflict, slot already occupied in &amp;lt;code&amp;gt;[lwe, rwe)&amp;lt;/code&amp;gt; (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
&lt;br /&gt;
When a D-SACK is pending and the standard scoreboard SACK would be&lt;br /&gt;
suppressed by dedup or rate-limit, the report is emitted as a&lt;br /&gt;
stand-alone SACK frame through the normal &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; path; when a&lt;br /&gt;
D-SACK report is pending the path bypasses dedup and the &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;&lt;br /&gt;
rate-limit, but the a-timer suppression on rcv inactivity still&lt;br /&gt;
applies.&lt;br /&gt;
&lt;br /&gt;
Bare ACKs are deferred via a per-flow delayed-ACK timer (one in&lt;br /&gt;
flight at a time, atomic test-and-set dedup; fires per [[#3. Protocol parameters|Section 3]]&lt;br /&gt;
after the first in-order arrival).  Suppressed if (1) no new&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, (2) rcv side is inactive (older than &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt;), or (3) the&lt;br /&gt;
sender just sent within &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;.  A pending D-SACK ride-through&lt;br /&gt;
bypasses (1) and (3); the a-timer gate (2) is unconditional.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 11. Flow control ==&lt;br /&gt;
&lt;br /&gt;
The receiver advertises &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; in every FC field.  The sender treats&lt;br /&gt;
its &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; as the absolute right edge: when&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt;= snd_cr.rwe&amp;lt;/code&amp;gt; the window is closed and &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt;&lt;br /&gt;
yields.  While closed, the sender periodically emits RDVS&lt;br /&gt;
(rendezvous) packets (cadence &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt;); the receiver replies with&lt;br /&gt;
a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;) that reopens the window.  Once the&lt;br /&gt;
window has been closed for longer than &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; the sender stops&lt;br /&gt;
emitting RDVS but does not tear the flow down - the writer keeps&lt;br /&gt;
blocking until either a peer-driven FC arrives or the KA&lt;br /&gt;
(keepalive) / r-timer marks the flow.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt; on receipt and MUST NOT shrink:&lt;br /&gt;
a backward &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is silently clamped to the current &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt;;&lt;br /&gt;
the FC packet still reopens the window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 12. RTT estimation ==&lt;br /&gt;
&lt;br /&gt;
Active RTTP probes ([[#1.4. RTTP payload|Section 1.4]]) carry a 32-bit &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt; (0&lt;br /&gt;
reserved) and a 16-byte random nonce echoed verbatim - defends&lt;br /&gt;
against spoofed replies.  A ring of &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; in-flight probes is&lt;br /&gt;
kept; an echo whose &amp;lt;code&amp;gt;(id, nonce)&amp;lt;/code&amp;gt; doesn&#039;t match the ring slot is&lt;br /&gt;
dropped.  A single RTTP sample is clamped to &amp;lt;code&amp;gt;RTT_CLAMP_MUL * srtt&amp;lt;/code&amp;gt;&lt;br /&gt;
(compile-time &amp;lt;code&amp;gt;RTT_CLAMP_MUL = 16&amp;lt;/code&amp;gt;) once &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded; the first&lt;br /&gt;
cold-probe sample feeds &amp;lt;code&amp;gt;rtt_update&amp;lt;/code&amp;gt; raw.&lt;br /&gt;
&lt;br /&gt;
Probe arming gates:&lt;br /&gt;
&lt;br /&gt;
;Cold (no &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; yet)&lt;br /&gt;
:the receive path arms at most one probe per 100 ms via &amp;lt;code&amp;gt;frcti_rcv_probe&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;PROBE_DUE_COLD&amp;lt;/code&amp;gt;); arming requires an incoming packet.  Active send-path arming bails while &amp;lt;code&amp;gt;srtt == 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
;Warm (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt;, called from &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt;)&lt;br /&gt;
:outstanding data (&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt; snd_cr.lwe&amp;lt;/code&amp;gt;), AND at least &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_rcv_rtt&amp;lt;/code&amp;gt; (last RTT receive of any kind), AND at least &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_snd_probe&amp;lt;/code&amp;gt; (last probe emit).&lt;br /&gt;
&lt;br /&gt;
Sample feeds either Linux&#039;s asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_LINUX_RTT_ESTIMATOR&amp;lt;/code&amp;gt;, default ON) or RFC 6298 symmetric EWMA&lt;br /&gt;
(compile option).  &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is floored at 10 ms when seeded from a&lt;br /&gt;
hint, at 1 us after every update (including the first seeding&lt;br /&gt;
sample); &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; floored at 100 ns.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;RTO = max(rto_min, 2 * srtt, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(the &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; floor is an FRCT addition not in RFC 6298).&lt;br /&gt;
Effective wheel deadline capped per [[#3. Protocol parameters|Section 3]].&lt;br /&gt;
&lt;br /&gt;
ACK-derived samples (&amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;rtt_sample_eligible&amp;lt;/code&amp;gt;), beyond&lt;br /&gt;
the cum-ACK advance gate in &amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno &amp;amp;gt; lwe&amp;lt;/code&amp;gt; and&lt;br /&gt;
&amp;lt;code&amp;gt;ackno &amp;amp;lt;= seqno&amp;lt;/code&amp;gt;), require all of: not in recovery; ACK packet does&lt;br /&gt;
not carry &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;; HoL slot&#039;s &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; bit clear; slot&#039;s &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;&lt;br /&gt;
pointer non-NULL (not SACK-consumed); &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; not below the &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
fence; &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; already seeded by an RTTP probe.  There is no ACK-only&lt;br /&gt;
seeding.&lt;br /&gt;
&lt;br /&gt;
Every eligible sample also feeds &amp;lt;code&amp;gt;RACK.min_RTT&amp;lt;/code&amp;gt; (RFC 8985 sec. 6.2)&lt;br /&gt;
via a windowed minimum: replace whenever the sample is strictly&lt;br /&gt;
smaller OR more than &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; (5 min, matches Linux&lt;br /&gt;
&amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) has elapsed since the current min was set.  The&lt;br /&gt;
downward branch is immediate (faster path picked up at once); the&lt;br /&gt;
upward branch is gated on the window (a transient queue burst does&lt;br /&gt;
not poison the estimate, but a sustained route change to a longer&lt;br /&gt;
path re-anchors &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; after at most one window).  Seeded from&lt;br /&gt;
&amp;lt;code&amp;gt;rtt_hint&amp;lt;/code&amp;gt; at &amp;lt;code&amp;gt;rtt_init&amp;lt;/code&amp;gt;; 0 acts as the unset sentinel and the base&lt;br /&gt;
in &amp;lt;code&amp;gt;rack_reorder_window&amp;lt;/code&amp;gt; falls back from &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt; (so&lt;br /&gt;
&amp;lt;code&amp;gt;R = mult * SRTT/4&amp;lt;/code&amp;gt;, capped at &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;, floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt;)&lt;br /&gt;
until the first sample.  See [[#6.2. Locked main path|Section 6.2]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 13. Liveness (keepalive) ==&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;code&amp;gt;qs.timeout &amp;amp;gt; 0&amp;lt;/code&amp;gt; a per-flow KA (keepalive) timer is armed.&lt;br /&gt;
Arming uses &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; for the deadline computation:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;deadline = min(snd_act + qs.timeout/4, rcv_act + qs.timeout)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(clamped to &amp;lt;code&amp;gt;now + qs.timeout/4&amp;lt;/code&amp;gt; if already past).  The timer fires&lt;br /&gt;
either on sender idleness (to send a KA) or on receiver idleness&lt;br /&gt;
(to declare the peer dead).  On fire (&amp;lt;code&amp;gt;ka_snd&amp;lt;/code&amp;gt;) the peer-dead test&lt;br /&gt;
uses &amp;lt;code&amp;gt;max(rcv_cr.act, t_ka_rcv)&amp;lt;/code&amp;gt; so a recent KA reply counts even&lt;br /&gt;
when no DATA has arrived:&lt;br /&gt;
&lt;br /&gt;
* If &amp;lt;code&amp;gt;now - max(rcv_cr.act, t_ka_rcv) &amp;amp;gt; qs.timeout&amp;lt;/code&amp;gt;, mark the flow &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; and notify the per-process flow-event set (&amp;lt;code&amp;gt;proc.fqset&amp;lt;/code&amp;gt;) with &amp;lt;code&amp;gt;FLOW_PEER&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Else if &amp;lt;code&amp;gt;snd_idle &amp;amp;gt; qs.timeout/4&amp;lt;/code&amp;gt;, emit a bare &amp;lt;code&amp;gt;KA | ACK&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) and re-arm.&lt;br /&gt;
* Else just re-arm.&lt;br /&gt;
&lt;br /&gt;
Note: &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; are the receive and transmit shared-memory&lt;br /&gt;
ring buffers.  The r-timer raises &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; on both (route is&lt;br /&gt;
broken); keepalive raises &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; only and notifies&lt;br /&gt;
the flow-event set (peer is silent, writer keeps &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; usable) -&lt;br /&gt;
distinct ACLs.  &amp;lt;code&amp;gt;qs.timeout == 0&amp;lt;/code&amp;gt; disables keepalive entirely; a&lt;br /&gt;
silent peer crash is then undetected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 14. Linger / teardown ==&lt;br /&gt;
&lt;br /&gt;
On &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt; computes a grace timeout&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;max(rcv_cr.act + rcv_cr.inact, snd_cr.act + snd_cr.inact) - now&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(floored at 0 and converted to seconds) and returns it; &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&lt;br /&gt;
forwards this to the IRMd as the dealloc grace.  The IRMd, not FRCT,&lt;br /&gt;
performs the wait.  Before computing the timeout, FRCT may emit a&lt;br /&gt;
final ACK when &amp;lt;code&amp;gt;rcv_cr.lwe != rcv_cr.seqno&amp;lt;/code&amp;gt; (the peer has not been&lt;br /&gt;
told the most recent cumulative ACK) AND the rcv side has been&lt;br /&gt;
active within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer not aged out).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt; is honoured only when &amp;lt;code&amp;gt;snd_cr.lwe &amp;amp;lt; edge&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;edge =&lt;br /&gt;
snd_fin_seqno&amp;lt;/code&amp;gt; after FIN has been sent in stream mode and&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno&amp;lt;/code&amp;gt; otherwise (data or FIN still in flight).  The drain&lt;br /&gt;
itself runs in &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&#039;s &amp;lt;code&amp;gt;while (FRCTI_LINGERING)&amp;lt;/code&amp;gt; loop, not in&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The fd is single-reader / single-writer (documented in the&lt;br /&gt;
manpages).  &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt; pumps &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; on every call (via&lt;br /&gt;
&amp;lt;code&amp;gt;flow_wait_window&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;flow_drain_rx_nb&amp;lt;/code&amp;gt;) and additionally blocks on&lt;br /&gt;
&amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; when the send window is closed.  A pure-writer thread thus&lt;br /&gt;
consumes ACKs without a dedicated reader.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 15. Heritage and adopted techniques ==&lt;br /&gt;
&lt;br /&gt;
Delta-t (Watson, 1981) is the primary heritage; FRCP descends from&lt;br /&gt;
the delta-t protocol family via the Recursive InterNetwork&lt;br /&gt;
Architecture (RINA; Day, &amp;quot;Patterns in Network Architecture&amp;quot;, 2008,&lt;br /&gt;
ch. 9).  Timer-based connection management&lt;br /&gt;
(no SYN/FIN handshake, per-flow state born on first DATA and&lt;br /&gt;
reclaimed after &amp;lt;code&amp;gt;t_mpl + a + r&amp;lt;/code&amp;gt; of silence), the DRF marker, and the&lt;br /&gt;
&amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; timers all come from delta-t.  See Watson,&lt;br /&gt;
&amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection&lt;br /&gt;
Management&amp;quot;, Computer Networks 5 (1981).&lt;br /&gt;
&lt;br /&gt;
The unified &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive and its&lt;br /&gt;
multi-axis QoS-cube argument ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) also come from RINA&lt;br /&gt;
(Day 2008, ch. 6; Grasa et al., &amp;quot;IRATI: investigating RINA as an&lt;br /&gt;
alternative to TCP/IP&amp;quot;, Computer Networks 92 (2015)) - reliability,&lt;br /&gt;
ordering, CRC presence, and encryption are flow attributes, not&lt;br /&gt;
separate sockets or protocols.&lt;br /&gt;
&lt;br /&gt;
The table below summarises additional adopted techniques and their&lt;br /&gt;
references.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FRCP mechanism !! Heritage !! Reference / note&lt;br /&gt;
|-&lt;br /&gt;
| Random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; || TCP ISN || RFC 6528 (Gont &amp;amp;amp; Bellovin, 2012).  QUIC PN-space reset (RFC 9000 sec. 12.3) is a structural analogue.&lt;br /&gt;
|-&lt;br /&gt;
| Cumulative ACK, left-window-edge advance || TCP || RFC 793 / RFC 9293&lt;br /&gt;
|-&lt;br /&gt;
| Receive window with non-shrink rule || TCP || RFC 793 sec. 3.7 / RFC 9293 sec. 3.8.6; RFC 1122 sec. 4.2.2.16 for the explicit non-shrink prohibition&lt;br /&gt;
|-&lt;br /&gt;
| Modular &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; arithmetic (&amp;lt;code&amp;gt;before&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;after&amp;lt;/code&amp;gt; helpers) || TCP || RFC 793 sec. 3.3 / RFC 9293 sec. 3.4&lt;br /&gt;
|-&lt;br /&gt;
| Selective ACK block list || TCP || RFC 2018 (Mathis et al., 1996).  Encoded as a typed FRCP packet rather than a TCP option, so framing is closer to QUIC ACK frames.  D-SACK (RFC 2883) carried in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt;; see [[#1.3. SACK payload|Section 1.3]].&lt;br /&gt;
|-&lt;br /&gt;
| NewReno-careful recovery with &amp;lt;code&amp;gt;recovery_high&amp;lt;/code&amp;gt; gate || TCP || RFC 6582 (Henderson et al., 2012); QUIC builds on the same model in RFC 9002 sec. 7.3.2.  Cwnd half absent (CC in IPCP).&lt;br /&gt;
|-&lt;br /&gt;
| RACK reordering window for fast retransmit || TCP || RFC 8985 (Cheng et al., 2021).  FRCP &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor against &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; collapse; matches RFC 8985 sec. 6.2 and Linux &amp;lt;code&amp;gt;tcp_rack_reo_wnd&amp;lt;/code&amp;gt;.  DSACK-driven &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; (sec. 6.2 step 4) is adopted; see [[#1.3. SACK payload|Section 1.3]] for the wire encoding.  The hybrid RACK-or-&amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; trigger from RFC 8985 sec. 6.2 step 4 is adopted ([[#8. Retransmission|Section 8]]).  QUIC&#039;s analogue in RFC 9002 sec. 6.1.2 uses &amp;lt;code&amp;gt;max(srtt, latest_rtt)&amp;lt;/code&amp;gt; as the base.&lt;br /&gt;
|-&lt;br /&gt;
| Karn&#039;s algorithm: no RTT sample on retransmits, RTO-collapse freeze || TCP || Karn &amp;amp;amp; Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, SIGCOMM 1987; RFC 6298 sec. 3.&lt;br /&gt;
|-&lt;br /&gt;
| RTO formula &amp;lt;code&amp;gt;RTO = max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt; || TCP || RFC 6298 (Paxson et al., 2011).  &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; = 250 us is below RFC 6298 sec. 2.4&#039;s 1 s SHOULD-floor - a recursive-layer choice.&lt;br /&gt;
|-&lt;br /&gt;
| Linux asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator (default) || Linux kernel || &amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;if(delta&amp;amp;lt;0) m&amp;amp;gt;&amp;amp;gt;=3&amp;lt;/code&amp;gt; dampening is a kernel divergence from RFC 6298.  RFC 6298 EWMA available behind a compile flag.&lt;br /&gt;
|-&lt;br /&gt;
| Delayed ACK with rate suppression || TCP || RFC 813 (Clark, 1982); RFC 1122 sec. 4.2.3.2; RFC 5681 sec. 4.2.  Single-deadline coalescing rather than &amp;quot;ack-every-other-segment&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| Zero-window-probe / persist-timer analogue (RDVS) || TCP || RFC 1122 sec. 4.2.2.17 / RFC 9293 sec. 3.8.6.1.  RDVS solicits an FC reply, distinct from QUIC &amp;lt;code&amp;gt;DATA_BLOCKED&amp;lt;/code&amp;gt; (RFC 9000 sec. 19.12), which is one-way notification.  &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; give-up departs from TCP.&lt;br /&gt;
|-&lt;br /&gt;
| Multiplexed control on a single PCI || SCTP / QUIC || SCTP chunk bundling (RFC 9260 sec. 6.10); QUIC frame multiplexing (RFC 9000 sec. 12.4).  Cleaner fit than TCP&#039;s separate-flag-bits design.&lt;br /&gt;
|-&lt;br /&gt;
| ACK ranges as multiple discontiguous acked blocks || QUIC || QUIC ACK frame (RFC 9000 sec. 19.3).  FRCP SACK is conceptually QUIC-frame-shaped even though encoded as absolute &amp;lt;code&amp;gt;[start,end]&amp;lt;/code&amp;gt; pairs.&lt;br /&gt;
|-&lt;br /&gt;
| Nonce-authenticated active RTT / liveness probing (RTTP) || QUIC &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;PATH_RESPONSE&amp;lt;/code&amp;gt; (RFC 9000 sec. 8.2, sec. 19.17, sec. 19.18).  WebRTC ICE consent-freshness (RFC 7675) is the same pattern.  QUIC&#039;s nonce is 8 octets; FRCP chooses 16.&lt;br /&gt;
|-&lt;br /&gt;
| Probing distinct from keepalive || QUIC || KA timer answers &amp;quot;peer alive?&amp;quot;, RTTP answers &amp;quot;path measurable?&amp;quot;, as in QUIC PING (RFC 9000 sec. 19.2) vs &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| Bare KA + ACK keepalive packets || QUIC / SCTP || QUIC PING (RFC 9000 sec. 19.2); SCTP HEARTBEAT / HEARTBEAT-ACK (RFC 9260 sec. 8.3).  SCTP HEARTBEAT also carries an opaque echoed blob, structurally similar to FRCP RTTP.&lt;br /&gt;
|-&lt;br /&gt;
| (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) fragment-role bits ([[#7.2. Fragmentation and reassembly|Section 7.2]]) || SCTP || RFC 9260 sec. 3.3.1 DATA chunk B/E bits encode the same four states (&amp;lt;code&amp;gt;B+E=SOLE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;B-only=FIRST&amp;lt;/code&amp;gt;, neither=&amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E-only=LAST&amp;lt;/code&amp;gt;).  Each fragment carries its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;/TSN and is independently retransmitted.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-offset reassembly (Sections [[#1.5. Stream PCI extension|1.5]], [[#16. Stream-mode flows|16]]) || QUIC || QUIC STREAM frame (RFC 9000 sec. 19.8) uses Offset + Length varints; FRCP uses fixed 32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;.  One stream per flow vs QUIC&#039;s many streams multiplexed.&lt;br /&gt;
|-&lt;br /&gt;
| FIN end-of-stream marker (Sections [[#1.2. Flag bits|1.2]], [[#16. Stream-mode flows|16]]) || TCP / QUIC || TCP FIN flag (RFC 9293 sec. 3.1) closes one half of the byte stream; QUIC STREAM frame FIN bit (RFC 9000 sec. 19.8) does the same per stream with an immutable final-size invariance (RFC 9000 sec. 4.5: the final size is fixed once observed).  FRCP&#039;s FIN consumes one packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (not one byte of stream space) and is idempotent on the sender side.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-credit flow control ([[#16. Stream-mode flows|Section 16]]) || QUIC || &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; (RFC 9000 sec. 4.1, sec. 19.10).  FRCP projects a per-flow byte budget onto the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;.  Single stream per flow collapses QUIC&#039;s &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.&lt;br /&gt;
|-&lt;br /&gt;
| Header protection (encrypted seqnos) || QUIC || QUIC RFC 9001 sec. 5.4 applies header protection on top of AEAD to mask the packet number.  FRCP&#039;s per-flow AEAD wrap ([[#16. Stream-mode flows|Section 16]]) is wider: it encrypts the entire PCI including &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; because the IPCP below already routes, so no destination connection-ID needs to stay in clear (cf. RFC 9000 sec. 5.2).&lt;br /&gt;
|-&lt;br /&gt;
| Two-bit fragment role polarity || SCTP || The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair follows SCTP B/E (begin = 1 / end = 1) rather than IPv4 MF (RFC 791 sec. 3.2), which has the inverse polarity (MF = 1 means NOT last).&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal reliability / ordering axes ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) || SCTP || PR-SCTP (RFC 3758, per-message partial reliability) and SCTP DATA U-bit (RFC 9260 sec. 3.3.1, per-message unordered) are the closest precedents for decoupling reliability from ordering; FRCP sets them per-flow rather than per-message.&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal CRC (&amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt;) || UDP-Lite || RFC 3828 (Larzon et al., 2004) lets the sender pick a per-packet Checksum Coverage and the receiver enforce a locally configured minimum (no in-band negotiation; sec. 3.1, sec. 3.3).  FRCP gates a full CRC trailer on &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; at flow setup.  Contrast TCP / SCTP (mandatory checksum) and QUIC (AEAD subsumes CRC).&lt;br /&gt;
|-&lt;br /&gt;
| Setup-time service negotiation || DCCP / SCTP / QUIC || DCCP Service Codes (RFC 4340 sec. 8.1.2, RFC 5595); SCTP INIT parameters (RFC 9260 sec. 3.3.2); QUIC transport parameters (RFC 9000 sec. 7.4).  All negotiate service properties at connection setup; only RINA&#039;s QoS cube exposes them as an orthogonal vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.1. Original to FRCP (no clean prior art) ===&lt;br /&gt;
&lt;br /&gt;
* Pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]): receiver-driven nudge exploiting &amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;.  Closest analogues are SCTP Gap Ack Blocks (RFC 9260 sec. 3.3.4) and DCCP Ack Vector (RFC 4340 sec. 11.4) - both let the receiver describe gaps to the sender, but neither targets the cross-epoch / pre-DRF case.&lt;br /&gt;
* &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; window-probe give-up: neither TCP (persist-timer probes until application or R2 abort, RFC 9293 sec. 3.8.6.1) nor QUIC has an explicit FC-give-up counter.  A recursive-network choice: outer layers can drop the flow.&lt;br /&gt;
* Skip-past-gap reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]): SCTP fragments and reassembles every flow regardless of reliability/ordering, using its own per-stream reassembly queue; QUIC fragments via STREAM offsets.  FRCP fragments best-effort flows too, but the receiver drops the broken prefix the moment a later run-start (&amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; role) is visible inside the &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;-wide reorder ring - no IP-frag-style timeout, no SCTP-style explicit abort.  If no later run-start arrives within the ring, &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt; and the partial run keeps its slots; the next inspect retries.  The trade-off: a permanently-lost &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; in a long isolated run holds slots until either a later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; appears in the ring or the writer stops, at which point the slots are reclaimed on flow teardown.&lt;br /&gt;
* Reassembly deferred to consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]), message mode only (&amp;lt;code&amp;gt;qos.service == SVC_MESSAGE&amp;lt;/code&amp;gt;): SCTP (RFC 9260 sec. 6.9), QUIC (RFC 9000 sec. 2.2), and TCP (RFC 9293) all hold reassembly state at the receive boundary.  FRCP message-mode leaves fragments in the shared-memory ring until &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; pulls and lands the SDU directly in the caller&#039;s buffer.  Stream mode ([[#16. Stream-mode flows|Section 16]]) uses the standard QUIC-style direct ring placement on receive and does not defer.  The optimisation is enabled by the Shared-Memory Subsystem (SSM) packet-buffer ring (see &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt; at [[#1.1. PCI header|Section 1.1]]); the analogue is OS-level scatter-gather I/O (&amp;lt;code&amp;gt;recvmsg+iovec&amp;lt;/code&amp;gt;), not a transport-layer prior art.&lt;br /&gt;
* TLP-equivalent tail-loss recovery (RFC 8985 sec. 7; RFC 9002 sec. 6.2): FRCP does not emit an explicit Tail Loss Probe packet, but the same goal is met implicitly by RACK loss detection ([[#8. Retransmission|Section 8]]) firing on a non-advancing cumulative ACK once the head-of-line slot ages past the RACK reorder window &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; - well below &amp;lt;code&amp;gt;RTO = max(2 * SRTT, SRTT + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.  A receiver-driven nudge is also available via the pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.2. Not adopted ===&lt;br /&gt;
&lt;br /&gt;
* Slow start, congestion window (cwnd), Additive Increase / Multiplicative Decrease (AIMD), NewReno cwnd inflation.  Congestion control lives in the IPCP CA policies and is driven by Explicit Congestion Notification (ECN, RFC 3168).&lt;br /&gt;
* Nagle / silly-window-syndrome (SWS) avoidance (RFC 896, RFC 1122 sec. 4.2.3.4).  (Deferred work, not adopted in the current spec.)&lt;br /&gt;
* TCP Timestamps (RFC 7323) / Protection Against Wrapped Sequences (PAWS) - RTT measurement uses RTTP, not per-segment timestamps.  A peer-supplied timestamp echoed on every ACK lets a malicious peer drive the &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; estimate arbitrarily low, collapsing the RTO and triggering a self-inflicted retransmit storm.  RTTP confines RTT measurement to nonce-authenticated probe round-trips, where a forged echo is rejected before it can reach the estimator.&lt;br /&gt;
* ECN (Explicit Congestion Notification) response inside FRCP (consumed by IPCP Congestion Avoidance / CA).&lt;br /&gt;
* IP-style fragment-offset reassembly (RFC 791 sec. 3.2; RFC 8200 sec. 4.5).  Message-mode FRCP relies on the FRCT &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; reorder ring keyed by &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (shared by FRTX and best-effort flows) to put fragments back in order; no separate offset field is needed and no IP-style hole-list reassembly buffer is kept.  Stream-mode FRCP does carry &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte offsets ([[#1.5. Stream PCI extension|Section 1.5]]) for direct ring placement on receive.&lt;br /&gt;
* QUIC STREAM offset+length framing on &#039;&#039;every&#039;&#039; flow (RFC 9000 sec. 19.8).  Message-mode FRCP uses the SCTP-style B/E flag-bit encoding (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) and skips the offsets; stream-mode FRCP adopts the QUIC offset model (heritage table above).&lt;br /&gt;
&lt;br /&gt;
== 16. Stream-mode flows ==&lt;br /&gt;
&lt;br /&gt;
When a flow is allocated with &amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt; both peers&lt;br /&gt;
switch to byte-stream semantics, layered on top of the FRTX reorder&lt;br /&gt;
machinery already described in Sections [[#6. Receive path|6]]-[[#8. Retransmission|8]].&lt;br /&gt;
&lt;br /&gt;
=== 16.1. Send ===&lt;br /&gt;
&lt;br /&gt;
The sender splits the caller&#039;s octets into chunks of at most&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt; octets (Sections [[#1.1. PCI header|1.1]]&lt;br /&gt;
and [[#1.5. Stream PCI extension|1.5]]).  Each chunk is one DATA packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte range copied from a monotonic stream counter.&lt;br /&gt;
In stream mode &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST be transmitted as&lt;br /&gt;
zero; the per-byte position is carried by the &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt;&lt;br /&gt;
extension instead.&lt;br /&gt;
&lt;br /&gt;
End-of-stream is signalled with a 0-byte DATA packet that has &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;&lt;br /&gt;
(bit 12) set, emitted on the FIN triggers listed in [[#1.2. Flag bits|Section 1.2]]&lt;br /&gt;
(WR-half close, &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, and any other path that yields the&lt;br /&gt;
final byte).  The sender MUST emit at most one FIN per flow; its&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; MUST equal &amp;lt;code&amp;gt;[final-byte, final-byte)&amp;lt;/code&amp;gt; (i.e., empty&lt;br /&gt;
interval at the final byte position; final-size invariance,&lt;br /&gt;
analogous to QUIC RFC 9000 sec. 4.5).  Idempotency is enforced by&lt;br /&gt;
an &amp;lt;code&amp;gt;snd_fin_sent&amp;lt;/code&amp;gt; guard.&lt;br /&gt;
&lt;br /&gt;
=== 16.2. Receive ===&lt;br /&gt;
&lt;br /&gt;
On arrival the receiver places the payload directly into a per-flow&lt;br /&gt;
byte-indexed receive ring of width &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; (octets) at the position&lt;br /&gt;
indicated by &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, with a two-segment &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; across the ring&lt;br /&gt;
boundary if needed.  Receipt is recorded in the FRTX reorder&lt;br /&gt;
machinery ([[#6.2. Locked main path|Section 6.2]]) augmented with the packet&#039;s &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;, and&lt;br /&gt;
FIN bit per slot.  When a packet&#039;s &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; front-overlaps&lt;br /&gt;
bytes already at or below the byte high-water mark, the overlap is&lt;br /&gt;
trimmed before placement so the same byte is never written twice.&lt;br /&gt;
After stashing, the receiver advances &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; and the byte high-water&lt;br /&gt;
mark across any newly-contiguous prefix.  Each slot advanced MUST&lt;br /&gt;
satisfy &amp;lt;code&amp;gt;start == the last-delivered slot&#039;s end&amp;lt;/code&amp;gt;; a slot whose&lt;br /&gt;
&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; does not equal that &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; is silently dropped at delivery time&lt;br /&gt;
(the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is consumed, no stream bytes contributed) and the high-&lt;br /&gt;
water mark does not advance past it.  The stream byte-stream&lt;br /&gt;
stalls at that point - there is no flow-tear-down on mismatch.&lt;br /&gt;
This filters spliced or off-path-injected slots that fall in&lt;br /&gt;
window without strong cryptographic authentication.&lt;br /&gt;
&lt;br /&gt;
A FIN slot marks end-of-stream at advance time only if its byte&lt;br /&gt;
position equals the last-delivered slot&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;; otherwise the FIN&lt;br /&gt;
is ignored and the corresponding &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; occupies a slot but&lt;br /&gt;
contributes no stream bytes.  No packet buffer is held after the&lt;br /&gt;
ring copy.&lt;br /&gt;
&lt;br /&gt;
=== 16.3. Read ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns up to &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt; octets from the contiguous prefix&lt;br /&gt;
&amp;lt;code&amp;gt;[next, high-water)&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;next&amp;lt;/code&amp;gt; is the byte the application has&lt;br /&gt;
already consumed up to and &amp;lt;code&amp;gt;high-water&amp;lt;/code&amp;gt; is the rightmost contiguous&lt;br /&gt;
byte received.  When the stream is fully drained AND end-of-stream&lt;br /&gt;
(EOS) was observed (&amp;lt;code&amp;gt;next == EOS&amp;lt;/code&amp;gt; byte position), &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns&lt;br /&gt;
0 (&amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) - the same shape POSIX &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt; uses on TCP after a peer&lt;br /&gt;
FIN.&lt;br /&gt;
&lt;br /&gt;
=== 16.4. Flow control ===&lt;br /&gt;
&lt;br /&gt;
ACK / SACK / RACK / RTO machinery is unchanged; the FRTX reorder&lt;br /&gt;
ring is reused as a per-&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; received-bitmap.  Let &amp;lt;code&amp;gt;per_pkt =&lt;br /&gt;
(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt;, the maximum stream-&lt;br /&gt;
byte payload one DATA packet can carry ([[#16.1. Send|Section 16.1]]).  The&lt;br /&gt;
receive window advertised in FC is clamped so the byte window&lt;br /&gt;
(&amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt;) cannot be overrun: the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is at most&lt;br /&gt;
&amp;lt;code&amp;gt;rcv_cr.lwe + ring_sz / per_pkt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC byte-credit flow-control model&lt;br /&gt;
(&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt;, RFC 9000 sec. 4.1 and sec. 19.10) projected onto&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; space.  With one stream per flow there is no &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; /&lt;br /&gt;
&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.  Receiver-side silly-window-syndrome&lt;br /&gt;
(SWS) avoidance (RFC 9293 sec. 3.8.6.2.2) is achieved by combining&lt;br /&gt;
the consume-time &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; bump with the global non-shrink rule from&lt;br /&gt;
[[#11. Flow control|Section 11]].&lt;br /&gt;
&lt;br /&gt;
=== 16.5. Security considerations ===&lt;br /&gt;
&lt;br /&gt;
Threat model.  An attacker that can observe (on-path passive) or&lt;br /&gt;
predict (off-path blind) the flow&#039;s seqnos and byte offsets on an&lt;br /&gt;
unencrypted stream flow can inject DATA or FIN at any in-window&lt;br /&gt;
position.  The in-line consistency checks above (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; == prior&lt;br /&gt;
&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; on advance; FIN MUST be 0-byte; FIN MUST sit at the final&lt;br /&gt;
byte position) realise the spirit of RFC 5961&#039;s &amp;quot;sequence-window&lt;br /&gt;
plus exact-position match for control bits&amp;quot; without an explicit&lt;br /&gt;
challenge-ACK probe; they make a few specific blind attack shapes&lt;br /&gt;
harder but are not cryptographic authentication. This is&lt;br /&gt;
comparable to TCP without the TCP Authentication Option (TCP-AO,&lt;br /&gt;
RFC 5925), tighter than a&lt;br /&gt;
pre-RFC-5961 TCP stack, and roughly equivalent to a modern&lt;br /&gt;
RFC 5961 stack against blind off-path injection - none of these&lt;br /&gt;
help once the attacker can sniff. TLS over TCP (RFC 8446)&lt;br /&gt;
encrypts only the TCP payload and leaves TCP seqnos, ACKs, FIN,&lt;br /&gt;
and RST in the clear, so TLS does NOT defend against TCP-header-&lt;br /&gt;
level injection; QUIC (RFC 9000) hides packet numbers under&lt;br /&gt;
header protection (RFC 9001 sec. 5.4), so this specific weakness&lt;br /&gt;
does not apply to QUIC.&lt;br /&gt;
&lt;br /&gt;
Mitigation: AEAD.  When the flow has encryption enabled the&lt;br /&gt;
recommended AEAD ciphers (AES-GCM, RFC 5288; or ChaCha20-Poly1305,&lt;br /&gt;
RFC 8439) wrap the entire FRCP packet on the wire - PCI, stream&lt;br /&gt;
extension, body, and the CRC trailer when &amp;lt;code&amp;gt;ber == 0&amp;lt;/code&amp;gt; - under a&lt;br /&gt;
per-flow symmetric key derived from the flow&#039;s own key exchange&lt;br /&gt;
([[#1.1. PCI header|Section 1.1]]).  The AEAD tag (~2^-128 forgery probability)&lt;br /&gt;
dominates the CRC (~2^-32) for integrity in this mode but the CRC&lt;br /&gt;
trailer is currently retained inside the wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
Implementations MUST NOT rely on the security properties below&lt;br /&gt;
when a non-AEAD cipher (e.g. AES-CTR alone) is negotiated; non-&lt;br /&gt;
AEAD modes provide confidentiality only and the threat-model&lt;br /&gt;
claims do not hold.&lt;br /&gt;
&lt;br /&gt;
With an AEAD cipher in use, seqnos, byte offsets, and the FIN bit&lt;br /&gt;
are both authenticated and confidential. Against an off-path or&lt;br /&gt;
on-path-passive attacker this is:&lt;br /&gt;
&lt;br /&gt;
* Stronger than TCP+TLS (TCP header in the clear).&lt;br /&gt;
* Stronger than TCP+TCP-AO (header authenticated but visible).&lt;br /&gt;
* Comparable to IPsec ESP transport mode (RFC 4303), which similarly authenticates and encrypts the upper-layer header plus payload, and to QUIC packet protection (RFC 9001 sec. 5), with the difference that QUIC must leave the destination connection ID in the clear for routing whereas FRCP relies on the IPCP below for delivery and can therefore encrypt its entire PCI.&lt;br /&gt;
&lt;br /&gt;
Keying granularity. Ouroboros flow allocation runs key exchange (&amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt;) per flow, so each &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; yields independent symmetric keys. This is finer-grained than QUIC (per-connection, RFC 9001, where one handshake covers all multiplexed streams) and finer-grained than typical IPsec deployment (per-host-pair Security Associations, SAs). Forward secrecy follows from the &amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt; when an ephemeral Diffie-Hellman exchange (DHE), or a hybrid mode (classical DH + post-quantum Key Encapsulation Mechanism / KEM), is selected.&lt;br /&gt;
&lt;br /&gt;
Replay protection. The AEAD layer itself does NOT carry an explicit anti-replay window (unlike IPsec ESP, RFC 4303 sec. 3.4.3, or DTLS, RFC 9147 sec. 4.5.1).  For FRCP-engaged flows the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space duplicate-suppression in [[#6.2. Locked main path|Section 6.2]] rejects replayed&lt;br /&gt;
DATA after the AEAD strips the wrap, because the AEAD authenticates the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a replay re-presents an old &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; that is then discarded either as a duplicate (still inside the receive window or as outside the receive window, depending on how far &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; has advanced since the original packet was delivered. RAW (&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) flows have no FRCP layer and therefore no replay protection at the AEAD layer either; deployments that need replay rejection on RAW flows SHOULD use SVC_MESSAGE.&lt;br /&gt;
&lt;br /&gt;
Layering. The AEAD wrap sits below FRCP on the data path, so RAW best-effort flows (&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;, the UDP-equivalent service of [[#2.2. Service modes (orthogonal axes)|Section 2.2]]) inherit the same per-flow integrity + confidentiality scope as FRCP-engaged flows - whatever the process and FRCP (if any) put on the wire is what the AEAD authenticates. No DTLS-equivalent layering is required for confidentiality and integrity; replay protection above AEAD is a separate concern as noted above.&lt;br /&gt;
&lt;br /&gt;
== 17. References ==&lt;br /&gt;
&lt;br /&gt;
This section lists the IETF documents, published works, and&lt;br /&gt;
source-code references cited inline elsewhere in this document.&lt;br /&gt;
IETF documents are cited inline as &amp;quot;RFC NNNN sec. X.Y&amp;quot;; books,&lt;br /&gt;
journal papers, and source-code references are cited inline by&lt;br /&gt;
author and year (or by file and function name) and are listed&lt;br /&gt;
here for convenience.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.1. IETF documents ===&lt;br /&gt;
&lt;br /&gt;
;[RFC 791]&lt;br /&gt;
:J. Postel, &amp;quot;Internet Protocol&amp;quot;, STD 5, RFC 791, September 1981.&lt;br /&gt;
;[RFC 793]&lt;br /&gt;
:J. Postel, &amp;quot;Transmission Control Protocol&amp;quot;, STD 7, RFC 793, September 1981.  Obsoleted by RFC 9293.&lt;br /&gt;
;[RFC 813]&lt;br /&gt;
:D. D. Clark, &amp;quot;Window and Acknowledgement Strategy in TCP&amp;quot;, RFC 813, July 1982.&lt;br /&gt;
;[RFC 896]&lt;br /&gt;
:J. Nagle, &amp;quot;Congestion Control in IP/TCP Internetworks&amp;quot;, RFC 896, January 1984.&lt;br /&gt;
;[RFC 1122]&lt;br /&gt;
:R. Braden (ed.), &amp;quot;Requirements for Internet Hosts -- Communication Layers&amp;quot;, STD 3, RFC 1122, October 1989.&lt;br /&gt;
;[RFC 2018]&lt;br /&gt;
:M. Mathis, J. Mahdavi, S. Floyd, A. Romanow, &amp;quot;TCP Selective Acknowledgment Options&amp;quot;, RFC 2018, October 1996.&lt;br /&gt;
;[RFC 2119]&lt;br /&gt;
:S. Bradner, &amp;quot;Key words for use in RFCs to Indicate Requirement Levels&amp;quot;, BCP 14, RFC 2119, March 1997.&lt;br /&gt;
;[RFC 2883]&lt;br /&gt;
:S. Floyd, J. Mahdavi, M. Mathis, M. Podolsky, &amp;quot;An Extension to the Selective Acknowledgement (SACK) Option for TCP&amp;quot;, RFC 2883, July 2000.&lt;br /&gt;
;[RFC 3758]&lt;br /&gt;
:R. Stewart, M. Ramalho, Q. Xie, M. Tuexen, P. Conrad, &amp;quot;Stream Control Transmission Protocol (SCTP) Partial Reliability Extension&amp;quot;, RFC 3758, May 2004.&lt;br /&gt;
;[RFC 3828]&lt;br /&gt;
:L-A. Larzon, M. Degermark, S. Pink, L-E. Jonsson (ed.), G. Fairhurst (ed.), &amp;quot;The Lightweight User Datagram Protocol (UDP-Lite)&amp;quot;, RFC 3828, July 2004.&lt;br /&gt;
;[RFC 4303]&lt;br /&gt;
:S. Kent, &amp;quot;IP Encapsulating Security Payload (ESP)&amp;quot;, RFC 4303, December 2005.&lt;br /&gt;
;[RFC 4340]&lt;br /&gt;
:E. Kohler, M. Handley, S. Floyd, &amp;quot;Datagram Congestion Control Protocol (DCCP)&amp;quot;, RFC 4340, March 2006.&lt;br /&gt;
;[RFC 5288]&lt;br /&gt;
:J. Salowey, A. Choudhury, D. McGrew, &amp;quot;AES Galois Counter Mode (GCM) Cipher Suites for TLS&amp;quot;, RFC 5288, August 2008.&lt;br /&gt;
;[RFC 5595]&lt;br /&gt;
:G. Fairhurst, &amp;quot;The Datagram Congestion Control Protocol (DCCP) Service Codes&amp;quot;, RFC 5595, September 2009.&lt;br /&gt;
;[RFC 5681]&lt;br /&gt;
:M. Allman, V. Paxson, E. Blanton, &amp;quot;TCP Congestion Control&amp;quot;, RFC 5681, September 2009.&lt;br /&gt;
;[RFC 5925]&lt;br /&gt;
:J. Touch, A. Mankin, R. Bonica, &amp;quot;The TCP Authentication Option&amp;quot;, RFC 5925, June 2010.&lt;br /&gt;
;[RFC 5961]&lt;br /&gt;
:A. Ramaiah, R. Stewart, M. Dalal, &amp;quot;Improving TCP&#039;s Robustness to Blind In-Window Attacks&amp;quot;, RFC 5961, August 2010.&lt;br /&gt;
;[RFC 6298]&lt;br /&gt;
:V. Paxson, M. Allman, J. Chu, M. Sargent, &amp;quot;Computing TCP&#039;s Retransmission Timer&amp;quot;, RFC 6298, June 2011.&lt;br /&gt;
;[RFC 6528]&lt;br /&gt;
:F. Gont, S. Bellovin, &amp;quot;Defending against Sequence Number Attacks&amp;quot;, RFC 6528, February 2012.  Obsoletes RFC 1948.&lt;br /&gt;
;[RFC 6582]&lt;br /&gt;
:T. Henderson, S. Floyd, A. Gurtov, Y. Nishida, &amp;quot;The NewReno Modification to TCP&#039;s Fast Recovery Algorithm&amp;quot;, RFC 6582, April 2012.&lt;br /&gt;
;[RFC 7323]&lt;br /&gt;
:D. Borman, B. Braden, V. Jacobson, R. Scheffenegger (ed.), &amp;quot;TCP Extensions for High Performance&amp;quot;, RFC 7323, September 2014.&lt;br /&gt;
;[RFC 7675]&lt;br /&gt;
:M. Perumal, D. Wing, R. Ravindranath, T. Reddy, M. Thomson, &amp;quot;Session Traversal Utilities for NAT (STUN) Usage for Consent Freshness&amp;quot;, RFC 7675, October 2015.&lt;br /&gt;
;[RFC 8174]&lt;br /&gt;
:B. Leiba, &amp;quot;Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words&amp;quot;, BCP 14, RFC 8174, May 2017.&lt;br /&gt;
;[RFC 8200]&lt;br /&gt;
:S. Deering, R. Hinden, &amp;quot;Internet Protocol, Version 6 (IPv6) Specification&amp;quot;, STD 86, RFC 8200, July 2017.&lt;br /&gt;
;[RFC 8439]&lt;br /&gt;
:Y. Nir, A. Langley, &amp;quot;ChaCha20 and Poly1305 for IETF Protocols&amp;quot;, RFC 8439, June 2018.&lt;br /&gt;
;[RFC 8446]&lt;br /&gt;
:E. Rescorla, &amp;quot;The Transport Layer Security (TLS) Protocol Version 1.3&amp;quot;, RFC 8446, August 2018.&lt;br /&gt;
;[RFC 8985]&lt;br /&gt;
:Y. Cheng, N. Cardwell, N. Dukkipati, P. Jha, &amp;quot;The RACK-TLP Loss Detection Algorithm for TCP&amp;quot;, RFC 8985, February 2021.&lt;br /&gt;
;[RFC 9000]&lt;br /&gt;
:J. Iyengar (ed.), M. Thomson (ed.), &amp;quot;QUIC: A UDP-Based Multiplexed and Secure Transport&amp;quot;, RFC 9000, May 2021.&lt;br /&gt;
;[RFC 9001]&lt;br /&gt;
:M. Thomson (ed.), S. Turner (ed.), &amp;quot;Using TLS to Secure QUIC&amp;quot;, RFC 9001, May 2021.&lt;br /&gt;
;[RFC 9002]&lt;br /&gt;
:J. Iyengar (ed.), I. Swett (ed.), &amp;quot;QUIC Loss Detection and Congestion Control&amp;quot;, RFC 9002, May 2021.&lt;br /&gt;
;[RFC 9147]&lt;br /&gt;
:E. Rescorla, H. Tschofenig, N. Modadugu, &amp;quot;The Datagram Transport Layer Security (DTLS) Protocol Version 1.3&amp;quot;, RFC 9147, April 2022.&lt;br /&gt;
;[RFC 9260]&lt;br /&gt;
:R. Stewart, M. Tuexen, K. Nielsen, &amp;quot;Stream Control Transmission Protocol&amp;quot;, RFC 9260, June 2022.  Obsoletes RFC 4960.&lt;br /&gt;
;[RFC 9293]&lt;br /&gt;
:W. Eddy (ed.), &amp;quot;Transmission Control Protocol (TCP)&amp;quot;, STD 7, RFC 9293, August 2022.  Obsoletes RFC 793 and several follow-ons; updates RFC 1122 and others.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.2. Books and journal papers ===&lt;br /&gt;
&lt;br /&gt;
;[Day08]&lt;br /&gt;
:J. Day, &amp;quot;Patterns in Network Architecture: A Return to Fundamentals&amp;quot;, Prentice Hall, 2008.&lt;br /&gt;
;[Grasa15]&lt;br /&gt;
:E. Grasa et al., &amp;quot;IRATI: investigating RINA as an alternative to TCP/IP&amp;quot;, Computer Networks, Vol. 92, December 2015.&lt;br /&gt;
;[KP87]&lt;br /&gt;
:P. Karn, C. Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, ACM SIGCOMM, August 1987.&lt;br /&gt;
;[Wat81]&lt;br /&gt;
:R. W. Watson, &amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection Management&amp;quot;, Computer Networks, Vol. 5, 1981.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.3. Source-code references ===&lt;br /&gt;
&lt;br /&gt;
;[Linux-RTT]&lt;br /&gt;
:&amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt; of the Linux kernel, defining the asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; variance update used as FRCP&#039;s default RTT estimator ([[#12. RTT estimation|Section 12]]).  Line-stable browseable copy at https://elixir.bootlin.com/linux/latest/source/net/ipv4/tcp_input.c.&lt;br /&gt;
&lt;br /&gt;
[[Category:Protocols]]&lt;br /&gt;
[[Category:Ouroboros internals]]&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1920</id>
		<title>Flow and Retransmission Control Protocol</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1920"/>
		<updated>2026-05-17T15:17:17Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 1.3. SACK payload */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:FRCP - Flow and Retransmission Control Protocol}}&lt;br /&gt;
&lt;br /&gt;
FRCP runs end-to-end between two peers over a flow.  It delivers&lt;br /&gt;
reliability, in-order delivery, flow control, and liveness.&lt;br /&gt;
Congestion Control (CC) is not in FRCP - that lives in the IPC&lt;br /&gt;
Process (IPCP) Congestion Avoidance (CA) policies, orthogonal to&lt;br /&gt;
FRCP.  Flow allocation, naming, and IPCP lifecycle are handled by&lt;br /&gt;
the IPC Resource Manager daemon (IRMd).&lt;br /&gt;
&lt;br /&gt;
FRCT (Flow and Retransmission Control Task) is the libouroboros&lt;br /&gt;
implementation of FRCP; the task lives in &amp;lt;code&amp;gt;src/lib/frct.c&amp;lt;/code&amp;gt;.  The&lt;br /&gt;
remainder of this document describes the FRCP wire protocol and the&lt;br /&gt;
behaviour FRCT realises.  Code symbols retain the &amp;lt;code&amp;gt;FRCT_&amp;lt;/code&amp;gt; prefix&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, ...) because they belong to the implementing&lt;br /&gt;
task; this document references them verbatim.&lt;br /&gt;
&lt;br /&gt;
The keywords &amp;quot;MUST&amp;quot;, &amp;quot;MUST NOT&amp;quot;, &amp;quot;REQUIRED&amp;quot;, &amp;quot;SHALL&amp;quot;, &amp;quot;SHALL NOT&amp;quot;,&lt;br /&gt;
&amp;quot;SHOULD&amp;quot;, &amp;quot;SHOULD NOT&amp;quot;, &amp;quot;RECOMMENDED&amp;quot;, &amp;quot;MAY&amp;quot;, and &amp;quot;OPTIONAL&amp;quot; in&lt;br /&gt;
this document are to be interpreted as described in &amp;lt;code&amp;gt;BCP&amp;lt;/code&amp;gt; 14 (Best&lt;br /&gt;
Current Practice; RFC 2119, RFC 8174) when, and only when, they&lt;br /&gt;
appear in all capitals.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:Unsigned 32-bit / 8-bit integers (kernel-C style).&lt;br /&gt;
;&amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:Nanoseconds.&lt;br /&gt;
&lt;br /&gt;
Modular sequence-number comparators (32-bit, modulo 2^32):&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;before(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;(int32_t)(a - b) &amp;amp;lt; 0&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;after(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;before(b, a)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Used throughout for &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; ordering checks.&lt;br /&gt;
&lt;br /&gt;
Round-Trip Time (RTT) abbreviations used throughout:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;&lt;br /&gt;
:Smoothed RTT estimate (RFC 6298).&lt;br /&gt;
;&amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt;&lt;br /&gt;
:Mean deviation of RTT (Linux variance estimator).&lt;br /&gt;
;&amp;lt;code&amp;gt;EWMA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Exponentially Weighted Moving Average.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTO&amp;lt;/code&amp;gt;&lt;br /&gt;
:Retransmission Timeout, &amp;lt;code&amp;gt;max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Timer-bound symbols &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer, ACK delay) and &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer,&lt;br /&gt;
retransmission window) are defined in [[#8. Retransmission|Section 8]]; &amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; (Maximum&lt;br /&gt;
Packet Lifetime) is introduced in [[#2.1. Per-flow state|Section 2.1]] (the &amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; field)&lt;br /&gt;
with heritage in [[#15. Heritage and adopted techniques|Section 15]].&lt;br /&gt;
&lt;br /&gt;
Wire-format diagrams follow the IETF convention: bit 0 is the&lt;br /&gt;
leftmost (most significant) bit and fields are in network byte&lt;br /&gt;
order unless stated otherwise.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1. Wire format ==&lt;br /&gt;
&lt;br /&gt;
=== 1.1. PCI header ===&lt;br /&gt;
&lt;br /&gt;
Fixed 16-octet base Protocol-Control Information (PCI) header&lt;br /&gt;
prefixed to every FRCP packet (RFC convention: bit 0 leftmost,&lt;br /&gt;
most-significant bit first).  All multi-byte fields except &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;&lt;br /&gt;
are in network byte order; &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt; is an opaque 16-bit value that&lt;br /&gt;
the receiver recomputes from the wire bytes and compares to the&lt;br /&gt;
in-place &amp;lt;code&amp;gt;pci-&amp;gt;hcs&amp;lt;/code&amp;gt; read, so its on-wire byte order need only&lt;br /&gt;
match between peers running compatible builds.  DATA packets on&lt;br /&gt;
stream-mode flows carry an additional 8-octet extension (see&lt;br /&gt;
[[#1.5. Stream PCI extension|Section 1.5]]); SACK and RTTP carry their own payloads after the&lt;br /&gt;
base PCI.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |             flags             |              hcs              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            window                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            seqno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            ackno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                     payload (variable) ...&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt;&lt;br /&gt;
:feature/type bitmap (see [[#1.2. Flag bits|Section 1.2]]).&lt;br /&gt;
;&amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;&lt;br /&gt;
:CRC-16-CCITT-FALSE Header Check Sequence (HCS) over &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; (+ stream extension when present); the two octets of the &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt; field itself are omitted from the CRC input.  Verified on receive before any flag-driven dispatch.&lt;br /&gt;
;&amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt;&lt;br /&gt;
:receiver-advertised right window edge (valid iff FC).&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-flow sequence number.&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;&lt;br /&gt;
:cumulative Acknowledgement (ACK) (valid iff ACK).&lt;br /&gt;
&lt;br /&gt;
A single packet can simultaneously carry DATA + ACK + FC (Flow&lt;br /&gt;
Control) + RXM (Retransmission) by ORing flag bits; the PCI&lt;br /&gt;
multiplexes control on the same wire frame in the spirit of SCTP&lt;br /&gt;
chunk bundling (RFC 9260 sec. 6.10) and QUIC frame multiplexing&lt;br /&gt;
(RFC 9000 sec. 12.4).  DATA-bearing packets carry the caller&#039;s&lt;br /&gt;
payload after the PCI; SACK (Selective Acknowledgement) and RTTP&lt;br /&gt;
(Round-Trip Time Probe) carry their own typed payloads after the&lt;br /&gt;
PCI.&lt;br /&gt;
&lt;br /&gt;
Optional framing (per-flow, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]]).  On the wire, the&lt;br /&gt;
order from inside out is:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Layer !! Scope&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| The FRCP packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body + CRC-32 ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| CRC-32 covers the body only (PCI is in HCS); appended iff &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; on DATA, or on every SACK packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ AEAD-wrap of above ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| Iff Authenticated Encryption with Associated Data (AEAD) is enabled.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* HCS in the PCI covers the header fields on every packet and is verified before any flag-driven dispatch.&lt;br /&gt;
* The CRC-32 trailer (IEEE 802.3 / zlib reflected polynomial &amp;lt;code&amp;gt;0xEDB88320&amp;lt;/code&amp;gt;, init &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;, xor-out &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;) covers the body on DATA when &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; and on every SACK packet; the trailer is written as a raw &amp;lt;code&amp;gt;uint32_t&amp;lt;/code&amp;gt; (the same convention as &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;: opaque on the wire as long as both peers run compatible builds).  The PCI is not under the CRC (Cyclic Redundancy Check) because the HCS already protects it.  It is appended before AEAD encryption and therefore rides inside the AEAD wrap when both are active; the AEAD tag (~2^-128 forgery probability) dominates the CRC (~2^-32) for integrity in that mode but the CRC trailer is currently retained.&lt;br /&gt;
* When encryption is enabled, the entire (possibly-CRC&#039;d) FRCP packet is wrapped with AEAD inside the shared-memory packet buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt;); the packet grows by the AEAD overhead, namely a leading nonce / Initialization Vector (IV) of &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_ivsz&amp;lt;/code&amp;gt;) and a trailing authentication tag of &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_tagsz&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Both CRC and AEAD are layered around the FRCP wire format and are not visible to the FRCP machinery itself.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.2. Flag bits ===&lt;br /&gt;
&lt;br /&gt;
Flag bits are numbered most-significant-bit first to match the wire&lt;br /&gt;
diagram (bit numbering per [[#1.1. PCI header|Section 1.1]]; bit 0 is the MSB of the&lt;br /&gt;
16-bit &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; field and lands at wire-position 0 in network byte&lt;br /&gt;
order).  Bits 13..15 are reserved and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Bit !! Mask !! Name !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| 0 || &amp;lt;code&amp;gt;0x8000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt; || Carries caller payload&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &amp;lt;code&amp;gt;0x4000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DRF&amp;lt;/code&amp;gt; || Data Run Flag: start of a fresh run&lt;br /&gt;
|-&lt;br /&gt;
| 2 || &amp;lt;code&amp;gt;0x2000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt; || Acknowledgement: &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field valid&lt;br /&gt;
|-&lt;br /&gt;
| 3 || &amp;lt;code&amp;gt;0x1000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt; || Negative ACK; &amp;lt;code&amp;gt;seqno = arrival_seqno-1&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || &amp;lt;code&amp;gt;0x0800&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt; || Flow Control: &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; field valid (&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || &amp;lt;code&amp;gt;0x0400&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt; || Rendezvous probe (window-closed)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || &amp;lt;code&amp;gt;0x0200&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; || First Fragment (role bit 0; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || &amp;lt;code&amp;gt;0x0100&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; || Last Fragment (role bit 1; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 8 || &amp;lt;code&amp;gt;0x0080&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RXM&amp;lt;/code&amp;gt; || Retransmission&lt;br /&gt;
|-&lt;br /&gt;
| 9 || &amp;lt;code&amp;gt;0x0040&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt; || Selective ACK block list in payload&lt;br /&gt;
|-&lt;br /&gt;
| 10 || &amp;lt;code&amp;gt;0x0020&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt; || RTT Probe / echo (payload follows)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || &amp;lt;code&amp;gt;0x0010&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt; || Keepalive&lt;br /&gt;
|-&lt;br /&gt;
| 12 || &amp;lt;code&amp;gt;0x0008&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt; || End-of-stream marker (stream mode)&lt;br /&gt;
|-&lt;br /&gt;
| 13-15 || -- || -- || Reserved (MUST be zero)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair encodes the fragment role of a DATA-bearing&lt;br /&gt;
Service Data Unit (SDU), SCTP-style begin/end flags (RFC 9260&lt;br /&gt;
sec. 3.3.1):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FFGM !! LFGM !! Role&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || Sole / un-fragmented SDU (begin AND end)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || First fragment of a multi-fragment SDU&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Middle fragment&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Last fragment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Each fragment is carried in its own FRCP packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;;&lt;br /&gt;
FRTX (the FRCT Retransmission service mode, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]])&lt;br /&gt;
recovers individual fragments via the normal Retransmission Timeout&lt;br /&gt;
(RTO) / SACK / Recent Acknowledgement (RACK, RFC 8985) path.  The&lt;br /&gt;
receiver reassembles the SDU at consume time once the contiguous&lt;br /&gt;
[FIRST .. LAST] run has fully arrived.  On non-DATA packets the role&lt;br /&gt;
bits are unused and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
In stream mode (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;, see [[#16. Stream-mode flows|Section 16]]) there are&lt;br /&gt;
no SDU boundaries to encode, so &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST&lt;br /&gt;
be transmitted as zero.  End-of-stream uses a dedicated bit (&amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;,&lt;br /&gt;
bit 12) carried on a 0-byte DATA packet, emitted at write-half close&lt;br /&gt;
(&amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;FLOWFRDONLY&amp;lt;/code&amp;gt;), during linger drain, and at &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;;&lt;br /&gt;
emission is idempotent (first call wins).  After contiguous delivery&lt;br /&gt;
of the FIN-bearing slot, the receiver latches &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt; at the FIN&#039;s&lt;br /&gt;
start offset; &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns 0 (end-of-file, &amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) once buffered&lt;br /&gt;
bytes have been drained up to &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt;.  Per-byte position is&lt;br /&gt;
carried by the [start, end) extension ([[#1.5. Stream PCI extension|Section 1.5]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.3. SACK payload ===&lt;br /&gt;
&lt;br /&gt;
A SACK packet has the &amp;lt;code&amp;gt;FRCT_ACK | FRCT_FC | FRCT_SACK&amp;lt;/code&amp;gt; flag bits set&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI, the&lt;br /&gt;
payload is a 2-octet block count (network byte order), 2 octets of&lt;br /&gt;
padding to 4-byte align the block list, then &amp;lt;code&amp;gt;n_blocks&amp;lt;/code&amp;gt; pairs of&lt;br /&gt;
32-bit start/end seqnos describing &#039;&#039;present&#039;&#039; (received) ranges&lt;br /&gt;
above the cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |           n_blocks            |        padding (2 octets)     |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[0]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            end[0]                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[1]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
                       ... n_blocks pairs total ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;n_blocks &amp;amp;lt;= SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; (2048).  The per-flow effective cap is&lt;br /&gt;
further bounded by &amp;lt;code&amp;gt;(frag_mtu - PCI - 4) / 8&amp;lt;/code&amp;gt; blocks per packet; SACK&lt;br /&gt;
packets carry no stream extension, so PCI here is the 16-octet base&lt;br /&gt;
header even on stream-mode flows.&lt;br /&gt;
&lt;br /&gt;
Wire invariant: every block produced by the receiver, except an&lt;br /&gt;
optional leading Duplicate SACK (D-SACK) block as described below,&lt;br /&gt;
describes a range strictly above the cumulative ACK carried in the&lt;br /&gt;
PCI &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field (&amp;lt;code&amp;gt;after(start[i], ackno)&amp;lt;/code&amp;gt;).  This makes the D-SACK&lt;br /&gt;
convention below unambiguous; the receiver-side builder MUST&lt;br /&gt;
preserve it.&lt;br /&gt;
&lt;br /&gt;
Duplicate SACK (D-SACK, RFC 2883) is signalled in-band: no flag&lt;br /&gt;
bit, no extra framing.  Modular seqno arithmetic uses the&lt;br /&gt;
&amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators defined in the Notation block.&lt;br /&gt;
&amp;lt;code&amp;gt;Block[0]&amp;lt;/code&amp;gt; carries a D-SACK report when either:&lt;br /&gt;
&lt;br /&gt;
;case 1 (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;before(blocks[0].start, ackno)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ackno - blocks[0].start&amp;lt;/code&amp;gt; is within &amp;lt;code&amp;gt;MAX_DSACK_LAG&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;== RQ_SIZE&amp;lt;/code&amp;gt;).  A single duplicate &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; observed below the cumulative ACK.&lt;br /&gt;
;case 2 (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;blocks[0]&amp;lt;/code&amp;gt; is a sub-range of some &amp;lt;code&amp;gt;blocks[i&amp;gt;0]&amp;lt;/code&amp;gt; (not exactly equal).  Reports a duplicate of an in-window &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; that the same packet&#039;s remaining SACK blocks already describe as received.&lt;br /&gt;
&lt;br /&gt;
On receipt the sender feeds &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; into the RACK&lt;br /&gt;
&amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; scaler (RFC 8985 sec. 6.2 step 4): bump on receipt&lt;br /&gt;
(cap 20), halve once per 16 cumulatively-ACK&#039;d seqnos since the&lt;br /&gt;
most recent D-SACK arrival or halve event, reset to 1 on an RTO&lt;br /&gt;
timer fire at the head-of-line.  D-SACK alone never enters&lt;br /&gt;
NewReno-careful recovery (see [[#8. Retransmission|Section 8]]); only non-D-SACK blocks&lt;br /&gt;
count as gaps.  The normal SACK-mark loop processes &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt;&lt;br /&gt;
idempotently regardless: the existing clamp-and-skip path makes&lt;br /&gt;
case-1 a no-op (&amp;lt;code&amp;gt;start &amp;amp;lt; snd_cr.lwe&amp;lt;/code&amp;gt; clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt;, the&lt;br /&gt;
inner loop then skips &amp;lt;code&amp;gt;k == snd_cr.lwe&amp;lt;/code&amp;gt;) and case-2 a re-NULL of&lt;br /&gt;
slots already marked received by later blocks.&lt;br /&gt;
&lt;br /&gt;
=== 1.4. RTTP payload ===&lt;br /&gt;
&lt;br /&gt;
An RTTP (Round-Trip Time Probe) packet has only the &amp;lt;code&amp;gt;FRCT_RTTP&amp;lt;/code&amp;gt; flag&lt;br /&gt;
set (bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI,&lt;br /&gt;
the payload is 24 octets (packed):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          probe_id                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          echo_id                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +                  nonce (16 octets, echoed verbatim)           +&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:sender counter, 0 on reply, 0 reserved.&lt;br /&gt;
;&amp;lt;code&amp;gt;echo_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:peer&#039;s &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;, 0 on outbound probe.&lt;br /&gt;
;&amp;lt;code&amp;gt;nonce&amp;lt;/code&amp;gt;&lt;br /&gt;
:random, echoed unmodified, memcmp&#039;d to defeat spoof.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.5. Stream PCI extension ===&lt;br /&gt;
&lt;br /&gt;
A stream-mode flow (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;) carries an extra&lt;br /&gt;
8-octet extension after the 16-octet base PCI on every DATA packet&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            start                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                             end                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset of the first payload byte in the stream.&lt;br /&gt;
;&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset one past the last payload byte; &amp;lt;code&amp;gt;end - start&amp;lt;/code&amp;gt; equals the on-wire payload length.&lt;br /&gt;
&lt;br /&gt;
Total stream-mode PCI for DATA packets is 24 octets (16 base + 8&lt;br /&gt;
extension); control packets (SACK, RTTP, bare ACK, KA, etc.) retain&lt;br /&gt;
the 16-octet base PCI.  Stream mode MUST be negotiated at flow&lt;br /&gt;
allocation; the extension is present iff stream mode is in use,&lt;br /&gt;
never on a per-packet basis.  Both peers MUST treat &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; as&lt;br /&gt;
monotonic 32-bit byte offsets; when a slot reaches the head of the&lt;br /&gt;
contiguous run with &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; not equal to the prior packet&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; the&lt;br /&gt;
slot is silently dropped at delivery time ([[#16. Stream-mode flows|Section 16]]) rather&lt;br /&gt;
than rejected at stash.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC STREAM-frame reassembly model (RFC 9000 sec. 19.8):&lt;br /&gt;
each packet carries its packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (this PCI&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; field) and a&lt;br /&gt;
separate stream byte position (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;).  Separating the two&lt;br /&gt;
avoids TCP&#039;s conflation of packet identity with byte position which&lt;br /&gt;
forces Karn&#039;s algorithm for Round-Trip Time (RTT) sampling (no RTT&lt;br /&gt;
sample on retransmits, RFC 6298 sec. 3); FRCP applies the&lt;br /&gt;
Karn-equivalent gate via a combination of per-packet &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;,&lt;br /&gt;
per-slot &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; flags, and a sample-fence &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; (see [[#2.1. Per-flow state|Section 2.1]]&lt;br /&gt;
and [[#12. RTT estimation|Section 12]]).  FRCP&#039;s fixed-32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; wrap at 4 GiB of&lt;br /&gt;
wire bytes, narrower than QUIC&#039;s 62-bit varint offset (cf. RFC 9000&lt;br /&gt;
sec. 16); the on-wire wrap is handled by the same modular &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;&lt;br /&gt;
/ &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators ([[#1.3. SACK payload|Section 1.3]]) FRCP uses for seqnos, which&lt;br /&gt;
remain unambiguous as long as the in-flight byte window stays&lt;br /&gt;
strictly under 2 GiB (the half-range of the signed-int32 difference&lt;br /&gt;
in &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;).  The default per-flow ring is 1 MiB; the&lt;br /&gt;
implementation caps &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; at 128 MiB (&amp;lt;code&amp;gt;FRCT_STREAM_RING_SZ_MAX&amp;lt;/code&amp;gt;),&lt;br /&gt;
well below the 2 GiB half-range bound.  The runtime byte counters&lt;br /&gt;
exposed via FUSE (Filesystem in Userspace) in the Ouroboros&lt;br /&gt;
Resource Information Base (RIB, a virtual-filesystem introspection&lt;br /&gt;
bridge) are platform &amp;lt;code&amp;gt;size_t&amp;lt;/code&amp;gt; and do not wrap on 64-bit hosts.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2. Per-flow state and service modes ==&lt;br /&gt;
&lt;br /&gt;
=== 2.1. Per-flow state ===&lt;br /&gt;
&lt;br /&gt;
Each flow keeps a sender control record and a receiver control&lt;br /&gt;
record:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: oldest unacked &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (cumulative ACK boundary as seen by sender); rcv: next in-order &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; expected&lt;br /&gt;
;&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: peer-advertised right window edge; rcv: locally-advertised right window edge&lt;br /&gt;
;&amp;lt;code&amp;gt;cflags&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-direction feature flags: retransmission (&amp;lt;code&amp;gt;FRCTFRTX&amp;lt;/code&amp;gt;), receiver flow control (&amp;lt;code&amp;gt;FRCTFRESCNTL&amp;lt;/code&amp;gt;), linger-on-close (&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt;); see &amp;lt;code&amp;gt;&amp;amp;lt;ouroboros/fccntl.h&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: next &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; to send; rcv: force-ACK trigger - set on a stale or dup DATA so the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; emits a fresh cumulative ACK&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; counter for standalone ACK-bearing control packets (delayed ACK, SACK, final ACK on dealloc); not bumped on piggybacked ACK riding a DATA packet (which uses the DATA &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;).  Used by wire-dup ACK detection; rcv: incoming-ACK dedup tracker&lt;br /&gt;
;&amp;lt;code&amp;gt;act&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:last activity (used by inactivity / DRF)&lt;br /&gt;
;&amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:inactivity threshold; sender = &amp;lt;code&amp;gt;3*mpl + a + r + 1s&amp;lt;/code&amp;gt;, receiver = &amp;lt;code&amp;gt;2*mpl + a + r + 1s&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;mpl&amp;lt;/code&amp;gt; is the Maximum Packet Lifetime (delta-t terminology; see [[#15. Heritage and adopted techniques|Section 15]]); &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; are the FRCT a-timer and r-timer bounds (see [[#8. Retransmission|Section 8]]).  The asymmetry is load-bearing for pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
The sender holds a per-slot ring &amp;lt;code&amp;gt;snd_slots[RQ_SIZE]&amp;lt;/code&amp;gt; keyed by&lt;br /&gt;
&amp;lt;code&amp;gt;(seqno mod RQ_SIZE)&amp;lt;/code&amp;gt;.  Each slot tracks its retransmit entry (&amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;),&lt;br /&gt;
last-send timestamp, and retransmit flag bits: &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (a&lt;br /&gt;
retransmit is pending or has fired, gates the next RTT sample&lt;br /&gt;
under Karn) and &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (one-shot fast-retransmit staged for&lt;br /&gt;
this loss event).&lt;br /&gt;
&lt;br /&gt;
The receiver holds a parallel reorder ring &amp;lt;code&amp;gt;rcv_slots[RQ_SIZE]&amp;lt;/code&amp;gt;&lt;br /&gt;
(referred to as &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; in prose) holding stashed out-of-order&lt;br /&gt;
packet-buffer indexes; both FRTX and best-effort flows share this&lt;br /&gt;
path.  The invariant &amp;lt;code&amp;gt;rwe - lwe &amp;amp;lt;= RQ_SIZE&amp;lt;/code&amp;gt; holds: on each consume&lt;br /&gt;
the receiver advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; by the consumed count, capping the&lt;br /&gt;
receive window at &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; slots.&lt;br /&gt;
&lt;br /&gt;
A separate fence variable &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; is bumped on every retransmit&lt;br /&gt;
(timer-fire, SACK-driven, fast-rxm, NACK-driven) and on every&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; ([[#4. Sequence-number rotation (DRF)|Section 4]]) to mark the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; range whose RTT samples&lt;br /&gt;
MUST be discarded.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 2.2. Service modes (orthogonal axes) ===&lt;br /&gt;
&lt;br /&gt;
FRCP exposes its wire features as a vector of independent QoS&lt;br /&gt;
axes selected at flow allocation time.  All flows go through the&lt;br /&gt;
same &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive; the &amp;lt;code&amp;gt;qosspec_t&amp;lt;/code&amp;gt; passed&lt;br /&gt;
in determines which protocol machinery engages on the wire.  This&lt;br /&gt;
contrasts with the POSIX BSD socket model where TCP and UDP&lt;br /&gt;
require different socket types (&amp;lt;code&amp;gt;SOCK_STREAM&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;SOCK_DGRAM&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
The axes:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = unordered (no FRCP engagement: raw datagrams, no PCI on the wire, UDP-equivalent at this layer); 1 = message-ordered (FRCP engaged; SDU boundaries preserved across fragmentation); 2 = stream (byte-oriented, no SDU boundaries; FRTX required)&lt;br /&gt;
;&amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = lossless service requested: FRTX retransmit machinery engages ([[#8. Retransmission|Section 8]]); MUST be 0 for &amp;lt;code&amp;gt;service=2&amp;lt;/code&amp;gt;.  Non-zero = best-effort, FRTX off.&lt;br /&gt;
;&amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bit Error Rate tolerance.  0 = error-free service requested: a CRC trailer is appended after the body of DATA packets and verified on receive (added / checked outside the FRCP PCI; see [[#1.1. PCI header|Section 1.1]]).  Non-zero = peer accepts errors; trailer omitted.  SACK control packets carry a CRC32 trailer regardless of &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; gate applies to DATA only.&lt;br /&gt;
;&amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt;&lt;br /&gt;
:Peer-timeout (ms); 0 disables the keepalive timer.  Independent of FRCP engagement.&lt;br /&gt;
&lt;br /&gt;
Encryption is a separate per-flow attribute set at flow setup;&lt;br /&gt;
when enabled it wraps the FRCP packet (PCI + body, plus the CRC&lt;br /&gt;
trailer if any) under AEAD, expanding the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; by &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt;&lt;br /&gt;
octets (nonce / tag).  The CRC trailer is currently kept inside&lt;br /&gt;
the AEAD wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
&lt;br /&gt;
Reachable combinations exported by &amp;lt;code&amp;gt;include/ouroboros/qos.h&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Cube !! &amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; !! Engaged&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw&amp;lt;/code&amp;gt; || 0 || 1 || 1 || Raw passthrough&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw_safe&amp;lt;/code&amp;gt; || 0 || 1 || 0 || Raw + CRC trailer&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; || 1 || 1 || 1 || FRCP, no FRTX, no CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt_safe&amp;lt;/code&amp;gt; || 1 || 1 || 0 || FRCP, no FRTX, CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_msg&amp;lt;/code&amp;gt; || 1 || 0 || 0 || FRCP + FRTX&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_stream&amp;lt;/code&amp;gt; || 2 || 0 || 0 || FRCP + FRTX, stream&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Forced couplings actually enforced by the public API:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;service == SVC_STREAM&amp;lt;/code&amp;gt; (2) requires &amp;lt;code&amp;gt;loss == 0&amp;lt;/code&amp;gt;; &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;flow_accept&amp;lt;/code&amp;gt; reject the pair otherwise with &amp;lt;code&amp;gt;-EINVAL&amp;lt;/code&amp;gt;.&lt;br /&gt;
* FRTX requires FRCP engagement (&amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;); requesting &amp;lt;code&amp;gt;loss = 0&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;service = SVC_RAW&amp;lt;/code&amp;gt; is structurally a no-op because no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; is created.&lt;br /&gt;
* The &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; build flag globally forces &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;.  Note: this flag defaults to ON, so default builds ship with CRC disabled until &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; is set to OFF.&lt;br /&gt;
&lt;br /&gt;
Caveat: the API does NOT force &amp;lt;code&amp;gt;ber = 0&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; has &amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;, which means the PCI&lt;br /&gt;
itself is not CRC-protected on that cube; the HCS ([[#1.1. PCI header|Section 1.1]])&lt;br /&gt;
remains the only integrity check on the header.&lt;br /&gt;
&lt;br /&gt;
The FRCP-no-FRTX regime (&amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;loss &amp;amp;gt; 0&amp;lt;/code&amp;gt;) is meaningful&lt;br /&gt;
and live: sequence numbering, in-order delivery, flow-control&lt;br /&gt;
advertisement, KA, DRF rotation, and SDU fragmentation /&lt;br /&gt;
reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]) all run.  Lost packets are dropped&lt;br /&gt;
rather than retransmitted; a permanently-lost mid-fragment is&lt;br /&gt;
dropped via skip-past-gap once a later SDU is visible in the&lt;br /&gt;
reorder ring.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. Protocol parameters ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Slot ring / rcv window width&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;START_WINDOW&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Initial &amp;lt;code&amp;gt;rwe-lwe&amp;lt;/code&amp;gt; after rotate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MAX(250 us build-tunable, 1&amp;amp;lt;&amp;amp;lt;RXMQ_RES)&amp;lt;/code&amp;gt;; per-flow via &amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;FRCTSRTOMIN&amp;lt;/code&amp;gt;).  Default ~1 ms with &amp;lt;code&amp;gt;RXMQ_RES=20&amp;lt;/code&amp;gt;. || RTO floor; also floored at the retransmit-wheel resolution (~1 ms by default).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt; || 20 || Backoff shift cap&lt;br /&gt;
|-&lt;br /&gt;
| RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MIN(reo_wnd_mult * min_RTT/4, SRTT)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; scales on D-SACK, cap 20 || Reorder window; per RFC 8985 sec. 6.2; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per sec. 6.2 step 4&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; || 300 s (5 min, Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; windowed re-anchor&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_WND_MULT_MAX&amp;lt;/code&amp;gt; || 20 (RFC 8985 sec. 6.2 step 4) || &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_DECAY_PKTS&amp;lt;/code&amp;gt; || 16 (RFC 8985 sec. 6.2 step 4 / &amp;lt;code&amp;gt;RACK.reo_wnd_persist&amp;lt;/code&amp;gt;) || Fresh-ACK&#039;d seq count per halving&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_DSACK_LAG&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || D-SACK sanity cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTT_QUARANTINE&amp;lt;/code&amp;gt; || 32 (&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; steps) || NewReno gate pad&lt;br /&gt;
|-&lt;br /&gt;
| SACK rate-limit || &amp;lt;code&amp;gt;SACK_MIN_GAP_NS&amp;lt;/code&amp;gt; (250 us, fixed) || Min SACK gap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; || 2048 (wire cap; per-flow capped at &amp;lt;code&amp;gt;(frag_mtu-PCI-4)/8&amp;lt;/code&amp;gt;) || Per-SACK block cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; || 32 || Per-pass staged retransmit cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; || 3 (RFC 8985 default) || Hybrid fast-rxm trigger ([[#8. Retransmission|Section 8]])&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MDEV_MUL&amp;lt;/code&amp;gt; || 2 (build-tunable via &amp;lt;code&amp;gt;FRCT_RTO_MDEV_MULTIPLIER&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; shift in &amp;lt;code&amp;gt;RTO = srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| RTTP nonce || 16 octets || Echoed verbatim&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; || 8 || In-flight probes&lt;br /&gt;
|-&lt;br /&gt;
| RTT clamp || &amp;lt;code&amp;gt;16 * srtt&amp;lt;/code&amp;gt; || Probe-sample upper bound (ACK-derived RTT samples gated by Karn / recovery only)&lt;br /&gt;
|-&lt;br /&gt;
| Cold-probe cadence || 100 ms (rx-driven; see [[#12. RTT estimation|Section 12]]) || Pre-&amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; RTTP rate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt; || 100 ms || RDVS emit cadence&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; || 1 s || RDVS give-up&lt;br /&gt;
|-&lt;br /&gt;
| Delayed-ACK fire || &amp;lt;code&amp;gt;2 * TICTIME&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt; = FRCT tick granularity, default 5 ms; &amp;lt;code&amp;gt;2*TICTIME = 10 ms&amp;lt;/code&amp;gt; by default) || Fired after the first in-order DATA arrival; tick is build-tunable&lt;br /&gt;
|-&lt;br /&gt;
| NACK send cooldown || &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; when an &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; sample exists, else 100 ms || Pre-DRF NACK rate-limit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_SDU&amp;lt;/code&amp;gt; || 1 MiB || Max reassembled SDU; configurable per flow&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The per-flow fragment Maximum Transmission Unit (MTU) is computed&lt;br /&gt;
at flow setup from the lower IPCP&#039;s mtu minus encryption&lt;br /&gt;
&amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; and CRC trailer; there is no FRCT-level default or&lt;br /&gt;
environment-variable override.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 4. Sequence-number rotation (DRF) ==&lt;br /&gt;
&lt;br /&gt;
The DRF (Data Run Flag) bit on an outbound packet means &amp;quot;this is&lt;br /&gt;
the start of a fresh data run&amp;quot; and is set whenever the sender has&lt;br /&gt;
nothing in flight (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Independently of that, if the sender has been idle longer than&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.inact&amp;lt;/code&amp;gt; AND the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;),&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; rolls a random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; before the send and&lt;br /&gt;
resets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    snd_cr.seqno  = random()&lt;br /&gt;
    snd_cr.lwe    = snd_cr.seqno&lt;br /&gt;
    snd_cr.rwe    = snd_cr.seqno + START_WINDOW&lt;br /&gt;
    rtt_lwe       = snd_cr.seqno&lt;br /&gt;
    in_recovery   = false   (recovery state, see Section 8)&lt;br /&gt;
    recovery_high = snd_cr.seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The receiver, on observing rcv-side inactivity&lt;br /&gt;
(&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), requires a DRF on the next&lt;br /&gt;
DATA packet; otherwise it replies with a rate-limited NACK (see&lt;br /&gt;
below).  Non-DATA control packets pass through without the DRF&lt;br /&gt;
requirement.  On DRF the receiver releases the &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots and&lt;br /&gt;
rebases&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    rcv_cr.lwe   = seqno&lt;br /&gt;
    rcv_cr.rwe   = seqno + RQ_SIZE&lt;br /&gt;
    rcv_cr.seqno = seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the inactive packet has DATA but no DRF, a rate-limited NACK is&lt;br /&gt;
fired back to the sender (cooldown per [[#3. Protocol parameters|Section 3]]); non-DATA stale&lt;br /&gt;
arrivals fall through to normal processing (no NACK, no drop).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 5. Send path ==&lt;br /&gt;
&lt;br /&gt;
# If the SDU exceeds &amp;lt;code&amp;gt;(frag_mtu - data_hdr_len)&amp;lt;/code&amp;gt;, the caller (&amp;lt;code&amp;gt;dev.c&amp;lt;/code&amp;gt;) fans it out into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - data_hdr_len))&amp;lt;/code&amp;gt; fragments, each emitted via &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; as its own DATA packet with a per-fragment role ([[#7.2. Fragmentation and reassembly|Section 7.2]]); both FRTX and best-effort flows fragment.  Raw flows (no FRCP engagement, &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) carry no PCI and return &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; for any SDU larger than one packet at the layer below.  An SDU that fits in a single packet is sent as SOLE.  &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; reserves PCI head room; sets DATA, plus DRF when the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
# &amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; if past sender inactivity and the pipe is empty ([[#4. Sequence-number rotation (DRF)|Section 4]]).&lt;br /&gt;
# Advertise FC (&amp;lt;code&amp;gt;pci.window = frcti_advert_rwe(frcti)&amp;lt;/code&amp;gt;, i.e. &amp;lt;code&amp;gt;rcv_cr.rwe&amp;lt;/code&amp;gt; clamped to &amp;lt;code&amp;gt;rcv_cr.lwe + ring_seq_cap&amp;lt;/code&amp;gt; in stream mode) when the receiver side is recent: &amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt; rcv_cr.inact&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Reliable mode (FRTX): leave &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; where it is; reset the slot at &amp;lt;code&amp;gt;RQ_SLOT(seqno)&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;snd_slots[p].time = now&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_slots[p].flags = 0&amp;lt;/code&amp;gt;); queue an &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; (saves a packet copy, arms a wheel timer at &amp;lt;code&amp;gt;now + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;).  Piggyback ACK (&amp;lt;code&amp;gt;pci.ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) while the a-timer for the most recent received DATA packet has not yet expired (&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt;= t_a&amp;lt;/code&amp;gt;); on piggyback, set &amp;lt;code&amp;gt;rcv_cr.seqno = rcv_cr.lwe&amp;lt;/code&amp;gt; so the next delayed-ACK fire is suppressed.  See [[#8. Retransmission|Section 8]] for &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; semantics.&lt;br /&gt;
# Best-effort mode (no FRTX): advance &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; immediately (&amp;lt;code&amp;gt;snd_cr.lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_cr.rwe = snd_cr.lwe + RQ_SIZE&amp;lt;/code&amp;gt;); no retransmit state.  No send-side RTT probe is armed in this mode (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt; requires an in-flight &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, which best-effort never has); the rx-driven cold seeder in &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; is the only probe path.&lt;br /&gt;
# In reliable mode, optionally arm an RTT probe ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 6. Receive path ==&lt;br /&gt;
&lt;br /&gt;
=== 6.1. Early-exit dispatch ===&lt;br /&gt;
&lt;br /&gt;
Keepalive (KA), RTT probe (RTTP), pre-DRF NACK, and rendezvous&lt;br /&gt;
(RDVS) packets short-circuit out of &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; before the locked&lt;br /&gt;
main path; each handler takes its own lock internally.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
      incoming packet&lt;br /&gt;
            |&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | KA?     |---yes--&amp;gt; ka_rcv  ; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RTTP?   |---yes--&amp;gt; rttp_rcv; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | NACK?   |---yes--&amp;gt; nack_rcv; return  (see Section 9)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RDVS?   |---yes--&amp;gt; rdv_rcv ; return  (reply bare FC, ackno=0)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       acquire wrlock; enter locked main path&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt;&lt;br /&gt;
:refresh &amp;lt;code&amp;gt;t_ka_rcv&amp;lt;/code&amp;gt;, honour piggybacked ACK.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt;&lt;br /&gt;
:probe (echo back nonce) or echo (verify nonce, sample RTT).&lt;br /&gt;
;&amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:pre-DRF, sender-side handler.  See [[#9. Pre-DRF NACK|Section 9]].&lt;br /&gt;
;&amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt;&lt;br /&gt;
:reply with a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;); &amp;lt;code&amp;gt;rdlock&amp;lt;/code&amp;gt; only.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 6.2. Locked main path ===&lt;br /&gt;
&lt;br /&gt;
Steps below run with the per-flow &amp;lt;code&amp;gt;frcti.lock&amp;lt;/code&amp;gt; held for writing&lt;br /&gt;
(&amp;lt;code&amp;gt;pthread_rwlock_wrlock&amp;lt;/code&amp;gt;) unless noted.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;&lt;br /&gt;
:Only meaningful when the receive side is stale.  On DRF (Data Run Flag): release &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots, rebase &amp;lt;code&amp;gt;rcv_cr&amp;lt;/code&amp;gt;, continue.  On stale DATA without DRF: fire a pre-DRF NACK if cooldown allows ([[#9. Pre-DRF NACK|Section 9]]), then discard the packet; on cooldown, drop without sending a NACK (a pending cumulative ACK from &amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; may still go out).  Non-DATA, non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; pure-DRF stale arrivals fall through after the DRF rebase branch.&lt;br /&gt;
&lt;br /&gt;
;DATA-only act refresh&lt;br /&gt;
:Refresh &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; only when &amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt; is set, so that non-DATA packets never block the next DRF rebase.&lt;br /&gt;
&lt;br /&gt;
;Wire-dup gate&lt;br /&gt;
:Before flag-driven dispatch, drop wire-duplicate ACKs and wire-duplicate DATA (&amp;lt;code&amp;gt;is_dup_ack&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt;).  The DATA check is bypassed for &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;-bearing arrivals so the piggybacked ACK / SACK / FC carried on a retransmitted DATA at an already-ACK&#039;d &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is still applied; the stale-in-window branch below then drops the packet.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Drop ACKs whose &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; falls outside &amp;lt;code&amp;gt;(snd_cr.lwe, snd_cr.seqno]&amp;lt;/code&amp;gt;.  If &amp;lt;code&amp;gt;ackno == snd_cr.lwe&amp;lt;/code&amp;gt; (non-advancing cumulative ACK), drive RACK fast-retransmit consideration ([[#8. Retransmission|Section 8]]).  Otherwise advance &amp;lt;code&amp;gt;snd_cr.lwe = ackno&amp;lt;/code&amp;gt;, collapse &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; to 0 (Karn-gated by &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; on the just-acknowledged slot, the old head-of-line), reset &amp;lt;code&amp;gt;dup_thresh&amp;lt;/code&amp;gt; to 0, update &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the send-time of the slot at &amp;lt;code&amp;gt;ackno-1&amp;lt;/code&amp;gt; (consumed by RACK and SACK below), decay &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per RFC 8985 sec. 6.2 step 4, exit NewReno-careful recovery (see [[#8. Retransmission|Section 8]]) on &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt;, and feed an RTT sample if eligible ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Walk the block list.  For each block (a present range above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;) NULL out &amp;lt;code&amp;gt;snd_slots[k].rxm&amp;lt;/code&amp;gt;, clear the slot&#039;s per-send flags, and advance &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the latest send-time covered (the Forward Acknowledgement / fack equivalent, Mathis &amp;amp;amp; Mahdavi 1996); the first block whose start clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; skips this fack update so that a head-of-line clamp does not falsely advance fack.  For un-SACKed gaps below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt;, stage a retransmit per slot that is (1) still owned (&amp;lt;code&amp;gt;rxm != NULL&amp;lt;/code&amp;gt;), (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (the RFC 8985 sec. 6.2 hybrid trigger).  Mark the slot &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; and NULL the &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; at stage time.  Capped at &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; staged retransmits per receive pass; what&#039;s left rides the next SACK.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bump &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; (clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt;, never shrinks) and mark window open.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bounds-check &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; against window.  On stale-dup (&amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;), set &amp;lt;code&amp;gt;rcv_cr.seqno = seqno&amp;lt;/code&amp;gt; to force a fresh ACK on the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt;, then drop.  On accept: both FRTX and best-effort stash the packet-buffer index into &amp;lt;code&amp;gt;rq[seqno mod RQ_SIZE]&amp;lt;/code&amp;gt;.  Fragments stash unchanged - the role bits are inspected only at consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]).  On out-of-order arrival, build a SACK reply if not rate-limited (per [[#3. Protocol parameters|Section 3]]) and not deduplicated against the previous &amp;lt;code&amp;gt;(rcv_cr.lwe, n_blocks)&amp;lt;/code&amp;gt; pair; D-SACK reports always bypass the dedup.  If both rate-limit and dedup suppress the reply, neither SACK nor delayed-ACK fires (the sender picks up the gap on its next ACK).  On in-order arrival, arm the delayed-ACK timer.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; exit&lt;br /&gt;
:Releases the per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;), then calls &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; synchronously after the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; release to surface any pending cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 7. Read path and reassembly ==&lt;br /&gt;
&lt;br /&gt;
=== 7.1. Read path ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns a full reassembled SDU (Service Data Unit) via&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; on every FRCP SDU-mode flow (FRTX or best-effort);&lt;br /&gt;
stream-mode is covered in [[#16. Stream-mode flows|Section 16]].  An incomplete head-of-line&lt;br /&gt;
(HoL) run yields &amp;lt;code&amp;gt;-EAGAIN&amp;lt;/code&amp;gt;; an oversized run yields &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; (the&lt;br /&gt;
run is dropped so the flow does not stall).  On best-effort flows,&lt;br /&gt;
a permanently-lost mid-fragment is dropped as soon as a later&lt;br /&gt;
complete SDU becomes visible in the ring ([[#7.2. Fragmentation and reassembly|Section 7.2]] skip-past-&lt;br /&gt;
gap).&lt;br /&gt;
&lt;br /&gt;
Raw flows carry no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt;, so &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns the next pending&lt;br /&gt;
packet-buffer index directly, with no role-bit inspection.  (Raw&lt;br /&gt;
service is selected via &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt; at flow allocation,&lt;br /&gt;
which suppresses &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; creation.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_pdu_ready&amp;lt;/code&amp;gt; is the no-advance peek used by &amp;lt;code&amp;gt;fevent&amp;lt;/code&amp;gt; (the&lt;br /&gt;
Ouroboros flow-event multiplexer, the &amp;lt;code&amp;gt;poll(2)&amp;lt;/code&amp;gt;-equivalent on&lt;br /&gt;
flows).  It returns ready only when the head-of-line run is&lt;br /&gt;
complete and the lead packet (a Protocol Data Unit, here one FRCP&lt;br /&gt;
packet) is present at &amp;lt;code&amp;gt;rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt;; any other state&lt;br /&gt;
(including the best-effort skip-past-gap case) returns not ready,&lt;br /&gt;
and &amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; is left to drop the broken prefix and re-&lt;br /&gt;
inspect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 7.2. Fragmentation and reassembly ===&lt;br /&gt;
&lt;br /&gt;
Send side (&amp;lt;code&amp;gt;flow_write_frag&amp;lt;/code&amp;gt;).  An SDU larger than&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - PCI)&amp;lt;/code&amp;gt; is split into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - PCI))&amp;lt;/code&amp;gt;&lt;br /&gt;
fragments; each fragment is its own FRCP packet with its own&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a per-fragment role flag pair ([[#1.2. Flag bits|Section 1.2]]).  Roles are&lt;br /&gt;
assigned at emit time:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! i !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=0&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=n-1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| else || &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A mid-loop allocation or transmit failure may yield a partial&lt;br /&gt;
write: the call returns the bytes already enqueued (&amp;lt;code&amp;gt;off &amp;amp;gt; 0&amp;lt;/code&amp;gt;) or&lt;br /&gt;
the underlying error (&amp;lt;code&amp;gt;off == 0&amp;lt;/code&amp;gt;).  Best-effort flows fragment&lt;br /&gt;
identically; on the receiver, a partial run with a permanently-&lt;br /&gt;
lost fragment is dropped when a later complete SDU is visible in&lt;br /&gt;
the ring (see skip-past-gap below).  Raw flows carry no PCI and&lt;br /&gt;
refuse anything larger than the layer&#039;s user MTU (&amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Wire-level recovery is fragment-agnostic on FRTX flows: each&lt;br /&gt;
fragment&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; flows through SACK / RACK / RTO / NACK exactly&lt;br /&gt;
as for a SOLE DATA packet, and reassembly does not re-enter the&lt;br /&gt;
loss-detection path.  Best-effort flows run the same &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
machinery (DRF, FC, ACK piggyback, pre-DRF NACK emit) but queue&lt;br /&gt;
no &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; state at the sender, so a lost MID is unrecoverable;&lt;br /&gt;
skip-past-gap handles it (below).&lt;br /&gt;
&lt;br /&gt;
Receive side.  Fragments stash into &amp;lt;code&amp;gt;rq[seqno]&amp;lt;/code&amp;gt; unchanged; role bits&lt;br /&gt;
are read only at consume time.  &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;, called from&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt;, walks the ring starting at the oldest still-&lt;br /&gt;
undelivered &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;base = rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt; (equal to &amp;lt;code&amp;gt;rcv_cr.lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
only when no partial run is in progress; during a partial run &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
has already advanced past &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;).  It produces one of three&lt;br /&gt;
outcomes:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Outcome !! Cause&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELIVER (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]=SOLE&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;), or &amp;lt;code&amp;gt;rq[base]=FIRST&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; follows in slots &amp;lt;code&amp;gt;[base+1..base+n-1]&amp;lt;/code&amp;gt; with all intermediate roles in &amp;lt;code&amp;gt;{MID,FIRST,LAST}&amp;lt;/code&amp;gt; contiguous.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DROP (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; without a preceding &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;); a &amp;lt;code&amp;gt;FIRST..[non-LAST]..new-FIRST&amp;lt;/code&amp;gt; or new-&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; mid-run (drop the broken prefix with &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; = run length minus 1, so the new &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; stays); or, on best-effort flows, a gap at &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; later in the ring (drop up to the new run start).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; absent or &amp;lt;code&amp;gt;FIRST..[non-LAST]&amp;lt;/code&amp;gt; with no later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; in the ring (FRTX waits for retx; best-effort waits for arrival).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DELIVER&amp;lt;/code&amp;gt; triggers &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;: a scatter-gather &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; of the &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;&lt;br /&gt;
consecutive fragments at &amp;lt;code&amp;gt;rq[base..base+n-1]&amp;lt;/code&amp;gt; directly into the&lt;br /&gt;
caller&#039;s buffer; each per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;) is&lt;br /&gt;
released and &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; advances by &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; was already advanced&lt;br /&gt;
incrementally as each contiguous fragment arrived; &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;&lt;br /&gt;
only restores the fixed-width invariant &amp;lt;code&amp;gt;rwe == lwe + RQ_SIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
No intermediate reassembly buffer is allocated.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; past the broken prefix (releasing the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;s)&lt;br /&gt;
and pulls &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; up to the new trailing edge if needed; the next&lt;br /&gt;
consume retries from the new &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;.  Oversize or arithmetically&lt;br /&gt;
overflowing delivery (sum of fragment lengths &amp;amp;gt; &amp;lt;code&amp;gt;max_rcv_sdu&amp;lt;/code&amp;gt;, sum&lt;br /&gt;
&amp;amp;gt; caller&#039;s buffer, or running-sum overflow) also drops the run&lt;br /&gt;
with &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skip-past-gap (best-effort only).  On FRTX, a gap in the run means&lt;br /&gt;
&amp;quot;waiting for retransmit&amp;quot; and &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;.&lt;br /&gt;
On best-effort flows the gap is permanent, so &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;&lt;br /&gt;
scans forward in the ring for the next &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;; if one is&lt;br /&gt;
visible within &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;, it returns &amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; for the broken prefix and&lt;br /&gt;
the consume loop retries at the new &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;.  Memory hold is bounded&lt;br /&gt;
by &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;; the partial releases on the next consume call once a&lt;br /&gt;
later complete run exists.  Voice-like flows (one &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; per SDU)&lt;br /&gt;
see no extra wait: any later &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; makes the prior gap droppable&lt;br /&gt;
immediately.&lt;br /&gt;
&lt;br /&gt;
The choice to defer reassembly to consume time keeps the receive&lt;br /&gt;
path zero-copy: fragments stay in the shared-memory ring until&lt;br /&gt;
the application pulls, and the SDU lands directly in the caller&#039;s&lt;br /&gt;
buffer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 8. Retransmission ==&lt;br /&gt;
&lt;br /&gt;
FRCP is bounded by two delta-t-derived timers (Watson 1981, see&lt;br /&gt;
[[#15. Heritage and adopted techniques|Section 15]]):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer): upper bound on ACK delay.  An ACK for a received DATA packet MUST be emitted within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; of receipt; an attempt to send an ACK after the a-timer has expired is suppressed (the sender&#039;s RTO is already in motion).&lt;br /&gt;
* &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer): upper bound on retransmission.  A given DATA packet MUST NOT be retransmitted after &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; has elapsed since its first send (&amp;lt;code&amp;gt;t0&amp;lt;/code&amp;gt;); when the bound is hit, the flow is declared down (raising the Ouroboros asynchronous flow condition &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt;, which marks the flow dead to both endpoints) rather than retransmitted again.&lt;br /&gt;
&lt;br /&gt;
Each in-flight FRTX &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; owns one &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt;, armed in a hashed&lt;br /&gt;
timing wheel; the wheel deadline is the slot&#039;s next eligible&lt;br /&gt;
retransmit time.&lt;br /&gt;
&lt;br /&gt;
;RTO timer&lt;br /&gt;
:On fire (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;), re-emit with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, mark &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (Karn-suppress next ACK&#039;s RTT sample), and (for the head-of-line (HoL) slot only) bump &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; up to &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt;.  Wheel deadline is &amp;lt;code&amp;gt;t_send + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;.  Re-armed unless consumed.  The RTO timer also clears &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (re-arming fast-retransmit eligibility), resets &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; to 1 on a HoL fire (RFC 8985 sec. 6.2 step 4 reset clause), and marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; if its &amp;lt;code&amp;gt;frct_tx&amp;lt;/code&amp;gt; call fails.&lt;br /&gt;
&lt;br /&gt;
;r-timer guard&lt;br /&gt;
:Before any retransmit attempt, check &amp;lt;code&amp;gt;(now - t0)&amp;lt;/code&amp;gt; against &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;.  If exceeded, the slot is no longer eligible for retransmit.  Only the RTO timer (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;) treats r-timer expiry as terminal: it marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; (peer unreachable).  Fast-retransmit, SACK-driven retransmit, and NACK-driven head-of-line re-emit silently skip aged-out slots and defer the flow-down decision to the next RTO fire.&lt;br /&gt;
&lt;br /&gt;
;Fast retransmit (hybrid trigger, RFC 8985 sec. 6.2)&lt;br /&gt;
:On a non-advancing cumulative ACK with the scoreboard advanced, fire one fast retransmit when EITHER (a) the head-of-line slot&#039;s latest send is older than the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; ([[#3. Protocol parameters|Section 3]]) and not yet aged out, OR (b) the SACK &amp;lt;code&amp;gt;dup-thresh&amp;lt;/code&amp;gt; count above &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; reaches &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; (= 3, RFC 8985 sec. 6.2 step 4).  Fires at most once per non-advancing cumulative-ACK value, gated by &amp;lt;code&amp;gt;rack_fired_lwe&amp;lt;/code&amp;gt; (the &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; at which fast-retransmit last fired).  Set &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; on the slot (one-shot per-slot gate) and enter NewReno-style careful recovery (see NewReno below in this section).&lt;br /&gt;
:The RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; uses the RFC 8985 sec. 6.2 form &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor.  Before the first RTT sample seeds &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; falls back to &amp;lt;code&amp;gt;MIN(reo_wnd_mult * SRTT / 4, SRTT)&amp;lt;/code&amp;gt;, still floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt; (consistent with the windowed-minimum fallback described in [[#12. RTT estimation|Section 12]]).  &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt; is a windowed minimum over the last &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; = 5 min of RTT samples (matches the Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt; default) so a route change to a longer path eventually re-anchors the reorder window without relying on &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; growth alone.&lt;br /&gt;
&lt;br /&gt;
;SACK-driven retransmit&lt;br /&gt;
:For each gap below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt; whose slot is (1) still owned, (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (same hybrid as fast-retransmit, see [[#6.2. Locked main path|Section 6.2]]), re-emit.  Each SACK-driven retransmit re-arms a fresh &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; so a lost retransmit can still be recovered by its own RTO timer.&lt;br /&gt;
&lt;br /&gt;
;NewReno&lt;br /&gt;
:On entry, &amp;lt;code&amp;gt;recovery_high = snd_cr.seqno + RTT_QUARANTINE&amp;lt;/code&amp;gt;.  Exit when &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt; (the latter means everything sent has been acknowledged).  &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; also clears recovery.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 9. Pre-DRF NACK ==&lt;br /&gt;
&lt;br /&gt;
The two sides have different inactivity thresholds (&amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), so a receiver can detect &amp;quot;stale data run&amp;quot; before the sender&#039;s own DRF logic kicks in.  NACK is the receiver-driven nudge that asks the sender to re-transmit the head of the run.&lt;br /&gt;
&lt;br /&gt;
;Send (&amp;lt;code&amp;gt;frcti_nack_snd&amp;lt;/code&amp;gt;, called by &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;FRCT_INACT_NEED_NACK&amp;lt;/code&amp;gt;)&lt;br /&gt;
:When an incoming DATA packet has no DRF and rcv-side activity is older than &amp;lt;code&amp;gt;rcv_cr.inact&amp;lt;/code&amp;gt;, the receiver emits a bare packet with &amp;lt;code&amp;gt;flags = FRCT_NACK&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;seqno = arrival_seqno - 1&amp;lt;/code&amp;gt; (informational only, not consulted by the receive handler).  The cooldown in [[#3. Protocol parameters|Section 3]] rate-limits the burst.  Non-DATA non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; non-DATA DRF still rebases via the DRF branch.&lt;br /&gt;
&lt;br /&gt;
;Receive (&amp;lt;code&amp;gt;frcti_nack_rcv&amp;lt;/code&amp;gt;)&lt;br /&gt;
:Dispatched in the early-exit branch ([[#6.1. Early-exit dispatch|Section 6.1]]), before &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;.  The sender copies the head-of-line (HoL) &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; packet, marks the slot &amp;lt;code&amp;gt;SND_RTX | SND_FAST_RXM&amp;lt;/code&amp;gt; (Karn-suppress next ACK, one-shot fast-rxm gate), sets &amp;lt;code&amp;gt;rtt_lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, and re-emits via &amp;lt;code&amp;gt;fast_rxm_send&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt; and a refreshed &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;.  The original &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed - the NACK emit is additive to the normal retransmit machinery, not a replacement.  No-op if nothing is in flight, the HoL slot has aged past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, or the HoL &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; pointer has been cleared by SACK or RACK.&lt;br /&gt;
&lt;br /&gt;
NACK has exactly one role: lost first-of-run (DRF) packet recovery. Until the DRF packet arrives, the receiver cannot rebase its window, so any subsequent in-flight packets look stale to the receiver. The NACK fires the moment a stale receiver sees DATA without DRF, telling the sender to re-emit the head-of-line (DRF) packet at NACK-cooldown latency rather than waiting for the initial RTO (which is the configured default until &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded by the first probe round-trip).  Mid-stream loss is NOT NACK-driven; it is recovered by the sender&#039;s RTO, fast retransmit, and SACK-driven retransmit paths ([[#8. Retransmission|Section 8]]) only.&lt;br /&gt;
&lt;br /&gt;
The existing &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed on a NACK re-emit, so the RTO path remains the eventual fallback.&lt;br /&gt;
&lt;br /&gt;
== 10. Cumulative + selective ACK ==&lt;br /&gt;
&lt;br /&gt;
Cumulative ACK is &amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;.  On out-of-order arrival the&lt;br /&gt;
receiver also emits a SACK packet ([[#1.3. SACK payload|Section 1.3]]) whose payload lists&lt;br /&gt;
&#039;&#039;present&#039;&#039; blocks above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; (analogous to TCP SACK / QUIC ACK&lt;br /&gt;
ranges).  SACKs are rate-limited per [[#3. Protocol parameters|Section 3]] and suppressed when&lt;br /&gt;
neither &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; nor block count has changed since the last SACK.&lt;br /&gt;
&lt;br /&gt;
D-SACK reports (RFC 2883) are emitted in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; of an&lt;br /&gt;
otherwise normal SACK frame (see [[#1.3. SACK payload|Section 1.3]] for the encoding).&lt;br /&gt;
Two receiver triggers arm a pending D-SACK report (single-slot,&lt;br /&gt;
latest-wins):&lt;br /&gt;
&lt;br /&gt;
* DATA arrival with &amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;, both wire-dup (no RXM, &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt; path) and retransmit (RXM, post-FC branch) (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
* &amp;lt;code&amp;gt;rq_accept&amp;lt;/code&amp;gt; conflict, slot already occupied in &amp;lt;code&amp;gt;[lwe, rwe)&amp;lt;/code&amp;gt; (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
&lt;br /&gt;
When a D-SACK is pending and the standard scoreboard SACK would be&lt;br /&gt;
suppressed by dedup or rate-limit, the report is emitted as a&lt;br /&gt;
stand-alone SACK frame through the normal &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; path; when a&lt;br /&gt;
D-SACK report is pending the path bypasses dedup and the &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;&lt;br /&gt;
rate-limit, but the a-timer suppression on rcv inactivity still&lt;br /&gt;
applies.&lt;br /&gt;
&lt;br /&gt;
Bare ACKs are deferred via a per-flow delayed-ACK timer (one in&lt;br /&gt;
flight at a time, atomic test-and-set dedup; fires per [[#3. Protocol parameters|Section 3]]&lt;br /&gt;
after the first in-order arrival).  Suppressed if (1) no new&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, (2) rcv side is inactive (older than &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt;), or (3) the&lt;br /&gt;
sender just sent within &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;.  A pending D-SACK ride-through&lt;br /&gt;
bypasses (1) and (3); the a-timer gate (2) is unconditional.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 11. Flow control ==&lt;br /&gt;
&lt;br /&gt;
The receiver advertises &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; in every FC field.  The sender treats&lt;br /&gt;
its &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; as the absolute right edge: when&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt;= snd_cr.rwe&amp;lt;/code&amp;gt; the window is closed and &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt;&lt;br /&gt;
yields.  While closed, the sender periodically emits RDVS&lt;br /&gt;
(rendezvous) packets (cadence &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt;); the receiver replies with&lt;br /&gt;
a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;) that reopens the window.  Once the&lt;br /&gt;
window has been closed for longer than &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; the sender stops&lt;br /&gt;
emitting RDVS but does not tear the flow down - the writer keeps&lt;br /&gt;
blocking until either a peer-driven FC arrives or the KA&lt;br /&gt;
(keepalive) / r-timer marks the flow.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt; on receipt and MUST NOT shrink:&lt;br /&gt;
a backward &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is silently clamped to the current &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt;;&lt;br /&gt;
the FC packet still reopens the window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 12. RTT estimation ==&lt;br /&gt;
&lt;br /&gt;
Active RTTP probes ([[#1.4. RTTP payload|Section 1.4]]) carry a 32-bit &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt; (0&lt;br /&gt;
reserved) and a 16-byte random nonce echoed verbatim - defends&lt;br /&gt;
against spoofed replies.  A ring of &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; in-flight probes is&lt;br /&gt;
kept; an echo whose &amp;lt;code&amp;gt;(id, nonce)&amp;lt;/code&amp;gt; doesn&#039;t match the ring slot is&lt;br /&gt;
dropped.  A single RTTP sample is clamped to &amp;lt;code&amp;gt;RTT_CLAMP_MUL * srtt&amp;lt;/code&amp;gt;&lt;br /&gt;
(compile-time &amp;lt;code&amp;gt;RTT_CLAMP_MUL = 16&amp;lt;/code&amp;gt;) once &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded; the first&lt;br /&gt;
cold-probe sample feeds &amp;lt;code&amp;gt;rtt_update&amp;lt;/code&amp;gt; raw.&lt;br /&gt;
&lt;br /&gt;
Probe arming gates:&lt;br /&gt;
&lt;br /&gt;
;Cold (no &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; yet)&lt;br /&gt;
:the receive path arms at most one probe per 100 ms via &amp;lt;code&amp;gt;frcti_rcv_probe&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;PROBE_DUE_COLD&amp;lt;/code&amp;gt;); arming requires an incoming packet.  Active send-path arming bails while &amp;lt;code&amp;gt;srtt == 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
;Warm (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt;, called from &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt;)&lt;br /&gt;
:outstanding data (&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt; snd_cr.lwe&amp;lt;/code&amp;gt;), AND at least &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_rcv_rtt&amp;lt;/code&amp;gt; (last RTT receive of any kind), AND at least &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_snd_probe&amp;lt;/code&amp;gt; (last probe emit).&lt;br /&gt;
&lt;br /&gt;
Sample feeds either Linux&#039;s asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_LINUX_RTT_ESTIMATOR&amp;lt;/code&amp;gt;, default ON) or RFC 6298 symmetric EWMA&lt;br /&gt;
(compile option).  &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is floored at 10 ms when seeded from a&lt;br /&gt;
hint, at 1 us after every update (including the first seeding&lt;br /&gt;
sample); &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; floored at 100 ns.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;RTO = max(rto_min, 2 * srtt, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(the &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; floor is an FRCT addition not in RFC 6298).&lt;br /&gt;
Effective wheel deadline capped per [[#3. Protocol parameters|Section 3]].&lt;br /&gt;
&lt;br /&gt;
ACK-derived samples (&amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;rtt_sample_eligible&amp;lt;/code&amp;gt;), beyond&lt;br /&gt;
the cum-ACK advance gate in &amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno &amp;amp;gt; lwe&amp;lt;/code&amp;gt; and&lt;br /&gt;
&amp;lt;code&amp;gt;ackno &amp;amp;lt;= seqno&amp;lt;/code&amp;gt;), require all of: not in recovery; ACK packet does&lt;br /&gt;
not carry &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;; HoL slot&#039;s &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; bit clear; slot&#039;s &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;&lt;br /&gt;
pointer non-NULL (not SACK-consumed); &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; not below the &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
fence; &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; already seeded by an RTTP probe.  There is no ACK-only&lt;br /&gt;
seeding.&lt;br /&gt;
&lt;br /&gt;
Every eligible sample also feeds &amp;lt;code&amp;gt;RACK.min_RTT&amp;lt;/code&amp;gt; (RFC 8985 sec. 6.2)&lt;br /&gt;
via a windowed minimum: replace whenever the sample is strictly&lt;br /&gt;
smaller OR more than &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; (5 min, matches Linux&lt;br /&gt;
&amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) has elapsed since the current min was set.  The&lt;br /&gt;
downward branch is immediate (faster path picked up at once); the&lt;br /&gt;
upward branch is gated on the window (a transient queue burst does&lt;br /&gt;
not poison the estimate, but a sustained route change to a longer&lt;br /&gt;
path re-anchors &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; after at most one window).  Seeded from&lt;br /&gt;
&amp;lt;code&amp;gt;rtt_hint&amp;lt;/code&amp;gt; at &amp;lt;code&amp;gt;rtt_init&amp;lt;/code&amp;gt;; 0 acts as the unset sentinel and the base&lt;br /&gt;
in &amp;lt;code&amp;gt;rack_reorder_window&amp;lt;/code&amp;gt; falls back from &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt; (so&lt;br /&gt;
&amp;lt;code&amp;gt;R = mult * SRTT/4&amp;lt;/code&amp;gt;, capped at &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;, floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt;)&lt;br /&gt;
until the first sample.  See [[#6.2. Locked main path|Section 6.2]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 13. Liveness (keepalive) ==&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;code&amp;gt;qs.timeout &amp;amp;gt; 0&amp;lt;/code&amp;gt; a per-flow KA (keepalive) timer is armed.&lt;br /&gt;
Arming uses &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; for the deadline computation:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;deadline = min(snd_act + qs.timeout/4, rcv_act + qs.timeout)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(clamped to &amp;lt;code&amp;gt;now + qs.timeout/4&amp;lt;/code&amp;gt; if already past).  The timer fires&lt;br /&gt;
either on sender idleness (to send a KA) or on receiver idleness&lt;br /&gt;
(to declare the peer dead).  On fire (&amp;lt;code&amp;gt;ka_snd&amp;lt;/code&amp;gt;) the peer-dead test&lt;br /&gt;
uses &amp;lt;code&amp;gt;max(rcv_cr.act, t_ka_rcv)&amp;lt;/code&amp;gt; so a recent KA reply counts even&lt;br /&gt;
when no DATA has arrived:&lt;br /&gt;
&lt;br /&gt;
* If &amp;lt;code&amp;gt;now - max(rcv_cr.act, t_ka_rcv) &amp;amp;gt; qs.timeout&amp;lt;/code&amp;gt;, mark the flow &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; and notify the per-process flow-event set (&amp;lt;code&amp;gt;proc.fqset&amp;lt;/code&amp;gt;) with &amp;lt;code&amp;gt;FLOW_PEER&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Else if &amp;lt;code&amp;gt;snd_idle &amp;amp;gt; qs.timeout/4&amp;lt;/code&amp;gt;, emit a bare &amp;lt;code&amp;gt;KA | ACK&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) and re-arm.&lt;br /&gt;
* Else just re-arm.&lt;br /&gt;
&lt;br /&gt;
Note: &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; are the receive and transmit shared-memory&lt;br /&gt;
ring buffers.  The r-timer raises &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; on both (route is&lt;br /&gt;
broken); keepalive raises &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; only and notifies&lt;br /&gt;
the flow-event set (peer is silent, writer keeps &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; usable) -&lt;br /&gt;
distinct ACLs.  &amp;lt;code&amp;gt;qs.timeout == 0&amp;lt;/code&amp;gt; disables keepalive entirely; a&lt;br /&gt;
silent peer crash is then undetected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 14. Linger / teardown ==&lt;br /&gt;
&lt;br /&gt;
On &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt; computes a grace timeout&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;max(rcv_cr.act + rcv_cr.inact, snd_cr.act + snd_cr.inact) - now&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(floored at 0 and converted to seconds) and returns it; &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&lt;br /&gt;
forwards this to the IRMd as the dealloc grace.  The IRMd, not FRCT,&lt;br /&gt;
performs the wait.  Before computing the timeout, FRCT may emit a&lt;br /&gt;
final ACK when &amp;lt;code&amp;gt;rcv_cr.lwe != rcv_cr.seqno&amp;lt;/code&amp;gt; (the peer has not been&lt;br /&gt;
told the most recent cumulative ACK) AND the rcv side has been&lt;br /&gt;
active within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer not aged out).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt; is honoured only when &amp;lt;code&amp;gt;snd_cr.lwe &amp;amp;lt; edge&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;edge =&lt;br /&gt;
snd_fin_seqno&amp;lt;/code&amp;gt; after FIN has been sent in stream mode and&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno&amp;lt;/code&amp;gt; otherwise (data or FIN still in flight).  The drain&lt;br /&gt;
itself runs in &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&#039;s &amp;lt;code&amp;gt;while (FRCTI_LINGERING)&amp;lt;/code&amp;gt; loop, not in&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The fd is single-reader / single-writer (documented in the&lt;br /&gt;
manpages).  &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt; pumps &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; on every call (via&lt;br /&gt;
&amp;lt;code&amp;gt;flow_wait_window&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;flow_drain_rx_nb&amp;lt;/code&amp;gt;) and additionally blocks on&lt;br /&gt;
&amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; when the send window is closed.  A pure-writer thread thus&lt;br /&gt;
consumes ACKs without a dedicated reader.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 15. Heritage and adopted techniques ==&lt;br /&gt;
&lt;br /&gt;
Delta-t (Watson, 1981) is the primary heritage; FRCP descends from&lt;br /&gt;
the delta-t protocol family via the Recursive InterNetwork&lt;br /&gt;
Architecture (RINA; Day, &amp;quot;Patterns in Network Architecture&amp;quot;, 2008,&lt;br /&gt;
ch. 9).  Timer-based connection management&lt;br /&gt;
(no SYN/FIN handshake, per-flow state born on first DATA and&lt;br /&gt;
reclaimed after &amp;lt;code&amp;gt;t_mpl + a + r&amp;lt;/code&amp;gt; of silence), the DRF marker, and the&lt;br /&gt;
&amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; timers all come from delta-t.  See Watson,&lt;br /&gt;
&amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection&lt;br /&gt;
Management&amp;quot;, Computer Networks 5 (1981).&lt;br /&gt;
&lt;br /&gt;
The unified &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive and its&lt;br /&gt;
multi-axis QoS-cube argument ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) also come from RINA&lt;br /&gt;
(Day 2008, ch. 6; Grasa et al., &amp;quot;IRATI: investigating RINA as an&lt;br /&gt;
alternative to TCP/IP&amp;quot;, Computer Networks 92 (2015)) - reliability,&lt;br /&gt;
ordering, CRC presence, and encryption are flow attributes, not&lt;br /&gt;
separate sockets or protocols.&lt;br /&gt;
&lt;br /&gt;
The table below summarises additional adopted techniques and their&lt;br /&gt;
references.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FRCP mechanism !! Heritage !! Reference / note&lt;br /&gt;
|-&lt;br /&gt;
| Random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; || TCP ISN || RFC 6528 (Gont &amp;amp;amp; Bellovin, 2012).  QUIC PN-space reset (RFC 9000 sec. 12.3) is a structural analogue.&lt;br /&gt;
|-&lt;br /&gt;
| Cumulative ACK, left-window-edge advance || TCP || RFC 793 / RFC 9293&lt;br /&gt;
|-&lt;br /&gt;
| Receive window with non-shrink rule || TCP || RFC 793 sec. 3.7 / RFC 9293 sec. 3.8.6; RFC 1122 sec. 4.2.2.16 for the explicit non-shrink prohibition&lt;br /&gt;
|-&lt;br /&gt;
| Modular &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; arithmetic (&amp;lt;code&amp;gt;before&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;after&amp;lt;/code&amp;gt; helpers) || TCP || RFC 793 sec. 3.3 / RFC 9293 sec. 3.4&lt;br /&gt;
|-&lt;br /&gt;
| Selective ACK block list || TCP || RFC 2018 (Mathis et al., 1996).  Encoded as a typed FRCP packet rather than a TCP option, so framing is closer to QUIC ACK frames.  D-SACK (RFC 2883) carried in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt;; see [[#1.3. SACK payload|Section 1.3]].&lt;br /&gt;
|-&lt;br /&gt;
| NewReno-careful recovery with &amp;lt;code&amp;gt;recovery_high&amp;lt;/code&amp;gt; gate || TCP || RFC 6582 (Henderson et al., 2012); QUIC builds on the same model in RFC 9002 sec. 7.3.2.  Cwnd half absent (CC in IPCP).&lt;br /&gt;
|-&lt;br /&gt;
| RACK reordering window for fast retransmit || TCP || RFC 8985 (Cheng et al., 2021).  FRCP &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor against &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; collapse; matches RFC 8985 sec. 6.2 and Linux &amp;lt;code&amp;gt;tcp_rack_reo_wnd&amp;lt;/code&amp;gt;.  DSACK-driven &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; (sec. 6.2 step 4) is adopted; see [[#1.3. SACK payload|Section 1.3]] for the wire encoding.  The hybrid RACK-or-&amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; trigger from RFC 8985 sec. 6.2 step 4 is adopted ([[#8. Retransmission|Section 8]]).  QUIC&#039;s analogue in RFC 9002 sec. 6.1.2 uses &amp;lt;code&amp;gt;max(srtt, latest_rtt)&amp;lt;/code&amp;gt; as the base.&lt;br /&gt;
|-&lt;br /&gt;
| Karn&#039;s algorithm: no RTT sample on retransmits, RTO-collapse freeze || TCP || Karn &amp;amp;amp; Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, SIGCOMM 1987; RFC 6298 sec. 3.&lt;br /&gt;
|-&lt;br /&gt;
| RTO formula &amp;lt;code&amp;gt;RTO = max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt; || TCP || RFC 6298 (Paxson et al., 2011).  &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; = 250 us is below RFC 6298 sec. 2.4&#039;s 1 s SHOULD-floor - a recursive-layer choice.&lt;br /&gt;
|-&lt;br /&gt;
| Linux asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator (default) || Linux kernel || &amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;if(delta&amp;amp;lt;0) m&amp;amp;gt;&amp;amp;gt;=3&amp;lt;/code&amp;gt; dampening is a kernel divergence from RFC 6298.  RFC 6298 EWMA available behind a compile flag.&lt;br /&gt;
|-&lt;br /&gt;
| Delayed ACK with rate suppression || TCP || RFC 813 (Clark, 1982); RFC 1122 sec. 4.2.3.2; RFC 5681 sec. 4.2.  Single-deadline coalescing rather than &amp;quot;ack-every-other-segment&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| Zero-window-probe / persist-timer analogue (RDVS) || TCP || RFC 1122 sec. 4.2.2.17 / RFC 9293 sec. 3.8.6.1.  RDVS solicits an FC reply, distinct from QUIC &amp;lt;code&amp;gt;DATA_BLOCKED&amp;lt;/code&amp;gt; (RFC 9000 sec. 19.12), which is one-way notification.  &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; give-up departs from TCP.&lt;br /&gt;
|-&lt;br /&gt;
| Multiplexed control on a single PCI || SCTP / QUIC || SCTP chunk bundling (RFC 9260 sec. 6.10); QUIC frame multiplexing (RFC 9000 sec. 12.4).  Cleaner fit than TCP&#039;s separate-flag-bits design.&lt;br /&gt;
|-&lt;br /&gt;
| ACK ranges as multiple discontiguous acked blocks || QUIC || QUIC ACK frame (RFC 9000 sec. 19.3).  FRCP SACK is conceptually QUIC-frame-shaped even though encoded as absolute &amp;lt;code&amp;gt;[start,end]&amp;lt;/code&amp;gt; pairs.&lt;br /&gt;
|-&lt;br /&gt;
| Nonce-authenticated active RTT / liveness probing (RTTP) || QUIC &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;PATH_RESPONSE&amp;lt;/code&amp;gt; (RFC 9000 sec. 8.2, sec. 19.17, sec. 19.18).  WebRTC ICE consent-freshness (RFC 7675) is the same pattern.  QUIC&#039;s nonce is 8 octets; FRCP chooses 16.&lt;br /&gt;
|-&lt;br /&gt;
| Probing distinct from keepalive || QUIC || KA timer answers &amp;quot;peer alive?&amp;quot;, RTTP answers &amp;quot;path measurable?&amp;quot;, as in QUIC PING (RFC 9000 sec. 19.2) vs &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| Bare KA + ACK keepalive packets || QUIC / SCTP || QUIC PING (RFC 9000 sec. 19.2); SCTP HEARTBEAT / HEARTBEAT-ACK (RFC 9260 sec. 8.3).  SCTP HEARTBEAT also carries an opaque echoed blob, structurally similar to FRCP RTTP.&lt;br /&gt;
|-&lt;br /&gt;
| (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) fragment-role bits ([[#7.2. Fragmentation and reassembly|Section 7.2]]) || SCTP || RFC 9260 sec. 3.3.1 DATA chunk B/E bits encode the same four states (&amp;lt;code&amp;gt;B+E=SOLE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;B-only=FIRST&amp;lt;/code&amp;gt;, neither=&amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E-only=LAST&amp;lt;/code&amp;gt;).  Each fragment carries its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;/TSN and is independently retransmitted.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-offset reassembly (Sections [[#1.5. Stream PCI extension|1.5]], [[#16. Stream-mode flows|16]]) || QUIC || QUIC STREAM frame (RFC 9000 sec. 19.8) uses Offset + Length varints; FRCP uses fixed 32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;.  One stream per flow vs QUIC&#039;s many streams multiplexed.&lt;br /&gt;
|-&lt;br /&gt;
| FIN end-of-stream marker (Sections [[#1.2. Flag bits|1.2]], [[#16. Stream-mode flows|16]]) || TCP / QUIC || TCP FIN flag (RFC 9293 sec. 3.1) closes one half of the byte stream; QUIC STREAM frame FIN bit (RFC 9000 sec. 19.8) does the same per stream with an immutable final-size invariance (RFC 9000 sec. 4.5: the final size is fixed once observed).  FRCP&#039;s FIN consumes one packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (not one byte of stream space) and is idempotent on the sender side.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-credit flow control ([[#16. Stream-mode flows|Section 16]]) || QUIC || &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; (RFC 9000 sec. 4.1, sec. 19.10).  FRCP projects a per-flow byte budget onto the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;.  Single stream per flow collapses QUIC&#039;s &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.&lt;br /&gt;
|-&lt;br /&gt;
| Header protection (encrypted seqnos) || QUIC || QUIC RFC 9001 sec. 5.4 applies header protection on top of AEAD to mask the packet number.  FRCP&#039;s per-flow AEAD wrap ([[#16. Stream-mode flows|Section 16]]) is wider: it encrypts the entire PCI including &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; because the IPCP below already routes, so no destination connection-ID needs to stay in clear (cf. RFC 9000 sec. 5.2).&lt;br /&gt;
|-&lt;br /&gt;
| Two-bit fragment role polarity || SCTP || The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair follows SCTP B/E (begin = 1 / end = 1) rather than IPv4 MF (RFC 791 sec. 3.2), which has the inverse polarity (MF = 1 means NOT last).&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal reliability / ordering axes ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) || SCTP || PR-SCTP (RFC 3758, per-message partial reliability) and SCTP DATA U-bit (RFC 9260 sec. 3.3.1, per-message unordered) are the closest precedents for decoupling reliability from ordering; FRCP sets them per-flow rather than per-message.&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal CRC (&amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt;) || UDP-Lite || RFC 3828 (Larzon et al., 2004) lets the sender pick a per-packet Checksum Coverage and the receiver enforce a locally configured minimum (no in-band negotiation; sec. 3.1, sec. 3.3).  FRCP gates a full CRC trailer on &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; at flow setup.  Contrast TCP / SCTP (mandatory checksum) and QUIC (AEAD subsumes CRC).&lt;br /&gt;
|-&lt;br /&gt;
| Setup-time service negotiation || DCCP / SCTP / QUIC || DCCP Service Codes (RFC 4340 sec. 8.1.2, RFC 5595); SCTP INIT parameters (RFC 9260 sec. 3.3.2); QUIC transport parameters (RFC 9000 sec. 7.4).  All negotiate service properties at connection setup; only RINA&#039;s QoS cube exposes them as an orthogonal vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.1. Original to FRCP (no clean prior art) ===&lt;br /&gt;
&lt;br /&gt;
* Pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]): receiver-driven nudge exploiting &amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;.  Closest analogues are SCTP Gap Ack Blocks (RFC 9260 sec. 3.3.4) and DCCP Ack Vector (RFC 4340 sec. 11.4) - both let the receiver describe gaps to the sender, but neither targets the cross-epoch / pre-DRF case.&lt;br /&gt;
* &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; window-probe give-up: neither TCP (persist-timer probes until application or R2 abort, RFC 9293 sec. 3.8.6.1) nor QUIC has an explicit FC-give-up counter.  A recursive-network choice: outer layers can drop the flow.&lt;br /&gt;
* Skip-past-gap reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]): SCTP fragments and reassembles every flow regardless of reliability/ordering, using its own per-stream reassembly queue; QUIC fragments via STREAM offsets.  FRCP fragments best-effort flows too, but the receiver drops the broken prefix the moment a later run-start (&amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; role) is visible inside the &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;-wide reorder ring - no IP-frag-style timeout, no SCTP-style explicit abort.  If no later run-start arrives within the ring, &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt; and the partial run keeps its slots; the next inspect retries.  The trade-off: a permanently-lost &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; in a long isolated run holds slots until either a later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; appears in the ring or the writer stops, at which point the slots are reclaimed on flow teardown.&lt;br /&gt;
* Reassembly deferred to consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]), message mode only (&amp;lt;code&amp;gt;qos.service == SVC_MESSAGE&amp;lt;/code&amp;gt;): SCTP (RFC 9260 sec. 6.9), QUIC (RFC 9000 sec. 2.2), and TCP (RFC 9293) all hold reassembly state at the receive boundary.  FRCP message-mode leaves fragments in the shared-memory ring until &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; pulls and lands the SDU directly in the caller&#039;s buffer.  Stream mode ([[#16. Stream-mode flows|Section 16]]) uses the standard QUIC-style direct ring placement on receive and does not defer.  The optimisation is enabled by the Shared-Memory Subsystem (SSM) packet-buffer ring (see &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt; at [[#1.1. PCI header|Section 1.1]]); the analogue is OS-level scatter-gather I/O (&amp;lt;code&amp;gt;recvmsg+iovec&amp;lt;/code&amp;gt;), not a transport-layer prior art.&lt;br /&gt;
* TLP-equivalent tail-loss recovery (RFC 8985 sec. 7; RFC 9002 sec. 6.2): FRCP does not emit an explicit Tail Loss Probe packet, but the same goal is met implicitly by RACK loss detection ([[#8. Retransmission|Section 8]]) firing on a non-advancing cumulative ACK once the head-of-line slot ages past the RACK reorder window &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; - well below &amp;lt;code&amp;gt;RTO = max(2 * SRTT, SRTT + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.  A receiver-driven nudge is also available via the pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.2. Not adopted ===&lt;br /&gt;
&lt;br /&gt;
* Slow start, congestion window (cwnd), Additive Increase / Multiplicative Decrease (AIMD), NewReno cwnd inflation.  Congestion control lives in the IPCP CA policies and is driven by Explicit Congestion Notification (ECN, RFC 3168).&lt;br /&gt;
* Nagle / silly-window-syndrome (SWS) avoidance (RFC 896, RFC 1122 sec. 4.2.3.4).  (Deferred work, not adopted in the current spec.)&lt;br /&gt;
* TCP Timestamps (RFC 7323) / Protection Against Wrapped Sequences (PAWS) - RTT measurement uses RTTP, not per-segment timestamps.  A peer-supplied timestamp echoed on every ACK lets a malicious peer drive the &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; estimate arbitrarily low, collapsing the RTO and triggering a self-inflicted retransmit storm.  RTTP confines RTT measurement to nonce-authenticated probe round-trips, where a forged echo is rejected before it can reach the estimator.&lt;br /&gt;
* ECN (Explicit Congestion Notification) response inside FRCP (consumed by IPCP Congestion Avoidance / CA).&lt;br /&gt;
* IP-style fragment-offset reassembly (RFC 791 sec. 3.2; RFC 8200 sec. 4.5).  Message-mode FRCP relies on the FRCT &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; reorder ring keyed by &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (shared by FRTX and best-effort flows) to put fragments back in order; no separate offset field is needed and no IP-style hole-list reassembly buffer is kept.  Stream-mode FRCP does carry &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte offsets ([[#1.5. Stream PCI extension|Section 1.5]]) for direct ring placement on receive.&lt;br /&gt;
* QUIC STREAM offset+length framing on &#039;&#039;every&#039;&#039; flow (RFC 9000 sec. 19.8).  Message-mode FRCP uses the SCTP-style B/E flag-bit encoding (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) and skips the offsets; stream-mode FRCP adopts the QUIC offset model (heritage table above).&lt;br /&gt;
&lt;br /&gt;
== 16. Stream-mode flows ==&lt;br /&gt;
&lt;br /&gt;
When a flow is allocated with &amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt; both peers&lt;br /&gt;
switch to byte-stream semantics, layered on top of the FRTX reorder&lt;br /&gt;
machinery already described in Sections [[#6. Receive path|6]]-[[#8. Retransmission|8]].&lt;br /&gt;
&lt;br /&gt;
=== 16.1. Send ===&lt;br /&gt;
&lt;br /&gt;
The sender splits the caller&#039;s octets into chunks of at most&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt; octets (Sections [[#1.1. PCI header|1.1]]&lt;br /&gt;
and [[#1.5. Stream PCI extension|1.5]]).  Each chunk is one DATA packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte range copied from a monotonic stream counter.&lt;br /&gt;
In stream mode &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST be transmitted as&lt;br /&gt;
zero; the per-byte position is carried by the &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt;&lt;br /&gt;
extension instead.&lt;br /&gt;
&lt;br /&gt;
End-of-stream is signalled with a 0-byte DATA packet that has &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;&lt;br /&gt;
(bit 12) set, emitted on the FIN triggers listed in [[#1.2. Flag bits|Section 1.2]]&lt;br /&gt;
(WR-half close, &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, and any other path that yields the&lt;br /&gt;
final byte).  The sender MUST emit at most one FIN per flow; its&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; MUST equal &amp;lt;code&amp;gt;[final-byte, final-byte)&amp;lt;/code&amp;gt; (i.e., empty&lt;br /&gt;
interval at the final byte position; final-size invariance,&lt;br /&gt;
analogous to QUIC RFC 9000 sec. 4.5).  Idempotency is enforced by&lt;br /&gt;
an &amp;lt;code&amp;gt;snd_fin_sent&amp;lt;/code&amp;gt; guard.&lt;br /&gt;
&lt;br /&gt;
=== 16.2. Receive ===&lt;br /&gt;
&lt;br /&gt;
On arrival the receiver places the payload directly into a per-flow&lt;br /&gt;
byte-indexed receive ring of width &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; (octets) at the position&lt;br /&gt;
indicated by &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, with a two-segment &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; across the ring&lt;br /&gt;
boundary if needed.  Receipt is recorded in the FRTX reorder&lt;br /&gt;
machinery ([[#6.2. Locked main path|Section 6.2]]) augmented with the packet&#039;s &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;, and&lt;br /&gt;
FIN bit per slot.  When a packet&#039;s &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; front-overlaps&lt;br /&gt;
bytes already at or below the byte high-water mark, the overlap is&lt;br /&gt;
trimmed before placement so the same byte is never written twice.&lt;br /&gt;
After stashing, the receiver advances &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; and the byte high-water&lt;br /&gt;
mark across any newly-contiguous prefix.  Each slot advanced MUST&lt;br /&gt;
satisfy &amp;lt;code&amp;gt;start == the last-delivered slot&#039;s end&amp;lt;/code&amp;gt;; a slot whose&lt;br /&gt;
&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; does not equal that &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; is silently dropped at delivery time&lt;br /&gt;
(the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is consumed, no stream bytes contributed) and the high-&lt;br /&gt;
water mark does not advance past it.  The stream byte-stream&lt;br /&gt;
stalls at that point - there is no flow-tear-down on mismatch.&lt;br /&gt;
This filters spliced or off-path-injected slots that fall in&lt;br /&gt;
window without strong cryptographic authentication.&lt;br /&gt;
&lt;br /&gt;
A FIN slot marks end-of-stream at advance time only if its byte&lt;br /&gt;
position equals the last-delivered slot&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;; otherwise the FIN&lt;br /&gt;
is ignored and the corresponding &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; occupies a slot but&lt;br /&gt;
contributes no stream bytes.  No packet buffer is held after the&lt;br /&gt;
ring copy.&lt;br /&gt;
&lt;br /&gt;
=== 16.3. Read ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns up to &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt; octets from the contiguous prefix&lt;br /&gt;
&amp;lt;code&amp;gt;[next, high-water)&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;next&amp;lt;/code&amp;gt; is the byte the application has&lt;br /&gt;
already consumed up to and &amp;lt;code&amp;gt;high-water&amp;lt;/code&amp;gt; is the rightmost contiguous&lt;br /&gt;
byte received.  When the stream is fully drained AND end-of-stream&lt;br /&gt;
(EOS) was observed (&amp;lt;code&amp;gt;next == EOS&amp;lt;/code&amp;gt; byte position), &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns&lt;br /&gt;
0 (&amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) - the same shape POSIX &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt; uses on TCP after a peer&lt;br /&gt;
FIN.&lt;br /&gt;
&lt;br /&gt;
=== 16.4. Flow control ===&lt;br /&gt;
&lt;br /&gt;
ACK / SACK / RACK / RTO machinery is unchanged; the FRTX reorder&lt;br /&gt;
ring is reused as a per-&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; received-bitmap.  Let &amp;lt;code&amp;gt;per_pkt =&lt;br /&gt;
(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt;, the maximum stream-&lt;br /&gt;
byte payload one DATA packet can carry ([[#16.1. Send|Section 16.1]]).  The&lt;br /&gt;
receive window advertised in FC is clamped so the byte window&lt;br /&gt;
(&amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt;) cannot be overrun: the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is at most&lt;br /&gt;
&amp;lt;code&amp;gt;rcv_cr.lwe + ring_sz / per_pkt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC byte-credit flow-control model&lt;br /&gt;
(&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt;, RFC 9000 sec. 4.1 and sec. 19.10) projected onto&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; space.  With one stream per flow there is no &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; /&lt;br /&gt;
&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.  Receiver-side silly-window-syndrome&lt;br /&gt;
(SWS) avoidance (RFC 9293 sec. 3.8.6.2.2) is achieved by combining&lt;br /&gt;
the consume-time &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; bump with the global non-shrink rule from&lt;br /&gt;
[[#11. Flow control|Section 11]].&lt;br /&gt;
&lt;br /&gt;
=== 16.5. Security considerations ===&lt;br /&gt;
&lt;br /&gt;
Threat model.  An attacker that can observe (on-path passive) or&lt;br /&gt;
predict (off-path blind) the flow&#039;s seqnos and byte offsets on an&lt;br /&gt;
unencrypted stream flow can inject DATA or FIN at any in-window&lt;br /&gt;
position.  The in-line consistency checks above (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; == prior&lt;br /&gt;
&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; on advance; FIN MUST be 0-byte; FIN MUST sit at the final&lt;br /&gt;
byte position) realise the spirit of RFC 5961&#039;s &amp;quot;sequence-window&lt;br /&gt;
plus exact-position match for control bits&amp;quot; without an explicit&lt;br /&gt;
challenge-ACK probe; they make a few specific blind attack shapes&lt;br /&gt;
harder but are not cryptographic authentication. This is&lt;br /&gt;
comparable to TCP without the TCP Authentication Option (TCP-AO,&lt;br /&gt;
RFC 5925), tighter than a&lt;br /&gt;
pre-RFC-5961 TCP stack, and roughly equivalent to a modern&lt;br /&gt;
RFC 5961 stack against blind off-path injection - none of these&lt;br /&gt;
help once the attacker can sniff. TLS over TCP (RFC 8446)&lt;br /&gt;
encrypts only the TCP payload and leaves TCP seqnos, ACKs, FIN,&lt;br /&gt;
and RST in the clear, so TLS does NOT defend against TCP-header-&lt;br /&gt;
level injection; QUIC (RFC 9000) hides packet numbers under&lt;br /&gt;
header protection (RFC 9001 sec. 5.4), so this specific weakness&lt;br /&gt;
does not apply to QUIC.&lt;br /&gt;
&lt;br /&gt;
Mitigation: AEAD.  When the flow has encryption enabled the&lt;br /&gt;
recommended AEAD ciphers (AES-GCM, RFC 5288; or ChaCha20-Poly1305,&lt;br /&gt;
RFC 8439) wrap the entire FRCP packet on the wire - PCI, stream&lt;br /&gt;
extension, body, and the CRC trailer when &amp;lt;code&amp;gt;ber == 0&amp;lt;/code&amp;gt; - under a&lt;br /&gt;
per-flow symmetric key derived from the flow&#039;s own key exchange&lt;br /&gt;
([[#1.1. PCI header|Section 1.1]]).  The AEAD tag (~2^-128 forgery probability)&lt;br /&gt;
dominates the CRC (~2^-32) for integrity in this mode but the CRC&lt;br /&gt;
trailer is currently retained inside the wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
Implementations MUST NOT rely on the security properties below&lt;br /&gt;
when a non-AEAD cipher (e.g. AES-CTR alone) is negotiated; non-&lt;br /&gt;
AEAD modes provide confidentiality only and the threat-model&lt;br /&gt;
claims do not hold.&lt;br /&gt;
&lt;br /&gt;
With an AEAD cipher in use, seqnos, byte offsets, and the FIN bit&lt;br /&gt;
are both authenticated and confidential. Against an off-path or&lt;br /&gt;
on-path-passive attacker this is:&lt;br /&gt;
&lt;br /&gt;
* Stronger than TCP+TLS (TCP header in the clear).&lt;br /&gt;
* Stronger than TCP+TCP-AO (header authenticated but visible).&lt;br /&gt;
* Comparable to IPsec ESP transport mode (RFC 4303), which similarly authenticates and encrypts the upper-layer header plus payload, and to QUIC packet protection (RFC 9001 sec. 5), with the difference that QUIC must leave the destination connection ID in the clear for routing whereas FRCP relies on the IPCP below for delivery and can therefore encrypt its entire PCI.&lt;br /&gt;
&lt;br /&gt;
Keying granularity. Ouroboros flow allocation runs key exchange (&amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt;) per flow, so each &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; yields independent symmetric keys. This is finer-grained than QUIC (per-connection, RFC 9001, where one handshake covers all multiplexed streams) and finer-grained than typical IPsec deployment (per-host-pair Security Associations, SAs). Forward secrecy follows from the &amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt; when an ephemeral Diffie-Hellman exchange (DHE), or a hybrid mode (classical DH + post-quantum Key Encapsulation Mechanism / KEM), is selected.&lt;br /&gt;
&lt;br /&gt;
Replay protection. The AEAD layer itself does NOT carry an explicit anti-replay window (unlike IPsec ESP, RFC 4303 sec. 3.4.3, or DTLS, RFC 9147 sec. 4.5.1).  For FRCP-engaged flows the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space duplicate-suppression in [[#6.2. Locked main path|Section 6.2]] rejects replayed&lt;br /&gt;
DATA after the AEAD strips the wrap, because the AEAD authenticates the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a replay re-presents an old &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; that is then discarded either as a duplicate (still inside the receive window or as outside the receive window, depending on how far &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; has advanced since the original packet was delivered. RAW (&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) flows have no FRCP layer and therefore no replay protection at the AEAD layer either; deployments that need replay rejection on RAW flows SHOULD use SVC_MESSAGE.&lt;br /&gt;
&lt;br /&gt;
Layering. The AEAD wrap sits below FRCP on the data path, so RAW best-effort flows (&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;, the UDP-equivalent service of [[#2.2. Service modes (orthogonal axes)|Section 2.2]]) inherit the same per-flow integrity + confidentiality scope as FRCP-engaged flows - whatever the process and FRCP (if any) put on the wire is what the AEAD authenticates. No DTLS-equivalent layering is required for confidentiality and integrity; replay protection above AEAD is a separate concern as noted above.&lt;br /&gt;
&lt;br /&gt;
== 17. References ==&lt;br /&gt;
&lt;br /&gt;
This section lists the IETF documents, published works, and&lt;br /&gt;
source-code references cited inline elsewhere in this document.&lt;br /&gt;
IETF documents are cited inline as &amp;quot;RFC NNNN sec. X.Y&amp;quot;; books,&lt;br /&gt;
journal papers, and source-code references are cited inline by&lt;br /&gt;
author and year (or by file and function name) and are listed&lt;br /&gt;
here for convenience.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.1. IETF documents ===&lt;br /&gt;
&lt;br /&gt;
;[RFC 791]&lt;br /&gt;
:J. Postel, &amp;quot;Internet Protocol&amp;quot;, STD 5, RFC 791, September 1981.&lt;br /&gt;
;[RFC 793]&lt;br /&gt;
:J. Postel, &amp;quot;Transmission Control Protocol&amp;quot;, STD 7, RFC 793, September 1981.  Obsoleted by RFC 9293.&lt;br /&gt;
;[RFC 813]&lt;br /&gt;
:D. D. Clark, &amp;quot;Window and Acknowledgement Strategy in TCP&amp;quot;, RFC 813, July 1982.&lt;br /&gt;
;[RFC 896]&lt;br /&gt;
:J. Nagle, &amp;quot;Congestion Control in IP/TCP Internetworks&amp;quot;, RFC 896, January 1984.&lt;br /&gt;
;[RFC 1122]&lt;br /&gt;
:R. Braden (ed.), &amp;quot;Requirements for Internet Hosts -- Communication Layers&amp;quot;, STD 3, RFC 1122, October 1989.&lt;br /&gt;
;[RFC 2018]&lt;br /&gt;
:M. Mathis, J. Mahdavi, S. Floyd, A. Romanow, &amp;quot;TCP Selective Acknowledgment Options&amp;quot;, RFC 2018, October 1996.&lt;br /&gt;
;[RFC 2119]&lt;br /&gt;
:S. Bradner, &amp;quot;Key words for use in RFCs to Indicate Requirement Levels&amp;quot;, BCP 14, RFC 2119, March 1997.&lt;br /&gt;
;[RFC 2883]&lt;br /&gt;
:S. Floyd, J. Mahdavi, M. Mathis, M. Podolsky, &amp;quot;An Extension to the Selective Acknowledgement (SACK) Option for TCP&amp;quot;, RFC 2883, July 2000.&lt;br /&gt;
;[RFC 3758]&lt;br /&gt;
:R. Stewart, M. Ramalho, Q. Xie, M. Tuexen, P. Conrad, &amp;quot;Stream Control Transmission Protocol (SCTP) Partial Reliability Extension&amp;quot;, RFC 3758, May 2004.&lt;br /&gt;
;[RFC 3828]&lt;br /&gt;
:L-A. Larzon, M. Degermark, S. Pink, L-E. Jonsson (ed.), G. Fairhurst (ed.), &amp;quot;The Lightweight User Datagram Protocol (UDP-Lite)&amp;quot;, RFC 3828, July 2004.&lt;br /&gt;
;[RFC 4303]&lt;br /&gt;
:S. Kent, &amp;quot;IP Encapsulating Security Payload (ESP)&amp;quot;, RFC 4303, December 2005.&lt;br /&gt;
;[RFC 4340]&lt;br /&gt;
:E. Kohler, M. Handley, S. Floyd, &amp;quot;Datagram Congestion Control Protocol (DCCP)&amp;quot;, RFC 4340, March 2006.&lt;br /&gt;
;[RFC 5288]&lt;br /&gt;
:J. Salowey, A. Choudhury, D. McGrew, &amp;quot;AES Galois Counter Mode (GCM) Cipher Suites for TLS&amp;quot;, RFC 5288, August 2008.&lt;br /&gt;
;[RFC 5595]&lt;br /&gt;
:G. Fairhurst, &amp;quot;The Datagram Congestion Control Protocol (DCCP) Service Codes&amp;quot;, RFC 5595, September 2009.&lt;br /&gt;
;[RFC 5681]&lt;br /&gt;
:M. Allman, V. Paxson, E. Blanton, &amp;quot;TCP Congestion Control&amp;quot;, RFC 5681, September 2009.&lt;br /&gt;
;[RFC 5925]&lt;br /&gt;
:J. Touch, A. Mankin, R. Bonica, &amp;quot;The TCP Authentication Option&amp;quot;, RFC 5925, June 2010.&lt;br /&gt;
;[RFC 5961]&lt;br /&gt;
:A. Ramaiah, R. Stewart, M. Dalal, &amp;quot;Improving TCP&#039;s Robustness to Blind In-Window Attacks&amp;quot;, RFC 5961, August 2010.&lt;br /&gt;
;[RFC 6298]&lt;br /&gt;
:V. Paxson, M. Allman, J. Chu, M. Sargent, &amp;quot;Computing TCP&#039;s Retransmission Timer&amp;quot;, RFC 6298, June 2011.&lt;br /&gt;
;[RFC 6528]&lt;br /&gt;
:F. Gont, S. Bellovin, &amp;quot;Defending against Sequence Number Attacks&amp;quot;, RFC 6528, February 2012.  Obsoletes RFC 1948.&lt;br /&gt;
;[RFC 6582]&lt;br /&gt;
:T. Henderson, S. Floyd, A. Gurtov, Y. Nishida, &amp;quot;The NewReno Modification to TCP&#039;s Fast Recovery Algorithm&amp;quot;, RFC 6582, April 2012.&lt;br /&gt;
;[RFC 7323]&lt;br /&gt;
:D. Borman, B. Braden, V. Jacobson, R. Scheffenegger (ed.), &amp;quot;TCP Extensions for High Performance&amp;quot;, RFC 7323, September 2014.&lt;br /&gt;
;[RFC 7675]&lt;br /&gt;
:M. Perumal, D. Wing, R. Ravindranath, T. Reddy, M. Thomson, &amp;quot;Session Traversal Utilities for NAT (STUN) Usage for Consent Freshness&amp;quot;, RFC 7675, October 2015.&lt;br /&gt;
;[RFC 8174]&lt;br /&gt;
:B. Leiba, &amp;quot;Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words&amp;quot;, BCP 14, RFC 8174, May 2017.&lt;br /&gt;
;[RFC 8200]&lt;br /&gt;
:S. Deering, R. Hinden, &amp;quot;Internet Protocol, Version 6 (IPv6) Specification&amp;quot;, STD 86, RFC 8200, July 2017.&lt;br /&gt;
;[RFC 8439]&lt;br /&gt;
:Y. Nir, A. Langley, &amp;quot;ChaCha20 and Poly1305 for IETF Protocols&amp;quot;, RFC 8439, June 2018.&lt;br /&gt;
;[RFC 8446]&lt;br /&gt;
:E. Rescorla, &amp;quot;The Transport Layer Security (TLS) Protocol Version 1.3&amp;quot;, RFC 8446, August 2018.&lt;br /&gt;
;[RFC 8985]&lt;br /&gt;
:Y. Cheng, N. Cardwell, N. Dukkipati, P. Jha, &amp;quot;The RACK-TLP Loss Detection Algorithm for TCP&amp;quot;, RFC 8985, February 2021.&lt;br /&gt;
;[RFC 9000]&lt;br /&gt;
:J. Iyengar (ed.), M. Thomson (ed.), &amp;quot;QUIC: A UDP-Based Multiplexed and Secure Transport&amp;quot;, RFC 9000, May 2021.&lt;br /&gt;
;[RFC 9001]&lt;br /&gt;
:M. Thomson (ed.), S. Turner (ed.), &amp;quot;Using TLS to Secure QUIC&amp;quot;, RFC 9001, May 2021.&lt;br /&gt;
;[RFC 9002]&lt;br /&gt;
:J. Iyengar (ed.), I. Swett (ed.), &amp;quot;QUIC Loss Detection and Congestion Control&amp;quot;, RFC 9002, May 2021.&lt;br /&gt;
;[RFC 9147]&lt;br /&gt;
:E. Rescorla, H. Tschofenig, N. Modadugu, &amp;quot;The Datagram Transport Layer Security (DTLS) Protocol Version 1.3&amp;quot;, RFC 9147, April 2022.&lt;br /&gt;
;[RFC 9260]&lt;br /&gt;
:R. Stewart, M. Tuexen, K. Nielsen, &amp;quot;Stream Control Transmission Protocol&amp;quot;, RFC 9260, June 2022.  Obsoletes RFC 4960.&lt;br /&gt;
;[RFC 9293]&lt;br /&gt;
:W. Eddy (ed.), &amp;quot;Transmission Control Protocol (TCP)&amp;quot;, STD 7, RFC 9293, August 2022.  Obsoletes RFC 793 and several follow-ons; updates RFC 1122 and others.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.2. Books and journal papers ===&lt;br /&gt;
&lt;br /&gt;
;[Day08]&lt;br /&gt;
:J. Day, &amp;quot;Patterns in Network Architecture: A Return to Fundamentals&amp;quot;, Prentice Hall, 2008.&lt;br /&gt;
;[Grasa15]&lt;br /&gt;
:E. Grasa et al., &amp;quot;IRATI: investigating RINA as an alternative to TCP/IP&amp;quot;, Computer Networks, Vol. 92, December 2015.&lt;br /&gt;
;[KP87]&lt;br /&gt;
:P. Karn, C. Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, ACM SIGCOMM, August 1987.&lt;br /&gt;
;[Wat81]&lt;br /&gt;
:R. W. Watson, &amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection Management&amp;quot;, Computer Networks, Vol. 5, 1981.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.3. Source-code references ===&lt;br /&gt;
&lt;br /&gt;
;[Linux-RTT]&lt;br /&gt;
:&amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt; of the Linux kernel, defining the asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; variance update used as FRCP&#039;s default RTT estimator ([[#12. RTT estimation|Section 12]]).  Line-stable browseable copy at https://elixir.bootlin.com/linux/latest/source/net/ipv4/tcp_input.c.&lt;br /&gt;
&lt;br /&gt;
[[Category:Protocols]]&lt;br /&gt;
[[Category:Ouroboros internals]]&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1919</id>
		<title>Flow and Retransmission Control Protocol</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1919"/>
		<updated>2026-05-17T15:12:21Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 16.5. Security considerations */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:FRCP - Flow and Retransmission Control Protocol}}&lt;br /&gt;
&lt;br /&gt;
FRCP runs end-to-end between two peers over a flow.  It delivers&lt;br /&gt;
reliability, in-order delivery, flow control, and liveness.&lt;br /&gt;
Congestion Control (CC) is not in FRCP - that lives in the IPC&lt;br /&gt;
Process (IPCP) Congestion Avoidance (CA) policies, orthogonal to&lt;br /&gt;
FRCP.  Flow allocation, naming, and IPCP lifecycle are handled by&lt;br /&gt;
the IPC Resource Manager daemon (IRMd).&lt;br /&gt;
&lt;br /&gt;
FRCT (Flow and Retransmission Control Task) is the libouroboros&lt;br /&gt;
implementation of FRCP; the task lives in &amp;lt;code&amp;gt;src/lib/frct.c&amp;lt;/code&amp;gt;.  The&lt;br /&gt;
remainder of this document describes the FRCP wire protocol and the&lt;br /&gt;
behaviour FRCT realises.  Code symbols retain the &amp;lt;code&amp;gt;FRCT_&amp;lt;/code&amp;gt; prefix&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, ...) because they belong to the implementing&lt;br /&gt;
task; this document references them verbatim.&lt;br /&gt;
&lt;br /&gt;
The keywords &amp;quot;MUST&amp;quot;, &amp;quot;MUST NOT&amp;quot;, &amp;quot;REQUIRED&amp;quot;, &amp;quot;SHALL&amp;quot;, &amp;quot;SHALL NOT&amp;quot;,&lt;br /&gt;
&amp;quot;SHOULD&amp;quot;, &amp;quot;SHOULD NOT&amp;quot;, &amp;quot;RECOMMENDED&amp;quot;, &amp;quot;MAY&amp;quot;, and &amp;quot;OPTIONAL&amp;quot; in&lt;br /&gt;
this document are to be interpreted as described in &amp;lt;code&amp;gt;BCP&amp;lt;/code&amp;gt; 14 (Best&lt;br /&gt;
Current Practice; RFC 2119, RFC 8174) when, and only when, they&lt;br /&gt;
appear in all capitals.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:Unsigned 32-bit / 8-bit integers (kernel-C style).&lt;br /&gt;
;&amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:Nanoseconds.&lt;br /&gt;
&lt;br /&gt;
Modular sequence-number comparators (32-bit, modulo 2^32):&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;before(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;(int32_t)(a - b) &amp;amp;lt; 0&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;after(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;before(b, a)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Used throughout for &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; ordering checks.&lt;br /&gt;
&lt;br /&gt;
Round-Trip Time (RTT) abbreviations used throughout:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;&lt;br /&gt;
:Smoothed RTT estimate (RFC 6298).&lt;br /&gt;
;&amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt;&lt;br /&gt;
:Mean deviation of RTT (Linux variance estimator).&lt;br /&gt;
;&amp;lt;code&amp;gt;EWMA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Exponentially Weighted Moving Average.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTO&amp;lt;/code&amp;gt;&lt;br /&gt;
:Retransmission Timeout, &amp;lt;code&amp;gt;max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Timer-bound symbols &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer, ACK delay) and &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer,&lt;br /&gt;
retransmission window) are defined in [[#8. Retransmission|Section 8]]; &amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; (Maximum&lt;br /&gt;
Packet Lifetime) is introduced in [[#2.1. Per-flow state|Section 2.1]] (the &amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; field)&lt;br /&gt;
with heritage in [[#15. Heritage and adopted techniques|Section 15]].&lt;br /&gt;
&lt;br /&gt;
Wire-format diagrams follow the IETF convention: bit 0 is the&lt;br /&gt;
leftmost (most significant) bit and fields are in network byte&lt;br /&gt;
order unless stated otherwise.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1. Wire format ==&lt;br /&gt;
&lt;br /&gt;
=== 1.1. PCI header ===&lt;br /&gt;
&lt;br /&gt;
Fixed 16-octet base Protocol-Control Information (PCI) header&lt;br /&gt;
prefixed to every FRCP packet (RFC convention: bit 0 leftmost,&lt;br /&gt;
most-significant bit first).  All multi-byte fields except &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;&lt;br /&gt;
are in network byte order; &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt; is an opaque 16-bit value that&lt;br /&gt;
the receiver recomputes from the wire bytes and compares to the&lt;br /&gt;
in-place &amp;lt;code&amp;gt;pci-&amp;gt;hcs&amp;lt;/code&amp;gt; read, so its on-wire byte order need only&lt;br /&gt;
match between peers running compatible builds.  DATA packets on&lt;br /&gt;
stream-mode flows carry an additional 8-octet extension (see&lt;br /&gt;
[[#1.5. Stream PCI extension|Section 1.5]]); SACK and RTTP carry their own payloads after the&lt;br /&gt;
base PCI.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |             flags             |              hcs              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            window                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            seqno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            ackno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                     payload (variable) ...&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt;&lt;br /&gt;
:feature/type bitmap (see [[#1.2. Flag bits|Section 1.2]]).&lt;br /&gt;
;&amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;&lt;br /&gt;
:CRC-16-CCITT-FALSE Header Check Sequence (HCS) over &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; (+ stream extension when present); the two octets of the &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt; field itself are omitted from the CRC input.  Verified on receive before any flag-driven dispatch.&lt;br /&gt;
;&amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt;&lt;br /&gt;
:receiver-advertised right window edge (valid iff FC).&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-flow sequence number.&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;&lt;br /&gt;
:cumulative Acknowledgement (ACK) (valid iff ACK).&lt;br /&gt;
&lt;br /&gt;
A single packet can simultaneously carry DATA + ACK + FC (Flow&lt;br /&gt;
Control) + RXM (Retransmission) by ORing flag bits; the PCI&lt;br /&gt;
multiplexes control on the same wire frame in the spirit of SCTP&lt;br /&gt;
chunk bundling (RFC 9260 sec. 6.10) and QUIC frame multiplexing&lt;br /&gt;
(RFC 9000 sec. 12.4).  DATA-bearing packets carry the caller&#039;s&lt;br /&gt;
payload after the PCI; SACK (Selective Acknowledgement) and RTTP&lt;br /&gt;
(Round-Trip Time Probe) carry their own typed payloads after the&lt;br /&gt;
PCI.&lt;br /&gt;
&lt;br /&gt;
Optional framing (per-flow, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]]).  On the wire, the&lt;br /&gt;
order from inside out is:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Layer !! Scope&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| The FRCP packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body + CRC-32 ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| CRC-32 covers the body only (PCI is in HCS); appended iff &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; on DATA, or on every SACK packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ AEAD-wrap of above ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| Iff Authenticated Encryption with Associated Data (AEAD) is enabled.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* HCS in the PCI covers the header fields on every packet and is verified before any flag-driven dispatch.&lt;br /&gt;
* The CRC-32 trailer (IEEE 802.3 / zlib reflected polynomial &amp;lt;code&amp;gt;0xEDB88320&amp;lt;/code&amp;gt;, init &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;, xor-out &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;) covers the body on DATA when &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; and on every SACK packet; the trailer is written as a raw &amp;lt;code&amp;gt;uint32_t&amp;lt;/code&amp;gt; (the same convention as &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;: opaque on the wire as long as both peers run compatible builds).  The PCI is not under the CRC (Cyclic Redundancy Check) because the HCS already protects it.  It is appended before AEAD encryption and therefore rides inside the AEAD wrap when both are active; the AEAD tag (~2^-128 forgery probability) dominates the CRC (~2^-32) for integrity in that mode but the CRC trailer is currently retained.&lt;br /&gt;
* When encryption is enabled, the entire (possibly-CRC&#039;d) FRCP packet is wrapped with AEAD inside the shared-memory packet buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt;); the packet grows by the AEAD overhead, namely a leading nonce / Initialization Vector (IV) of &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_ivsz&amp;lt;/code&amp;gt;) and a trailing authentication tag of &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_tagsz&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Both CRC and AEAD are layered around the FRCP wire format and are not visible to the FRCP machinery itself.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.2. Flag bits ===&lt;br /&gt;
&lt;br /&gt;
Flag bits are numbered most-significant-bit first to match the wire&lt;br /&gt;
diagram (bit numbering per [[#1.1. PCI header|Section 1.1]]; bit 0 is the MSB of the&lt;br /&gt;
16-bit &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; field and lands at wire-position 0 in network byte&lt;br /&gt;
order).  Bits 13..15 are reserved and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Bit !! Mask !! Name !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| 0 || &amp;lt;code&amp;gt;0x8000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt; || Carries caller payload&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &amp;lt;code&amp;gt;0x4000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DRF&amp;lt;/code&amp;gt; || Data Run Flag: start of a fresh run&lt;br /&gt;
|-&lt;br /&gt;
| 2 || &amp;lt;code&amp;gt;0x2000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt; || Acknowledgement: &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field valid&lt;br /&gt;
|-&lt;br /&gt;
| 3 || &amp;lt;code&amp;gt;0x1000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt; || Negative ACK; &amp;lt;code&amp;gt;seqno = arrival_seqno-1&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || &amp;lt;code&amp;gt;0x0800&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt; || Flow Control: &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; field valid (&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || &amp;lt;code&amp;gt;0x0400&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt; || Rendezvous probe (window-closed)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || &amp;lt;code&amp;gt;0x0200&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; || First Fragment (role bit 0; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || &amp;lt;code&amp;gt;0x0100&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; || Last Fragment (role bit 1; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 8 || &amp;lt;code&amp;gt;0x0080&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RXM&amp;lt;/code&amp;gt; || Retransmission&lt;br /&gt;
|-&lt;br /&gt;
| 9 || &amp;lt;code&amp;gt;0x0040&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt; || Selective ACK block list in payload&lt;br /&gt;
|-&lt;br /&gt;
| 10 || &amp;lt;code&amp;gt;0x0020&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt; || RTT Probe / echo (payload follows)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || &amp;lt;code&amp;gt;0x0010&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt; || Keepalive&lt;br /&gt;
|-&lt;br /&gt;
| 12 || &amp;lt;code&amp;gt;0x0008&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt; || End-of-stream marker (stream mode)&lt;br /&gt;
|-&lt;br /&gt;
| 13-15 || -- || -- || Reserved (MUST be zero)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair encodes the fragment role of a DATA-bearing&lt;br /&gt;
Service Data Unit (SDU), SCTP-style begin/end flags (RFC 9260&lt;br /&gt;
sec. 3.3.1):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FFGM !! LFGM !! Role&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || Sole / un-fragmented SDU (begin AND end)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || First fragment of a multi-fragment SDU&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Middle fragment&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Last fragment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Each fragment is carried in its own FRCP packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;;&lt;br /&gt;
FRTX (the FRCT Retransmission service mode, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]])&lt;br /&gt;
recovers individual fragments via the normal Retransmission Timeout&lt;br /&gt;
(RTO) / SACK / Recent Acknowledgement (RACK, RFC 8985) path.  The&lt;br /&gt;
receiver reassembles the SDU at consume time once the contiguous&lt;br /&gt;
[FIRST .. LAST] run has fully arrived.  On non-DATA packets the role&lt;br /&gt;
bits are unused and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
In stream mode (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;, see [[#16. Stream-mode flows|Section 16]]) there are&lt;br /&gt;
no SDU boundaries to encode, so &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST&lt;br /&gt;
be transmitted as zero.  End-of-stream uses a dedicated bit (&amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;,&lt;br /&gt;
bit 12) carried on a 0-byte DATA packet, emitted at write-half close&lt;br /&gt;
(&amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;FLOWFRDONLY&amp;lt;/code&amp;gt;), during linger drain, and at &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;;&lt;br /&gt;
emission is idempotent (first call wins).  After contiguous delivery&lt;br /&gt;
of the FIN-bearing slot, the receiver latches &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt; at the FIN&#039;s&lt;br /&gt;
start offset; &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns 0 (end-of-file, &amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) once buffered&lt;br /&gt;
bytes have been drained up to &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt;.  Per-byte position is&lt;br /&gt;
carried by the [start, end) extension ([[#1.5. Stream PCI extension|Section 1.5]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.3. SACK payload ===&lt;br /&gt;
&lt;br /&gt;
A SACK packet has the &amp;lt;code&amp;gt;FRCT_ACK | FRCT_FC | FRCT_SACK&amp;lt;/code&amp;gt; flag bits set&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI, the&lt;br /&gt;
payload is a 2-octet block count (network byte order), 2 octets of&lt;br /&gt;
padding to 4-byte align the block list, then &amp;lt;code&amp;gt;n_blocks&amp;lt;/code&amp;gt; pairs of&lt;br /&gt;
32-bit start/end seqnos describing &#039;&#039;present&#039;&#039; (received) ranges&lt;br /&gt;
above the cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |           n_blocks            |        padding (2 octets)     |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[0]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            end[0]                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[1]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
                       ... n_blocks pairs total ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;n_blocks &amp;amp;lt;= SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; (2048).  The per-flow effective cap is&lt;br /&gt;
further bounded by &amp;lt;code&amp;gt;(frag_mtu - PCI - 4) / 8&amp;lt;/code&amp;gt; blocks per packet; SACK&lt;br /&gt;
packets carry no stream extension, so PCI here is the 16-octet base&lt;br /&gt;
header even on stream-mode flows.&lt;br /&gt;
&lt;br /&gt;
Wire invariant: every block produced by the receiver, except an&lt;br /&gt;
optional leading Duplicate SACK (D-SACK) block as described below,&lt;br /&gt;
describes a range strictly above the cumulative ACK carried in the&lt;br /&gt;
PCI &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field (&amp;lt;code&amp;gt;after(start[i], ackno)&amp;lt;/code&amp;gt;).  This makes the D-SACK&lt;br /&gt;
convention below unambiguous; the receiver-side builder MUST&lt;br /&gt;
preserve it.&lt;br /&gt;
&lt;br /&gt;
Duplicate SACK (D-SACK, RFC 2883) is signalled in-band: no flag&lt;br /&gt;
bit, no extra framing.  Modular seqno arithmetic uses the&lt;br /&gt;
&amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators defined in the Notation block.&lt;br /&gt;
&amp;lt;code&amp;gt;Block[0]&amp;lt;/code&amp;gt; carries a D-SACK report when either:&lt;br /&gt;
&lt;br /&gt;
;case 1 (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;before(blocks[0].start, ackno)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ackno - blocks[0].start&amp;lt;/code&amp;gt; is within &amp;lt;code&amp;gt;MAX_DSACK_LAG&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;== RQ_SIZE&amp;lt;/code&amp;gt;).  A single duplicate &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; observed below the cumulative ACK.&lt;br /&gt;
;case 2 (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;blocks[0]&amp;lt;/code&amp;gt; is a sub-range of some &amp;lt;code&amp;gt;blocks[i&amp;gt;0]&amp;lt;/code&amp;gt; (not exactly equal).  Reports a duplicate of an in-window &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; that the same packet&#039;s remaining SACK blocks already describe as received.&lt;br /&gt;
&lt;br /&gt;
Senders that do not implement D-SACK process &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; through the&lt;br /&gt;
normal SACK-mark loop and the existing clamp-and-skip path makes&lt;br /&gt;
case-1 a no-op (&amp;lt;code&amp;gt;start &amp;amp;lt; snd_cr.lwe&amp;lt;/code&amp;gt; clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt;, the inner&lt;br /&gt;
loop then skips &amp;lt;code&amp;gt;k == snd_cr.lwe&amp;lt;/code&amp;gt;) and case-2 idempotent (same slots&lt;br /&gt;
NULL&#039;d twice).  D-SACK-aware senders feed the report into the RACK&lt;br /&gt;
&amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; scaler (RFC 8985 sec. 6.2 step 4): bump on receipt&lt;br /&gt;
(cap 20), halve once per 16 cumulatively-ACK&#039;d seqnos since the&lt;br /&gt;
most recent D-SACK arrival or halve event, reset to 1 on an RTO&lt;br /&gt;
timer fire at the head-of-line.  D-SACK alone never enters&lt;br /&gt;
NewReno-careful recovery (see [[#8. Retransmission|Section 8]]); only non-D-SACK blocks&lt;br /&gt;
count as gaps.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.4. RTTP payload ===&lt;br /&gt;
&lt;br /&gt;
An RTTP (Round-Trip Time Probe) packet has only the &amp;lt;code&amp;gt;FRCT_RTTP&amp;lt;/code&amp;gt; flag&lt;br /&gt;
set (bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI,&lt;br /&gt;
the payload is 24 octets (packed):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          probe_id                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          echo_id                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +                  nonce (16 octets, echoed verbatim)           +&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:sender counter, 0 on reply, 0 reserved.&lt;br /&gt;
;&amp;lt;code&amp;gt;echo_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:peer&#039;s &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;, 0 on outbound probe.&lt;br /&gt;
;&amp;lt;code&amp;gt;nonce&amp;lt;/code&amp;gt;&lt;br /&gt;
:random, echoed unmodified, memcmp&#039;d to defeat spoof.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.5. Stream PCI extension ===&lt;br /&gt;
&lt;br /&gt;
A stream-mode flow (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;) carries an extra&lt;br /&gt;
8-octet extension after the 16-octet base PCI on every DATA packet&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            start                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                             end                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset of the first payload byte in the stream.&lt;br /&gt;
;&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset one past the last payload byte; &amp;lt;code&amp;gt;end - start&amp;lt;/code&amp;gt; equals the on-wire payload length.&lt;br /&gt;
&lt;br /&gt;
Total stream-mode PCI for DATA packets is 24 octets (16 base + 8&lt;br /&gt;
extension); control packets (SACK, RTTP, bare ACK, KA, etc.) retain&lt;br /&gt;
the 16-octet base PCI.  Stream mode MUST be negotiated at flow&lt;br /&gt;
allocation; the extension is present iff stream mode is in use,&lt;br /&gt;
never on a per-packet basis.  Both peers MUST treat &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; as&lt;br /&gt;
monotonic 32-bit byte offsets; when a slot reaches the head of the&lt;br /&gt;
contiguous run with &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; not equal to the prior packet&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; the&lt;br /&gt;
slot is silently dropped at delivery time ([[#16. Stream-mode flows|Section 16]]) rather&lt;br /&gt;
than rejected at stash.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC STREAM-frame reassembly model (RFC 9000 sec. 19.8):&lt;br /&gt;
each packet carries its packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (this PCI&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; field) and a&lt;br /&gt;
separate stream byte position (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;).  Separating the two&lt;br /&gt;
avoids TCP&#039;s conflation of packet identity with byte position which&lt;br /&gt;
forces Karn&#039;s algorithm for Round-Trip Time (RTT) sampling (no RTT&lt;br /&gt;
sample on retransmits, RFC 6298 sec. 3); FRCP applies the&lt;br /&gt;
Karn-equivalent gate via a combination of per-packet &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;,&lt;br /&gt;
per-slot &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; flags, and a sample-fence &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; (see [[#2.1. Per-flow state|Section 2.1]]&lt;br /&gt;
and [[#12. RTT estimation|Section 12]]).  FRCP&#039;s fixed-32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; wrap at 4 GiB of&lt;br /&gt;
wire bytes, narrower than QUIC&#039;s 62-bit varint offset (cf. RFC 9000&lt;br /&gt;
sec. 16); the on-wire wrap is handled by the same modular &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;&lt;br /&gt;
/ &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators ([[#1.3. SACK payload|Section 1.3]]) FRCP uses for seqnos, which&lt;br /&gt;
remain unambiguous as long as the in-flight byte window stays&lt;br /&gt;
strictly under 2 GiB (the half-range of the signed-int32 difference&lt;br /&gt;
in &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;).  The default per-flow ring is 1 MiB; the&lt;br /&gt;
implementation caps &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; at 128 MiB (&amp;lt;code&amp;gt;FRCT_STREAM_RING_SZ_MAX&amp;lt;/code&amp;gt;),&lt;br /&gt;
well below the 2 GiB half-range bound.  The runtime byte counters&lt;br /&gt;
exposed via FUSE (Filesystem in Userspace) in the Ouroboros&lt;br /&gt;
Resource Information Base (RIB, a virtual-filesystem introspection&lt;br /&gt;
bridge) are platform &amp;lt;code&amp;gt;size_t&amp;lt;/code&amp;gt; and do not wrap on 64-bit hosts.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2. Per-flow state and service modes ==&lt;br /&gt;
&lt;br /&gt;
=== 2.1. Per-flow state ===&lt;br /&gt;
&lt;br /&gt;
Each flow keeps a sender control record and a receiver control&lt;br /&gt;
record:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: oldest unacked &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (cumulative ACK boundary as seen by sender); rcv: next in-order &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; expected&lt;br /&gt;
;&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: peer-advertised right window edge; rcv: locally-advertised right window edge&lt;br /&gt;
;&amp;lt;code&amp;gt;cflags&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-direction feature flags: retransmission (&amp;lt;code&amp;gt;FRCTFRTX&amp;lt;/code&amp;gt;), receiver flow control (&amp;lt;code&amp;gt;FRCTFRESCNTL&amp;lt;/code&amp;gt;), linger-on-close (&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt;); see &amp;lt;code&amp;gt;&amp;amp;lt;ouroboros/fccntl.h&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: next &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; to send; rcv: force-ACK trigger - set on a stale or dup DATA so the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; emits a fresh cumulative ACK&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; counter for standalone ACK-bearing control packets (delayed ACK, SACK, final ACK on dealloc); not bumped on piggybacked ACK riding a DATA packet (which uses the DATA &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;).  Used by wire-dup ACK detection; rcv: incoming-ACK dedup tracker&lt;br /&gt;
;&amp;lt;code&amp;gt;act&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:last activity (used by inactivity / DRF)&lt;br /&gt;
;&amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:inactivity threshold; sender = &amp;lt;code&amp;gt;3*mpl + a + r + 1s&amp;lt;/code&amp;gt;, receiver = &amp;lt;code&amp;gt;2*mpl + a + r + 1s&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;mpl&amp;lt;/code&amp;gt; is the Maximum Packet Lifetime (delta-t terminology; see [[#15. Heritage and adopted techniques|Section 15]]); &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; are the FRCT a-timer and r-timer bounds (see [[#8. Retransmission|Section 8]]).  The asymmetry is load-bearing for pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
The sender holds a per-slot ring &amp;lt;code&amp;gt;snd_slots[RQ_SIZE]&amp;lt;/code&amp;gt; keyed by&lt;br /&gt;
&amp;lt;code&amp;gt;(seqno mod RQ_SIZE)&amp;lt;/code&amp;gt;.  Each slot tracks its retransmit entry (&amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;),&lt;br /&gt;
last-send timestamp, and retransmit flag bits: &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (a&lt;br /&gt;
retransmit is pending or has fired, gates the next RTT sample&lt;br /&gt;
under Karn) and &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (one-shot fast-retransmit staged for&lt;br /&gt;
this loss event).&lt;br /&gt;
&lt;br /&gt;
The receiver holds a parallel reorder ring &amp;lt;code&amp;gt;rcv_slots[RQ_SIZE]&amp;lt;/code&amp;gt;&lt;br /&gt;
(referred to as &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; in prose) holding stashed out-of-order&lt;br /&gt;
packet-buffer indexes; both FRTX and best-effort flows share this&lt;br /&gt;
path.  The invariant &amp;lt;code&amp;gt;rwe - lwe &amp;amp;lt;= RQ_SIZE&amp;lt;/code&amp;gt; holds: on each consume&lt;br /&gt;
the receiver advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; by the consumed count, capping the&lt;br /&gt;
receive window at &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; slots.&lt;br /&gt;
&lt;br /&gt;
A separate fence variable &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; is bumped on every retransmit&lt;br /&gt;
(timer-fire, SACK-driven, fast-rxm, NACK-driven) and on every&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; ([[#4. Sequence-number rotation (DRF)|Section 4]]) to mark the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; range whose RTT samples&lt;br /&gt;
MUST be discarded.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 2.2. Service modes (orthogonal axes) ===&lt;br /&gt;
&lt;br /&gt;
FRCP exposes its wire features as a vector of independent QoS&lt;br /&gt;
axes selected at flow allocation time.  All flows go through the&lt;br /&gt;
same &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive; the &amp;lt;code&amp;gt;qosspec_t&amp;lt;/code&amp;gt; passed&lt;br /&gt;
in determines which protocol machinery engages on the wire.  This&lt;br /&gt;
contrasts with the POSIX BSD socket model where TCP and UDP&lt;br /&gt;
require different socket types (&amp;lt;code&amp;gt;SOCK_STREAM&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;SOCK_DGRAM&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
The axes:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = unordered (no FRCP engagement: raw datagrams, no PCI on the wire, UDP-equivalent at this layer); 1 = message-ordered (FRCP engaged; SDU boundaries preserved across fragmentation); 2 = stream (byte-oriented, no SDU boundaries; FRTX required)&lt;br /&gt;
;&amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = lossless service requested: FRTX retransmit machinery engages ([[#8. Retransmission|Section 8]]); MUST be 0 for &amp;lt;code&amp;gt;service=2&amp;lt;/code&amp;gt;.  Non-zero = best-effort, FRTX off.&lt;br /&gt;
;&amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bit Error Rate tolerance.  0 = error-free service requested: a CRC trailer is appended after the body of DATA packets and verified on receive (added / checked outside the FRCP PCI; see [[#1.1. PCI header|Section 1.1]]).  Non-zero = peer accepts errors; trailer omitted.  SACK control packets carry a CRC32 trailer regardless of &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; gate applies to DATA only.&lt;br /&gt;
;&amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt;&lt;br /&gt;
:Peer-timeout (ms); 0 disables the keepalive timer.  Independent of FRCP engagement.&lt;br /&gt;
&lt;br /&gt;
Encryption is a separate per-flow attribute set at flow setup;&lt;br /&gt;
when enabled it wraps the FRCP packet (PCI + body, plus the CRC&lt;br /&gt;
trailer if any) under AEAD, expanding the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; by &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt;&lt;br /&gt;
octets (nonce / tag).  The CRC trailer is currently kept inside&lt;br /&gt;
the AEAD wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
&lt;br /&gt;
Reachable combinations exported by &amp;lt;code&amp;gt;include/ouroboros/qos.h&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Cube !! &amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; !! Engaged&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw&amp;lt;/code&amp;gt; || 0 || 1 || 1 || Raw passthrough&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw_safe&amp;lt;/code&amp;gt; || 0 || 1 || 0 || Raw + CRC trailer&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; || 1 || 1 || 1 || FRCP, no FRTX, no CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt_safe&amp;lt;/code&amp;gt; || 1 || 1 || 0 || FRCP, no FRTX, CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_msg&amp;lt;/code&amp;gt; || 1 || 0 || 0 || FRCP + FRTX&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_stream&amp;lt;/code&amp;gt; || 2 || 0 || 0 || FRCP + FRTX, stream&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Forced couplings actually enforced by the public API:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;service == SVC_STREAM&amp;lt;/code&amp;gt; (2) requires &amp;lt;code&amp;gt;loss == 0&amp;lt;/code&amp;gt;; &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;flow_accept&amp;lt;/code&amp;gt; reject the pair otherwise with &amp;lt;code&amp;gt;-EINVAL&amp;lt;/code&amp;gt;.&lt;br /&gt;
* FRTX requires FRCP engagement (&amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;); requesting &amp;lt;code&amp;gt;loss = 0&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;service = SVC_RAW&amp;lt;/code&amp;gt; is structurally a no-op because no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; is created.&lt;br /&gt;
* The &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; build flag globally forces &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;.  Note: this flag defaults to ON, so default builds ship with CRC disabled until &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; is set to OFF.&lt;br /&gt;
&lt;br /&gt;
Caveat: the API does NOT force &amp;lt;code&amp;gt;ber = 0&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; has &amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;, which means the PCI&lt;br /&gt;
itself is not CRC-protected on that cube; the HCS ([[#1.1. PCI header|Section 1.1]])&lt;br /&gt;
remains the only integrity check on the header.&lt;br /&gt;
&lt;br /&gt;
The FRCP-no-FRTX regime (&amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;loss &amp;amp;gt; 0&amp;lt;/code&amp;gt;) is meaningful&lt;br /&gt;
and live: sequence numbering, in-order delivery, flow-control&lt;br /&gt;
advertisement, KA, DRF rotation, and SDU fragmentation /&lt;br /&gt;
reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]) all run.  Lost packets are dropped&lt;br /&gt;
rather than retransmitted; a permanently-lost mid-fragment is&lt;br /&gt;
dropped via skip-past-gap once a later SDU is visible in the&lt;br /&gt;
reorder ring.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. Protocol parameters ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Slot ring / rcv window width&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;START_WINDOW&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Initial &amp;lt;code&amp;gt;rwe-lwe&amp;lt;/code&amp;gt; after rotate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MAX(250 us build-tunable, 1&amp;amp;lt;&amp;amp;lt;RXMQ_RES)&amp;lt;/code&amp;gt;; per-flow via &amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;FRCTSRTOMIN&amp;lt;/code&amp;gt;).  Default ~1 ms with &amp;lt;code&amp;gt;RXMQ_RES=20&amp;lt;/code&amp;gt;. || RTO floor; also floored at the retransmit-wheel resolution (~1 ms by default).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt; || 20 || Backoff shift cap&lt;br /&gt;
|-&lt;br /&gt;
| RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MIN(reo_wnd_mult * min_RTT/4, SRTT)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; scales on D-SACK, cap 20 || Reorder window; per RFC 8985 sec. 6.2; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per sec. 6.2 step 4&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; || 300 s (5 min, Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; windowed re-anchor&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_WND_MULT_MAX&amp;lt;/code&amp;gt; || 20 (RFC 8985 sec. 6.2 step 4) || &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_DECAY_PKTS&amp;lt;/code&amp;gt; || 16 (RFC 8985 sec. 6.2 step 4 / &amp;lt;code&amp;gt;RACK.reo_wnd_persist&amp;lt;/code&amp;gt;) || Fresh-ACK&#039;d seq count per halving&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_DSACK_LAG&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || D-SACK sanity cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTT_QUARANTINE&amp;lt;/code&amp;gt; || 32 (&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; steps) || NewReno gate pad&lt;br /&gt;
|-&lt;br /&gt;
| SACK rate-limit || &amp;lt;code&amp;gt;SACK_MIN_GAP_NS&amp;lt;/code&amp;gt; (250 us, fixed) || Min SACK gap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; || 2048 (wire cap; per-flow capped at &amp;lt;code&amp;gt;(frag_mtu-PCI-4)/8&amp;lt;/code&amp;gt;) || Per-SACK block cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; || 32 || Per-pass staged retransmit cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; || 3 (RFC 8985 default) || Hybrid fast-rxm trigger ([[#8. Retransmission|Section 8]])&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MDEV_MUL&amp;lt;/code&amp;gt; || 2 (build-tunable via &amp;lt;code&amp;gt;FRCT_RTO_MDEV_MULTIPLIER&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; shift in &amp;lt;code&amp;gt;RTO = srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| RTTP nonce || 16 octets || Echoed verbatim&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; || 8 || In-flight probes&lt;br /&gt;
|-&lt;br /&gt;
| RTT clamp || &amp;lt;code&amp;gt;16 * srtt&amp;lt;/code&amp;gt; || Probe-sample upper bound (ACK-derived RTT samples gated by Karn / recovery only)&lt;br /&gt;
|-&lt;br /&gt;
| Cold-probe cadence || 100 ms (rx-driven; see [[#12. RTT estimation|Section 12]]) || Pre-&amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; RTTP rate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt; || 100 ms || RDVS emit cadence&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; || 1 s || RDVS give-up&lt;br /&gt;
|-&lt;br /&gt;
| Delayed-ACK fire || &amp;lt;code&amp;gt;2 * TICTIME&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt; = FRCT tick granularity, default 5 ms; &amp;lt;code&amp;gt;2*TICTIME = 10 ms&amp;lt;/code&amp;gt; by default) || Fired after the first in-order DATA arrival; tick is build-tunable&lt;br /&gt;
|-&lt;br /&gt;
| NACK send cooldown || &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; when an &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; sample exists, else 100 ms || Pre-DRF NACK rate-limit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_SDU&amp;lt;/code&amp;gt; || 1 MiB || Max reassembled SDU; configurable per flow&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The per-flow fragment Maximum Transmission Unit (MTU) is computed&lt;br /&gt;
at flow setup from the lower IPCP&#039;s mtu minus encryption&lt;br /&gt;
&amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; and CRC trailer; there is no FRCT-level default or&lt;br /&gt;
environment-variable override.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 4. Sequence-number rotation (DRF) ==&lt;br /&gt;
&lt;br /&gt;
The DRF (Data Run Flag) bit on an outbound packet means &amp;quot;this is&lt;br /&gt;
the start of a fresh data run&amp;quot; and is set whenever the sender has&lt;br /&gt;
nothing in flight (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Independently of that, if the sender has been idle longer than&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.inact&amp;lt;/code&amp;gt; AND the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;),&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; rolls a random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; before the send and&lt;br /&gt;
resets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    snd_cr.seqno  = random()&lt;br /&gt;
    snd_cr.lwe    = snd_cr.seqno&lt;br /&gt;
    snd_cr.rwe    = snd_cr.seqno + START_WINDOW&lt;br /&gt;
    rtt_lwe       = snd_cr.seqno&lt;br /&gt;
    in_recovery   = false   (recovery state, see Section 8)&lt;br /&gt;
    recovery_high = snd_cr.seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The receiver, on observing rcv-side inactivity&lt;br /&gt;
(&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), requires a DRF on the next&lt;br /&gt;
DATA packet; otherwise it replies with a rate-limited NACK (see&lt;br /&gt;
below).  Non-DATA control packets pass through without the DRF&lt;br /&gt;
requirement.  On DRF the receiver releases the &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots and&lt;br /&gt;
rebases&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    rcv_cr.lwe   = seqno&lt;br /&gt;
    rcv_cr.rwe   = seqno + RQ_SIZE&lt;br /&gt;
    rcv_cr.seqno = seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the inactive packet has DATA but no DRF, a rate-limited NACK is&lt;br /&gt;
fired back to the sender (cooldown per [[#3. Protocol parameters|Section 3]]); non-DATA stale&lt;br /&gt;
arrivals fall through to normal processing (no NACK, no drop).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 5. Send path ==&lt;br /&gt;
&lt;br /&gt;
# If the SDU exceeds &amp;lt;code&amp;gt;(frag_mtu - data_hdr_len)&amp;lt;/code&amp;gt;, the caller (&amp;lt;code&amp;gt;dev.c&amp;lt;/code&amp;gt;) fans it out into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - data_hdr_len))&amp;lt;/code&amp;gt; fragments, each emitted via &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; as its own DATA packet with a per-fragment role ([[#7.2. Fragmentation and reassembly|Section 7.2]]); both FRTX and best-effort flows fragment.  Raw flows (no FRCP engagement, &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) carry no PCI and return &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; for any SDU larger than one packet at the layer below.  An SDU that fits in a single packet is sent as SOLE.  &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; reserves PCI head room; sets DATA, plus DRF when the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
# &amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; if past sender inactivity and the pipe is empty ([[#4. Sequence-number rotation (DRF)|Section 4]]).&lt;br /&gt;
# Advertise FC (&amp;lt;code&amp;gt;pci.window = frcti_advert_rwe(frcti)&amp;lt;/code&amp;gt;, i.e. &amp;lt;code&amp;gt;rcv_cr.rwe&amp;lt;/code&amp;gt; clamped to &amp;lt;code&amp;gt;rcv_cr.lwe + ring_seq_cap&amp;lt;/code&amp;gt; in stream mode) when the receiver side is recent: &amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt; rcv_cr.inact&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Reliable mode (FRTX): leave &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; where it is; reset the slot at &amp;lt;code&amp;gt;RQ_SLOT(seqno)&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;snd_slots[p].time = now&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_slots[p].flags = 0&amp;lt;/code&amp;gt;); queue an &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; (saves a packet copy, arms a wheel timer at &amp;lt;code&amp;gt;now + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;).  Piggyback ACK (&amp;lt;code&amp;gt;pci.ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) while the a-timer for the most recent received DATA packet has not yet expired (&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt;= t_a&amp;lt;/code&amp;gt;); on piggyback, set &amp;lt;code&amp;gt;rcv_cr.seqno = rcv_cr.lwe&amp;lt;/code&amp;gt; so the next delayed-ACK fire is suppressed.  See [[#8. Retransmission|Section 8]] for &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; semantics.&lt;br /&gt;
# Best-effort mode (no FRTX): advance &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; immediately (&amp;lt;code&amp;gt;snd_cr.lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_cr.rwe = snd_cr.lwe + RQ_SIZE&amp;lt;/code&amp;gt;); no retransmit state.  No send-side RTT probe is armed in this mode (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt; requires an in-flight &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, which best-effort never has); the rx-driven cold seeder in &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; is the only probe path.&lt;br /&gt;
# In reliable mode, optionally arm an RTT probe ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 6. Receive path ==&lt;br /&gt;
&lt;br /&gt;
=== 6.1. Early-exit dispatch ===&lt;br /&gt;
&lt;br /&gt;
Keepalive (KA), RTT probe (RTTP), pre-DRF NACK, and rendezvous&lt;br /&gt;
(RDVS) packets short-circuit out of &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; before the locked&lt;br /&gt;
main path; each handler takes its own lock internally.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
      incoming packet&lt;br /&gt;
            |&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | KA?     |---yes--&amp;gt; ka_rcv  ; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RTTP?   |---yes--&amp;gt; rttp_rcv; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | NACK?   |---yes--&amp;gt; nack_rcv; return  (see Section 9)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RDVS?   |---yes--&amp;gt; rdv_rcv ; return  (reply bare FC, ackno=0)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       acquire wrlock; enter locked main path&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt;&lt;br /&gt;
:refresh &amp;lt;code&amp;gt;t_ka_rcv&amp;lt;/code&amp;gt;, honour piggybacked ACK.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt;&lt;br /&gt;
:probe (echo back nonce) or echo (verify nonce, sample RTT).&lt;br /&gt;
;&amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:pre-DRF, sender-side handler.  See [[#9. Pre-DRF NACK|Section 9]].&lt;br /&gt;
;&amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt;&lt;br /&gt;
:reply with a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;); &amp;lt;code&amp;gt;rdlock&amp;lt;/code&amp;gt; only.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 6.2. Locked main path ===&lt;br /&gt;
&lt;br /&gt;
Steps below run with the per-flow &amp;lt;code&amp;gt;frcti.lock&amp;lt;/code&amp;gt; held for writing&lt;br /&gt;
(&amp;lt;code&amp;gt;pthread_rwlock_wrlock&amp;lt;/code&amp;gt;) unless noted.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;&lt;br /&gt;
:Only meaningful when the receive side is stale.  On DRF (Data Run Flag): release &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots, rebase &amp;lt;code&amp;gt;rcv_cr&amp;lt;/code&amp;gt;, continue.  On stale DATA without DRF: fire a pre-DRF NACK if cooldown allows ([[#9. Pre-DRF NACK|Section 9]]), then discard the packet; on cooldown, drop without sending a NACK (a pending cumulative ACK from &amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; may still go out).  Non-DATA, non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; pure-DRF stale arrivals fall through after the DRF rebase branch.&lt;br /&gt;
&lt;br /&gt;
;DATA-only act refresh&lt;br /&gt;
:Refresh &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; only when &amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt; is set, so that non-DATA packets never block the next DRF rebase.&lt;br /&gt;
&lt;br /&gt;
;Wire-dup gate&lt;br /&gt;
:Before flag-driven dispatch, drop wire-duplicate ACKs and wire-duplicate DATA (&amp;lt;code&amp;gt;is_dup_ack&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt;).  The DATA check is bypassed for &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;-bearing arrivals so the piggybacked ACK / SACK / FC carried on a retransmitted DATA at an already-ACK&#039;d &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is still applied; the stale-in-window branch below then drops the packet.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Drop ACKs whose &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; falls outside &amp;lt;code&amp;gt;(snd_cr.lwe, snd_cr.seqno]&amp;lt;/code&amp;gt;.  If &amp;lt;code&amp;gt;ackno == snd_cr.lwe&amp;lt;/code&amp;gt; (non-advancing cumulative ACK), drive RACK fast-retransmit consideration ([[#8. Retransmission|Section 8]]).  Otherwise advance &amp;lt;code&amp;gt;snd_cr.lwe = ackno&amp;lt;/code&amp;gt;, collapse &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; to 0 (Karn-gated by &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; on the just-acknowledged slot, the old head-of-line), reset &amp;lt;code&amp;gt;dup_thresh&amp;lt;/code&amp;gt; to 0, update &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the send-time of the slot at &amp;lt;code&amp;gt;ackno-1&amp;lt;/code&amp;gt; (consumed by RACK and SACK below), decay &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per RFC 8985 sec. 6.2 step 4, exit NewReno-careful recovery (see [[#8. Retransmission|Section 8]]) on &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt;, and feed an RTT sample if eligible ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Walk the block list.  For each block (a present range above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;) NULL out &amp;lt;code&amp;gt;snd_slots[k].rxm&amp;lt;/code&amp;gt;, clear the slot&#039;s per-send flags, and advance &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the latest send-time covered (the Forward Acknowledgement / fack equivalent, Mathis &amp;amp;amp; Mahdavi 1996); the first block whose start clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; skips this fack update so that a head-of-line clamp does not falsely advance fack.  For un-SACKed gaps below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt;, stage a retransmit per slot that is (1) still owned (&amp;lt;code&amp;gt;rxm != NULL&amp;lt;/code&amp;gt;), (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (the RFC 8985 sec. 6.2 hybrid trigger).  Mark the slot &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; and NULL the &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; at stage time.  Capped at &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; staged retransmits per receive pass; what&#039;s left rides the next SACK.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bump &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; (clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt;, never shrinks) and mark window open.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bounds-check &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; against window.  On stale-dup (&amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;), set &amp;lt;code&amp;gt;rcv_cr.seqno = seqno&amp;lt;/code&amp;gt; to force a fresh ACK on the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt;, then drop.  On accept: both FRTX and best-effort stash the packet-buffer index into &amp;lt;code&amp;gt;rq[seqno mod RQ_SIZE]&amp;lt;/code&amp;gt;.  Fragments stash unchanged - the role bits are inspected only at consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]).  On out-of-order arrival, build a SACK reply if not rate-limited (per [[#3. Protocol parameters|Section 3]]) and not deduplicated against the previous &amp;lt;code&amp;gt;(rcv_cr.lwe, n_blocks)&amp;lt;/code&amp;gt; pair; D-SACK reports always bypass the dedup.  If both rate-limit and dedup suppress the reply, neither SACK nor delayed-ACK fires (the sender picks up the gap on its next ACK).  On in-order arrival, arm the delayed-ACK timer.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; exit&lt;br /&gt;
:Releases the per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;), then calls &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; synchronously after the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; release to surface any pending cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 7. Read path and reassembly ==&lt;br /&gt;
&lt;br /&gt;
=== 7.1. Read path ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns a full reassembled SDU (Service Data Unit) via&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; on every FRCP SDU-mode flow (FRTX or best-effort);&lt;br /&gt;
stream-mode is covered in [[#16. Stream-mode flows|Section 16]].  An incomplete head-of-line&lt;br /&gt;
(HoL) run yields &amp;lt;code&amp;gt;-EAGAIN&amp;lt;/code&amp;gt;; an oversized run yields &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; (the&lt;br /&gt;
run is dropped so the flow does not stall).  On best-effort flows,&lt;br /&gt;
a permanently-lost mid-fragment is dropped as soon as a later&lt;br /&gt;
complete SDU becomes visible in the ring ([[#7.2. Fragmentation and reassembly|Section 7.2]] skip-past-&lt;br /&gt;
gap).&lt;br /&gt;
&lt;br /&gt;
Raw flows carry no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt;, so &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns the next pending&lt;br /&gt;
packet-buffer index directly, with no role-bit inspection.  (Raw&lt;br /&gt;
service is selected via &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt; at flow allocation,&lt;br /&gt;
which suppresses &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; creation.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_pdu_ready&amp;lt;/code&amp;gt; is the no-advance peek used by &amp;lt;code&amp;gt;fevent&amp;lt;/code&amp;gt; (the&lt;br /&gt;
Ouroboros flow-event multiplexer, the &amp;lt;code&amp;gt;poll(2)&amp;lt;/code&amp;gt;-equivalent on&lt;br /&gt;
flows).  It returns ready only when the head-of-line run is&lt;br /&gt;
complete and the lead packet (a Protocol Data Unit, here one FRCP&lt;br /&gt;
packet) is present at &amp;lt;code&amp;gt;rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt;; any other state&lt;br /&gt;
(including the best-effort skip-past-gap case) returns not ready,&lt;br /&gt;
and &amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; is left to drop the broken prefix and re-&lt;br /&gt;
inspect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 7.2. Fragmentation and reassembly ===&lt;br /&gt;
&lt;br /&gt;
Send side (&amp;lt;code&amp;gt;flow_write_frag&amp;lt;/code&amp;gt;).  An SDU larger than&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - PCI)&amp;lt;/code&amp;gt; is split into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - PCI))&amp;lt;/code&amp;gt;&lt;br /&gt;
fragments; each fragment is its own FRCP packet with its own&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a per-fragment role flag pair ([[#1.2. Flag bits|Section 1.2]]).  Roles are&lt;br /&gt;
assigned at emit time:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! i !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=0&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=n-1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| else || &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A mid-loop allocation or transmit failure may yield a partial&lt;br /&gt;
write: the call returns the bytes already enqueued (&amp;lt;code&amp;gt;off &amp;amp;gt; 0&amp;lt;/code&amp;gt;) or&lt;br /&gt;
the underlying error (&amp;lt;code&amp;gt;off == 0&amp;lt;/code&amp;gt;).  Best-effort flows fragment&lt;br /&gt;
identically; on the receiver, a partial run with a permanently-&lt;br /&gt;
lost fragment is dropped when a later complete SDU is visible in&lt;br /&gt;
the ring (see skip-past-gap below).  Raw flows carry no PCI and&lt;br /&gt;
refuse anything larger than the layer&#039;s user MTU (&amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Wire-level recovery is fragment-agnostic on FRTX flows: each&lt;br /&gt;
fragment&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; flows through SACK / RACK / RTO / NACK exactly&lt;br /&gt;
as for a SOLE DATA packet, and reassembly does not re-enter the&lt;br /&gt;
loss-detection path.  Best-effort flows run the same &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
machinery (DRF, FC, ACK piggyback, pre-DRF NACK emit) but queue&lt;br /&gt;
no &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; state at the sender, so a lost MID is unrecoverable;&lt;br /&gt;
skip-past-gap handles it (below).&lt;br /&gt;
&lt;br /&gt;
Receive side.  Fragments stash into &amp;lt;code&amp;gt;rq[seqno]&amp;lt;/code&amp;gt; unchanged; role bits&lt;br /&gt;
are read only at consume time.  &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;, called from&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt;, walks the ring starting at the oldest still-&lt;br /&gt;
undelivered &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;base = rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt; (equal to &amp;lt;code&amp;gt;rcv_cr.lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
only when no partial run is in progress; during a partial run &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
has already advanced past &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;).  It produces one of three&lt;br /&gt;
outcomes:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Outcome !! Cause&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELIVER (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]=SOLE&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;), or &amp;lt;code&amp;gt;rq[base]=FIRST&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; follows in slots &amp;lt;code&amp;gt;[base+1..base+n-1]&amp;lt;/code&amp;gt; with all intermediate roles in &amp;lt;code&amp;gt;{MID,FIRST,LAST}&amp;lt;/code&amp;gt; contiguous.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DROP (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; without a preceding &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;); a &amp;lt;code&amp;gt;FIRST..[non-LAST]..new-FIRST&amp;lt;/code&amp;gt; or new-&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; mid-run (drop the broken prefix with &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; = run length minus 1, so the new &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; stays); or, on best-effort flows, a gap at &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; later in the ring (drop up to the new run start).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; absent or &amp;lt;code&amp;gt;FIRST..[non-LAST]&amp;lt;/code&amp;gt; with no later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; in the ring (FRTX waits for retx; best-effort waits for arrival).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DELIVER&amp;lt;/code&amp;gt; triggers &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;: a scatter-gather &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; of the &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;&lt;br /&gt;
consecutive fragments at &amp;lt;code&amp;gt;rq[base..base+n-1]&amp;lt;/code&amp;gt; directly into the&lt;br /&gt;
caller&#039;s buffer; each per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;) is&lt;br /&gt;
released and &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; advances by &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; was already advanced&lt;br /&gt;
incrementally as each contiguous fragment arrived; &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;&lt;br /&gt;
only restores the fixed-width invariant &amp;lt;code&amp;gt;rwe == lwe + RQ_SIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
No intermediate reassembly buffer is allocated.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; past the broken prefix (releasing the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;s)&lt;br /&gt;
and pulls &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; up to the new trailing edge if needed; the next&lt;br /&gt;
consume retries from the new &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;.  Oversize or arithmetically&lt;br /&gt;
overflowing delivery (sum of fragment lengths &amp;amp;gt; &amp;lt;code&amp;gt;max_rcv_sdu&amp;lt;/code&amp;gt;, sum&lt;br /&gt;
&amp;amp;gt; caller&#039;s buffer, or running-sum overflow) also drops the run&lt;br /&gt;
with &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skip-past-gap (best-effort only).  On FRTX, a gap in the run means&lt;br /&gt;
&amp;quot;waiting for retransmit&amp;quot; and &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;.&lt;br /&gt;
On best-effort flows the gap is permanent, so &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;&lt;br /&gt;
scans forward in the ring for the next &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;; if one is&lt;br /&gt;
visible within &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;, it returns &amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; for the broken prefix and&lt;br /&gt;
the consume loop retries at the new &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;.  Memory hold is bounded&lt;br /&gt;
by &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;; the partial releases on the next consume call once a&lt;br /&gt;
later complete run exists.  Voice-like flows (one &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; per SDU)&lt;br /&gt;
see no extra wait: any later &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; makes the prior gap droppable&lt;br /&gt;
immediately.&lt;br /&gt;
&lt;br /&gt;
The choice to defer reassembly to consume time keeps the receive&lt;br /&gt;
path zero-copy: fragments stay in the shared-memory ring until&lt;br /&gt;
the application pulls, and the SDU lands directly in the caller&#039;s&lt;br /&gt;
buffer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 8. Retransmission ==&lt;br /&gt;
&lt;br /&gt;
FRCP is bounded by two delta-t-derived timers (Watson 1981, see&lt;br /&gt;
[[#15. Heritage and adopted techniques|Section 15]]):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer): upper bound on ACK delay.  An ACK for a received DATA packet MUST be emitted within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; of receipt; an attempt to send an ACK after the a-timer has expired is suppressed (the sender&#039;s RTO is already in motion).&lt;br /&gt;
* &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer): upper bound on retransmission.  A given DATA packet MUST NOT be retransmitted after &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; has elapsed since its first send (&amp;lt;code&amp;gt;t0&amp;lt;/code&amp;gt;); when the bound is hit, the flow is declared down (raising the Ouroboros asynchronous flow condition &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt;, which marks the flow dead to both endpoints) rather than retransmitted again.&lt;br /&gt;
&lt;br /&gt;
Each in-flight FRTX &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; owns one &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt;, armed in a hashed&lt;br /&gt;
timing wheel; the wheel deadline is the slot&#039;s next eligible&lt;br /&gt;
retransmit time.&lt;br /&gt;
&lt;br /&gt;
;RTO timer&lt;br /&gt;
:On fire (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;), re-emit with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, mark &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (Karn-suppress next ACK&#039;s RTT sample), and (for the head-of-line (HoL) slot only) bump &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; up to &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt;.  Wheel deadline is &amp;lt;code&amp;gt;t_send + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;.  Re-armed unless consumed.  The RTO timer also clears &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (re-arming fast-retransmit eligibility), resets &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; to 1 on a HoL fire (RFC 8985 sec. 6.2 step 4 reset clause), and marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; if its &amp;lt;code&amp;gt;frct_tx&amp;lt;/code&amp;gt; call fails.&lt;br /&gt;
&lt;br /&gt;
;r-timer guard&lt;br /&gt;
:Before any retransmit attempt, check &amp;lt;code&amp;gt;(now - t0)&amp;lt;/code&amp;gt; against &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;.  If exceeded, the slot is no longer eligible for retransmit.  Only the RTO timer (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;) treats r-timer expiry as terminal: it marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; (peer unreachable).  Fast-retransmit, SACK-driven retransmit, and NACK-driven head-of-line re-emit silently skip aged-out slots and defer the flow-down decision to the next RTO fire.&lt;br /&gt;
&lt;br /&gt;
;Fast retransmit (hybrid trigger, RFC 8985 sec. 6.2)&lt;br /&gt;
:On a non-advancing cumulative ACK with the scoreboard advanced, fire one fast retransmit when EITHER (a) the head-of-line slot&#039;s latest send is older than the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; ([[#3. Protocol parameters|Section 3]]) and not yet aged out, OR (b) the SACK &amp;lt;code&amp;gt;dup-thresh&amp;lt;/code&amp;gt; count above &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; reaches &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; (= 3, RFC 8985 sec. 6.2 step 4).  Fires at most once per non-advancing cumulative-ACK value, gated by &amp;lt;code&amp;gt;rack_fired_lwe&amp;lt;/code&amp;gt; (the &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; at which fast-retransmit last fired).  Set &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; on the slot (one-shot per-slot gate) and enter NewReno-style careful recovery (see NewReno below in this section).&lt;br /&gt;
:The RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; uses the RFC 8985 sec. 6.2 form &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor.  Before the first RTT sample seeds &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; falls back to &amp;lt;code&amp;gt;MIN(reo_wnd_mult * SRTT / 4, SRTT)&amp;lt;/code&amp;gt;, still floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt; (consistent with the windowed-minimum fallback described in [[#12. RTT estimation|Section 12]]).  &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt; is a windowed minimum over the last &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; = 5 min of RTT samples (matches the Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt; default) so a route change to a longer path eventually re-anchors the reorder window without relying on &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; growth alone.&lt;br /&gt;
&lt;br /&gt;
;SACK-driven retransmit&lt;br /&gt;
:For each gap below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt; whose slot is (1) still owned, (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (same hybrid as fast-retransmit, see [[#6.2. Locked main path|Section 6.2]]), re-emit.  Each SACK-driven retransmit re-arms a fresh &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; so a lost retransmit can still be recovered by its own RTO timer.&lt;br /&gt;
&lt;br /&gt;
;NewReno&lt;br /&gt;
:On entry, &amp;lt;code&amp;gt;recovery_high = snd_cr.seqno + RTT_QUARANTINE&amp;lt;/code&amp;gt;.  Exit when &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt; (the latter means everything sent has been acknowledged).  &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; also clears recovery.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 9. Pre-DRF NACK ==&lt;br /&gt;
&lt;br /&gt;
The two sides have different inactivity thresholds (&amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), so a receiver can detect &amp;quot;stale data run&amp;quot; before the sender&#039;s own DRF logic kicks in.  NACK is the receiver-driven nudge that asks the sender to re-transmit the head of the run.&lt;br /&gt;
&lt;br /&gt;
;Send (&amp;lt;code&amp;gt;frcti_nack_snd&amp;lt;/code&amp;gt;, called by &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;FRCT_INACT_NEED_NACK&amp;lt;/code&amp;gt;)&lt;br /&gt;
:When an incoming DATA packet has no DRF and rcv-side activity is older than &amp;lt;code&amp;gt;rcv_cr.inact&amp;lt;/code&amp;gt;, the receiver emits a bare packet with &amp;lt;code&amp;gt;flags = FRCT_NACK&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;seqno = arrival_seqno - 1&amp;lt;/code&amp;gt; (informational only, not consulted by the receive handler).  The cooldown in [[#3. Protocol parameters|Section 3]] rate-limits the burst.  Non-DATA non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; non-DATA DRF still rebases via the DRF branch.&lt;br /&gt;
&lt;br /&gt;
;Receive (&amp;lt;code&amp;gt;frcti_nack_rcv&amp;lt;/code&amp;gt;)&lt;br /&gt;
:Dispatched in the early-exit branch ([[#6.1. Early-exit dispatch|Section 6.1]]), before &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;.  The sender copies the head-of-line (HoL) &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; packet, marks the slot &amp;lt;code&amp;gt;SND_RTX | SND_FAST_RXM&amp;lt;/code&amp;gt; (Karn-suppress next ACK, one-shot fast-rxm gate), sets &amp;lt;code&amp;gt;rtt_lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, and re-emits via &amp;lt;code&amp;gt;fast_rxm_send&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt; and a refreshed &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;.  The original &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed - the NACK emit is additive to the normal retransmit machinery, not a replacement.  No-op if nothing is in flight, the HoL slot has aged past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, or the HoL &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; pointer has been cleared by SACK or RACK.&lt;br /&gt;
&lt;br /&gt;
NACK has exactly one role: lost first-of-run (DRF) packet recovery. Until the DRF packet arrives, the receiver cannot rebase its window, so any subsequent in-flight packets look stale to the receiver. The NACK fires the moment a stale receiver sees DATA without DRF, telling the sender to re-emit the head-of-line (DRF) packet at NACK-cooldown latency rather than waiting for the initial RTO (which is the configured default until &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded by the first probe round-trip).  Mid-stream loss is NOT NACK-driven; it is recovered by the sender&#039;s RTO, fast retransmit, and SACK-driven retransmit paths ([[#8. Retransmission|Section 8]]) only.&lt;br /&gt;
&lt;br /&gt;
The existing &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed on a NACK re-emit, so the RTO path remains the eventual fallback.&lt;br /&gt;
&lt;br /&gt;
== 10. Cumulative + selective ACK ==&lt;br /&gt;
&lt;br /&gt;
Cumulative ACK is &amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;.  On out-of-order arrival the&lt;br /&gt;
receiver also emits a SACK packet ([[#1.3. SACK payload|Section 1.3]]) whose payload lists&lt;br /&gt;
&#039;&#039;present&#039;&#039; blocks above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; (analogous to TCP SACK / QUIC ACK&lt;br /&gt;
ranges).  SACKs are rate-limited per [[#3. Protocol parameters|Section 3]] and suppressed when&lt;br /&gt;
neither &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; nor block count has changed since the last SACK.&lt;br /&gt;
&lt;br /&gt;
D-SACK reports (RFC 2883) are emitted in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; of an&lt;br /&gt;
otherwise normal SACK frame (see [[#1.3. SACK payload|Section 1.3]] for the encoding).&lt;br /&gt;
Two receiver triggers arm a pending D-SACK report (single-slot,&lt;br /&gt;
latest-wins):&lt;br /&gt;
&lt;br /&gt;
* DATA arrival with &amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;, both wire-dup (no RXM, &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt; path) and retransmit (RXM, post-FC branch) (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
* &amp;lt;code&amp;gt;rq_accept&amp;lt;/code&amp;gt; conflict, slot already occupied in &amp;lt;code&amp;gt;[lwe, rwe)&amp;lt;/code&amp;gt; (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
&lt;br /&gt;
When a D-SACK is pending and the standard scoreboard SACK would be&lt;br /&gt;
suppressed by dedup or rate-limit, the report is emitted as a&lt;br /&gt;
stand-alone SACK frame through the normal &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; path; when a&lt;br /&gt;
D-SACK report is pending the path bypasses dedup and the &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;&lt;br /&gt;
rate-limit, but the a-timer suppression on rcv inactivity still&lt;br /&gt;
applies.&lt;br /&gt;
&lt;br /&gt;
Bare ACKs are deferred via a per-flow delayed-ACK timer (one in&lt;br /&gt;
flight at a time, atomic test-and-set dedup; fires per [[#3. Protocol parameters|Section 3]]&lt;br /&gt;
after the first in-order arrival).  Suppressed if (1) no new&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, (2) rcv side is inactive (older than &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt;), or (3) the&lt;br /&gt;
sender just sent within &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;.  A pending D-SACK ride-through&lt;br /&gt;
bypasses (1) and (3); the a-timer gate (2) is unconditional.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 11. Flow control ==&lt;br /&gt;
&lt;br /&gt;
The receiver advertises &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; in every FC field.  The sender treats&lt;br /&gt;
its &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; as the absolute right edge: when&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt;= snd_cr.rwe&amp;lt;/code&amp;gt; the window is closed and &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt;&lt;br /&gt;
yields.  While closed, the sender periodically emits RDVS&lt;br /&gt;
(rendezvous) packets (cadence &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt;); the receiver replies with&lt;br /&gt;
a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;) that reopens the window.  Once the&lt;br /&gt;
window has been closed for longer than &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; the sender stops&lt;br /&gt;
emitting RDVS but does not tear the flow down - the writer keeps&lt;br /&gt;
blocking until either a peer-driven FC arrives or the KA&lt;br /&gt;
(keepalive) / r-timer marks the flow.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt; on receipt and MUST NOT shrink:&lt;br /&gt;
a backward &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is silently clamped to the current &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt;;&lt;br /&gt;
the FC packet still reopens the window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 12. RTT estimation ==&lt;br /&gt;
&lt;br /&gt;
Active RTTP probes ([[#1.4. RTTP payload|Section 1.4]]) carry a 32-bit &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt; (0&lt;br /&gt;
reserved) and a 16-byte random nonce echoed verbatim - defends&lt;br /&gt;
against spoofed replies.  A ring of &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; in-flight probes is&lt;br /&gt;
kept; an echo whose &amp;lt;code&amp;gt;(id, nonce)&amp;lt;/code&amp;gt; doesn&#039;t match the ring slot is&lt;br /&gt;
dropped.  A single RTTP sample is clamped to &amp;lt;code&amp;gt;RTT_CLAMP_MUL * srtt&amp;lt;/code&amp;gt;&lt;br /&gt;
(compile-time &amp;lt;code&amp;gt;RTT_CLAMP_MUL = 16&amp;lt;/code&amp;gt;) once &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded; the first&lt;br /&gt;
cold-probe sample feeds &amp;lt;code&amp;gt;rtt_update&amp;lt;/code&amp;gt; raw.&lt;br /&gt;
&lt;br /&gt;
Probe arming gates:&lt;br /&gt;
&lt;br /&gt;
;Cold (no &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; yet)&lt;br /&gt;
:the receive path arms at most one probe per 100 ms via &amp;lt;code&amp;gt;frcti_rcv_probe&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;PROBE_DUE_COLD&amp;lt;/code&amp;gt;); arming requires an incoming packet.  Active send-path arming bails while &amp;lt;code&amp;gt;srtt == 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
;Warm (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt;, called from &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt;)&lt;br /&gt;
:outstanding data (&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt; snd_cr.lwe&amp;lt;/code&amp;gt;), AND at least &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_rcv_rtt&amp;lt;/code&amp;gt; (last RTT receive of any kind), AND at least &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_snd_probe&amp;lt;/code&amp;gt; (last probe emit).&lt;br /&gt;
&lt;br /&gt;
Sample feeds either Linux&#039;s asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_LINUX_RTT_ESTIMATOR&amp;lt;/code&amp;gt;, default ON) or RFC 6298 symmetric EWMA&lt;br /&gt;
(compile option).  &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is floored at 10 ms when seeded from a&lt;br /&gt;
hint, at 1 us after every update (including the first seeding&lt;br /&gt;
sample); &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; floored at 100 ns.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;RTO = max(rto_min, 2 * srtt, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(the &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; floor is an FRCT addition not in RFC 6298).&lt;br /&gt;
Effective wheel deadline capped per [[#3. Protocol parameters|Section 3]].&lt;br /&gt;
&lt;br /&gt;
ACK-derived samples (&amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;rtt_sample_eligible&amp;lt;/code&amp;gt;), beyond&lt;br /&gt;
the cum-ACK advance gate in &amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno &amp;amp;gt; lwe&amp;lt;/code&amp;gt; and&lt;br /&gt;
&amp;lt;code&amp;gt;ackno &amp;amp;lt;= seqno&amp;lt;/code&amp;gt;), require all of: not in recovery; ACK packet does&lt;br /&gt;
not carry &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;; HoL slot&#039;s &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; bit clear; slot&#039;s &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;&lt;br /&gt;
pointer non-NULL (not SACK-consumed); &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; not below the &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
fence; &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; already seeded by an RTTP probe.  There is no ACK-only&lt;br /&gt;
seeding.&lt;br /&gt;
&lt;br /&gt;
Every eligible sample also feeds &amp;lt;code&amp;gt;RACK.min_RTT&amp;lt;/code&amp;gt; (RFC 8985 sec. 6.2)&lt;br /&gt;
via a windowed minimum: replace whenever the sample is strictly&lt;br /&gt;
smaller OR more than &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; (5 min, matches Linux&lt;br /&gt;
&amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) has elapsed since the current min was set.  The&lt;br /&gt;
downward branch is immediate (faster path picked up at once); the&lt;br /&gt;
upward branch is gated on the window (a transient queue burst does&lt;br /&gt;
not poison the estimate, but a sustained route change to a longer&lt;br /&gt;
path re-anchors &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; after at most one window).  Seeded from&lt;br /&gt;
&amp;lt;code&amp;gt;rtt_hint&amp;lt;/code&amp;gt; at &amp;lt;code&amp;gt;rtt_init&amp;lt;/code&amp;gt;; 0 acts as the unset sentinel and the base&lt;br /&gt;
in &amp;lt;code&amp;gt;rack_reorder_window&amp;lt;/code&amp;gt; falls back from &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt; (so&lt;br /&gt;
&amp;lt;code&amp;gt;R = mult * SRTT/4&amp;lt;/code&amp;gt;, capped at &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;, floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt;)&lt;br /&gt;
until the first sample.  See [[#6.2. Locked main path|Section 6.2]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 13. Liveness (keepalive) ==&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;code&amp;gt;qs.timeout &amp;amp;gt; 0&amp;lt;/code&amp;gt; a per-flow KA (keepalive) timer is armed.&lt;br /&gt;
Arming uses &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; for the deadline computation:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;deadline = min(snd_act + qs.timeout/4, rcv_act + qs.timeout)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(clamped to &amp;lt;code&amp;gt;now + qs.timeout/4&amp;lt;/code&amp;gt; if already past).  The timer fires&lt;br /&gt;
either on sender idleness (to send a KA) or on receiver idleness&lt;br /&gt;
(to declare the peer dead).  On fire (&amp;lt;code&amp;gt;ka_snd&amp;lt;/code&amp;gt;) the peer-dead test&lt;br /&gt;
uses &amp;lt;code&amp;gt;max(rcv_cr.act, t_ka_rcv)&amp;lt;/code&amp;gt; so a recent KA reply counts even&lt;br /&gt;
when no DATA has arrived:&lt;br /&gt;
&lt;br /&gt;
* If &amp;lt;code&amp;gt;now - max(rcv_cr.act, t_ka_rcv) &amp;amp;gt; qs.timeout&amp;lt;/code&amp;gt;, mark the flow &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; and notify the per-process flow-event set (&amp;lt;code&amp;gt;proc.fqset&amp;lt;/code&amp;gt;) with &amp;lt;code&amp;gt;FLOW_PEER&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Else if &amp;lt;code&amp;gt;snd_idle &amp;amp;gt; qs.timeout/4&amp;lt;/code&amp;gt;, emit a bare &amp;lt;code&amp;gt;KA | ACK&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) and re-arm.&lt;br /&gt;
* Else just re-arm.&lt;br /&gt;
&lt;br /&gt;
Note: &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; are the receive and transmit shared-memory&lt;br /&gt;
ring buffers.  The r-timer raises &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; on both (route is&lt;br /&gt;
broken); keepalive raises &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; only and notifies&lt;br /&gt;
the flow-event set (peer is silent, writer keeps &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; usable) -&lt;br /&gt;
distinct ACLs.  &amp;lt;code&amp;gt;qs.timeout == 0&amp;lt;/code&amp;gt; disables keepalive entirely; a&lt;br /&gt;
silent peer crash is then undetected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 14. Linger / teardown ==&lt;br /&gt;
&lt;br /&gt;
On &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt; computes a grace timeout&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;max(rcv_cr.act + rcv_cr.inact, snd_cr.act + snd_cr.inact) - now&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(floored at 0 and converted to seconds) and returns it; &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&lt;br /&gt;
forwards this to the IRMd as the dealloc grace.  The IRMd, not FRCT,&lt;br /&gt;
performs the wait.  Before computing the timeout, FRCT may emit a&lt;br /&gt;
final ACK when &amp;lt;code&amp;gt;rcv_cr.lwe != rcv_cr.seqno&amp;lt;/code&amp;gt; (the peer has not been&lt;br /&gt;
told the most recent cumulative ACK) AND the rcv side has been&lt;br /&gt;
active within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer not aged out).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt; is honoured only when &amp;lt;code&amp;gt;snd_cr.lwe &amp;amp;lt; edge&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;edge =&lt;br /&gt;
snd_fin_seqno&amp;lt;/code&amp;gt; after FIN has been sent in stream mode and&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno&amp;lt;/code&amp;gt; otherwise (data or FIN still in flight).  The drain&lt;br /&gt;
itself runs in &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&#039;s &amp;lt;code&amp;gt;while (FRCTI_LINGERING)&amp;lt;/code&amp;gt; loop, not in&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The fd is single-reader / single-writer (documented in the&lt;br /&gt;
manpages).  &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt; pumps &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; on every call (via&lt;br /&gt;
&amp;lt;code&amp;gt;flow_wait_window&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;flow_drain_rx_nb&amp;lt;/code&amp;gt;) and additionally blocks on&lt;br /&gt;
&amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; when the send window is closed.  A pure-writer thread thus&lt;br /&gt;
consumes ACKs without a dedicated reader.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 15. Heritage and adopted techniques ==&lt;br /&gt;
&lt;br /&gt;
Delta-t (Watson, 1981) is the primary heritage; FRCP descends from&lt;br /&gt;
the delta-t protocol family via the Recursive InterNetwork&lt;br /&gt;
Architecture (RINA; Day, &amp;quot;Patterns in Network Architecture&amp;quot;, 2008,&lt;br /&gt;
ch. 9).  Timer-based connection management&lt;br /&gt;
(no SYN/FIN handshake, per-flow state born on first DATA and&lt;br /&gt;
reclaimed after &amp;lt;code&amp;gt;t_mpl + a + r&amp;lt;/code&amp;gt; of silence), the DRF marker, and the&lt;br /&gt;
&amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; timers all come from delta-t.  See Watson,&lt;br /&gt;
&amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection&lt;br /&gt;
Management&amp;quot;, Computer Networks 5 (1981).&lt;br /&gt;
&lt;br /&gt;
The unified &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive and its&lt;br /&gt;
multi-axis QoS-cube argument ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) also come from RINA&lt;br /&gt;
(Day 2008, ch. 6; Grasa et al., &amp;quot;IRATI: investigating RINA as an&lt;br /&gt;
alternative to TCP/IP&amp;quot;, Computer Networks 92 (2015)) - reliability,&lt;br /&gt;
ordering, CRC presence, and encryption are flow attributes, not&lt;br /&gt;
separate sockets or protocols.&lt;br /&gt;
&lt;br /&gt;
The table below summarises additional adopted techniques and their&lt;br /&gt;
references.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FRCP mechanism !! Heritage !! Reference / note&lt;br /&gt;
|-&lt;br /&gt;
| Random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; || TCP ISN || RFC 6528 (Gont &amp;amp;amp; Bellovin, 2012).  QUIC PN-space reset (RFC 9000 sec. 12.3) is a structural analogue.&lt;br /&gt;
|-&lt;br /&gt;
| Cumulative ACK, left-window-edge advance || TCP || RFC 793 / RFC 9293&lt;br /&gt;
|-&lt;br /&gt;
| Receive window with non-shrink rule || TCP || RFC 793 sec. 3.7 / RFC 9293 sec. 3.8.6; RFC 1122 sec. 4.2.2.16 for the explicit non-shrink prohibition&lt;br /&gt;
|-&lt;br /&gt;
| Modular &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; arithmetic (&amp;lt;code&amp;gt;before&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;after&amp;lt;/code&amp;gt; helpers) || TCP || RFC 793 sec. 3.3 / RFC 9293 sec. 3.4&lt;br /&gt;
|-&lt;br /&gt;
| Selective ACK block list || TCP || RFC 2018 (Mathis et al., 1996).  Encoded as a typed FRCP packet rather than a TCP option, so framing is closer to QUIC ACK frames.  D-SACK (RFC 2883) carried in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt;; see [[#1.3. SACK payload|Section 1.3]].&lt;br /&gt;
|-&lt;br /&gt;
| NewReno-careful recovery with &amp;lt;code&amp;gt;recovery_high&amp;lt;/code&amp;gt; gate || TCP || RFC 6582 (Henderson et al., 2012); QUIC builds on the same model in RFC 9002 sec. 7.3.2.  Cwnd half absent (CC in IPCP).&lt;br /&gt;
|-&lt;br /&gt;
| RACK reordering window for fast retransmit || TCP || RFC 8985 (Cheng et al., 2021).  FRCP &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor against &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; collapse; matches RFC 8985 sec. 6.2 and Linux &amp;lt;code&amp;gt;tcp_rack_reo_wnd&amp;lt;/code&amp;gt;.  DSACK-driven &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; (sec. 6.2 step 4) is adopted; see [[#1.3. SACK payload|Section 1.3]] for the wire encoding.  The hybrid RACK-or-&amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; trigger from RFC 8985 sec. 6.2 step 4 is adopted ([[#8. Retransmission|Section 8]]).  QUIC&#039;s analogue in RFC 9002 sec. 6.1.2 uses &amp;lt;code&amp;gt;max(srtt, latest_rtt)&amp;lt;/code&amp;gt; as the base.&lt;br /&gt;
|-&lt;br /&gt;
| Karn&#039;s algorithm: no RTT sample on retransmits, RTO-collapse freeze || TCP || Karn &amp;amp;amp; Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, SIGCOMM 1987; RFC 6298 sec. 3.&lt;br /&gt;
|-&lt;br /&gt;
| RTO formula &amp;lt;code&amp;gt;RTO = max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt; || TCP || RFC 6298 (Paxson et al., 2011).  &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; = 250 us is below RFC 6298 sec. 2.4&#039;s 1 s SHOULD-floor - a recursive-layer choice.&lt;br /&gt;
|-&lt;br /&gt;
| Linux asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator (default) || Linux kernel || &amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;if(delta&amp;amp;lt;0) m&amp;amp;gt;&amp;amp;gt;=3&amp;lt;/code&amp;gt; dampening is a kernel divergence from RFC 6298.  RFC 6298 EWMA available behind a compile flag.&lt;br /&gt;
|-&lt;br /&gt;
| Delayed ACK with rate suppression || TCP || RFC 813 (Clark, 1982); RFC 1122 sec. 4.2.3.2; RFC 5681 sec. 4.2.  Single-deadline coalescing rather than &amp;quot;ack-every-other-segment&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| Zero-window-probe / persist-timer analogue (RDVS) || TCP || RFC 1122 sec. 4.2.2.17 / RFC 9293 sec. 3.8.6.1.  RDVS solicits an FC reply, distinct from QUIC &amp;lt;code&amp;gt;DATA_BLOCKED&amp;lt;/code&amp;gt; (RFC 9000 sec. 19.12), which is one-way notification.  &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; give-up departs from TCP.&lt;br /&gt;
|-&lt;br /&gt;
| Multiplexed control on a single PCI || SCTP / QUIC || SCTP chunk bundling (RFC 9260 sec. 6.10); QUIC frame multiplexing (RFC 9000 sec. 12.4).  Cleaner fit than TCP&#039;s separate-flag-bits design.&lt;br /&gt;
|-&lt;br /&gt;
| ACK ranges as multiple discontiguous acked blocks || QUIC || QUIC ACK frame (RFC 9000 sec. 19.3).  FRCP SACK is conceptually QUIC-frame-shaped even though encoded as absolute &amp;lt;code&amp;gt;[start,end]&amp;lt;/code&amp;gt; pairs.&lt;br /&gt;
|-&lt;br /&gt;
| Nonce-authenticated active RTT / liveness probing (RTTP) || QUIC &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;PATH_RESPONSE&amp;lt;/code&amp;gt; (RFC 9000 sec. 8.2, sec. 19.17, sec. 19.18).  WebRTC ICE consent-freshness (RFC 7675) is the same pattern.  QUIC&#039;s nonce is 8 octets; FRCP chooses 16.&lt;br /&gt;
|-&lt;br /&gt;
| Probing distinct from keepalive || QUIC || KA timer answers &amp;quot;peer alive?&amp;quot;, RTTP answers &amp;quot;path measurable?&amp;quot;, as in QUIC PING (RFC 9000 sec. 19.2) vs &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| Bare KA + ACK keepalive packets || QUIC / SCTP || QUIC PING (RFC 9000 sec. 19.2); SCTP HEARTBEAT / HEARTBEAT-ACK (RFC 9260 sec. 8.3).  SCTP HEARTBEAT also carries an opaque echoed blob, structurally similar to FRCP RTTP.&lt;br /&gt;
|-&lt;br /&gt;
| (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) fragment-role bits ([[#7.2. Fragmentation and reassembly|Section 7.2]]) || SCTP || RFC 9260 sec. 3.3.1 DATA chunk B/E bits encode the same four states (&amp;lt;code&amp;gt;B+E=SOLE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;B-only=FIRST&amp;lt;/code&amp;gt;, neither=&amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E-only=LAST&amp;lt;/code&amp;gt;).  Each fragment carries its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;/TSN and is independently retransmitted.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-offset reassembly (Sections [[#1.5. Stream PCI extension|1.5]], [[#16. Stream-mode flows|16]]) || QUIC || QUIC STREAM frame (RFC 9000 sec. 19.8) uses Offset + Length varints; FRCP uses fixed 32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;.  One stream per flow vs QUIC&#039;s many streams multiplexed.&lt;br /&gt;
|-&lt;br /&gt;
| FIN end-of-stream marker (Sections [[#1.2. Flag bits|1.2]], [[#16. Stream-mode flows|16]]) || TCP / QUIC || TCP FIN flag (RFC 9293 sec. 3.1) closes one half of the byte stream; QUIC STREAM frame FIN bit (RFC 9000 sec. 19.8) does the same per stream with an immutable final-size invariance (RFC 9000 sec. 4.5: the final size is fixed once observed).  FRCP&#039;s FIN consumes one packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (not one byte of stream space) and is idempotent on the sender side.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-credit flow control ([[#16. Stream-mode flows|Section 16]]) || QUIC || &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; (RFC 9000 sec. 4.1, sec. 19.10).  FRCP projects a per-flow byte budget onto the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;.  Single stream per flow collapses QUIC&#039;s &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.&lt;br /&gt;
|-&lt;br /&gt;
| Header protection (encrypted seqnos) || QUIC || QUIC RFC 9001 sec. 5.4 applies header protection on top of AEAD to mask the packet number.  FRCP&#039;s per-flow AEAD wrap ([[#16. Stream-mode flows|Section 16]]) is wider: it encrypts the entire PCI including &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; because the IPCP below already routes, so no destination connection-ID needs to stay in clear (cf. RFC 9000 sec. 5.2).&lt;br /&gt;
|-&lt;br /&gt;
| Two-bit fragment role polarity || SCTP || The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair follows SCTP B/E (begin = 1 / end = 1) rather than IPv4 MF (RFC 791 sec. 3.2), which has the inverse polarity (MF = 1 means NOT last).&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal reliability / ordering axes ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) || SCTP || PR-SCTP (RFC 3758, per-message partial reliability) and SCTP DATA U-bit (RFC 9260 sec. 3.3.1, per-message unordered) are the closest precedents for decoupling reliability from ordering; FRCP sets them per-flow rather than per-message.&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal CRC (&amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt;) || UDP-Lite || RFC 3828 (Larzon et al., 2004) lets the sender pick a per-packet Checksum Coverage and the receiver enforce a locally configured minimum (no in-band negotiation; sec. 3.1, sec. 3.3).  FRCP gates a full CRC trailer on &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; at flow setup.  Contrast TCP / SCTP (mandatory checksum) and QUIC (AEAD subsumes CRC).&lt;br /&gt;
|-&lt;br /&gt;
| Setup-time service negotiation || DCCP / SCTP / QUIC || DCCP Service Codes (RFC 4340 sec. 8.1.2, RFC 5595); SCTP INIT parameters (RFC 9260 sec. 3.3.2); QUIC transport parameters (RFC 9000 sec. 7.4).  All negotiate service properties at connection setup; only RINA&#039;s QoS cube exposes them as an orthogonal vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.1. Original to FRCP (no clean prior art) ===&lt;br /&gt;
&lt;br /&gt;
* Pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]): receiver-driven nudge exploiting &amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;.  Closest analogues are SCTP Gap Ack Blocks (RFC 9260 sec. 3.3.4) and DCCP Ack Vector (RFC 4340 sec. 11.4) - both let the receiver describe gaps to the sender, but neither targets the cross-epoch / pre-DRF case.&lt;br /&gt;
* &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; window-probe give-up: neither TCP (persist-timer probes until application or R2 abort, RFC 9293 sec. 3.8.6.1) nor QUIC has an explicit FC-give-up counter.  A recursive-network choice: outer layers can drop the flow.&lt;br /&gt;
* Skip-past-gap reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]): SCTP fragments and reassembles every flow regardless of reliability/ordering, using its own per-stream reassembly queue; QUIC fragments via STREAM offsets.  FRCP fragments best-effort flows too, but the receiver drops the broken prefix the moment a later run-start (&amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; role) is visible inside the &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;-wide reorder ring - no IP-frag-style timeout, no SCTP-style explicit abort.  If no later run-start arrives within the ring, &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt; and the partial run keeps its slots; the next inspect retries.  The trade-off: a permanently-lost &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; in a long isolated run holds slots until either a later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; appears in the ring or the writer stops, at which point the slots are reclaimed on flow teardown.&lt;br /&gt;
* Reassembly deferred to consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]), message mode only (&amp;lt;code&amp;gt;qos.service == SVC_MESSAGE&amp;lt;/code&amp;gt;): SCTP (RFC 9260 sec. 6.9), QUIC (RFC 9000 sec. 2.2), and TCP (RFC 9293) all hold reassembly state at the receive boundary.  FRCP message-mode leaves fragments in the shared-memory ring until &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; pulls and lands the SDU directly in the caller&#039;s buffer.  Stream mode ([[#16. Stream-mode flows|Section 16]]) uses the standard QUIC-style direct ring placement on receive and does not defer.  The optimisation is enabled by the Shared-Memory Subsystem (SSM) packet-buffer ring (see &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt; at [[#1.1. PCI header|Section 1.1]]); the analogue is OS-level scatter-gather I/O (&amp;lt;code&amp;gt;recvmsg+iovec&amp;lt;/code&amp;gt;), not a transport-layer prior art.&lt;br /&gt;
* TLP-equivalent tail-loss recovery (RFC 8985 sec. 7; RFC 9002 sec. 6.2): FRCP does not emit an explicit Tail Loss Probe packet, but the same goal is met implicitly by RACK loss detection ([[#8. Retransmission|Section 8]]) firing on a non-advancing cumulative ACK once the head-of-line slot ages past the RACK reorder window &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; - well below &amp;lt;code&amp;gt;RTO = max(2 * SRTT, SRTT + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.  A receiver-driven nudge is also available via the pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.2. Not adopted ===&lt;br /&gt;
&lt;br /&gt;
* Slow start, congestion window (cwnd), Additive Increase / Multiplicative Decrease (AIMD), NewReno cwnd inflation.  Congestion control lives in the IPCP CA policies and is driven by Explicit Congestion Notification (ECN, RFC 3168).&lt;br /&gt;
* Nagle / silly-window-syndrome (SWS) avoidance (RFC 896, RFC 1122 sec. 4.2.3.4).  (Deferred work, not adopted in the current spec.)&lt;br /&gt;
* TCP Timestamps (RFC 7323) / Protection Against Wrapped Sequences (PAWS) - RTT measurement uses RTTP, not per-segment timestamps.  A peer-supplied timestamp echoed on every ACK lets a malicious peer drive the &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; estimate arbitrarily low, collapsing the RTO and triggering a self-inflicted retransmit storm.  RTTP confines RTT measurement to nonce-authenticated probe round-trips, where a forged echo is rejected before it can reach the estimator.&lt;br /&gt;
* ECN (Explicit Congestion Notification) response inside FRCP (consumed by IPCP Congestion Avoidance / CA).&lt;br /&gt;
* IP-style fragment-offset reassembly (RFC 791 sec. 3.2; RFC 8200 sec. 4.5).  Message-mode FRCP relies on the FRCT &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; reorder ring keyed by &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (shared by FRTX and best-effort flows) to put fragments back in order; no separate offset field is needed and no IP-style hole-list reassembly buffer is kept.  Stream-mode FRCP does carry &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte offsets ([[#1.5. Stream PCI extension|Section 1.5]]) for direct ring placement on receive.&lt;br /&gt;
* QUIC STREAM offset+length framing on &#039;&#039;every&#039;&#039; flow (RFC 9000 sec. 19.8).  Message-mode FRCP uses the SCTP-style B/E flag-bit encoding (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) and skips the offsets; stream-mode FRCP adopts the QUIC offset model (heritage table above).&lt;br /&gt;
&lt;br /&gt;
== 16. Stream-mode flows ==&lt;br /&gt;
&lt;br /&gt;
When a flow is allocated with &amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt; both peers&lt;br /&gt;
switch to byte-stream semantics, layered on top of the FRTX reorder&lt;br /&gt;
machinery already described in Sections [[#6. Receive path|6]]-[[#8. Retransmission|8]].&lt;br /&gt;
&lt;br /&gt;
=== 16.1. Send ===&lt;br /&gt;
&lt;br /&gt;
The sender splits the caller&#039;s octets into chunks of at most&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt; octets (Sections [[#1.1. PCI header|1.1]]&lt;br /&gt;
and [[#1.5. Stream PCI extension|1.5]]).  Each chunk is one DATA packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte range copied from a monotonic stream counter.&lt;br /&gt;
In stream mode &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST be transmitted as&lt;br /&gt;
zero; the per-byte position is carried by the &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt;&lt;br /&gt;
extension instead.&lt;br /&gt;
&lt;br /&gt;
End-of-stream is signalled with a 0-byte DATA packet that has &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;&lt;br /&gt;
(bit 12) set, emitted on the FIN triggers listed in [[#1.2. Flag bits|Section 1.2]]&lt;br /&gt;
(WR-half close, &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, and any other path that yields the&lt;br /&gt;
final byte).  The sender MUST emit at most one FIN per flow; its&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; MUST equal &amp;lt;code&amp;gt;[final-byte, final-byte)&amp;lt;/code&amp;gt; (i.e., empty&lt;br /&gt;
interval at the final byte position; final-size invariance,&lt;br /&gt;
analogous to QUIC RFC 9000 sec. 4.5).  Idempotency is enforced by&lt;br /&gt;
an &amp;lt;code&amp;gt;snd_fin_sent&amp;lt;/code&amp;gt; guard.&lt;br /&gt;
&lt;br /&gt;
=== 16.2. Receive ===&lt;br /&gt;
&lt;br /&gt;
On arrival the receiver places the payload directly into a per-flow&lt;br /&gt;
byte-indexed receive ring of width &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; (octets) at the position&lt;br /&gt;
indicated by &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, with a two-segment &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; across the ring&lt;br /&gt;
boundary if needed.  Receipt is recorded in the FRTX reorder&lt;br /&gt;
machinery ([[#6.2. Locked main path|Section 6.2]]) augmented with the packet&#039;s &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;, and&lt;br /&gt;
FIN bit per slot.  When a packet&#039;s &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; front-overlaps&lt;br /&gt;
bytes already at or below the byte high-water mark, the overlap is&lt;br /&gt;
trimmed before placement so the same byte is never written twice.&lt;br /&gt;
After stashing, the receiver advances &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; and the byte high-water&lt;br /&gt;
mark across any newly-contiguous prefix.  Each slot advanced MUST&lt;br /&gt;
satisfy &amp;lt;code&amp;gt;start == the last-delivered slot&#039;s end&amp;lt;/code&amp;gt;; a slot whose&lt;br /&gt;
&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; does not equal that &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; is silently dropped at delivery time&lt;br /&gt;
(the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is consumed, no stream bytes contributed) and the high-&lt;br /&gt;
water mark does not advance past it.  The stream byte-stream&lt;br /&gt;
stalls at that point - there is no flow-tear-down on mismatch.&lt;br /&gt;
This filters spliced or off-path-injected slots that fall in&lt;br /&gt;
window without strong cryptographic authentication.&lt;br /&gt;
&lt;br /&gt;
A FIN slot marks end-of-stream at advance time only if its byte&lt;br /&gt;
position equals the last-delivered slot&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;; otherwise the FIN&lt;br /&gt;
is ignored and the corresponding &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; occupies a slot but&lt;br /&gt;
contributes no stream bytes.  No packet buffer is held after the&lt;br /&gt;
ring copy.&lt;br /&gt;
&lt;br /&gt;
=== 16.3. Read ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns up to &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt; octets from the contiguous prefix&lt;br /&gt;
&amp;lt;code&amp;gt;[next, high-water)&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;next&amp;lt;/code&amp;gt; is the byte the application has&lt;br /&gt;
already consumed up to and &amp;lt;code&amp;gt;high-water&amp;lt;/code&amp;gt; is the rightmost contiguous&lt;br /&gt;
byte received.  When the stream is fully drained AND end-of-stream&lt;br /&gt;
(EOS) was observed (&amp;lt;code&amp;gt;next == EOS&amp;lt;/code&amp;gt; byte position), &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns&lt;br /&gt;
0 (&amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) - the same shape POSIX &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt; uses on TCP after a peer&lt;br /&gt;
FIN.&lt;br /&gt;
&lt;br /&gt;
=== 16.4. Flow control ===&lt;br /&gt;
&lt;br /&gt;
ACK / SACK / RACK / RTO machinery is unchanged; the FRTX reorder&lt;br /&gt;
ring is reused as a per-&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; received-bitmap.  Let &amp;lt;code&amp;gt;per_pkt =&lt;br /&gt;
(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt;, the maximum stream-&lt;br /&gt;
byte payload one DATA packet can carry ([[#16.1. Send|Section 16.1]]).  The&lt;br /&gt;
receive window advertised in FC is clamped so the byte window&lt;br /&gt;
(&amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt;) cannot be overrun: the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is at most&lt;br /&gt;
&amp;lt;code&amp;gt;rcv_cr.lwe + ring_sz / per_pkt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC byte-credit flow-control model&lt;br /&gt;
(&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt;, RFC 9000 sec. 4.1 and sec. 19.10) projected onto&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; space.  With one stream per flow there is no &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; /&lt;br /&gt;
&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.  Receiver-side silly-window-syndrome&lt;br /&gt;
(SWS) avoidance (RFC 9293 sec. 3.8.6.2.2) is achieved by combining&lt;br /&gt;
the consume-time &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; bump with the global non-shrink rule from&lt;br /&gt;
[[#11. Flow control|Section 11]].&lt;br /&gt;
&lt;br /&gt;
=== 16.5. Security considerations ===&lt;br /&gt;
&lt;br /&gt;
Threat model.  An attacker that can observe (on-path passive) or&lt;br /&gt;
predict (off-path blind) the flow&#039;s seqnos and byte offsets on an&lt;br /&gt;
unencrypted stream flow can inject DATA or FIN at any in-window&lt;br /&gt;
position.  The in-line consistency checks above (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; == prior&lt;br /&gt;
&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; on advance; FIN MUST be 0-byte; FIN MUST sit at the final&lt;br /&gt;
byte position) realise the spirit of RFC 5961&#039;s &amp;quot;sequence-window&lt;br /&gt;
plus exact-position match for control bits&amp;quot; without an explicit&lt;br /&gt;
challenge-ACK probe; they make a few specific blind attack shapes&lt;br /&gt;
harder but are not cryptographic authentication. This is&lt;br /&gt;
comparable to TCP without the TCP Authentication Option (TCP-AO,&lt;br /&gt;
RFC 5925), tighter than a&lt;br /&gt;
pre-RFC-5961 TCP stack, and roughly equivalent to a modern&lt;br /&gt;
RFC 5961 stack against blind off-path injection - none of these&lt;br /&gt;
help once the attacker can sniff. TLS over TCP (RFC 8446)&lt;br /&gt;
encrypts only the TCP payload and leaves TCP seqnos, ACKs, FIN,&lt;br /&gt;
and RST in the clear, so TLS does NOT defend against TCP-header-&lt;br /&gt;
level injection; QUIC (RFC 9000) hides packet numbers under&lt;br /&gt;
header protection (RFC 9001 sec. 5.4), so this specific weakness&lt;br /&gt;
does not apply to QUIC.&lt;br /&gt;
&lt;br /&gt;
Mitigation: AEAD.  When the flow has encryption enabled the&lt;br /&gt;
recommended AEAD ciphers (AES-GCM, RFC 5288; or ChaCha20-Poly1305,&lt;br /&gt;
RFC 8439) wrap the entire FRCP packet on the wire - PCI, stream&lt;br /&gt;
extension, body, and the CRC trailer when &amp;lt;code&amp;gt;ber == 0&amp;lt;/code&amp;gt; - under a&lt;br /&gt;
per-flow symmetric key derived from the flow&#039;s own key exchange&lt;br /&gt;
([[#1.1. PCI header|Section 1.1]]).  The AEAD tag (~2^-128 forgery probability)&lt;br /&gt;
dominates the CRC (~2^-32) for integrity in this mode but the CRC&lt;br /&gt;
trailer is currently retained inside the wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
Implementations MUST NOT rely on the security properties below&lt;br /&gt;
when a non-AEAD cipher (e.g. AES-CTR alone) is negotiated; non-&lt;br /&gt;
AEAD modes provide confidentiality only and the threat-model&lt;br /&gt;
claims do not hold.&lt;br /&gt;
&lt;br /&gt;
With an AEAD cipher in use, seqnos, byte offsets, and the FIN bit&lt;br /&gt;
are both authenticated and confidential. Against an off-path or&lt;br /&gt;
on-path-passive attacker this is:&lt;br /&gt;
&lt;br /&gt;
* Stronger than TCP+TLS (TCP header in the clear).&lt;br /&gt;
* Stronger than TCP+TCP-AO (header authenticated but visible).&lt;br /&gt;
* Comparable to IPsec ESP transport mode (RFC 4303), which similarly authenticates and encrypts the upper-layer header plus payload, and to QUIC packet protection (RFC 9001 sec. 5), with the difference that QUIC must leave the destination connection ID in the clear for routing whereas FRCP relies on the IPCP below for delivery and can therefore encrypt its entire PCI.&lt;br /&gt;
&lt;br /&gt;
Keying granularity. Ouroboros flow allocation runs key exchange (&amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt;) per flow, so each &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; yields independent symmetric keys. This is finer-grained than QUIC (per-connection, RFC 9001, where one handshake covers all multiplexed streams) and finer-grained than typical IPsec deployment (per-host-pair Security Associations, SAs). Forward secrecy follows from the &amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt; when an ephemeral Diffie-Hellman exchange (DHE), or a hybrid mode (classical DH + post-quantum Key Encapsulation Mechanism / KEM), is selected.&lt;br /&gt;
&lt;br /&gt;
Replay protection. The AEAD layer itself does NOT carry an explicit anti-replay window (unlike IPsec ESP, RFC 4303 sec. 3.4.3, or DTLS, RFC 9147 sec. 4.5.1).  For FRCP-engaged flows the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space duplicate-suppression in [[#6.2. Locked main path|Section 6.2]] rejects replayed&lt;br /&gt;
DATA after the AEAD strips the wrap, because the AEAD authenticates the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a replay re-presents an old &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; that is then discarded either as a duplicate (still inside the receive window or as outside the receive window, depending on how far &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; has advanced since the original packet was delivered. RAW (&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) flows have no FRCP layer and therefore no replay protection at the AEAD layer either; deployments that need replay rejection on RAW flows SHOULD use SVC_MESSAGE.&lt;br /&gt;
&lt;br /&gt;
Layering. The AEAD wrap sits below FRCP on the data path, so RAW best-effort flows (&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;, the UDP-equivalent service of [[#2.2. Service modes (orthogonal axes)|Section 2.2]]) inherit the same per-flow integrity + confidentiality scope as FRCP-engaged flows - whatever the process and FRCP (if any) put on the wire is what the AEAD authenticates. No DTLS-equivalent layering is required for confidentiality and integrity; replay protection above AEAD is a separate concern as noted above.&lt;br /&gt;
&lt;br /&gt;
== 17. References ==&lt;br /&gt;
&lt;br /&gt;
This section lists the IETF documents, published works, and&lt;br /&gt;
source-code references cited inline elsewhere in this document.&lt;br /&gt;
IETF documents are cited inline as &amp;quot;RFC NNNN sec. X.Y&amp;quot;; books,&lt;br /&gt;
journal papers, and source-code references are cited inline by&lt;br /&gt;
author and year (or by file and function name) and are listed&lt;br /&gt;
here for convenience.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.1. IETF documents ===&lt;br /&gt;
&lt;br /&gt;
;[RFC 791]&lt;br /&gt;
:J. Postel, &amp;quot;Internet Protocol&amp;quot;, STD 5, RFC 791, September 1981.&lt;br /&gt;
;[RFC 793]&lt;br /&gt;
:J. Postel, &amp;quot;Transmission Control Protocol&amp;quot;, STD 7, RFC 793, September 1981.  Obsoleted by RFC 9293.&lt;br /&gt;
;[RFC 813]&lt;br /&gt;
:D. D. Clark, &amp;quot;Window and Acknowledgement Strategy in TCP&amp;quot;, RFC 813, July 1982.&lt;br /&gt;
;[RFC 896]&lt;br /&gt;
:J. Nagle, &amp;quot;Congestion Control in IP/TCP Internetworks&amp;quot;, RFC 896, January 1984.&lt;br /&gt;
;[RFC 1122]&lt;br /&gt;
:R. Braden (ed.), &amp;quot;Requirements for Internet Hosts -- Communication Layers&amp;quot;, STD 3, RFC 1122, October 1989.&lt;br /&gt;
;[RFC 2018]&lt;br /&gt;
:M. Mathis, J. Mahdavi, S. Floyd, A. Romanow, &amp;quot;TCP Selective Acknowledgment Options&amp;quot;, RFC 2018, October 1996.&lt;br /&gt;
;[RFC 2119]&lt;br /&gt;
:S. Bradner, &amp;quot;Key words for use in RFCs to Indicate Requirement Levels&amp;quot;, BCP 14, RFC 2119, March 1997.&lt;br /&gt;
;[RFC 2883]&lt;br /&gt;
:S. Floyd, J. Mahdavi, M. Mathis, M. Podolsky, &amp;quot;An Extension to the Selective Acknowledgement (SACK) Option for TCP&amp;quot;, RFC 2883, July 2000.&lt;br /&gt;
;[RFC 3758]&lt;br /&gt;
:R. Stewart, M. Ramalho, Q. Xie, M. Tuexen, P. Conrad, &amp;quot;Stream Control Transmission Protocol (SCTP) Partial Reliability Extension&amp;quot;, RFC 3758, May 2004.&lt;br /&gt;
;[RFC 3828]&lt;br /&gt;
:L-A. Larzon, M. Degermark, S. Pink, L-E. Jonsson (ed.), G. Fairhurst (ed.), &amp;quot;The Lightweight User Datagram Protocol (UDP-Lite)&amp;quot;, RFC 3828, July 2004.&lt;br /&gt;
;[RFC 4303]&lt;br /&gt;
:S. Kent, &amp;quot;IP Encapsulating Security Payload (ESP)&amp;quot;, RFC 4303, December 2005.&lt;br /&gt;
;[RFC 4340]&lt;br /&gt;
:E. Kohler, M. Handley, S. Floyd, &amp;quot;Datagram Congestion Control Protocol (DCCP)&amp;quot;, RFC 4340, March 2006.&lt;br /&gt;
;[RFC 5288]&lt;br /&gt;
:J. Salowey, A. Choudhury, D. McGrew, &amp;quot;AES Galois Counter Mode (GCM) Cipher Suites for TLS&amp;quot;, RFC 5288, August 2008.&lt;br /&gt;
;[RFC 5595]&lt;br /&gt;
:G. Fairhurst, &amp;quot;The Datagram Congestion Control Protocol (DCCP) Service Codes&amp;quot;, RFC 5595, September 2009.&lt;br /&gt;
;[RFC 5681]&lt;br /&gt;
:M. Allman, V. Paxson, E. Blanton, &amp;quot;TCP Congestion Control&amp;quot;, RFC 5681, September 2009.&lt;br /&gt;
;[RFC 5925]&lt;br /&gt;
:J. Touch, A. Mankin, R. Bonica, &amp;quot;The TCP Authentication Option&amp;quot;, RFC 5925, June 2010.&lt;br /&gt;
;[RFC 5961]&lt;br /&gt;
:A. Ramaiah, R. Stewart, M. Dalal, &amp;quot;Improving TCP&#039;s Robustness to Blind In-Window Attacks&amp;quot;, RFC 5961, August 2010.&lt;br /&gt;
;[RFC 6298]&lt;br /&gt;
:V. Paxson, M. Allman, J. Chu, M. Sargent, &amp;quot;Computing TCP&#039;s Retransmission Timer&amp;quot;, RFC 6298, June 2011.&lt;br /&gt;
;[RFC 6528]&lt;br /&gt;
:F. Gont, S. Bellovin, &amp;quot;Defending against Sequence Number Attacks&amp;quot;, RFC 6528, February 2012.  Obsoletes RFC 1948.&lt;br /&gt;
;[RFC 6582]&lt;br /&gt;
:T. Henderson, S. Floyd, A. Gurtov, Y. Nishida, &amp;quot;The NewReno Modification to TCP&#039;s Fast Recovery Algorithm&amp;quot;, RFC 6582, April 2012.&lt;br /&gt;
;[RFC 7323]&lt;br /&gt;
:D. Borman, B. Braden, V. Jacobson, R. Scheffenegger (ed.), &amp;quot;TCP Extensions for High Performance&amp;quot;, RFC 7323, September 2014.&lt;br /&gt;
;[RFC 7675]&lt;br /&gt;
:M. Perumal, D. Wing, R. Ravindranath, T. Reddy, M. Thomson, &amp;quot;Session Traversal Utilities for NAT (STUN) Usage for Consent Freshness&amp;quot;, RFC 7675, October 2015.&lt;br /&gt;
;[RFC 8174]&lt;br /&gt;
:B. Leiba, &amp;quot;Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words&amp;quot;, BCP 14, RFC 8174, May 2017.&lt;br /&gt;
;[RFC 8200]&lt;br /&gt;
:S. Deering, R. Hinden, &amp;quot;Internet Protocol, Version 6 (IPv6) Specification&amp;quot;, STD 86, RFC 8200, July 2017.&lt;br /&gt;
;[RFC 8439]&lt;br /&gt;
:Y. Nir, A. Langley, &amp;quot;ChaCha20 and Poly1305 for IETF Protocols&amp;quot;, RFC 8439, June 2018.&lt;br /&gt;
;[RFC 8446]&lt;br /&gt;
:E. Rescorla, &amp;quot;The Transport Layer Security (TLS) Protocol Version 1.3&amp;quot;, RFC 8446, August 2018.&lt;br /&gt;
;[RFC 8985]&lt;br /&gt;
:Y. Cheng, N. Cardwell, N. Dukkipati, P. Jha, &amp;quot;The RACK-TLP Loss Detection Algorithm for TCP&amp;quot;, RFC 8985, February 2021.&lt;br /&gt;
;[RFC 9000]&lt;br /&gt;
:J. Iyengar (ed.), M. Thomson (ed.), &amp;quot;QUIC: A UDP-Based Multiplexed and Secure Transport&amp;quot;, RFC 9000, May 2021.&lt;br /&gt;
;[RFC 9001]&lt;br /&gt;
:M. Thomson (ed.), S. Turner (ed.), &amp;quot;Using TLS to Secure QUIC&amp;quot;, RFC 9001, May 2021.&lt;br /&gt;
;[RFC 9002]&lt;br /&gt;
:J. Iyengar (ed.), I. Swett (ed.), &amp;quot;QUIC Loss Detection and Congestion Control&amp;quot;, RFC 9002, May 2021.&lt;br /&gt;
;[RFC 9147]&lt;br /&gt;
:E. Rescorla, H. Tschofenig, N. Modadugu, &amp;quot;The Datagram Transport Layer Security (DTLS) Protocol Version 1.3&amp;quot;, RFC 9147, April 2022.&lt;br /&gt;
;[RFC 9260]&lt;br /&gt;
:R. Stewart, M. Tuexen, K. Nielsen, &amp;quot;Stream Control Transmission Protocol&amp;quot;, RFC 9260, June 2022.  Obsoletes RFC 4960.&lt;br /&gt;
;[RFC 9293]&lt;br /&gt;
:W. Eddy (ed.), &amp;quot;Transmission Control Protocol (TCP)&amp;quot;, STD 7, RFC 9293, August 2022.  Obsoletes RFC 793 and several follow-ons; updates RFC 1122 and others.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.2. Books and journal papers ===&lt;br /&gt;
&lt;br /&gt;
;[Day08]&lt;br /&gt;
:J. Day, &amp;quot;Patterns in Network Architecture: A Return to Fundamentals&amp;quot;, Prentice Hall, 2008.&lt;br /&gt;
;[Grasa15]&lt;br /&gt;
:E. Grasa et al., &amp;quot;IRATI: investigating RINA as an alternative to TCP/IP&amp;quot;, Computer Networks, Vol. 92, December 2015.&lt;br /&gt;
;[KP87]&lt;br /&gt;
:P. Karn, C. Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, ACM SIGCOMM, August 1987.&lt;br /&gt;
;[Wat81]&lt;br /&gt;
:R. W. Watson, &amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection Management&amp;quot;, Computer Networks, Vol. 5, 1981.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.3. Source-code references ===&lt;br /&gt;
&lt;br /&gt;
;[Linux-RTT]&lt;br /&gt;
:&amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt; of the Linux kernel, defining the asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; variance update used as FRCP&#039;s default RTT estimator ([[#12. RTT estimation|Section 12]]).  Line-stable browseable copy at https://elixir.bootlin.com/linux/latest/source/net/ipv4/tcp_input.c.&lt;br /&gt;
&lt;br /&gt;
[[Category:Protocols]]&lt;br /&gt;
[[Category:Ouroboros internals]]&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1918</id>
		<title>Flow and Retransmission Control Protocol</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1918"/>
		<updated>2026-05-17T15:11:27Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 16.5. Security considerations */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:FRCP - Flow and Retransmission Control Protocol}}&lt;br /&gt;
&lt;br /&gt;
FRCP runs end-to-end between two peers over a flow.  It delivers&lt;br /&gt;
reliability, in-order delivery, flow control, and liveness.&lt;br /&gt;
Congestion Control (CC) is not in FRCP - that lives in the IPC&lt;br /&gt;
Process (IPCP) Congestion Avoidance (CA) policies, orthogonal to&lt;br /&gt;
FRCP.  Flow allocation, naming, and IPCP lifecycle are handled by&lt;br /&gt;
the IPC Resource Manager daemon (IRMd).&lt;br /&gt;
&lt;br /&gt;
FRCT (Flow and Retransmission Control Task) is the libouroboros&lt;br /&gt;
implementation of FRCP; the task lives in &amp;lt;code&amp;gt;src/lib/frct.c&amp;lt;/code&amp;gt;.  The&lt;br /&gt;
remainder of this document describes the FRCP wire protocol and the&lt;br /&gt;
behaviour FRCT realises.  Code symbols retain the &amp;lt;code&amp;gt;FRCT_&amp;lt;/code&amp;gt; prefix&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, ...) because they belong to the implementing&lt;br /&gt;
task; this document references them verbatim.&lt;br /&gt;
&lt;br /&gt;
The keywords &amp;quot;MUST&amp;quot;, &amp;quot;MUST NOT&amp;quot;, &amp;quot;REQUIRED&amp;quot;, &amp;quot;SHALL&amp;quot;, &amp;quot;SHALL NOT&amp;quot;,&lt;br /&gt;
&amp;quot;SHOULD&amp;quot;, &amp;quot;SHOULD NOT&amp;quot;, &amp;quot;RECOMMENDED&amp;quot;, &amp;quot;MAY&amp;quot;, and &amp;quot;OPTIONAL&amp;quot; in&lt;br /&gt;
this document are to be interpreted as described in &amp;lt;code&amp;gt;BCP&amp;lt;/code&amp;gt; 14 (Best&lt;br /&gt;
Current Practice; RFC 2119, RFC 8174) when, and only when, they&lt;br /&gt;
appear in all capitals.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:Unsigned 32-bit / 8-bit integers (kernel-C style).&lt;br /&gt;
;&amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:Nanoseconds.&lt;br /&gt;
&lt;br /&gt;
Modular sequence-number comparators (32-bit, modulo 2^32):&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;before(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;(int32_t)(a - b) &amp;amp;lt; 0&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;after(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;before(b, a)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Used throughout for &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; ordering checks.&lt;br /&gt;
&lt;br /&gt;
Round-Trip Time (RTT) abbreviations used throughout:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;&lt;br /&gt;
:Smoothed RTT estimate (RFC 6298).&lt;br /&gt;
;&amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt;&lt;br /&gt;
:Mean deviation of RTT (Linux variance estimator).&lt;br /&gt;
;&amp;lt;code&amp;gt;EWMA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Exponentially Weighted Moving Average.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTO&amp;lt;/code&amp;gt;&lt;br /&gt;
:Retransmission Timeout, &amp;lt;code&amp;gt;max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Timer-bound symbols &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer, ACK delay) and &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer,&lt;br /&gt;
retransmission window) are defined in [[#8. Retransmission|Section 8]]; &amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; (Maximum&lt;br /&gt;
Packet Lifetime) is introduced in [[#2.1. Per-flow state|Section 2.1]] (the &amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; field)&lt;br /&gt;
with heritage in [[#15. Heritage and adopted techniques|Section 15]].&lt;br /&gt;
&lt;br /&gt;
Wire-format diagrams follow the IETF convention: bit 0 is the&lt;br /&gt;
leftmost (most significant) bit and fields are in network byte&lt;br /&gt;
order unless stated otherwise.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1. Wire format ==&lt;br /&gt;
&lt;br /&gt;
=== 1.1. PCI header ===&lt;br /&gt;
&lt;br /&gt;
Fixed 16-octet base Protocol-Control Information (PCI) header&lt;br /&gt;
prefixed to every FRCP packet (RFC convention: bit 0 leftmost,&lt;br /&gt;
most-significant bit first).  All multi-byte fields except &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;&lt;br /&gt;
are in network byte order; &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt; is an opaque 16-bit value that&lt;br /&gt;
the receiver recomputes from the wire bytes and compares to the&lt;br /&gt;
in-place &amp;lt;code&amp;gt;pci-&amp;gt;hcs&amp;lt;/code&amp;gt; read, so its on-wire byte order need only&lt;br /&gt;
match between peers running compatible builds.  DATA packets on&lt;br /&gt;
stream-mode flows carry an additional 8-octet extension (see&lt;br /&gt;
[[#1.5. Stream PCI extension|Section 1.5]]); SACK and RTTP carry their own payloads after the&lt;br /&gt;
base PCI.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |             flags             |              hcs              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            window                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            seqno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            ackno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                     payload (variable) ...&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt;&lt;br /&gt;
:feature/type bitmap (see [[#1.2. Flag bits|Section 1.2]]).&lt;br /&gt;
;&amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;&lt;br /&gt;
:CRC-16-CCITT-FALSE Header Check Sequence (HCS) over &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; (+ stream extension when present); the two octets of the &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt; field itself are omitted from the CRC input.  Verified on receive before any flag-driven dispatch.&lt;br /&gt;
;&amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt;&lt;br /&gt;
:receiver-advertised right window edge (valid iff FC).&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-flow sequence number.&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;&lt;br /&gt;
:cumulative Acknowledgement (ACK) (valid iff ACK).&lt;br /&gt;
&lt;br /&gt;
A single packet can simultaneously carry DATA + ACK + FC (Flow&lt;br /&gt;
Control) + RXM (Retransmission) by ORing flag bits; the PCI&lt;br /&gt;
multiplexes control on the same wire frame in the spirit of SCTP&lt;br /&gt;
chunk bundling (RFC 9260 sec. 6.10) and QUIC frame multiplexing&lt;br /&gt;
(RFC 9000 sec. 12.4).  DATA-bearing packets carry the caller&#039;s&lt;br /&gt;
payload after the PCI; SACK (Selective Acknowledgement) and RTTP&lt;br /&gt;
(Round-Trip Time Probe) carry their own typed payloads after the&lt;br /&gt;
PCI.&lt;br /&gt;
&lt;br /&gt;
Optional framing (per-flow, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]]).  On the wire, the&lt;br /&gt;
order from inside out is:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Layer !! Scope&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| The FRCP packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body + CRC-32 ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| CRC-32 covers the body only (PCI is in HCS); appended iff &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; on DATA, or on every SACK packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ AEAD-wrap of above ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| Iff Authenticated Encryption with Associated Data (AEAD) is enabled.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* HCS in the PCI covers the header fields on every packet and is verified before any flag-driven dispatch.&lt;br /&gt;
* The CRC-32 trailer (IEEE 802.3 / zlib reflected polynomial &amp;lt;code&amp;gt;0xEDB88320&amp;lt;/code&amp;gt;, init &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;, xor-out &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;) covers the body on DATA when &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; and on every SACK packet; the trailer is written as a raw &amp;lt;code&amp;gt;uint32_t&amp;lt;/code&amp;gt; (the same convention as &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;: opaque on the wire as long as both peers run compatible builds).  The PCI is not under the CRC (Cyclic Redundancy Check) because the HCS already protects it.  It is appended before AEAD encryption and therefore rides inside the AEAD wrap when both are active; the AEAD tag (~2^-128 forgery probability) dominates the CRC (~2^-32) for integrity in that mode but the CRC trailer is currently retained.&lt;br /&gt;
* When encryption is enabled, the entire (possibly-CRC&#039;d) FRCP packet is wrapped with AEAD inside the shared-memory packet buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt;); the packet grows by the AEAD overhead, namely a leading nonce / Initialization Vector (IV) of &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_ivsz&amp;lt;/code&amp;gt;) and a trailing authentication tag of &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_tagsz&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Both CRC and AEAD are layered around the FRCP wire format and are not visible to the FRCP machinery itself.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.2. Flag bits ===&lt;br /&gt;
&lt;br /&gt;
Flag bits are numbered most-significant-bit first to match the wire&lt;br /&gt;
diagram (bit numbering per [[#1.1. PCI header|Section 1.1]]; bit 0 is the MSB of the&lt;br /&gt;
16-bit &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; field and lands at wire-position 0 in network byte&lt;br /&gt;
order).  Bits 13..15 are reserved and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Bit !! Mask !! Name !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| 0 || &amp;lt;code&amp;gt;0x8000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt; || Carries caller payload&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &amp;lt;code&amp;gt;0x4000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DRF&amp;lt;/code&amp;gt; || Data Run Flag: start of a fresh run&lt;br /&gt;
|-&lt;br /&gt;
| 2 || &amp;lt;code&amp;gt;0x2000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt; || Acknowledgement: &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field valid&lt;br /&gt;
|-&lt;br /&gt;
| 3 || &amp;lt;code&amp;gt;0x1000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt; || Negative ACK; &amp;lt;code&amp;gt;seqno = arrival_seqno-1&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || &amp;lt;code&amp;gt;0x0800&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt; || Flow Control: &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; field valid (&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || &amp;lt;code&amp;gt;0x0400&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt; || Rendezvous probe (window-closed)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || &amp;lt;code&amp;gt;0x0200&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; || First Fragment (role bit 0; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || &amp;lt;code&amp;gt;0x0100&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; || Last Fragment (role bit 1; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 8 || &amp;lt;code&amp;gt;0x0080&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RXM&amp;lt;/code&amp;gt; || Retransmission&lt;br /&gt;
|-&lt;br /&gt;
| 9 || &amp;lt;code&amp;gt;0x0040&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt; || Selective ACK block list in payload&lt;br /&gt;
|-&lt;br /&gt;
| 10 || &amp;lt;code&amp;gt;0x0020&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt; || RTT Probe / echo (payload follows)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || &amp;lt;code&amp;gt;0x0010&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt; || Keepalive&lt;br /&gt;
|-&lt;br /&gt;
| 12 || &amp;lt;code&amp;gt;0x0008&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt; || End-of-stream marker (stream mode)&lt;br /&gt;
|-&lt;br /&gt;
| 13-15 || -- || -- || Reserved (MUST be zero)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair encodes the fragment role of a DATA-bearing&lt;br /&gt;
Service Data Unit (SDU), SCTP-style begin/end flags (RFC 9260&lt;br /&gt;
sec. 3.3.1):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FFGM !! LFGM !! Role&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || Sole / un-fragmented SDU (begin AND end)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || First fragment of a multi-fragment SDU&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Middle fragment&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Last fragment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Each fragment is carried in its own FRCP packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;;&lt;br /&gt;
FRTX (the FRCT Retransmission service mode, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]])&lt;br /&gt;
recovers individual fragments via the normal Retransmission Timeout&lt;br /&gt;
(RTO) / SACK / Recent Acknowledgement (RACK, RFC 8985) path.  The&lt;br /&gt;
receiver reassembles the SDU at consume time once the contiguous&lt;br /&gt;
[FIRST .. LAST] run has fully arrived.  On non-DATA packets the role&lt;br /&gt;
bits are unused and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
In stream mode (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;, see [[#16. Stream-mode flows|Section 16]]) there are&lt;br /&gt;
no SDU boundaries to encode, so &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST&lt;br /&gt;
be transmitted as zero.  End-of-stream uses a dedicated bit (&amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;,&lt;br /&gt;
bit 12) carried on a 0-byte DATA packet, emitted at write-half close&lt;br /&gt;
(&amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;FLOWFRDONLY&amp;lt;/code&amp;gt;), during linger drain, and at &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;;&lt;br /&gt;
emission is idempotent (first call wins).  After contiguous delivery&lt;br /&gt;
of the FIN-bearing slot, the receiver latches &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt; at the FIN&#039;s&lt;br /&gt;
start offset; &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns 0 (end-of-file, &amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) once buffered&lt;br /&gt;
bytes have been drained up to &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt;.  Per-byte position is&lt;br /&gt;
carried by the [start, end) extension ([[#1.5. Stream PCI extension|Section 1.5]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.3. SACK payload ===&lt;br /&gt;
&lt;br /&gt;
A SACK packet has the &amp;lt;code&amp;gt;FRCT_ACK | FRCT_FC | FRCT_SACK&amp;lt;/code&amp;gt; flag bits set&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI, the&lt;br /&gt;
payload is a 2-octet block count (network byte order), 2 octets of&lt;br /&gt;
padding to 4-byte align the block list, then &amp;lt;code&amp;gt;n_blocks&amp;lt;/code&amp;gt; pairs of&lt;br /&gt;
32-bit start/end seqnos describing &#039;&#039;present&#039;&#039; (received) ranges&lt;br /&gt;
above the cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |           n_blocks            |        padding (2 octets)     |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[0]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            end[0]                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[1]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
                       ... n_blocks pairs total ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;n_blocks &amp;amp;lt;= SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; (2048).  The per-flow effective cap is&lt;br /&gt;
further bounded by &amp;lt;code&amp;gt;(frag_mtu - PCI - 4) / 8&amp;lt;/code&amp;gt; blocks per packet; SACK&lt;br /&gt;
packets carry no stream extension, so PCI here is the 16-octet base&lt;br /&gt;
header even on stream-mode flows.&lt;br /&gt;
&lt;br /&gt;
Wire invariant: every block produced by the receiver, except an&lt;br /&gt;
optional leading Duplicate SACK (D-SACK) block as described below,&lt;br /&gt;
describes a range strictly above the cumulative ACK carried in the&lt;br /&gt;
PCI &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field (&amp;lt;code&amp;gt;after(start[i], ackno)&amp;lt;/code&amp;gt;).  This makes the D-SACK&lt;br /&gt;
convention below unambiguous; the receiver-side builder MUST&lt;br /&gt;
preserve it.&lt;br /&gt;
&lt;br /&gt;
Duplicate SACK (D-SACK, RFC 2883) is signalled in-band: no flag&lt;br /&gt;
bit, no extra framing.  Modular seqno arithmetic uses the&lt;br /&gt;
&amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators defined in the Notation block.&lt;br /&gt;
&amp;lt;code&amp;gt;Block[0]&amp;lt;/code&amp;gt; carries a D-SACK report when either:&lt;br /&gt;
&lt;br /&gt;
;case 1 (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;before(blocks[0].start, ackno)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ackno - blocks[0].start&amp;lt;/code&amp;gt; is within &amp;lt;code&amp;gt;MAX_DSACK_LAG&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;== RQ_SIZE&amp;lt;/code&amp;gt;).  A single duplicate &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; observed below the cumulative ACK.&lt;br /&gt;
;case 2 (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;blocks[0]&amp;lt;/code&amp;gt; is a sub-range of some &amp;lt;code&amp;gt;blocks[i&amp;gt;0]&amp;lt;/code&amp;gt; (not exactly equal).  Reports a duplicate of an in-window &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; that the same packet&#039;s remaining SACK blocks already describe as received.&lt;br /&gt;
&lt;br /&gt;
Senders that do not implement D-SACK process &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; through the&lt;br /&gt;
normal SACK-mark loop and the existing clamp-and-skip path makes&lt;br /&gt;
case-1 a no-op (&amp;lt;code&amp;gt;start &amp;amp;lt; snd_cr.lwe&amp;lt;/code&amp;gt; clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt;, the inner&lt;br /&gt;
loop then skips &amp;lt;code&amp;gt;k == snd_cr.lwe&amp;lt;/code&amp;gt;) and case-2 idempotent (same slots&lt;br /&gt;
NULL&#039;d twice).  D-SACK-aware senders feed the report into the RACK&lt;br /&gt;
&amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; scaler (RFC 8985 sec. 6.2 step 4): bump on receipt&lt;br /&gt;
(cap 20), halve once per 16 cumulatively-ACK&#039;d seqnos since the&lt;br /&gt;
most recent D-SACK arrival or halve event, reset to 1 on an RTO&lt;br /&gt;
timer fire at the head-of-line.  D-SACK alone never enters&lt;br /&gt;
NewReno-careful recovery (see [[#8. Retransmission|Section 8]]); only non-D-SACK blocks&lt;br /&gt;
count as gaps.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.4. RTTP payload ===&lt;br /&gt;
&lt;br /&gt;
An RTTP (Round-Trip Time Probe) packet has only the &amp;lt;code&amp;gt;FRCT_RTTP&amp;lt;/code&amp;gt; flag&lt;br /&gt;
set (bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI,&lt;br /&gt;
the payload is 24 octets (packed):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          probe_id                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          echo_id                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +                  nonce (16 octets, echoed verbatim)           +&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:sender counter, 0 on reply, 0 reserved.&lt;br /&gt;
;&amp;lt;code&amp;gt;echo_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:peer&#039;s &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;, 0 on outbound probe.&lt;br /&gt;
;&amp;lt;code&amp;gt;nonce&amp;lt;/code&amp;gt;&lt;br /&gt;
:random, echoed unmodified, memcmp&#039;d to defeat spoof.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.5. Stream PCI extension ===&lt;br /&gt;
&lt;br /&gt;
A stream-mode flow (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;) carries an extra&lt;br /&gt;
8-octet extension after the 16-octet base PCI on every DATA packet&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            start                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                             end                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset of the first payload byte in the stream.&lt;br /&gt;
;&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset one past the last payload byte; &amp;lt;code&amp;gt;end - start&amp;lt;/code&amp;gt; equals the on-wire payload length.&lt;br /&gt;
&lt;br /&gt;
Total stream-mode PCI for DATA packets is 24 octets (16 base + 8&lt;br /&gt;
extension); control packets (SACK, RTTP, bare ACK, KA, etc.) retain&lt;br /&gt;
the 16-octet base PCI.  Stream mode MUST be negotiated at flow&lt;br /&gt;
allocation; the extension is present iff stream mode is in use,&lt;br /&gt;
never on a per-packet basis.  Both peers MUST treat &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; as&lt;br /&gt;
monotonic 32-bit byte offsets; when a slot reaches the head of the&lt;br /&gt;
contiguous run with &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; not equal to the prior packet&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; the&lt;br /&gt;
slot is silently dropped at delivery time ([[#16. Stream-mode flows|Section 16]]) rather&lt;br /&gt;
than rejected at stash.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC STREAM-frame reassembly model (RFC 9000 sec. 19.8):&lt;br /&gt;
each packet carries its packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (this PCI&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; field) and a&lt;br /&gt;
separate stream byte position (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;).  Separating the two&lt;br /&gt;
avoids TCP&#039;s conflation of packet identity with byte position which&lt;br /&gt;
forces Karn&#039;s algorithm for Round-Trip Time (RTT) sampling (no RTT&lt;br /&gt;
sample on retransmits, RFC 6298 sec. 3); FRCP applies the&lt;br /&gt;
Karn-equivalent gate via a combination of per-packet &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;,&lt;br /&gt;
per-slot &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; flags, and a sample-fence &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; (see [[#2.1. Per-flow state|Section 2.1]]&lt;br /&gt;
and [[#12. RTT estimation|Section 12]]).  FRCP&#039;s fixed-32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; wrap at 4 GiB of&lt;br /&gt;
wire bytes, narrower than QUIC&#039;s 62-bit varint offset (cf. RFC 9000&lt;br /&gt;
sec. 16); the on-wire wrap is handled by the same modular &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;&lt;br /&gt;
/ &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators ([[#1.3. SACK payload|Section 1.3]]) FRCP uses for seqnos, which&lt;br /&gt;
remain unambiguous as long as the in-flight byte window stays&lt;br /&gt;
strictly under 2 GiB (the half-range of the signed-int32 difference&lt;br /&gt;
in &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;).  The default per-flow ring is 1 MiB; the&lt;br /&gt;
implementation caps &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; at 128 MiB (&amp;lt;code&amp;gt;FRCT_STREAM_RING_SZ_MAX&amp;lt;/code&amp;gt;),&lt;br /&gt;
well below the 2 GiB half-range bound.  The runtime byte counters&lt;br /&gt;
exposed via FUSE (Filesystem in Userspace) in the Ouroboros&lt;br /&gt;
Resource Information Base (RIB, a virtual-filesystem introspection&lt;br /&gt;
bridge) are platform &amp;lt;code&amp;gt;size_t&amp;lt;/code&amp;gt; and do not wrap on 64-bit hosts.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2. Per-flow state and service modes ==&lt;br /&gt;
&lt;br /&gt;
=== 2.1. Per-flow state ===&lt;br /&gt;
&lt;br /&gt;
Each flow keeps a sender control record and a receiver control&lt;br /&gt;
record:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: oldest unacked &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (cumulative ACK boundary as seen by sender); rcv: next in-order &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; expected&lt;br /&gt;
;&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: peer-advertised right window edge; rcv: locally-advertised right window edge&lt;br /&gt;
;&amp;lt;code&amp;gt;cflags&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-direction feature flags: retransmission (&amp;lt;code&amp;gt;FRCTFRTX&amp;lt;/code&amp;gt;), receiver flow control (&amp;lt;code&amp;gt;FRCTFRESCNTL&amp;lt;/code&amp;gt;), linger-on-close (&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt;); see &amp;lt;code&amp;gt;&amp;amp;lt;ouroboros/fccntl.h&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: next &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; to send; rcv: force-ACK trigger - set on a stale or dup DATA so the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; emits a fresh cumulative ACK&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; counter for standalone ACK-bearing control packets (delayed ACK, SACK, final ACK on dealloc); not bumped on piggybacked ACK riding a DATA packet (which uses the DATA &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;).  Used by wire-dup ACK detection; rcv: incoming-ACK dedup tracker&lt;br /&gt;
;&amp;lt;code&amp;gt;act&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:last activity (used by inactivity / DRF)&lt;br /&gt;
;&amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:inactivity threshold; sender = &amp;lt;code&amp;gt;3*mpl + a + r + 1s&amp;lt;/code&amp;gt;, receiver = &amp;lt;code&amp;gt;2*mpl + a + r + 1s&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;mpl&amp;lt;/code&amp;gt; is the Maximum Packet Lifetime (delta-t terminology; see [[#15. Heritage and adopted techniques|Section 15]]); &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; are the FRCT a-timer and r-timer bounds (see [[#8. Retransmission|Section 8]]).  The asymmetry is load-bearing for pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
The sender holds a per-slot ring &amp;lt;code&amp;gt;snd_slots[RQ_SIZE]&amp;lt;/code&amp;gt; keyed by&lt;br /&gt;
&amp;lt;code&amp;gt;(seqno mod RQ_SIZE)&amp;lt;/code&amp;gt;.  Each slot tracks its retransmit entry (&amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;),&lt;br /&gt;
last-send timestamp, and retransmit flag bits: &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (a&lt;br /&gt;
retransmit is pending or has fired, gates the next RTT sample&lt;br /&gt;
under Karn) and &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (one-shot fast-retransmit staged for&lt;br /&gt;
this loss event).&lt;br /&gt;
&lt;br /&gt;
The receiver holds a parallel reorder ring &amp;lt;code&amp;gt;rcv_slots[RQ_SIZE]&amp;lt;/code&amp;gt;&lt;br /&gt;
(referred to as &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; in prose) holding stashed out-of-order&lt;br /&gt;
packet-buffer indexes; both FRTX and best-effort flows share this&lt;br /&gt;
path.  The invariant &amp;lt;code&amp;gt;rwe - lwe &amp;amp;lt;= RQ_SIZE&amp;lt;/code&amp;gt; holds: on each consume&lt;br /&gt;
the receiver advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; by the consumed count, capping the&lt;br /&gt;
receive window at &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; slots.&lt;br /&gt;
&lt;br /&gt;
A separate fence variable &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; is bumped on every retransmit&lt;br /&gt;
(timer-fire, SACK-driven, fast-rxm, NACK-driven) and on every&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; ([[#4. Sequence-number rotation (DRF)|Section 4]]) to mark the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; range whose RTT samples&lt;br /&gt;
MUST be discarded.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 2.2. Service modes (orthogonal axes) ===&lt;br /&gt;
&lt;br /&gt;
FRCP exposes its wire features as a vector of independent QoS&lt;br /&gt;
axes selected at flow allocation time.  All flows go through the&lt;br /&gt;
same &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive; the &amp;lt;code&amp;gt;qosspec_t&amp;lt;/code&amp;gt; passed&lt;br /&gt;
in determines which protocol machinery engages on the wire.  This&lt;br /&gt;
contrasts with the POSIX BSD socket model where TCP and UDP&lt;br /&gt;
require different socket types (&amp;lt;code&amp;gt;SOCK_STREAM&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;SOCK_DGRAM&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
The axes:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = unordered (no FRCP engagement: raw datagrams, no PCI on the wire, UDP-equivalent at this layer); 1 = message-ordered (FRCP engaged; SDU boundaries preserved across fragmentation); 2 = stream (byte-oriented, no SDU boundaries; FRTX required)&lt;br /&gt;
;&amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = lossless service requested: FRTX retransmit machinery engages ([[#8. Retransmission|Section 8]]); MUST be 0 for &amp;lt;code&amp;gt;service=2&amp;lt;/code&amp;gt;.  Non-zero = best-effort, FRTX off.&lt;br /&gt;
;&amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bit Error Rate tolerance.  0 = error-free service requested: a CRC trailer is appended after the body of DATA packets and verified on receive (added / checked outside the FRCP PCI; see [[#1.1. PCI header|Section 1.1]]).  Non-zero = peer accepts errors; trailer omitted.  SACK control packets carry a CRC32 trailer regardless of &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; gate applies to DATA only.&lt;br /&gt;
;&amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt;&lt;br /&gt;
:Peer-timeout (ms); 0 disables the keepalive timer.  Independent of FRCP engagement.&lt;br /&gt;
&lt;br /&gt;
Encryption is a separate per-flow attribute set at flow setup;&lt;br /&gt;
when enabled it wraps the FRCP packet (PCI + body, plus the CRC&lt;br /&gt;
trailer if any) under AEAD, expanding the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; by &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt;&lt;br /&gt;
octets (nonce / tag).  The CRC trailer is currently kept inside&lt;br /&gt;
the AEAD wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
&lt;br /&gt;
Reachable combinations exported by &amp;lt;code&amp;gt;include/ouroboros/qos.h&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Cube !! &amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; !! Engaged&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw&amp;lt;/code&amp;gt; || 0 || 1 || 1 || Raw passthrough&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw_safe&amp;lt;/code&amp;gt; || 0 || 1 || 0 || Raw + CRC trailer&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; || 1 || 1 || 1 || FRCP, no FRTX, no CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt_safe&amp;lt;/code&amp;gt; || 1 || 1 || 0 || FRCP, no FRTX, CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_msg&amp;lt;/code&amp;gt; || 1 || 0 || 0 || FRCP + FRTX&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_stream&amp;lt;/code&amp;gt; || 2 || 0 || 0 || FRCP + FRTX, stream&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Forced couplings actually enforced by the public API:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;service == SVC_STREAM&amp;lt;/code&amp;gt; (2) requires &amp;lt;code&amp;gt;loss == 0&amp;lt;/code&amp;gt;; &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;flow_accept&amp;lt;/code&amp;gt; reject the pair otherwise with &amp;lt;code&amp;gt;-EINVAL&amp;lt;/code&amp;gt;.&lt;br /&gt;
* FRTX requires FRCP engagement (&amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;); requesting &amp;lt;code&amp;gt;loss = 0&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;service = SVC_RAW&amp;lt;/code&amp;gt; is structurally a no-op because no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; is created.&lt;br /&gt;
* The &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; build flag globally forces &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;.  Note: this flag defaults to ON, so default builds ship with CRC disabled until &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; is set to OFF.&lt;br /&gt;
&lt;br /&gt;
Caveat: the API does NOT force &amp;lt;code&amp;gt;ber = 0&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; has &amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;, which means the PCI&lt;br /&gt;
itself is not CRC-protected on that cube; the HCS ([[#1.1. PCI header|Section 1.1]])&lt;br /&gt;
remains the only integrity check on the header.&lt;br /&gt;
&lt;br /&gt;
The FRCP-no-FRTX regime (&amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;loss &amp;amp;gt; 0&amp;lt;/code&amp;gt;) is meaningful&lt;br /&gt;
and live: sequence numbering, in-order delivery, flow-control&lt;br /&gt;
advertisement, KA, DRF rotation, and SDU fragmentation /&lt;br /&gt;
reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]) all run.  Lost packets are dropped&lt;br /&gt;
rather than retransmitted; a permanently-lost mid-fragment is&lt;br /&gt;
dropped via skip-past-gap once a later SDU is visible in the&lt;br /&gt;
reorder ring.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. Protocol parameters ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Slot ring / rcv window width&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;START_WINDOW&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Initial &amp;lt;code&amp;gt;rwe-lwe&amp;lt;/code&amp;gt; after rotate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MAX(250 us build-tunable, 1&amp;amp;lt;&amp;amp;lt;RXMQ_RES)&amp;lt;/code&amp;gt;; per-flow via &amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;FRCTSRTOMIN&amp;lt;/code&amp;gt;).  Default ~1 ms with &amp;lt;code&amp;gt;RXMQ_RES=20&amp;lt;/code&amp;gt;. || RTO floor; also floored at the retransmit-wheel resolution (~1 ms by default).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt; || 20 || Backoff shift cap&lt;br /&gt;
|-&lt;br /&gt;
| RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MIN(reo_wnd_mult * min_RTT/4, SRTT)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; scales on D-SACK, cap 20 || Reorder window; per RFC 8985 sec. 6.2; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per sec. 6.2 step 4&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; || 300 s (5 min, Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; windowed re-anchor&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_WND_MULT_MAX&amp;lt;/code&amp;gt; || 20 (RFC 8985 sec. 6.2 step 4) || &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_DECAY_PKTS&amp;lt;/code&amp;gt; || 16 (RFC 8985 sec. 6.2 step 4 / &amp;lt;code&amp;gt;RACK.reo_wnd_persist&amp;lt;/code&amp;gt;) || Fresh-ACK&#039;d seq count per halving&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_DSACK_LAG&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || D-SACK sanity cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTT_QUARANTINE&amp;lt;/code&amp;gt; || 32 (&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; steps) || NewReno gate pad&lt;br /&gt;
|-&lt;br /&gt;
| SACK rate-limit || &amp;lt;code&amp;gt;SACK_MIN_GAP_NS&amp;lt;/code&amp;gt; (250 us, fixed) || Min SACK gap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; || 2048 (wire cap; per-flow capped at &amp;lt;code&amp;gt;(frag_mtu-PCI-4)/8&amp;lt;/code&amp;gt;) || Per-SACK block cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; || 32 || Per-pass staged retransmit cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; || 3 (RFC 8985 default) || Hybrid fast-rxm trigger ([[#8. Retransmission|Section 8]])&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MDEV_MUL&amp;lt;/code&amp;gt; || 2 (build-tunable via &amp;lt;code&amp;gt;FRCT_RTO_MDEV_MULTIPLIER&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; shift in &amp;lt;code&amp;gt;RTO = srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| RTTP nonce || 16 octets || Echoed verbatim&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; || 8 || In-flight probes&lt;br /&gt;
|-&lt;br /&gt;
| RTT clamp || &amp;lt;code&amp;gt;16 * srtt&amp;lt;/code&amp;gt; || Probe-sample upper bound (ACK-derived RTT samples gated by Karn / recovery only)&lt;br /&gt;
|-&lt;br /&gt;
| Cold-probe cadence || 100 ms (rx-driven; see [[#12. RTT estimation|Section 12]]) || Pre-&amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; RTTP rate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt; || 100 ms || RDVS emit cadence&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; || 1 s || RDVS give-up&lt;br /&gt;
|-&lt;br /&gt;
| Delayed-ACK fire || &amp;lt;code&amp;gt;2 * TICTIME&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt; = FRCT tick granularity, default 5 ms; &amp;lt;code&amp;gt;2*TICTIME = 10 ms&amp;lt;/code&amp;gt; by default) || Fired after the first in-order DATA arrival; tick is build-tunable&lt;br /&gt;
|-&lt;br /&gt;
| NACK send cooldown || &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; when an &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; sample exists, else 100 ms || Pre-DRF NACK rate-limit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_SDU&amp;lt;/code&amp;gt; || 1 MiB || Max reassembled SDU; configurable per flow&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The per-flow fragment Maximum Transmission Unit (MTU) is computed&lt;br /&gt;
at flow setup from the lower IPCP&#039;s mtu minus encryption&lt;br /&gt;
&amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; and CRC trailer; there is no FRCT-level default or&lt;br /&gt;
environment-variable override.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 4. Sequence-number rotation (DRF) ==&lt;br /&gt;
&lt;br /&gt;
The DRF (Data Run Flag) bit on an outbound packet means &amp;quot;this is&lt;br /&gt;
the start of a fresh data run&amp;quot; and is set whenever the sender has&lt;br /&gt;
nothing in flight (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Independently of that, if the sender has been idle longer than&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.inact&amp;lt;/code&amp;gt; AND the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;),&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; rolls a random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; before the send and&lt;br /&gt;
resets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    snd_cr.seqno  = random()&lt;br /&gt;
    snd_cr.lwe    = snd_cr.seqno&lt;br /&gt;
    snd_cr.rwe    = snd_cr.seqno + START_WINDOW&lt;br /&gt;
    rtt_lwe       = snd_cr.seqno&lt;br /&gt;
    in_recovery   = false   (recovery state, see Section 8)&lt;br /&gt;
    recovery_high = snd_cr.seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The receiver, on observing rcv-side inactivity&lt;br /&gt;
(&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), requires a DRF on the next&lt;br /&gt;
DATA packet; otherwise it replies with a rate-limited NACK (see&lt;br /&gt;
below).  Non-DATA control packets pass through without the DRF&lt;br /&gt;
requirement.  On DRF the receiver releases the &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots and&lt;br /&gt;
rebases&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    rcv_cr.lwe   = seqno&lt;br /&gt;
    rcv_cr.rwe   = seqno + RQ_SIZE&lt;br /&gt;
    rcv_cr.seqno = seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the inactive packet has DATA but no DRF, a rate-limited NACK is&lt;br /&gt;
fired back to the sender (cooldown per [[#3. Protocol parameters|Section 3]]); non-DATA stale&lt;br /&gt;
arrivals fall through to normal processing (no NACK, no drop).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 5. Send path ==&lt;br /&gt;
&lt;br /&gt;
# If the SDU exceeds &amp;lt;code&amp;gt;(frag_mtu - data_hdr_len)&amp;lt;/code&amp;gt;, the caller (&amp;lt;code&amp;gt;dev.c&amp;lt;/code&amp;gt;) fans it out into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - data_hdr_len))&amp;lt;/code&amp;gt; fragments, each emitted via &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; as its own DATA packet with a per-fragment role ([[#7.2. Fragmentation and reassembly|Section 7.2]]); both FRTX and best-effort flows fragment.  Raw flows (no FRCP engagement, &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) carry no PCI and return &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; for any SDU larger than one packet at the layer below.  An SDU that fits in a single packet is sent as SOLE.  &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; reserves PCI head room; sets DATA, plus DRF when the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
# &amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; if past sender inactivity and the pipe is empty ([[#4. Sequence-number rotation (DRF)|Section 4]]).&lt;br /&gt;
# Advertise FC (&amp;lt;code&amp;gt;pci.window = frcti_advert_rwe(frcti)&amp;lt;/code&amp;gt;, i.e. &amp;lt;code&amp;gt;rcv_cr.rwe&amp;lt;/code&amp;gt; clamped to &amp;lt;code&amp;gt;rcv_cr.lwe + ring_seq_cap&amp;lt;/code&amp;gt; in stream mode) when the receiver side is recent: &amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt; rcv_cr.inact&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Reliable mode (FRTX): leave &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; where it is; reset the slot at &amp;lt;code&amp;gt;RQ_SLOT(seqno)&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;snd_slots[p].time = now&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_slots[p].flags = 0&amp;lt;/code&amp;gt;); queue an &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; (saves a packet copy, arms a wheel timer at &amp;lt;code&amp;gt;now + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;).  Piggyback ACK (&amp;lt;code&amp;gt;pci.ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) while the a-timer for the most recent received DATA packet has not yet expired (&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt;= t_a&amp;lt;/code&amp;gt;); on piggyback, set &amp;lt;code&amp;gt;rcv_cr.seqno = rcv_cr.lwe&amp;lt;/code&amp;gt; so the next delayed-ACK fire is suppressed.  See [[#8. Retransmission|Section 8]] for &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; semantics.&lt;br /&gt;
# Best-effort mode (no FRTX): advance &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; immediately (&amp;lt;code&amp;gt;snd_cr.lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_cr.rwe = snd_cr.lwe + RQ_SIZE&amp;lt;/code&amp;gt;); no retransmit state.  No send-side RTT probe is armed in this mode (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt; requires an in-flight &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, which best-effort never has); the rx-driven cold seeder in &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; is the only probe path.&lt;br /&gt;
# In reliable mode, optionally arm an RTT probe ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 6. Receive path ==&lt;br /&gt;
&lt;br /&gt;
=== 6.1. Early-exit dispatch ===&lt;br /&gt;
&lt;br /&gt;
Keepalive (KA), RTT probe (RTTP), pre-DRF NACK, and rendezvous&lt;br /&gt;
(RDVS) packets short-circuit out of &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; before the locked&lt;br /&gt;
main path; each handler takes its own lock internally.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
      incoming packet&lt;br /&gt;
            |&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | KA?     |---yes--&amp;gt; ka_rcv  ; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RTTP?   |---yes--&amp;gt; rttp_rcv; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | NACK?   |---yes--&amp;gt; nack_rcv; return  (see Section 9)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RDVS?   |---yes--&amp;gt; rdv_rcv ; return  (reply bare FC, ackno=0)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       acquire wrlock; enter locked main path&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt;&lt;br /&gt;
:refresh &amp;lt;code&amp;gt;t_ka_rcv&amp;lt;/code&amp;gt;, honour piggybacked ACK.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt;&lt;br /&gt;
:probe (echo back nonce) or echo (verify nonce, sample RTT).&lt;br /&gt;
;&amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:pre-DRF, sender-side handler.  See [[#9. Pre-DRF NACK|Section 9]].&lt;br /&gt;
;&amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt;&lt;br /&gt;
:reply with a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;); &amp;lt;code&amp;gt;rdlock&amp;lt;/code&amp;gt; only.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 6.2. Locked main path ===&lt;br /&gt;
&lt;br /&gt;
Steps below run with the per-flow &amp;lt;code&amp;gt;frcti.lock&amp;lt;/code&amp;gt; held for writing&lt;br /&gt;
(&amp;lt;code&amp;gt;pthread_rwlock_wrlock&amp;lt;/code&amp;gt;) unless noted.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;&lt;br /&gt;
:Only meaningful when the receive side is stale.  On DRF (Data Run Flag): release &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots, rebase &amp;lt;code&amp;gt;rcv_cr&amp;lt;/code&amp;gt;, continue.  On stale DATA without DRF: fire a pre-DRF NACK if cooldown allows ([[#9. Pre-DRF NACK|Section 9]]), then discard the packet; on cooldown, drop without sending a NACK (a pending cumulative ACK from &amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; may still go out).  Non-DATA, non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; pure-DRF stale arrivals fall through after the DRF rebase branch.&lt;br /&gt;
&lt;br /&gt;
;DATA-only act refresh&lt;br /&gt;
:Refresh &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; only when &amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt; is set, so that non-DATA packets never block the next DRF rebase.&lt;br /&gt;
&lt;br /&gt;
;Wire-dup gate&lt;br /&gt;
:Before flag-driven dispatch, drop wire-duplicate ACKs and wire-duplicate DATA (&amp;lt;code&amp;gt;is_dup_ack&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt;).  The DATA check is bypassed for &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;-bearing arrivals so the piggybacked ACK / SACK / FC carried on a retransmitted DATA at an already-ACK&#039;d &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is still applied; the stale-in-window branch below then drops the packet.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Drop ACKs whose &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; falls outside &amp;lt;code&amp;gt;(snd_cr.lwe, snd_cr.seqno]&amp;lt;/code&amp;gt;.  If &amp;lt;code&amp;gt;ackno == snd_cr.lwe&amp;lt;/code&amp;gt; (non-advancing cumulative ACK), drive RACK fast-retransmit consideration ([[#8. Retransmission|Section 8]]).  Otherwise advance &amp;lt;code&amp;gt;snd_cr.lwe = ackno&amp;lt;/code&amp;gt;, collapse &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; to 0 (Karn-gated by &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; on the just-acknowledged slot, the old head-of-line), reset &amp;lt;code&amp;gt;dup_thresh&amp;lt;/code&amp;gt; to 0, update &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the send-time of the slot at &amp;lt;code&amp;gt;ackno-1&amp;lt;/code&amp;gt; (consumed by RACK and SACK below), decay &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per RFC 8985 sec. 6.2 step 4, exit NewReno-careful recovery (see [[#8. Retransmission|Section 8]]) on &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt;, and feed an RTT sample if eligible ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Walk the block list.  For each block (a present range above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;) NULL out &amp;lt;code&amp;gt;snd_slots[k].rxm&amp;lt;/code&amp;gt;, clear the slot&#039;s per-send flags, and advance &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the latest send-time covered (the Forward Acknowledgement / fack equivalent, Mathis &amp;amp;amp; Mahdavi 1996); the first block whose start clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; skips this fack update so that a head-of-line clamp does not falsely advance fack.  For un-SACKed gaps below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt;, stage a retransmit per slot that is (1) still owned (&amp;lt;code&amp;gt;rxm != NULL&amp;lt;/code&amp;gt;), (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (the RFC 8985 sec. 6.2 hybrid trigger).  Mark the slot &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; and NULL the &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; at stage time.  Capped at &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; staged retransmits per receive pass; what&#039;s left rides the next SACK.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bump &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; (clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt;, never shrinks) and mark window open.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bounds-check &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; against window.  On stale-dup (&amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;), set &amp;lt;code&amp;gt;rcv_cr.seqno = seqno&amp;lt;/code&amp;gt; to force a fresh ACK on the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt;, then drop.  On accept: both FRTX and best-effort stash the packet-buffer index into &amp;lt;code&amp;gt;rq[seqno mod RQ_SIZE]&amp;lt;/code&amp;gt;.  Fragments stash unchanged - the role bits are inspected only at consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]).  On out-of-order arrival, build a SACK reply if not rate-limited (per [[#3. Protocol parameters|Section 3]]) and not deduplicated against the previous &amp;lt;code&amp;gt;(rcv_cr.lwe, n_blocks)&amp;lt;/code&amp;gt; pair; D-SACK reports always bypass the dedup.  If both rate-limit and dedup suppress the reply, neither SACK nor delayed-ACK fires (the sender picks up the gap on its next ACK).  On in-order arrival, arm the delayed-ACK timer.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; exit&lt;br /&gt;
:Releases the per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;), then calls &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; synchronously after the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; release to surface any pending cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 7. Read path and reassembly ==&lt;br /&gt;
&lt;br /&gt;
=== 7.1. Read path ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns a full reassembled SDU (Service Data Unit) via&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; on every FRCP SDU-mode flow (FRTX or best-effort);&lt;br /&gt;
stream-mode is covered in [[#16. Stream-mode flows|Section 16]].  An incomplete head-of-line&lt;br /&gt;
(HoL) run yields &amp;lt;code&amp;gt;-EAGAIN&amp;lt;/code&amp;gt;; an oversized run yields &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; (the&lt;br /&gt;
run is dropped so the flow does not stall).  On best-effort flows,&lt;br /&gt;
a permanently-lost mid-fragment is dropped as soon as a later&lt;br /&gt;
complete SDU becomes visible in the ring ([[#7.2. Fragmentation and reassembly|Section 7.2]] skip-past-&lt;br /&gt;
gap).&lt;br /&gt;
&lt;br /&gt;
Raw flows carry no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt;, so &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns the next pending&lt;br /&gt;
packet-buffer index directly, with no role-bit inspection.  (Raw&lt;br /&gt;
service is selected via &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt; at flow allocation,&lt;br /&gt;
which suppresses &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; creation.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_pdu_ready&amp;lt;/code&amp;gt; is the no-advance peek used by &amp;lt;code&amp;gt;fevent&amp;lt;/code&amp;gt; (the&lt;br /&gt;
Ouroboros flow-event multiplexer, the &amp;lt;code&amp;gt;poll(2)&amp;lt;/code&amp;gt;-equivalent on&lt;br /&gt;
flows).  It returns ready only when the head-of-line run is&lt;br /&gt;
complete and the lead packet (a Protocol Data Unit, here one FRCP&lt;br /&gt;
packet) is present at &amp;lt;code&amp;gt;rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt;; any other state&lt;br /&gt;
(including the best-effort skip-past-gap case) returns not ready,&lt;br /&gt;
and &amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; is left to drop the broken prefix and re-&lt;br /&gt;
inspect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 7.2. Fragmentation and reassembly ===&lt;br /&gt;
&lt;br /&gt;
Send side (&amp;lt;code&amp;gt;flow_write_frag&amp;lt;/code&amp;gt;).  An SDU larger than&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - PCI)&amp;lt;/code&amp;gt; is split into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - PCI))&amp;lt;/code&amp;gt;&lt;br /&gt;
fragments; each fragment is its own FRCP packet with its own&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a per-fragment role flag pair ([[#1.2. Flag bits|Section 1.2]]).  Roles are&lt;br /&gt;
assigned at emit time:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! i !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=0&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=n-1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| else || &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A mid-loop allocation or transmit failure may yield a partial&lt;br /&gt;
write: the call returns the bytes already enqueued (&amp;lt;code&amp;gt;off &amp;amp;gt; 0&amp;lt;/code&amp;gt;) or&lt;br /&gt;
the underlying error (&amp;lt;code&amp;gt;off == 0&amp;lt;/code&amp;gt;).  Best-effort flows fragment&lt;br /&gt;
identically; on the receiver, a partial run with a permanently-&lt;br /&gt;
lost fragment is dropped when a later complete SDU is visible in&lt;br /&gt;
the ring (see skip-past-gap below).  Raw flows carry no PCI and&lt;br /&gt;
refuse anything larger than the layer&#039;s user MTU (&amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Wire-level recovery is fragment-agnostic on FRTX flows: each&lt;br /&gt;
fragment&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; flows through SACK / RACK / RTO / NACK exactly&lt;br /&gt;
as for a SOLE DATA packet, and reassembly does not re-enter the&lt;br /&gt;
loss-detection path.  Best-effort flows run the same &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
machinery (DRF, FC, ACK piggyback, pre-DRF NACK emit) but queue&lt;br /&gt;
no &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; state at the sender, so a lost MID is unrecoverable;&lt;br /&gt;
skip-past-gap handles it (below).&lt;br /&gt;
&lt;br /&gt;
Receive side.  Fragments stash into &amp;lt;code&amp;gt;rq[seqno]&amp;lt;/code&amp;gt; unchanged; role bits&lt;br /&gt;
are read only at consume time.  &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;, called from&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt;, walks the ring starting at the oldest still-&lt;br /&gt;
undelivered &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;base = rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt; (equal to &amp;lt;code&amp;gt;rcv_cr.lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
only when no partial run is in progress; during a partial run &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
has already advanced past &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;).  It produces one of three&lt;br /&gt;
outcomes:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Outcome !! Cause&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELIVER (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]=SOLE&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;), or &amp;lt;code&amp;gt;rq[base]=FIRST&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; follows in slots &amp;lt;code&amp;gt;[base+1..base+n-1]&amp;lt;/code&amp;gt; with all intermediate roles in &amp;lt;code&amp;gt;{MID,FIRST,LAST}&amp;lt;/code&amp;gt; contiguous.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DROP (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; without a preceding &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;); a &amp;lt;code&amp;gt;FIRST..[non-LAST]..new-FIRST&amp;lt;/code&amp;gt; or new-&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; mid-run (drop the broken prefix with &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; = run length minus 1, so the new &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; stays); or, on best-effort flows, a gap at &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; later in the ring (drop up to the new run start).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; absent or &amp;lt;code&amp;gt;FIRST..[non-LAST]&amp;lt;/code&amp;gt; with no later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; in the ring (FRTX waits for retx; best-effort waits for arrival).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DELIVER&amp;lt;/code&amp;gt; triggers &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;: a scatter-gather &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; of the &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;&lt;br /&gt;
consecutive fragments at &amp;lt;code&amp;gt;rq[base..base+n-1]&amp;lt;/code&amp;gt; directly into the&lt;br /&gt;
caller&#039;s buffer; each per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;) is&lt;br /&gt;
released and &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; advances by &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; was already advanced&lt;br /&gt;
incrementally as each contiguous fragment arrived; &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;&lt;br /&gt;
only restores the fixed-width invariant &amp;lt;code&amp;gt;rwe == lwe + RQ_SIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
No intermediate reassembly buffer is allocated.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; past the broken prefix (releasing the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;s)&lt;br /&gt;
and pulls &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; up to the new trailing edge if needed; the next&lt;br /&gt;
consume retries from the new &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;.  Oversize or arithmetically&lt;br /&gt;
overflowing delivery (sum of fragment lengths &amp;amp;gt; &amp;lt;code&amp;gt;max_rcv_sdu&amp;lt;/code&amp;gt;, sum&lt;br /&gt;
&amp;amp;gt; caller&#039;s buffer, or running-sum overflow) also drops the run&lt;br /&gt;
with &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skip-past-gap (best-effort only).  On FRTX, a gap in the run means&lt;br /&gt;
&amp;quot;waiting for retransmit&amp;quot; and &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;.&lt;br /&gt;
On best-effort flows the gap is permanent, so &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;&lt;br /&gt;
scans forward in the ring for the next &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;; if one is&lt;br /&gt;
visible within &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;, it returns &amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; for the broken prefix and&lt;br /&gt;
the consume loop retries at the new &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;.  Memory hold is bounded&lt;br /&gt;
by &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;; the partial releases on the next consume call once a&lt;br /&gt;
later complete run exists.  Voice-like flows (one &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; per SDU)&lt;br /&gt;
see no extra wait: any later &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; makes the prior gap droppable&lt;br /&gt;
immediately.&lt;br /&gt;
&lt;br /&gt;
The choice to defer reassembly to consume time keeps the receive&lt;br /&gt;
path zero-copy: fragments stay in the shared-memory ring until&lt;br /&gt;
the application pulls, and the SDU lands directly in the caller&#039;s&lt;br /&gt;
buffer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 8. Retransmission ==&lt;br /&gt;
&lt;br /&gt;
FRCP is bounded by two delta-t-derived timers (Watson 1981, see&lt;br /&gt;
[[#15. Heritage and adopted techniques|Section 15]]):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer): upper bound on ACK delay.  An ACK for a received DATA packet MUST be emitted within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; of receipt; an attempt to send an ACK after the a-timer has expired is suppressed (the sender&#039;s RTO is already in motion).&lt;br /&gt;
* &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer): upper bound on retransmission.  A given DATA packet MUST NOT be retransmitted after &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; has elapsed since its first send (&amp;lt;code&amp;gt;t0&amp;lt;/code&amp;gt;); when the bound is hit, the flow is declared down (raising the Ouroboros asynchronous flow condition &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt;, which marks the flow dead to both endpoints) rather than retransmitted again.&lt;br /&gt;
&lt;br /&gt;
Each in-flight FRTX &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; owns one &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt;, armed in a hashed&lt;br /&gt;
timing wheel; the wheel deadline is the slot&#039;s next eligible&lt;br /&gt;
retransmit time.&lt;br /&gt;
&lt;br /&gt;
;RTO timer&lt;br /&gt;
:On fire (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;), re-emit with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, mark &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (Karn-suppress next ACK&#039;s RTT sample), and (for the head-of-line (HoL) slot only) bump &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; up to &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt;.  Wheel deadline is &amp;lt;code&amp;gt;t_send + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;.  Re-armed unless consumed.  The RTO timer also clears &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (re-arming fast-retransmit eligibility), resets &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; to 1 on a HoL fire (RFC 8985 sec. 6.2 step 4 reset clause), and marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; if its &amp;lt;code&amp;gt;frct_tx&amp;lt;/code&amp;gt; call fails.&lt;br /&gt;
&lt;br /&gt;
;r-timer guard&lt;br /&gt;
:Before any retransmit attempt, check &amp;lt;code&amp;gt;(now - t0)&amp;lt;/code&amp;gt; against &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;.  If exceeded, the slot is no longer eligible for retransmit.  Only the RTO timer (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;) treats r-timer expiry as terminal: it marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; (peer unreachable).  Fast-retransmit, SACK-driven retransmit, and NACK-driven head-of-line re-emit silently skip aged-out slots and defer the flow-down decision to the next RTO fire.&lt;br /&gt;
&lt;br /&gt;
;Fast retransmit (hybrid trigger, RFC 8985 sec. 6.2)&lt;br /&gt;
:On a non-advancing cumulative ACK with the scoreboard advanced, fire one fast retransmit when EITHER (a) the head-of-line slot&#039;s latest send is older than the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; ([[#3. Protocol parameters|Section 3]]) and not yet aged out, OR (b) the SACK &amp;lt;code&amp;gt;dup-thresh&amp;lt;/code&amp;gt; count above &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; reaches &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; (= 3, RFC 8985 sec. 6.2 step 4).  Fires at most once per non-advancing cumulative-ACK value, gated by &amp;lt;code&amp;gt;rack_fired_lwe&amp;lt;/code&amp;gt; (the &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; at which fast-retransmit last fired).  Set &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; on the slot (one-shot per-slot gate) and enter NewReno-style careful recovery (see NewReno below in this section).&lt;br /&gt;
:The RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; uses the RFC 8985 sec. 6.2 form &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor.  Before the first RTT sample seeds &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; falls back to &amp;lt;code&amp;gt;MIN(reo_wnd_mult * SRTT / 4, SRTT)&amp;lt;/code&amp;gt;, still floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt; (consistent with the windowed-minimum fallback described in [[#12. RTT estimation|Section 12]]).  &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt; is a windowed minimum over the last &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; = 5 min of RTT samples (matches the Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt; default) so a route change to a longer path eventually re-anchors the reorder window without relying on &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; growth alone.&lt;br /&gt;
&lt;br /&gt;
;SACK-driven retransmit&lt;br /&gt;
:For each gap below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt; whose slot is (1) still owned, (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (same hybrid as fast-retransmit, see [[#6.2. Locked main path|Section 6.2]]), re-emit.  Each SACK-driven retransmit re-arms a fresh &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; so a lost retransmit can still be recovered by its own RTO timer.&lt;br /&gt;
&lt;br /&gt;
;NewReno&lt;br /&gt;
:On entry, &amp;lt;code&amp;gt;recovery_high = snd_cr.seqno + RTT_QUARANTINE&amp;lt;/code&amp;gt;.  Exit when &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt; (the latter means everything sent has been acknowledged).  &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; also clears recovery.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 9. Pre-DRF NACK ==&lt;br /&gt;
&lt;br /&gt;
The two sides have different inactivity thresholds (&amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), so a receiver can detect &amp;quot;stale data run&amp;quot; before the sender&#039;s own DRF logic kicks in.  NACK is the receiver-driven nudge that asks the sender to re-transmit the head of the run.&lt;br /&gt;
&lt;br /&gt;
;Send (&amp;lt;code&amp;gt;frcti_nack_snd&amp;lt;/code&amp;gt;, called by &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;FRCT_INACT_NEED_NACK&amp;lt;/code&amp;gt;)&lt;br /&gt;
:When an incoming DATA packet has no DRF and rcv-side activity is older than &amp;lt;code&amp;gt;rcv_cr.inact&amp;lt;/code&amp;gt;, the receiver emits a bare packet with &amp;lt;code&amp;gt;flags = FRCT_NACK&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;seqno = arrival_seqno - 1&amp;lt;/code&amp;gt; (informational only, not consulted by the receive handler).  The cooldown in [[#3. Protocol parameters|Section 3]] rate-limits the burst.  Non-DATA non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; non-DATA DRF still rebases via the DRF branch.&lt;br /&gt;
&lt;br /&gt;
;Receive (&amp;lt;code&amp;gt;frcti_nack_rcv&amp;lt;/code&amp;gt;)&lt;br /&gt;
:Dispatched in the early-exit branch ([[#6.1. Early-exit dispatch|Section 6.1]]), before &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;.  The sender copies the head-of-line (HoL) &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; packet, marks the slot &amp;lt;code&amp;gt;SND_RTX | SND_FAST_RXM&amp;lt;/code&amp;gt; (Karn-suppress next ACK, one-shot fast-rxm gate), sets &amp;lt;code&amp;gt;rtt_lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, and re-emits via &amp;lt;code&amp;gt;fast_rxm_send&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt; and a refreshed &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;.  The original &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed - the NACK emit is additive to the normal retransmit machinery, not a replacement.  No-op if nothing is in flight, the HoL slot has aged past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, or the HoL &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; pointer has been cleared by SACK or RACK.&lt;br /&gt;
&lt;br /&gt;
NACK has exactly one role: lost first-of-run (DRF) packet recovery. Until the DRF packet arrives, the receiver cannot rebase its window, so any subsequent in-flight packets look stale to the receiver. The NACK fires the moment a stale receiver sees DATA without DRF, telling the sender to re-emit the head-of-line (DRF) packet at NACK-cooldown latency rather than waiting for the initial RTO (which is the configured default until &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded by the first probe round-trip).  Mid-stream loss is NOT NACK-driven; it is recovered by the sender&#039;s RTO, fast retransmit, and SACK-driven retransmit paths ([[#8. Retransmission|Section 8]]) only.&lt;br /&gt;
&lt;br /&gt;
The existing &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed on a NACK re-emit, so the RTO path remains the eventual fallback.&lt;br /&gt;
&lt;br /&gt;
== 10. Cumulative + selective ACK ==&lt;br /&gt;
&lt;br /&gt;
Cumulative ACK is &amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;.  On out-of-order arrival the&lt;br /&gt;
receiver also emits a SACK packet ([[#1.3. SACK payload|Section 1.3]]) whose payload lists&lt;br /&gt;
&#039;&#039;present&#039;&#039; blocks above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; (analogous to TCP SACK / QUIC ACK&lt;br /&gt;
ranges).  SACKs are rate-limited per [[#3. Protocol parameters|Section 3]] and suppressed when&lt;br /&gt;
neither &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; nor block count has changed since the last SACK.&lt;br /&gt;
&lt;br /&gt;
D-SACK reports (RFC 2883) are emitted in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; of an&lt;br /&gt;
otherwise normal SACK frame (see [[#1.3. SACK payload|Section 1.3]] for the encoding).&lt;br /&gt;
Two receiver triggers arm a pending D-SACK report (single-slot,&lt;br /&gt;
latest-wins):&lt;br /&gt;
&lt;br /&gt;
* DATA arrival with &amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;, both wire-dup (no RXM, &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt; path) and retransmit (RXM, post-FC branch) (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
* &amp;lt;code&amp;gt;rq_accept&amp;lt;/code&amp;gt; conflict, slot already occupied in &amp;lt;code&amp;gt;[lwe, rwe)&amp;lt;/code&amp;gt; (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
&lt;br /&gt;
When a D-SACK is pending and the standard scoreboard SACK would be&lt;br /&gt;
suppressed by dedup or rate-limit, the report is emitted as a&lt;br /&gt;
stand-alone SACK frame through the normal &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; path; when a&lt;br /&gt;
D-SACK report is pending the path bypasses dedup and the &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;&lt;br /&gt;
rate-limit, but the a-timer suppression on rcv inactivity still&lt;br /&gt;
applies.&lt;br /&gt;
&lt;br /&gt;
Bare ACKs are deferred via a per-flow delayed-ACK timer (one in&lt;br /&gt;
flight at a time, atomic test-and-set dedup; fires per [[#3. Protocol parameters|Section 3]]&lt;br /&gt;
after the first in-order arrival).  Suppressed if (1) no new&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, (2) rcv side is inactive (older than &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt;), or (3) the&lt;br /&gt;
sender just sent within &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;.  A pending D-SACK ride-through&lt;br /&gt;
bypasses (1) and (3); the a-timer gate (2) is unconditional.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 11. Flow control ==&lt;br /&gt;
&lt;br /&gt;
The receiver advertises &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; in every FC field.  The sender treats&lt;br /&gt;
its &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; as the absolute right edge: when&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt;= snd_cr.rwe&amp;lt;/code&amp;gt; the window is closed and &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt;&lt;br /&gt;
yields.  While closed, the sender periodically emits RDVS&lt;br /&gt;
(rendezvous) packets (cadence &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt;); the receiver replies with&lt;br /&gt;
a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;) that reopens the window.  Once the&lt;br /&gt;
window has been closed for longer than &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; the sender stops&lt;br /&gt;
emitting RDVS but does not tear the flow down - the writer keeps&lt;br /&gt;
blocking until either a peer-driven FC arrives or the KA&lt;br /&gt;
(keepalive) / r-timer marks the flow.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt; on receipt and MUST NOT shrink:&lt;br /&gt;
a backward &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is silently clamped to the current &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt;;&lt;br /&gt;
the FC packet still reopens the window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 12. RTT estimation ==&lt;br /&gt;
&lt;br /&gt;
Active RTTP probes ([[#1.4. RTTP payload|Section 1.4]]) carry a 32-bit &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt; (0&lt;br /&gt;
reserved) and a 16-byte random nonce echoed verbatim - defends&lt;br /&gt;
against spoofed replies.  A ring of &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; in-flight probes is&lt;br /&gt;
kept; an echo whose &amp;lt;code&amp;gt;(id, nonce)&amp;lt;/code&amp;gt; doesn&#039;t match the ring slot is&lt;br /&gt;
dropped.  A single RTTP sample is clamped to &amp;lt;code&amp;gt;RTT_CLAMP_MUL * srtt&amp;lt;/code&amp;gt;&lt;br /&gt;
(compile-time &amp;lt;code&amp;gt;RTT_CLAMP_MUL = 16&amp;lt;/code&amp;gt;) once &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded; the first&lt;br /&gt;
cold-probe sample feeds &amp;lt;code&amp;gt;rtt_update&amp;lt;/code&amp;gt; raw.&lt;br /&gt;
&lt;br /&gt;
Probe arming gates:&lt;br /&gt;
&lt;br /&gt;
;Cold (no &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; yet)&lt;br /&gt;
:the receive path arms at most one probe per 100 ms via &amp;lt;code&amp;gt;frcti_rcv_probe&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;PROBE_DUE_COLD&amp;lt;/code&amp;gt;); arming requires an incoming packet.  Active send-path arming bails while &amp;lt;code&amp;gt;srtt == 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
;Warm (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt;, called from &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt;)&lt;br /&gt;
:outstanding data (&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt; snd_cr.lwe&amp;lt;/code&amp;gt;), AND at least &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_rcv_rtt&amp;lt;/code&amp;gt; (last RTT receive of any kind), AND at least &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_snd_probe&amp;lt;/code&amp;gt; (last probe emit).&lt;br /&gt;
&lt;br /&gt;
Sample feeds either Linux&#039;s asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_LINUX_RTT_ESTIMATOR&amp;lt;/code&amp;gt;, default ON) or RFC 6298 symmetric EWMA&lt;br /&gt;
(compile option).  &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is floored at 10 ms when seeded from a&lt;br /&gt;
hint, at 1 us after every update (including the first seeding&lt;br /&gt;
sample); &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; floored at 100 ns.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;RTO = max(rto_min, 2 * srtt, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(the &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; floor is an FRCT addition not in RFC 6298).&lt;br /&gt;
Effective wheel deadline capped per [[#3. Protocol parameters|Section 3]].&lt;br /&gt;
&lt;br /&gt;
ACK-derived samples (&amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;rtt_sample_eligible&amp;lt;/code&amp;gt;), beyond&lt;br /&gt;
the cum-ACK advance gate in &amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno &amp;amp;gt; lwe&amp;lt;/code&amp;gt; and&lt;br /&gt;
&amp;lt;code&amp;gt;ackno &amp;amp;lt;= seqno&amp;lt;/code&amp;gt;), require all of: not in recovery; ACK packet does&lt;br /&gt;
not carry &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;; HoL slot&#039;s &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; bit clear; slot&#039;s &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;&lt;br /&gt;
pointer non-NULL (not SACK-consumed); &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; not below the &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
fence; &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; already seeded by an RTTP probe.  There is no ACK-only&lt;br /&gt;
seeding.&lt;br /&gt;
&lt;br /&gt;
Every eligible sample also feeds &amp;lt;code&amp;gt;RACK.min_RTT&amp;lt;/code&amp;gt; (RFC 8985 sec. 6.2)&lt;br /&gt;
via a windowed minimum: replace whenever the sample is strictly&lt;br /&gt;
smaller OR more than &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; (5 min, matches Linux&lt;br /&gt;
&amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) has elapsed since the current min was set.  The&lt;br /&gt;
downward branch is immediate (faster path picked up at once); the&lt;br /&gt;
upward branch is gated on the window (a transient queue burst does&lt;br /&gt;
not poison the estimate, but a sustained route change to a longer&lt;br /&gt;
path re-anchors &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; after at most one window).  Seeded from&lt;br /&gt;
&amp;lt;code&amp;gt;rtt_hint&amp;lt;/code&amp;gt; at &amp;lt;code&amp;gt;rtt_init&amp;lt;/code&amp;gt;; 0 acts as the unset sentinel and the base&lt;br /&gt;
in &amp;lt;code&amp;gt;rack_reorder_window&amp;lt;/code&amp;gt; falls back from &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt; (so&lt;br /&gt;
&amp;lt;code&amp;gt;R = mult * SRTT/4&amp;lt;/code&amp;gt;, capped at &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;, floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt;)&lt;br /&gt;
until the first sample.  See [[#6.2. Locked main path|Section 6.2]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 13. Liveness (keepalive) ==&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;code&amp;gt;qs.timeout &amp;amp;gt; 0&amp;lt;/code&amp;gt; a per-flow KA (keepalive) timer is armed.&lt;br /&gt;
Arming uses &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; for the deadline computation:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;deadline = min(snd_act + qs.timeout/4, rcv_act + qs.timeout)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(clamped to &amp;lt;code&amp;gt;now + qs.timeout/4&amp;lt;/code&amp;gt; if already past).  The timer fires&lt;br /&gt;
either on sender idleness (to send a KA) or on receiver idleness&lt;br /&gt;
(to declare the peer dead).  On fire (&amp;lt;code&amp;gt;ka_snd&amp;lt;/code&amp;gt;) the peer-dead test&lt;br /&gt;
uses &amp;lt;code&amp;gt;max(rcv_cr.act, t_ka_rcv)&amp;lt;/code&amp;gt; so a recent KA reply counts even&lt;br /&gt;
when no DATA has arrived:&lt;br /&gt;
&lt;br /&gt;
* If &amp;lt;code&amp;gt;now - max(rcv_cr.act, t_ka_rcv) &amp;amp;gt; qs.timeout&amp;lt;/code&amp;gt;, mark the flow &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; and notify the per-process flow-event set (&amp;lt;code&amp;gt;proc.fqset&amp;lt;/code&amp;gt;) with &amp;lt;code&amp;gt;FLOW_PEER&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Else if &amp;lt;code&amp;gt;snd_idle &amp;amp;gt; qs.timeout/4&amp;lt;/code&amp;gt;, emit a bare &amp;lt;code&amp;gt;KA | ACK&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) and re-arm.&lt;br /&gt;
* Else just re-arm.&lt;br /&gt;
&lt;br /&gt;
Note: &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; are the receive and transmit shared-memory&lt;br /&gt;
ring buffers.  The r-timer raises &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; on both (route is&lt;br /&gt;
broken); keepalive raises &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; only and notifies&lt;br /&gt;
the flow-event set (peer is silent, writer keeps &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; usable) -&lt;br /&gt;
distinct ACLs.  &amp;lt;code&amp;gt;qs.timeout == 0&amp;lt;/code&amp;gt; disables keepalive entirely; a&lt;br /&gt;
silent peer crash is then undetected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 14. Linger / teardown ==&lt;br /&gt;
&lt;br /&gt;
On &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt; computes a grace timeout&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;max(rcv_cr.act + rcv_cr.inact, snd_cr.act + snd_cr.inact) - now&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(floored at 0 and converted to seconds) and returns it; &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&lt;br /&gt;
forwards this to the IRMd as the dealloc grace.  The IRMd, not FRCT,&lt;br /&gt;
performs the wait.  Before computing the timeout, FRCT may emit a&lt;br /&gt;
final ACK when &amp;lt;code&amp;gt;rcv_cr.lwe != rcv_cr.seqno&amp;lt;/code&amp;gt; (the peer has not been&lt;br /&gt;
told the most recent cumulative ACK) AND the rcv side has been&lt;br /&gt;
active within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer not aged out).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt; is honoured only when &amp;lt;code&amp;gt;snd_cr.lwe &amp;amp;lt; edge&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;edge =&lt;br /&gt;
snd_fin_seqno&amp;lt;/code&amp;gt; after FIN has been sent in stream mode and&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno&amp;lt;/code&amp;gt; otherwise (data or FIN still in flight).  The drain&lt;br /&gt;
itself runs in &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&#039;s &amp;lt;code&amp;gt;while (FRCTI_LINGERING)&amp;lt;/code&amp;gt; loop, not in&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The fd is single-reader / single-writer (documented in the&lt;br /&gt;
manpages).  &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt; pumps &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; on every call (via&lt;br /&gt;
&amp;lt;code&amp;gt;flow_wait_window&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;flow_drain_rx_nb&amp;lt;/code&amp;gt;) and additionally blocks on&lt;br /&gt;
&amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; when the send window is closed.  A pure-writer thread thus&lt;br /&gt;
consumes ACKs without a dedicated reader.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 15. Heritage and adopted techniques ==&lt;br /&gt;
&lt;br /&gt;
Delta-t (Watson, 1981) is the primary heritage; FRCP descends from&lt;br /&gt;
the delta-t protocol family via the Recursive InterNetwork&lt;br /&gt;
Architecture (RINA; Day, &amp;quot;Patterns in Network Architecture&amp;quot;, 2008,&lt;br /&gt;
ch. 9).  Timer-based connection management&lt;br /&gt;
(no SYN/FIN handshake, per-flow state born on first DATA and&lt;br /&gt;
reclaimed after &amp;lt;code&amp;gt;t_mpl + a + r&amp;lt;/code&amp;gt; of silence), the DRF marker, and the&lt;br /&gt;
&amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; timers all come from delta-t.  See Watson,&lt;br /&gt;
&amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection&lt;br /&gt;
Management&amp;quot;, Computer Networks 5 (1981).&lt;br /&gt;
&lt;br /&gt;
The unified &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive and its&lt;br /&gt;
multi-axis QoS-cube argument ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) also come from RINA&lt;br /&gt;
(Day 2008, ch. 6; Grasa et al., &amp;quot;IRATI: investigating RINA as an&lt;br /&gt;
alternative to TCP/IP&amp;quot;, Computer Networks 92 (2015)) - reliability,&lt;br /&gt;
ordering, CRC presence, and encryption are flow attributes, not&lt;br /&gt;
separate sockets or protocols.&lt;br /&gt;
&lt;br /&gt;
The table below summarises additional adopted techniques and their&lt;br /&gt;
references.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FRCP mechanism !! Heritage !! Reference / note&lt;br /&gt;
|-&lt;br /&gt;
| Random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; || TCP ISN || RFC 6528 (Gont &amp;amp;amp; Bellovin, 2012).  QUIC PN-space reset (RFC 9000 sec. 12.3) is a structural analogue.&lt;br /&gt;
|-&lt;br /&gt;
| Cumulative ACK, left-window-edge advance || TCP || RFC 793 / RFC 9293&lt;br /&gt;
|-&lt;br /&gt;
| Receive window with non-shrink rule || TCP || RFC 793 sec. 3.7 / RFC 9293 sec. 3.8.6; RFC 1122 sec. 4.2.2.16 for the explicit non-shrink prohibition&lt;br /&gt;
|-&lt;br /&gt;
| Modular &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; arithmetic (&amp;lt;code&amp;gt;before&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;after&amp;lt;/code&amp;gt; helpers) || TCP || RFC 793 sec. 3.3 / RFC 9293 sec. 3.4&lt;br /&gt;
|-&lt;br /&gt;
| Selective ACK block list || TCP || RFC 2018 (Mathis et al., 1996).  Encoded as a typed FRCP packet rather than a TCP option, so framing is closer to QUIC ACK frames.  D-SACK (RFC 2883) carried in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt;; see [[#1.3. SACK payload|Section 1.3]].&lt;br /&gt;
|-&lt;br /&gt;
| NewReno-careful recovery with &amp;lt;code&amp;gt;recovery_high&amp;lt;/code&amp;gt; gate || TCP || RFC 6582 (Henderson et al., 2012); QUIC builds on the same model in RFC 9002 sec. 7.3.2.  Cwnd half absent (CC in IPCP).&lt;br /&gt;
|-&lt;br /&gt;
| RACK reordering window for fast retransmit || TCP || RFC 8985 (Cheng et al., 2021).  FRCP &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor against &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; collapse; matches RFC 8985 sec. 6.2 and Linux &amp;lt;code&amp;gt;tcp_rack_reo_wnd&amp;lt;/code&amp;gt;.  DSACK-driven &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; (sec. 6.2 step 4) is adopted; see [[#1.3. SACK payload|Section 1.3]] for the wire encoding.  The hybrid RACK-or-&amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; trigger from RFC 8985 sec. 6.2 step 4 is adopted ([[#8. Retransmission|Section 8]]).  QUIC&#039;s analogue in RFC 9002 sec. 6.1.2 uses &amp;lt;code&amp;gt;max(srtt, latest_rtt)&amp;lt;/code&amp;gt; as the base.&lt;br /&gt;
|-&lt;br /&gt;
| Karn&#039;s algorithm: no RTT sample on retransmits, RTO-collapse freeze || TCP || Karn &amp;amp;amp; Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, SIGCOMM 1987; RFC 6298 sec. 3.&lt;br /&gt;
|-&lt;br /&gt;
| RTO formula &amp;lt;code&amp;gt;RTO = max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt; || TCP || RFC 6298 (Paxson et al., 2011).  &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; = 250 us is below RFC 6298 sec. 2.4&#039;s 1 s SHOULD-floor - a recursive-layer choice.&lt;br /&gt;
|-&lt;br /&gt;
| Linux asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator (default) || Linux kernel || &amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;if(delta&amp;amp;lt;0) m&amp;amp;gt;&amp;amp;gt;=3&amp;lt;/code&amp;gt; dampening is a kernel divergence from RFC 6298.  RFC 6298 EWMA available behind a compile flag.&lt;br /&gt;
|-&lt;br /&gt;
| Delayed ACK with rate suppression || TCP || RFC 813 (Clark, 1982); RFC 1122 sec. 4.2.3.2; RFC 5681 sec. 4.2.  Single-deadline coalescing rather than &amp;quot;ack-every-other-segment&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| Zero-window-probe / persist-timer analogue (RDVS) || TCP || RFC 1122 sec. 4.2.2.17 / RFC 9293 sec. 3.8.6.1.  RDVS solicits an FC reply, distinct from QUIC &amp;lt;code&amp;gt;DATA_BLOCKED&amp;lt;/code&amp;gt; (RFC 9000 sec. 19.12), which is one-way notification.  &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; give-up departs from TCP.&lt;br /&gt;
|-&lt;br /&gt;
| Multiplexed control on a single PCI || SCTP / QUIC || SCTP chunk bundling (RFC 9260 sec. 6.10); QUIC frame multiplexing (RFC 9000 sec. 12.4).  Cleaner fit than TCP&#039;s separate-flag-bits design.&lt;br /&gt;
|-&lt;br /&gt;
| ACK ranges as multiple discontiguous acked blocks || QUIC || QUIC ACK frame (RFC 9000 sec. 19.3).  FRCP SACK is conceptually QUIC-frame-shaped even though encoded as absolute &amp;lt;code&amp;gt;[start,end]&amp;lt;/code&amp;gt; pairs.&lt;br /&gt;
|-&lt;br /&gt;
| Nonce-authenticated active RTT / liveness probing (RTTP) || QUIC &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;PATH_RESPONSE&amp;lt;/code&amp;gt; (RFC 9000 sec. 8.2, sec. 19.17, sec. 19.18).  WebRTC ICE consent-freshness (RFC 7675) is the same pattern.  QUIC&#039;s nonce is 8 octets; FRCP chooses 16.&lt;br /&gt;
|-&lt;br /&gt;
| Probing distinct from keepalive || QUIC || KA timer answers &amp;quot;peer alive?&amp;quot;, RTTP answers &amp;quot;path measurable?&amp;quot;, as in QUIC PING (RFC 9000 sec. 19.2) vs &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| Bare KA + ACK keepalive packets || QUIC / SCTP || QUIC PING (RFC 9000 sec. 19.2); SCTP HEARTBEAT / HEARTBEAT-ACK (RFC 9260 sec. 8.3).  SCTP HEARTBEAT also carries an opaque echoed blob, structurally similar to FRCP RTTP.&lt;br /&gt;
|-&lt;br /&gt;
| (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) fragment-role bits ([[#7.2. Fragmentation and reassembly|Section 7.2]]) || SCTP || RFC 9260 sec. 3.3.1 DATA chunk B/E bits encode the same four states (&amp;lt;code&amp;gt;B+E=SOLE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;B-only=FIRST&amp;lt;/code&amp;gt;, neither=&amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E-only=LAST&amp;lt;/code&amp;gt;).  Each fragment carries its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;/TSN and is independently retransmitted.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-offset reassembly (Sections [[#1.5. Stream PCI extension|1.5]], [[#16. Stream-mode flows|16]]) || QUIC || QUIC STREAM frame (RFC 9000 sec. 19.8) uses Offset + Length varints; FRCP uses fixed 32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;.  One stream per flow vs QUIC&#039;s many streams multiplexed.&lt;br /&gt;
|-&lt;br /&gt;
| FIN end-of-stream marker (Sections [[#1.2. Flag bits|1.2]], [[#16. Stream-mode flows|16]]) || TCP / QUIC || TCP FIN flag (RFC 9293 sec. 3.1) closes one half of the byte stream; QUIC STREAM frame FIN bit (RFC 9000 sec. 19.8) does the same per stream with an immutable final-size invariance (RFC 9000 sec. 4.5: the final size is fixed once observed).  FRCP&#039;s FIN consumes one packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (not one byte of stream space) and is idempotent on the sender side.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-credit flow control ([[#16. Stream-mode flows|Section 16]]) || QUIC || &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; (RFC 9000 sec. 4.1, sec. 19.10).  FRCP projects a per-flow byte budget onto the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;.  Single stream per flow collapses QUIC&#039;s &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.&lt;br /&gt;
|-&lt;br /&gt;
| Header protection (encrypted seqnos) || QUIC || QUIC RFC 9001 sec. 5.4 applies header protection on top of AEAD to mask the packet number.  FRCP&#039;s per-flow AEAD wrap ([[#16. Stream-mode flows|Section 16]]) is wider: it encrypts the entire PCI including &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; because the IPCP below already routes, so no destination connection-ID needs to stay in clear (cf. RFC 9000 sec. 5.2).&lt;br /&gt;
|-&lt;br /&gt;
| Two-bit fragment role polarity || SCTP || The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair follows SCTP B/E (begin = 1 / end = 1) rather than IPv4 MF (RFC 791 sec. 3.2), which has the inverse polarity (MF = 1 means NOT last).&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal reliability / ordering axes ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) || SCTP || PR-SCTP (RFC 3758, per-message partial reliability) and SCTP DATA U-bit (RFC 9260 sec. 3.3.1, per-message unordered) are the closest precedents for decoupling reliability from ordering; FRCP sets them per-flow rather than per-message.&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal CRC (&amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt;) || UDP-Lite || RFC 3828 (Larzon et al., 2004) lets the sender pick a per-packet Checksum Coverage and the receiver enforce a locally configured minimum (no in-band negotiation; sec. 3.1, sec. 3.3).  FRCP gates a full CRC trailer on &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; at flow setup.  Contrast TCP / SCTP (mandatory checksum) and QUIC (AEAD subsumes CRC).&lt;br /&gt;
|-&lt;br /&gt;
| Setup-time service negotiation || DCCP / SCTP / QUIC || DCCP Service Codes (RFC 4340 sec. 8.1.2, RFC 5595); SCTP INIT parameters (RFC 9260 sec. 3.3.2); QUIC transport parameters (RFC 9000 sec. 7.4).  All negotiate service properties at connection setup; only RINA&#039;s QoS cube exposes them as an orthogonal vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.1. Original to FRCP (no clean prior art) ===&lt;br /&gt;
&lt;br /&gt;
* Pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]): receiver-driven nudge exploiting &amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;.  Closest analogues are SCTP Gap Ack Blocks (RFC 9260 sec. 3.3.4) and DCCP Ack Vector (RFC 4340 sec. 11.4) - both let the receiver describe gaps to the sender, but neither targets the cross-epoch / pre-DRF case.&lt;br /&gt;
* &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; window-probe give-up: neither TCP (persist-timer probes until application or R2 abort, RFC 9293 sec. 3.8.6.1) nor QUIC has an explicit FC-give-up counter.  A recursive-network choice: outer layers can drop the flow.&lt;br /&gt;
* Skip-past-gap reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]): SCTP fragments and reassembles every flow regardless of reliability/ordering, using its own per-stream reassembly queue; QUIC fragments via STREAM offsets.  FRCP fragments best-effort flows too, but the receiver drops the broken prefix the moment a later run-start (&amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; role) is visible inside the &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;-wide reorder ring - no IP-frag-style timeout, no SCTP-style explicit abort.  If no later run-start arrives within the ring, &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt; and the partial run keeps its slots; the next inspect retries.  The trade-off: a permanently-lost &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; in a long isolated run holds slots until either a later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; appears in the ring or the writer stops, at which point the slots are reclaimed on flow teardown.&lt;br /&gt;
* Reassembly deferred to consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]), message mode only (&amp;lt;code&amp;gt;qos.service == SVC_MESSAGE&amp;lt;/code&amp;gt;): SCTP (RFC 9260 sec. 6.9), QUIC (RFC 9000 sec. 2.2), and TCP (RFC 9293) all hold reassembly state at the receive boundary.  FRCP message-mode leaves fragments in the shared-memory ring until &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; pulls and lands the SDU directly in the caller&#039;s buffer.  Stream mode ([[#16. Stream-mode flows|Section 16]]) uses the standard QUIC-style direct ring placement on receive and does not defer.  The optimisation is enabled by the Shared-Memory Subsystem (SSM) packet-buffer ring (see &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt; at [[#1.1. PCI header|Section 1.1]]); the analogue is OS-level scatter-gather I/O (&amp;lt;code&amp;gt;recvmsg+iovec&amp;lt;/code&amp;gt;), not a transport-layer prior art.&lt;br /&gt;
* TLP-equivalent tail-loss recovery (RFC 8985 sec. 7; RFC 9002 sec. 6.2): FRCP does not emit an explicit Tail Loss Probe packet, but the same goal is met implicitly by RACK loss detection ([[#8. Retransmission|Section 8]]) firing on a non-advancing cumulative ACK once the head-of-line slot ages past the RACK reorder window &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; - well below &amp;lt;code&amp;gt;RTO = max(2 * SRTT, SRTT + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.  A receiver-driven nudge is also available via the pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.2. Not adopted ===&lt;br /&gt;
&lt;br /&gt;
* Slow start, congestion window (cwnd), Additive Increase / Multiplicative Decrease (AIMD), NewReno cwnd inflation.  Congestion control lives in the IPCP CA policies and is driven by Explicit Congestion Notification (ECN, RFC 3168).&lt;br /&gt;
* Nagle / silly-window-syndrome (SWS) avoidance (RFC 896, RFC 1122 sec. 4.2.3.4).  (Deferred work, not adopted in the current spec.)&lt;br /&gt;
* TCP Timestamps (RFC 7323) / Protection Against Wrapped Sequences (PAWS) - RTT measurement uses RTTP, not per-segment timestamps.  A peer-supplied timestamp echoed on every ACK lets a malicious peer drive the &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; estimate arbitrarily low, collapsing the RTO and triggering a self-inflicted retransmit storm.  RTTP confines RTT measurement to nonce-authenticated probe round-trips, where a forged echo is rejected before it can reach the estimator.&lt;br /&gt;
* ECN (Explicit Congestion Notification) response inside FRCP (consumed by IPCP Congestion Avoidance / CA).&lt;br /&gt;
* IP-style fragment-offset reassembly (RFC 791 sec. 3.2; RFC 8200 sec. 4.5).  Message-mode FRCP relies on the FRCT &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; reorder ring keyed by &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (shared by FRTX and best-effort flows) to put fragments back in order; no separate offset field is needed and no IP-style hole-list reassembly buffer is kept.  Stream-mode FRCP does carry &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte offsets ([[#1.5. Stream PCI extension|Section 1.5]]) for direct ring placement on receive.&lt;br /&gt;
* QUIC STREAM offset+length framing on &#039;&#039;every&#039;&#039; flow (RFC 9000 sec. 19.8).  Message-mode FRCP uses the SCTP-style B/E flag-bit encoding (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) and skips the offsets; stream-mode FRCP adopts the QUIC offset model (heritage table above).&lt;br /&gt;
&lt;br /&gt;
== 16. Stream-mode flows ==&lt;br /&gt;
&lt;br /&gt;
When a flow is allocated with &amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt; both peers&lt;br /&gt;
switch to byte-stream semantics, layered on top of the FRTX reorder&lt;br /&gt;
machinery already described in Sections [[#6. Receive path|6]]-[[#8. Retransmission|8]].&lt;br /&gt;
&lt;br /&gt;
=== 16.1. Send ===&lt;br /&gt;
&lt;br /&gt;
The sender splits the caller&#039;s octets into chunks of at most&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt; octets (Sections [[#1.1. PCI header|1.1]]&lt;br /&gt;
and [[#1.5. Stream PCI extension|1.5]]).  Each chunk is one DATA packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte range copied from a monotonic stream counter.&lt;br /&gt;
In stream mode &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST be transmitted as&lt;br /&gt;
zero; the per-byte position is carried by the &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt;&lt;br /&gt;
extension instead.&lt;br /&gt;
&lt;br /&gt;
End-of-stream is signalled with a 0-byte DATA packet that has &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;&lt;br /&gt;
(bit 12) set, emitted on the FIN triggers listed in [[#1.2. Flag bits|Section 1.2]]&lt;br /&gt;
(WR-half close, &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, and any other path that yields the&lt;br /&gt;
final byte).  The sender MUST emit at most one FIN per flow; its&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; MUST equal &amp;lt;code&amp;gt;[final-byte, final-byte)&amp;lt;/code&amp;gt; (i.e., empty&lt;br /&gt;
interval at the final byte position; final-size invariance,&lt;br /&gt;
analogous to QUIC RFC 9000 sec. 4.5).  Idempotency is enforced by&lt;br /&gt;
an &amp;lt;code&amp;gt;snd_fin_sent&amp;lt;/code&amp;gt; guard.&lt;br /&gt;
&lt;br /&gt;
=== 16.2. Receive ===&lt;br /&gt;
&lt;br /&gt;
On arrival the receiver places the payload directly into a per-flow&lt;br /&gt;
byte-indexed receive ring of width &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; (octets) at the position&lt;br /&gt;
indicated by &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, with a two-segment &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; across the ring&lt;br /&gt;
boundary if needed.  Receipt is recorded in the FRTX reorder&lt;br /&gt;
machinery ([[#6.2. Locked main path|Section 6.2]]) augmented with the packet&#039;s &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;, and&lt;br /&gt;
FIN bit per slot.  When a packet&#039;s &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; front-overlaps&lt;br /&gt;
bytes already at or below the byte high-water mark, the overlap is&lt;br /&gt;
trimmed before placement so the same byte is never written twice.&lt;br /&gt;
After stashing, the receiver advances &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; and the byte high-water&lt;br /&gt;
mark across any newly-contiguous prefix.  Each slot advanced MUST&lt;br /&gt;
satisfy &amp;lt;code&amp;gt;start == the last-delivered slot&#039;s end&amp;lt;/code&amp;gt;; a slot whose&lt;br /&gt;
&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; does not equal that &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; is silently dropped at delivery time&lt;br /&gt;
(the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is consumed, no stream bytes contributed) and the high-&lt;br /&gt;
water mark does not advance past it.  The stream byte-stream&lt;br /&gt;
stalls at that point - there is no flow-tear-down on mismatch.&lt;br /&gt;
This filters spliced or off-path-injected slots that fall in&lt;br /&gt;
window without strong cryptographic authentication.&lt;br /&gt;
&lt;br /&gt;
A FIN slot marks end-of-stream at advance time only if its byte&lt;br /&gt;
position equals the last-delivered slot&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;; otherwise the FIN&lt;br /&gt;
is ignored and the corresponding &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; occupies a slot but&lt;br /&gt;
contributes no stream bytes.  No packet buffer is held after the&lt;br /&gt;
ring copy.&lt;br /&gt;
&lt;br /&gt;
=== 16.3. Read ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns up to &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt; octets from the contiguous prefix&lt;br /&gt;
&amp;lt;code&amp;gt;[next, high-water)&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;next&amp;lt;/code&amp;gt; is the byte the application has&lt;br /&gt;
already consumed up to and &amp;lt;code&amp;gt;high-water&amp;lt;/code&amp;gt; is the rightmost contiguous&lt;br /&gt;
byte received.  When the stream is fully drained AND end-of-stream&lt;br /&gt;
(EOS) was observed (&amp;lt;code&amp;gt;next == EOS&amp;lt;/code&amp;gt; byte position), &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns&lt;br /&gt;
0 (&amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) - the same shape POSIX &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt; uses on TCP after a peer&lt;br /&gt;
FIN.&lt;br /&gt;
&lt;br /&gt;
=== 16.4. Flow control ===&lt;br /&gt;
&lt;br /&gt;
ACK / SACK / RACK / RTO machinery is unchanged; the FRTX reorder&lt;br /&gt;
ring is reused as a per-&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; received-bitmap.  Let &amp;lt;code&amp;gt;per_pkt =&lt;br /&gt;
(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt;, the maximum stream-&lt;br /&gt;
byte payload one DATA packet can carry ([[#16.1. Send|Section 16.1]]).  The&lt;br /&gt;
receive window advertised in FC is clamped so the byte window&lt;br /&gt;
(&amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt;) cannot be overrun: the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is at most&lt;br /&gt;
&amp;lt;code&amp;gt;rcv_cr.lwe + ring_sz / per_pkt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC byte-credit flow-control model&lt;br /&gt;
(&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt;, RFC 9000 sec. 4.1 and sec. 19.10) projected onto&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; space.  With one stream per flow there is no &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; /&lt;br /&gt;
&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.  Receiver-side silly-window-syndrome&lt;br /&gt;
(SWS) avoidance (RFC 9293 sec. 3.8.6.2.2) is achieved by combining&lt;br /&gt;
the consume-time &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; bump with the global non-shrink rule from&lt;br /&gt;
[[#11. Flow control|Section 11]].&lt;br /&gt;
&lt;br /&gt;
=== 16.5. Security considerations ===&lt;br /&gt;
&lt;br /&gt;
Threat model.  An attacker that can observe (on-path passive) or&lt;br /&gt;
predict (off-path blind) the flow&#039;s seqnos and byte offsets on an&lt;br /&gt;
unencrypted stream flow can inject DATA or FIN at any in-window&lt;br /&gt;
position.  The in-line consistency checks above (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; == prior&lt;br /&gt;
&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; on advance; FIN MUST be 0-byte; FIN MUST sit at the final&lt;br /&gt;
byte position) realise the spirit of RFC 5961&#039;s &amp;quot;sequence-window&lt;br /&gt;
plus exact-position match for control bits&amp;quot; without an explicit&lt;br /&gt;
challenge-ACK probe; they make a few specific blind attack shapes&lt;br /&gt;
harder but are not cryptographic authentication. This is&lt;br /&gt;
comparable to TCP without the TCP Authentication Option (TCP-AO,&lt;br /&gt;
RFC 5925), tighter than a&lt;br /&gt;
pre-RFC-5961 TCP stack, and roughly equivalent to a modern&lt;br /&gt;
RFC 5961 stack against blind off-path injection - none of these&lt;br /&gt;
help once the attacker can sniff. TLS over TCP (RFC 8446)&lt;br /&gt;
encrypts only the TCP payload and leaves TCP seqnos, ACKs, FIN,&lt;br /&gt;
and RST in the clear, so TLS does NOT defend against TCP-header-&lt;br /&gt;
level injection; QUIC (RFC 9000) hides packet numbers under&lt;br /&gt;
header protection (RFC 9001 sec. 5.4), so this specific weakness&lt;br /&gt;
does not apply to QUIC.&lt;br /&gt;
&lt;br /&gt;
Mitigation: AEAD.  When the flow has encryption enabled the&lt;br /&gt;
recommended AEAD ciphers (AES-GCM, RFC 5288; or ChaCha20-Poly1305,&lt;br /&gt;
RFC 8439) wrap the entire FRCP packet on the wire - PCI, stream&lt;br /&gt;
extension, body, and the CRC trailer when &amp;lt;code&amp;gt;ber == 0&amp;lt;/code&amp;gt; - under a&lt;br /&gt;
per-flow symmetric key derived from the flow&#039;s own key exchange&lt;br /&gt;
([[#1.1. PCI header|Section 1.1]]).  The AEAD tag (~2^-128 forgery probability)&lt;br /&gt;
dominates the CRC (~2^-32) for integrity in this mode but the CRC&lt;br /&gt;
trailer is currently retained inside the wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
Implementations MUST NOT rely on the security properties below&lt;br /&gt;
when a non-AEAD cipher (e.g. AES-CTR alone) is negotiated; non-&lt;br /&gt;
AEAD modes provide confidentiality only and the threat-model&lt;br /&gt;
claims do not hold.&lt;br /&gt;
&lt;br /&gt;
With an AEAD cipher in use, seqnos, byte offsets, and the FIN bit&lt;br /&gt;
are both authenticated and confidential. Against an off-path or&lt;br /&gt;
on-path-passive attacker this is:&lt;br /&gt;
&lt;br /&gt;
* Stronger than TCP+TLS (TCP header in the clear).&lt;br /&gt;
* Stronger than TCP+TCP-AO (header authenticated but visible).&lt;br /&gt;
* Comparable to IPsec ESP transport mode (RFC 4303), which similarly authenticates and encrypts the upper-layer header plus payload, and to QUIC packet protection (RFC 9001 sec. 5), with the difference that QUIC must leave the destination connection ID in the clear for routing whereas FRCP relies on the IPCP below for delivery and can therefore encrypt its entire PCI.&lt;br /&gt;
&lt;br /&gt;
Keying granularity. Ouroboros flow allocation runs key exchange (&amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt;) per flow, so each &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; yields independent symmetric keys. This is finer-grained than QUIC (per-connection, RFC 9001, where one handshake covers all multiplexed streams) and finer-grained than typical IPsec deployment (per-host-pair Security Associations, SAs). Forward secrecy follows from the &amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt; when an ephemeral Diffie-Hellman exchange (DHE), or a hybrid mode (classical DH + post-quantum Key Encapsulation Mechanism / KEM), is selected.&lt;br /&gt;
&lt;br /&gt;
Replay protection. The AEAD layer itself does NOT carry an explicit anti-replay window (unlike IPsec ESP, RFC 4303 sec. 3.4.3, or DTLS, RFC 9147 sec. 4.5.1).  For FRCP-engaged flows the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space duplicate-suppression in [[#6.2. Locked main path|Section 6.2]] rejects replayed&lt;br /&gt;
DATA after the AEAD strips the wrap, because the AEAD authenticates the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a replay re-presents an old &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; that is then discarded either as a duplicate (still inside the receive window or as outside the receive window, depending on how far &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; has advanced since the original packet was delivered. RAW (&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) flows have no FRCP layer and therefore no replay protection at the AEAD layer either; deployments that need replay rejection on RAW flows SHOULD use SVC_MESSAGE.&lt;br /&gt;
&lt;br /&gt;
Layering. The AEAD wrap sits below FRCP on the data path, so RAW best-effort flows (&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;, the UDP-equivalent service of [[#2.2. Service modes (orthogonal axes)|Section 2.2]]) inherit the same per-flow integrity + confidentiality scope as FRCP-engaged flows - whatever the IPCP and FRCP (if any) put on the wire is what the AEAD authenticates. No DTLS-equivalent layering is required for confidentiality and integrity; replay protection above AEAD is a separate concern as noted above.&lt;br /&gt;
&lt;br /&gt;
== 17. References ==&lt;br /&gt;
&lt;br /&gt;
This section lists the IETF documents, published works, and&lt;br /&gt;
source-code references cited inline elsewhere in this document.&lt;br /&gt;
IETF documents are cited inline as &amp;quot;RFC NNNN sec. X.Y&amp;quot;; books,&lt;br /&gt;
journal papers, and source-code references are cited inline by&lt;br /&gt;
author and year (or by file and function name) and are listed&lt;br /&gt;
here for convenience.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.1. IETF documents ===&lt;br /&gt;
&lt;br /&gt;
;[RFC 791]&lt;br /&gt;
:J. Postel, &amp;quot;Internet Protocol&amp;quot;, STD 5, RFC 791, September 1981.&lt;br /&gt;
;[RFC 793]&lt;br /&gt;
:J. Postel, &amp;quot;Transmission Control Protocol&amp;quot;, STD 7, RFC 793, September 1981.  Obsoleted by RFC 9293.&lt;br /&gt;
;[RFC 813]&lt;br /&gt;
:D. D. Clark, &amp;quot;Window and Acknowledgement Strategy in TCP&amp;quot;, RFC 813, July 1982.&lt;br /&gt;
;[RFC 896]&lt;br /&gt;
:J. Nagle, &amp;quot;Congestion Control in IP/TCP Internetworks&amp;quot;, RFC 896, January 1984.&lt;br /&gt;
;[RFC 1122]&lt;br /&gt;
:R. Braden (ed.), &amp;quot;Requirements for Internet Hosts -- Communication Layers&amp;quot;, STD 3, RFC 1122, October 1989.&lt;br /&gt;
;[RFC 2018]&lt;br /&gt;
:M. Mathis, J. Mahdavi, S. Floyd, A. Romanow, &amp;quot;TCP Selective Acknowledgment Options&amp;quot;, RFC 2018, October 1996.&lt;br /&gt;
;[RFC 2119]&lt;br /&gt;
:S. Bradner, &amp;quot;Key words for use in RFCs to Indicate Requirement Levels&amp;quot;, BCP 14, RFC 2119, March 1997.&lt;br /&gt;
;[RFC 2883]&lt;br /&gt;
:S. Floyd, J. Mahdavi, M. Mathis, M. Podolsky, &amp;quot;An Extension to the Selective Acknowledgement (SACK) Option for TCP&amp;quot;, RFC 2883, July 2000.&lt;br /&gt;
;[RFC 3758]&lt;br /&gt;
:R. Stewart, M. Ramalho, Q. Xie, M. Tuexen, P. Conrad, &amp;quot;Stream Control Transmission Protocol (SCTP) Partial Reliability Extension&amp;quot;, RFC 3758, May 2004.&lt;br /&gt;
;[RFC 3828]&lt;br /&gt;
:L-A. Larzon, M. Degermark, S. Pink, L-E. Jonsson (ed.), G. Fairhurst (ed.), &amp;quot;The Lightweight User Datagram Protocol (UDP-Lite)&amp;quot;, RFC 3828, July 2004.&lt;br /&gt;
;[RFC 4303]&lt;br /&gt;
:S. Kent, &amp;quot;IP Encapsulating Security Payload (ESP)&amp;quot;, RFC 4303, December 2005.&lt;br /&gt;
;[RFC 4340]&lt;br /&gt;
:E. Kohler, M. Handley, S. Floyd, &amp;quot;Datagram Congestion Control Protocol (DCCP)&amp;quot;, RFC 4340, March 2006.&lt;br /&gt;
;[RFC 5288]&lt;br /&gt;
:J. Salowey, A. Choudhury, D. McGrew, &amp;quot;AES Galois Counter Mode (GCM) Cipher Suites for TLS&amp;quot;, RFC 5288, August 2008.&lt;br /&gt;
;[RFC 5595]&lt;br /&gt;
:G. Fairhurst, &amp;quot;The Datagram Congestion Control Protocol (DCCP) Service Codes&amp;quot;, RFC 5595, September 2009.&lt;br /&gt;
;[RFC 5681]&lt;br /&gt;
:M. Allman, V. Paxson, E. Blanton, &amp;quot;TCP Congestion Control&amp;quot;, RFC 5681, September 2009.&lt;br /&gt;
;[RFC 5925]&lt;br /&gt;
:J. Touch, A. Mankin, R. Bonica, &amp;quot;The TCP Authentication Option&amp;quot;, RFC 5925, June 2010.&lt;br /&gt;
;[RFC 5961]&lt;br /&gt;
:A. Ramaiah, R. Stewart, M. Dalal, &amp;quot;Improving TCP&#039;s Robustness to Blind In-Window Attacks&amp;quot;, RFC 5961, August 2010.&lt;br /&gt;
;[RFC 6298]&lt;br /&gt;
:V. Paxson, M. Allman, J. Chu, M. Sargent, &amp;quot;Computing TCP&#039;s Retransmission Timer&amp;quot;, RFC 6298, June 2011.&lt;br /&gt;
;[RFC 6528]&lt;br /&gt;
:F. Gont, S. Bellovin, &amp;quot;Defending against Sequence Number Attacks&amp;quot;, RFC 6528, February 2012.  Obsoletes RFC 1948.&lt;br /&gt;
;[RFC 6582]&lt;br /&gt;
:T. Henderson, S. Floyd, A. Gurtov, Y. Nishida, &amp;quot;The NewReno Modification to TCP&#039;s Fast Recovery Algorithm&amp;quot;, RFC 6582, April 2012.&lt;br /&gt;
;[RFC 7323]&lt;br /&gt;
:D. Borman, B. Braden, V. Jacobson, R. Scheffenegger (ed.), &amp;quot;TCP Extensions for High Performance&amp;quot;, RFC 7323, September 2014.&lt;br /&gt;
;[RFC 7675]&lt;br /&gt;
:M. Perumal, D. Wing, R. Ravindranath, T. Reddy, M. Thomson, &amp;quot;Session Traversal Utilities for NAT (STUN) Usage for Consent Freshness&amp;quot;, RFC 7675, October 2015.&lt;br /&gt;
;[RFC 8174]&lt;br /&gt;
:B. Leiba, &amp;quot;Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words&amp;quot;, BCP 14, RFC 8174, May 2017.&lt;br /&gt;
;[RFC 8200]&lt;br /&gt;
:S. Deering, R. Hinden, &amp;quot;Internet Protocol, Version 6 (IPv6) Specification&amp;quot;, STD 86, RFC 8200, July 2017.&lt;br /&gt;
;[RFC 8439]&lt;br /&gt;
:Y. Nir, A. Langley, &amp;quot;ChaCha20 and Poly1305 for IETF Protocols&amp;quot;, RFC 8439, June 2018.&lt;br /&gt;
;[RFC 8446]&lt;br /&gt;
:E. Rescorla, &amp;quot;The Transport Layer Security (TLS) Protocol Version 1.3&amp;quot;, RFC 8446, August 2018.&lt;br /&gt;
;[RFC 8985]&lt;br /&gt;
:Y. Cheng, N. Cardwell, N. Dukkipati, P. Jha, &amp;quot;The RACK-TLP Loss Detection Algorithm for TCP&amp;quot;, RFC 8985, February 2021.&lt;br /&gt;
;[RFC 9000]&lt;br /&gt;
:J. Iyengar (ed.), M. Thomson (ed.), &amp;quot;QUIC: A UDP-Based Multiplexed and Secure Transport&amp;quot;, RFC 9000, May 2021.&lt;br /&gt;
;[RFC 9001]&lt;br /&gt;
:M. Thomson (ed.), S. Turner (ed.), &amp;quot;Using TLS to Secure QUIC&amp;quot;, RFC 9001, May 2021.&lt;br /&gt;
;[RFC 9002]&lt;br /&gt;
:J. Iyengar (ed.), I. Swett (ed.), &amp;quot;QUIC Loss Detection and Congestion Control&amp;quot;, RFC 9002, May 2021.&lt;br /&gt;
;[RFC 9147]&lt;br /&gt;
:E. Rescorla, H. Tschofenig, N. Modadugu, &amp;quot;The Datagram Transport Layer Security (DTLS) Protocol Version 1.3&amp;quot;, RFC 9147, April 2022.&lt;br /&gt;
;[RFC 9260]&lt;br /&gt;
:R. Stewart, M. Tuexen, K. Nielsen, &amp;quot;Stream Control Transmission Protocol&amp;quot;, RFC 9260, June 2022.  Obsoletes RFC 4960.&lt;br /&gt;
;[RFC 9293]&lt;br /&gt;
:W. Eddy (ed.), &amp;quot;Transmission Control Protocol (TCP)&amp;quot;, STD 7, RFC 9293, August 2022.  Obsoletes RFC 793 and several follow-ons; updates RFC 1122 and others.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.2. Books and journal papers ===&lt;br /&gt;
&lt;br /&gt;
;[Day08]&lt;br /&gt;
:J. Day, &amp;quot;Patterns in Network Architecture: A Return to Fundamentals&amp;quot;, Prentice Hall, 2008.&lt;br /&gt;
;[Grasa15]&lt;br /&gt;
:E. Grasa et al., &amp;quot;IRATI: investigating RINA as an alternative to TCP/IP&amp;quot;, Computer Networks, Vol. 92, December 2015.&lt;br /&gt;
;[KP87]&lt;br /&gt;
:P. Karn, C. Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, ACM SIGCOMM, August 1987.&lt;br /&gt;
;[Wat81]&lt;br /&gt;
:R. W. Watson, &amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection Management&amp;quot;, Computer Networks, Vol. 5, 1981.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.3. Source-code references ===&lt;br /&gt;
&lt;br /&gt;
;[Linux-RTT]&lt;br /&gt;
:&amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt; of the Linux kernel, defining the asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; variance update used as FRCP&#039;s default RTT estimator ([[#12. RTT estimation|Section 12]]).  Line-stable browseable copy at https://elixir.bootlin.com/linux/latest/source/net/ipv4/tcp_input.c.&lt;br /&gt;
&lt;br /&gt;
[[Category:Protocols]]&lt;br /&gt;
[[Category:Ouroboros internals]]&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1917</id>
		<title>Flow and Retransmission Control Protocol</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1917"/>
		<updated>2026-05-17T14:59:41Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 15. Heritage and adopted techniques */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:FRCP - Flow and Retransmission Control Protocol}}&lt;br /&gt;
&lt;br /&gt;
FRCP runs end-to-end between two peers over a flow.  It delivers&lt;br /&gt;
reliability, in-order delivery, flow control, and liveness.&lt;br /&gt;
Congestion Control (CC) is not in FRCP - that lives in the IPC&lt;br /&gt;
Process (IPCP) Congestion Avoidance (CA) policies, orthogonal to&lt;br /&gt;
FRCP.  Flow allocation, naming, and IPCP lifecycle are handled by&lt;br /&gt;
the IPC Resource Manager daemon (IRMd).&lt;br /&gt;
&lt;br /&gt;
FRCT (Flow and Retransmission Control Task) is the libouroboros&lt;br /&gt;
implementation of FRCP; the task lives in &amp;lt;code&amp;gt;src/lib/frct.c&amp;lt;/code&amp;gt;.  The&lt;br /&gt;
remainder of this document describes the FRCP wire protocol and the&lt;br /&gt;
behaviour FRCT realises.  Code symbols retain the &amp;lt;code&amp;gt;FRCT_&amp;lt;/code&amp;gt; prefix&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, ...) because they belong to the implementing&lt;br /&gt;
task; this document references them verbatim.&lt;br /&gt;
&lt;br /&gt;
The keywords &amp;quot;MUST&amp;quot;, &amp;quot;MUST NOT&amp;quot;, &amp;quot;REQUIRED&amp;quot;, &amp;quot;SHALL&amp;quot;, &amp;quot;SHALL NOT&amp;quot;,&lt;br /&gt;
&amp;quot;SHOULD&amp;quot;, &amp;quot;SHOULD NOT&amp;quot;, &amp;quot;RECOMMENDED&amp;quot;, &amp;quot;MAY&amp;quot;, and &amp;quot;OPTIONAL&amp;quot; in&lt;br /&gt;
this document are to be interpreted as described in &amp;lt;code&amp;gt;BCP&amp;lt;/code&amp;gt; 14 (Best&lt;br /&gt;
Current Practice; RFC 2119, RFC 8174) when, and only when, they&lt;br /&gt;
appear in all capitals.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:Unsigned 32-bit / 8-bit integers (kernel-C style).&lt;br /&gt;
;&amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:Nanoseconds.&lt;br /&gt;
&lt;br /&gt;
Modular sequence-number comparators (32-bit, modulo 2^32):&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;before(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;(int32_t)(a - b) &amp;amp;lt; 0&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;after(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;before(b, a)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Used throughout for &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; ordering checks.&lt;br /&gt;
&lt;br /&gt;
Round-Trip Time (RTT) abbreviations used throughout:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;&lt;br /&gt;
:Smoothed RTT estimate (RFC 6298).&lt;br /&gt;
;&amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt;&lt;br /&gt;
:Mean deviation of RTT (Linux variance estimator).&lt;br /&gt;
;&amp;lt;code&amp;gt;EWMA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Exponentially Weighted Moving Average.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTO&amp;lt;/code&amp;gt;&lt;br /&gt;
:Retransmission Timeout, &amp;lt;code&amp;gt;max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Timer-bound symbols &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer, ACK delay) and &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer,&lt;br /&gt;
retransmission window) are defined in [[#8. Retransmission|Section 8]]; &amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; (Maximum&lt;br /&gt;
Packet Lifetime) is introduced in [[#2.1. Per-flow state|Section 2.1]] (the &amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; field)&lt;br /&gt;
with heritage in [[#15. Heritage and adopted techniques|Section 15]].&lt;br /&gt;
&lt;br /&gt;
Wire-format diagrams follow the IETF convention: bit 0 is the&lt;br /&gt;
leftmost (most significant) bit and fields are in network byte&lt;br /&gt;
order unless stated otherwise.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1. Wire format ==&lt;br /&gt;
&lt;br /&gt;
=== 1.1. PCI header ===&lt;br /&gt;
&lt;br /&gt;
Fixed 16-octet base Protocol-Control Information (PCI) header&lt;br /&gt;
prefixed to every FRCP packet (RFC convention: bit 0 leftmost,&lt;br /&gt;
most-significant bit first).  All multi-byte fields except &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;&lt;br /&gt;
are in network byte order; &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt; is an opaque 16-bit value that&lt;br /&gt;
the receiver recomputes from the wire bytes and compares to the&lt;br /&gt;
in-place &amp;lt;code&amp;gt;pci-&amp;gt;hcs&amp;lt;/code&amp;gt; read, so its on-wire byte order need only&lt;br /&gt;
match between peers running compatible builds.  DATA packets on&lt;br /&gt;
stream-mode flows carry an additional 8-octet extension (see&lt;br /&gt;
[[#1.5. Stream PCI extension|Section 1.5]]); SACK and RTTP carry their own payloads after the&lt;br /&gt;
base PCI.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |             flags             |              hcs              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            window                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            seqno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            ackno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                     payload (variable) ...&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt;&lt;br /&gt;
:feature/type bitmap (see [[#1.2. Flag bits|Section 1.2]]).&lt;br /&gt;
;&amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;&lt;br /&gt;
:CRC-16-CCITT-FALSE Header Check Sequence (HCS) over &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; (+ stream extension when present); the two octets of the &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt; field itself are omitted from the CRC input.  Verified on receive before any flag-driven dispatch.&lt;br /&gt;
;&amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt;&lt;br /&gt;
:receiver-advertised right window edge (valid iff FC).&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-flow sequence number.&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;&lt;br /&gt;
:cumulative Acknowledgement (ACK) (valid iff ACK).&lt;br /&gt;
&lt;br /&gt;
A single packet can simultaneously carry DATA + ACK + FC (Flow&lt;br /&gt;
Control) + RXM (Retransmission) by ORing flag bits; the PCI&lt;br /&gt;
multiplexes control on the same wire frame in the spirit of SCTP&lt;br /&gt;
chunk bundling (RFC 9260 sec. 6.10) and QUIC frame multiplexing&lt;br /&gt;
(RFC 9000 sec. 12.4).  DATA-bearing packets carry the caller&#039;s&lt;br /&gt;
payload after the PCI; SACK (Selective Acknowledgement) and RTTP&lt;br /&gt;
(Round-Trip Time Probe) carry their own typed payloads after the&lt;br /&gt;
PCI.&lt;br /&gt;
&lt;br /&gt;
Optional framing (per-flow, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]]).  On the wire, the&lt;br /&gt;
order from inside out is:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Layer !! Scope&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| The FRCP packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body + CRC-32 ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| CRC-32 covers the body only (PCI is in HCS); appended iff &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; on DATA, or on every SACK packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ AEAD-wrap of above ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| Iff Authenticated Encryption with Associated Data (AEAD) is enabled.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* HCS in the PCI covers the header fields on every packet and is verified before any flag-driven dispatch.&lt;br /&gt;
* The CRC-32 trailer (IEEE 802.3 / zlib reflected polynomial &amp;lt;code&amp;gt;0xEDB88320&amp;lt;/code&amp;gt;, init &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;, xor-out &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;) covers the body on DATA when &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; and on every SACK packet; the trailer is written as a raw &amp;lt;code&amp;gt;uint32_t&amp;lt;/code&amp;gt; (the same convention as &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;: opaque on the wire as long as both peers run compatible builds).  The PCI is not under the CRC (Cyclic Redundancy Check) because the HCS already protects it.  It is appended before AEAD encryption and therefore rides inside the AEAD wrap when both are active; the AEAD tag (~2^-128 forgery probability) dominates the CRC (~2^-32) for integrity in that mode but the CRC trailer is currently retained.&lt;br /&gt;
* When encryption is enabled, the entire (possibly-CRC&#039;d) FRCP packet is wrapped with AEAD inside the shared-memory packet buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt;); the packet grows by the AEAD overhead, namely a leading nonce / Initialization Vector (IV) of &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_ivsz&amp;lt;/code&amp;gt;) and a trailing authentication tag of &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_tagsz&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Both CRC and AEAD are layered around the FRCP wire format and are not visible to the FRCP machinery itself.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.2. Flag bits ===&lt;br /&gt;
&lt;br /&gt;
Flag bits are numbered most-significant-bit first to match the wire&lt;br /&gt;
diagram (bit numbering per [[#1.1. PCI header|Section 1.1]]; bit 0 is the MSB of the&lt;br /&gt;
16-bit &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; field and lands at wire-position 0 in network byte&lt;br /&gt;
order).  Bits 13..15 are reserved and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Bit !! Mask !! Name !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| 0 || &amp;lt;code&amp;gt;0x8000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt; || Carries caller payload&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &amp;lt;code&amp;gt;0x4000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DRF&amp;lt;/code&amp;gt; || Data Run Flag: start of a fresh run&lt;br /&gt;
|-&lt;br /&gt;
| 2 || &amp;lt;code&amp;gt;0x2000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt; || Acknowledgement: &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field valid&lt;br /&gt;
|-&lt;br /&gt;
| 3 || &amp;lt;code&amp;gt;0x1000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt; || Negative ACK; &amp;lt;code&amp;gt;seqno = arrival_seqno-1&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || &amp;lt;code&amp;gt;0x0800&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt; || Flow Control: &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; field valid (&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || &amp;lt;code&amp;gt;0x0400&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt; || Rendezvous probe (window-closed)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || &amp;lt;code&amp;gt;0x0200&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; || First Fragment (role bit 0; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || &amp;lt;code&amp;gt;0x0100&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; || Last Fragment (role bit 1; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 8 || &amp;lt;code&amp;gt;0x0080&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RXM&amp;lt;/code&amp;gt; || Retransmission&lt;br /&gt;
|-&lt;br /&gt;
| 9 || &amp;lt;code&amp;gt;0x0040&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt; || Selective ACK block list in payload&lt;br /&gt;
|-&lt;br /&gt;
| 10 || &amp;lt;code&amp;gt;0x0020&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt; || RTT Probe / echo (payload follows)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || &amp;lt;code&amp;gt;0x0010&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt; || Keepalive&lt;br /&gt;
|-&lt;br /&gt;
| 12 || &amp;lt;code&amp;gt;0x0008&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt; || End-of-stream marker (stream mode)&lt;br /&gt;
|-&lt;br /&gt;
| 13-15 || -- || -- || Reserved (MUST be zero)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair encodes the fragment role of a DATA-bearing&lt;br /&gt;
Service Data Unit (SDU), SCTP-style begin/end flags (RFC 9260&lt;br /&gt;
sec. 3.3.1):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FFGM !! LFGM !! Role&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || Sole / un-fragmented SDU (begin AND end)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || First fragment of a multi-fragment SDU&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Middle fragment&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Last fragment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Each fragment is carried in its own FRCP packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;;&lt;br /&gt;
FRTX (the FRCT Retransmission service mode, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]])&lt;br /&gt;
recovers individual fragments via the normal Retransmission Timeout&lt;br /&gt;
(RTO) / SACK / Recent Acknowledgement (RACK, RFC 8985) path.  The&lt;br /&gt;
receiver reassembles the SDU at consume time once the contiguous&lt;br /&gt;
[FIRST .. LAST] run has fully arrived.  On non-DATA packets the role&lt;br /&gt;
bits are unused and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
In stream mode (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;, see [[#16. Stream-mode flows|Section 16]]) there are&lt;br /&gt;
no SDU boundaries to encode, so &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST&lt;br /&gt;
be transmitted as zero.  End-of-stream uses a dedicated bit (&amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;,&lt;br /&gt;
bit 12) carried on a 0-byte DATA packet, emitted at write-half close&lt;br /&gt;
(&amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;FLOWFRDONLY&amp;lt;/code&amp;gt;), during linger drain, and at &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;;&lt;br /&gt;
emission is idempotent (first call wins).  After contiguous delivery&lt;br /&gt;
of the FIN-bearing slot, the receiver latches &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt; at the FIN&#039;s&lt;br /&gt;
start offset; &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns 0 (end-of-file, &amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) once buffered&lt;br /&gt;
bytes have been drained up to &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt;.  Per-byte position is&lt;br /&gt;
carried by the [start, end) extension ([[#1.5. Stream PCI extension|Section 1.5]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.3. SACK payload ===&lt;br /&gt;
&lt;br /&gt;
A SACK packet has the &amp;lt;code&amp;gt;FRCT_ACK | FRCT_FC | FRCT_SACK&amp;lt;/code&amp;gt; flag bits set&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI, the&lt;br /&gt;
payload is a 2-octet block count (network byte order), 2 octets of&lt;br /&gt;
padding to 4-byte align the block list, then &amp;lt;code&amp;gt;n_blocks&amp;lt;/code&amp;gt; pairs of&lt;br /&gt;
32-bit start/end seqnos describing &#039;&#039;present&#039;&#039; (received) ranges&lt;br /&gt;
above the cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |           n_blocks            |        padding (2 octets)     |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[0]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            end[0]                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[1]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
                       ... n_blocks pairs total ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;n_blocks &amp;amp;lt;= SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; (2048).  The per-flow effective cap is&lt;br /&gt;
further bounded by &amp;lt;code&amp;gt;(frag_mtu - PCI - 4) / 8&amp;lt;/code&amp;gt; blocks per packet; SACK&lt;br /&gt;
packets carry no stream extension, so PCI here is the 16-octet base&lt;br /&gt;
header even on stream-mode flows.&lt;br /&gt;
&lt;br /&gt;
Wire invariant: every block produced by the receiver, except an&lt;br /&gt;
optional leading Duplicate SACK (D-SACK) block as described below,&lt;br /&gt;
describes a range strictly above the cumulative ACK carried in the&lt;br /&gt;
PCI &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field (&amp;lt;code&amp;gt;after(start[i], ackno)&amp;lt;/code&amp;gt;).  This makes the D-SACK&lt;br /&gt;
convention below unambiguous; the receiver-side builder MUST&lt;br /&gt;
preserve it.&lt;br /&gt;
&lt;br /&gt;
Duplicate SACK (D-SACK, RFC 2883) is signalled in-band: no flag&lt;br /&gt;
bit, no extra framing.  Modular seqno arithmetic uses the&lt;br /&gt;
&amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators defined in the Notation block.&lt;br /&gt;
&amp;lt;code&amp;gt;Block[0]&amp;lt;/code&amp;gt; carries a D-SACK report when either:&lt;br /&gt;
&lt;br /&gt;
;case 1 (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;before(blocks[0].start, ackno)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ackno - blocks[0].start&amp;lt;/code&amp;gt; is within &amp;lt;code&amp;gt;MAX_DSACK_LAG&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;== RQ_SIZE&amp;lt;/code&amp;gt;).  A single duplicate &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; observed below the cumulative ACK.&lt;br /&gt;
;case 2 (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;blocks[0]&amp;lt;/code&amp;gt; is a sub-range of some &amp;lt;code&amp;gt;blocks[i&amp;gt;0]&amp;lt;/code&amp;gt; (not exactly equal).  Reports a duplicate of an in-window &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; that the same packet&#039;s remaining SACK blocks already describe as received.&lt;br /&gt;
&lt;br /&gt;
Senders that do not implement D-SACK process &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; through the&lt;br /&gt;
normal SACK-mark loop and the existing clamp-and-skip path makes&lt;br /&gt;
case-1 a no-op (&amp;lt;code&amp;gt;start &amp;amp;lt; snd_cr.lwe&amp;lt;/code&amp;gt; clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt;, the inner&lt;br /&gt;
loop then skips &amp;lt;code&amp;gt;k == snd_cr.lwe&amp;lt;/code&amp;gt;) and case-2 idempotent (same slots&lt;br /&gt;
NULL&#039;d twice).  D-SACK-aware senders feed the report into the RACK&lt;br /&gt;
&amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; scaler (RFC 8985 sec. 6.2 step 4): bump on receipt&lt;br /&gt;
(cap 20), halve once per 16 cumulatively-ACK&#039;d seqnos since the&lt;br /&gt;
most recent D-SACK arrival or halve event, reset to 1 on an RTO&lt;br /&gt;
timer fire at the head-of-line.  D-SACK alone never enters&lt;br /&gt;
NewReno-careful recovery (see [[#8. Retransmission|Section 8]]); only non-D-SACK blocks&lt;br /&gt;
count as gaps.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.4. RTTP payload ===&lt;br /&gt;
&lt;br /&gt;
An RTTP (Round-Trip Time Probe) packet has only the &amp;lt;code&amp;gt;FRCT_RTTP&amp;lt;/code&amp;gt; flag&lt;br /&gt;
set (bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI,&lt;br /&gt;
the payload is 24 octets (packed):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          probe_id                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          echo_id                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +                  nonce (16 octets, echoed verbatim)           +&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:sender counter, 0 on reply, 0 reserved.&lt;br /&gt;
;&amp;lt;code&amp;gt;echo_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:peer&#039;s &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;, 0 on outbound probe.&lt;br /&gt;
;&amp;lt;code&amp;gt;nonce&amp;lt;/code&amp;gt;&lt;br /&gt;
:random, echoed unmodified, memcmp&#039;d to defeat spoof.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.5. Stream PCI extension ===&lt;br /&gt;
&lt;br /&gt;
A stream-mode flow (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;) carries an extra&lt;br /&gt;
8-octet extension after the 16-octet base PCI on every DATA packet&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            start                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                             end                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset of the first payload byte in the stream.&lt;br /&gt;
;&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset one past the last payload byte; &amp;lt;code&amp;gt;end - start&amp;lt;/code&amp;gt; equals the on-wire payload length.&lt;br /&gt;
&lt;br /&gt;
Total stream-mode PCI for DATA packets is 24 octets (16 base + 8&lt;br /&gt;
extension); control packets (SACK, RTTP, bare ACK, KA, etc.) retain&lt;br /&gt;
the 16-octet base PCI.  Stream mode MUST be negotiated at flow&lt;br /&gt;
allocation; the extension is present iff stream mode is in use,&lt;br /&gt;
never on a per-packet basis.  Both peers MUST treat &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; as&lt;br /&gt;
monotonic 32-bit byte offsets; when a slot reaches the head of the&lt;br /&gt;
contiguous run with &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; not equal to the prior packet&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; the&lt;br /&gt;
slot is silently dropped at delivery time ([[#16. Stream-mode flows|Section 16]]) rather&lt;br /&gt;
than rejected at stash.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC STREAM-frame reassembly model (RFC 9000 sec. 19.8):&lt;br /&gt;
each packet carries its packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (this PCI&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; field) and a&lt;br /&gt;
separate stream byte position (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;).  Separating the two&lt;br /&gt;
avoids TCP&#039;s conflation of packet identity with byte position which&lt;br /&gt;
forces Karn&#039;s algorithm for Round-Trip Time (RTT) sampling (no RTT&lt;br /&gt;
sample on retransmits, RFC 6298 sec. 3); FRCP applies the&lt;br /&gt;
Karn-equivalent gate via a combination of per-packet &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;,&lt;br /&gt;
per-slot &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; flags, and a sample-fence &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; (see [[#2.1. Per-flow state|Section 2.1]]&lt;br /&gt;
and [[#12. RTT estimation|Section 12]]).  FRCP&#039;s fixed-32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; wrap at 4 GiB of&lt;br /&gt;
wire bytes, narrower than QUIC&#039;s 62-bit varint offset (cf. RFC 9000&lt;br /&gt;
sec. 16); the on-wire wrap is handled by the same modular &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;&lt;br /&gt;
/ &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators ([[#1.3. SACK payload|Section 1.3]]) FRCP uses for seqnos, which&lt;br /&gt;
remain unambiguous as long as the in-flight byte window stays&lt;br /&gt;
strictly under 2 GiB (the half-range of the signed-int32 difference&lt;br /&gt;
in &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;).  The default per-flow ring is 1 MiB; the&lt;br /&gt;
implementation caps &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; at 128 MiB (&amp;lt;code&amp;gt;FRCT_STREAM_RING_SZ_MAX&amp;lt;/code&amp;gt;),&lt;br /&gt;
well below the 2 GiB half-range bound.  The runtime byte counters&lt;br /&gt;
exposed via FUSE (Filesystem in Userspace) in the Ouroboros&lt;br /&gt;
Resource Information Base (RIB, a virtual-filesystem introspection&lt;br /&gt;
bridge) are platform &amp;lt;code&amp;gt;size_t&amp;lt;/code&amp;gt; and do not wrap on 64-bit hosts.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2. Per-flow state and service modes ==&lt;br /&gt;
&lt;br /&gt;
=== 2.1. Per-flow state ===&lt;br /&gt;
&lt;br /&gt;
Each flow keeps a sender control record and a receiver control&lt;br /&gt;
record:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: oldest unacked &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (cumulative ACK boundary as seen by sender); rcv: next in-order &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; expected&lt;br /&gt;
;&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: peer-advertised right window edge; rcv: locally-advertised right window edge&lt;br /&gt;
;&amp;lt;code&amp;gt;cflags&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-direction feature flags: retransmission (&amp;lt;code&amp;gt;FRCTFRTX&amp;lt;/code&amp;gt;), receiver flow control (&amp;lt;code&amp;gt;FRCTFRESCNTL&amp;lt;/code&amp;gt;), linger-on-close (&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt;); see &amp;lt;code&amp;gt;&amp;amp;lt;ouroboros/fccntl.h&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: next &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; to send; rcv: force-ACK trigger - set on a stale or dup DATA so the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; emits a fresh cumulative ACK&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; counter for standalone ACK-bearing control packets (delayed ACK, SACK, final ACK on dealloc); not bumped on piggybacked ACK riding a DATA packet (which uses the DATA &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;).  Used by wire-dup ACK detection; rcv: incoming-ACK dedup tracker&lt;br /&gt;
;&amp;lt;code&amp;gt;act&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:last activity (used by inactivity / DRF)&lt;br /&gt;
;&amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:inactivity threshold; sender = &amp;lt;code&amp;gt;3*mpl + a + r + 1s&amp;lt;/code&amp;gt;, receiver = &amp;lt;code&amp;gt;2*mpl + a + r + 1s&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;mpl&amp;lt;/code&amp;gt; is the Maximum Packet Lifetime (delta-t terminology; see [[#15. Heritage and adopted techniques|Section 15]]); &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; are the FRCT a-timer and r-timer bounds (see [[#8. Retransmission|Section 8]]).  The asymmetry is load-bearing for pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
The sender holds a per-slot ring &amp;lt;code&amp;gt;snd_slots[RQ_SIZE]&amp;lt;/code&amp;gt; keyed by&lt;br /&gt;
&amp;lt;code&amp;gt;(seqno mod RQ_SIZE)&amp;lt;/code&amp;gt;.  Each slot tracks its retransmit entry (&amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;),&lt;br /&gt;
last-send timestamp, and retransmit flag bits: &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (a&lt;br /&gt;
retransmit is pending or has fired, gates the next RTT sample&lt;br /&gt;
under Karn) and &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (one-shot fast-retransmit staged for&lt;br /&gt;
this loss event).&lt;br /&gt;
&lt;br /&gt;
The receiver holds a parallel reorder ring &amp;lt;code&amp;gt;rcv_slots[RQ_SIZE]&amp;lt;/code&amp;gt;&lt;br /&gt;
(referred to as &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; in prose) holding stashed out-of-order&lt;br /&gt;
packet-buffer indexes; both FRTX and best-effort flows share this&lt;br /&gt;
path.  The invariant &amp;lt;code&amp;gt;rwe - lwe &amp;amp;lt;= RQ_SIZE&amp;lt;/code&amp;gt; holds: on each consume&lt;br /&gt;
the receiver advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; by the consumed count, capping the&lt;br /&gt;
receive window at &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; slots.&lt;br /&gt;
&lt;br /&gt;
A separate fence variable &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; is bumped on every retransmit&lt;br /&gt;
(timer-fire, SACK-driven, fast-rxm, NACK-driven) and on every&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; ([[#4. Sequence-number rotation (DRF)|Section 4]]) to mark the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; range whose RTT samples&lt;br /&gt;
MUST be discarded.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 2.2. Service modes (orthogonal axes) ===&lt;br /&gt;
&lt;br /&gt;
FRCP exposes its wire features as a vector of independent QoS&lt;br /&gt;
axes selected at flow allocation time.  All flows go through the&lt;br /&gt;
same &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive; the &amp;lt;code&amp;gt;qosspec_t&amp;lt;/code&amp;gt; passed&lt;br /&gt;
in determines which protocol machinery engages on the wire.  This&lt;br /&gt;
contrasts with the POSIX BSD socket model where TCP and UDP&lt;br /&gt;
require different socket types (&amp;lt;code&amp;gt;SOCK_STREAM&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;SOCK_DGRAM&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
The axes:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = unordered (no FRCP engagement: raw datagrams, no PCI on the wire, UDP-equivalent at this layer); 1 = message-ordered (FRCP engaged; SDU boundaries preserved across fragmentation); 2 = stream (byte-oriented, no SDU boundaries; FRTX required)&lt;br /&gt;
;&amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = lossless service requested: FRTX retransmit machinery engages ([[#8. Retransmission|Section 8]]); MUST be 0 for &amp;lt;code&amp;gt;service=2&amp;lt;/code&amp;gt;.  Non-zero = best-effort, FRTX off.&lt;br /&gt;
;&amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bit Error Rate tolerance.  0 = error-free service requested: a CRC trailer is appended after the body of DATA packets and verified on receive (added / checked outside the FRCP PCI; see [[#1.1. PCI header|Section 1.1]]).  Non-zero = peer accepts errors; trailer omitted.  SACK control packets carry a CRC32 trailer regardless of &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; gate applies to DATA only.&lt;br /&gt;
;&amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt;&lt;br /&gt;
:Peer-timeout (ms); 0 disables the keepalive timer.  Independent of FRCP engagement.&lt;br /&gt;
&lt;br /&gt;
Encryption is a separate per-flow attribute set at flow setup;&lt;br /&gt;
when enabled it wraps the FRCP packet (PCI + body, plus the CRC&lt;br /&gt;
trailer if any) under AEAD, expanding the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; by &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt;&lt;br /&gt;
octets (nonce / tag).  The CRC trailer is currently kept inside&lt;br /&gt;
the AEAD wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
&lt;br /&gt;
Reachable combinations exported by &amp;lt;code&amp;gt;include/ouroboros/qos.h&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Cube !! &amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; !! Engaged&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw&amp;lt;/code&amp;gt; || 0 || 1 || 1 || Raw passthrough&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw_safe&amp;lt;/code&amp;gt; || 0 || 1 || 0 || Raw + CRC trailer&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; || 1 || 1 || 1 || FRCP, no FRTX, no CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt_safe&amp;lt;/code&amp;gt; || 1 || 1 || 0 || FRCP, no FRTX, CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_msg&amp;lt;/code&amp;gt; || 1 || 0 || 0 || FRCP + FRTX&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_stream&amp;lt;/code&amp;gt; || 2 || 0 || 0 || FRCP + FRTX, stream&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Forced couplings actually enforced by the public API:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;service == SVC_STREAM&amp;lt;/code&amp;gt; (2) requires &amp;lt;code&amp;gt;loss == 0&amp;lt;/code&amp;gt;; &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;flow_accept&amp;lt;/code&amp;gt; reject the pair otherwise with &amp;lt;code&amp;gt;-EINVAL&amp;lt;/code&amp;gt;.&lt;br /&gt;
* FRTX requires FRCP engagement (&amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;); requesting &amp;lt;code&amp;gt;loss = 0&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;service = SVC_RAW&amp;lt;/code&amp;gt; is structurally a no-op because no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; is created.&lt;br /&gt;
* The &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; build flag globally forces &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;.  Note: this flag defaults to ON, so default builds ship with CRC disabled until &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; is set to OFF.&lt;br /&gt;
&lt;br /&gt;
Caveat: the API does NOT force &amp;lt;code&amp;gt;ber = 0&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; has &amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;, which means the PCI&lt;br /&gt;
itself is not CRC-protected on that cube; the HCS ([[#1.1. PCI header|Section 1.1]])&lt;br /&gt;
remains the only integrity check on the header.&lt;br /&gt;
&lt;br /&gt;
The FRCP-no-FRTX regime (&amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;loss &amp;amp;gt; 0&amp;lt;/code&amp;gt;) is meaningful&lt;br /&gt;
and live: sequence numbering, in-order delivery, flow-control&lt;br /&gt;
advertisement, KA, DRF rotation, and SDU fragmentation /&lt;br /&gt;
reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]) all run.  Lost packets are dropped&lt;br /&gt;
rather than retransmitted; a permanently-lost mid-fragment is&lt;br /&gt;
dropped via skip-past-gap once a later SDU is visible in the&lt;br /&gt;
reorder ring.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. Protocol parameters ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Slot ring / rcv window width&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;START_WINDOW&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Initial &amp;lt;code&amp;gt;rwe-lwe&amp;lt;/code&amp;gt; after rotate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MAX(250 us build-tunable, 1&amp;amp;lt;&amp;amp;lt;RXMQ_RES)&amp;lt;/code&amp;gt;; per-flow via &amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;FRCTSRTOMIN&amp;lt;/code&amp;gt;).  Default ~1 ms with &amp;lt;code&amp;gt;RXMQ_RES=20&amp;lt;/code&amp;gt;. || RTO floor; also floored at the retransmit-wheel resolution (~1 ms by default).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt; || 20 || Backoff shift cap&lt;br /&gt;
|-&lt;br /&gt;
| RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MIN(reo_wnd_mult * min_RTT/4, SRTT)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; scales on D-SACK, cap 20 || Reorder window; per RFC 8985 sec. 6.2; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per sec. 6.2 step 4&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; || 300 s (5 min, Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; windowed re-anchor&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_WND_MULT_MAX&amp;lt;/code&amp;gt; || 20 (RFC 8985 sec. 6.2 step 4) || &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_DECAY_PKTS&amp;lt;/code&amp;gt; || 16 (RFC 8985 sec. 6.2 step 4 / &amp;lt;code&amp;gt;RACK.reo_wnd_persist&amp;lt;/code&amp;gt;) || Fresh-ACK&#039;d seq count per halving&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_DSACK_LAG&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || D-SACK sanity cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTT_QUARANTINE&amp;lt;/code&amp;gt; || 32 (&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; steps) || NewReno gate pad&lt;br /&gt;
|-&lt;br /&gt;
| SACK rate-limit || &amp;lt;code&amp;gt;SACK_MIN_GAP_NS&amp;lt;/code&amp;gt; (250 us, fixed) || Min SACK gap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; || 2048 (wire cap; per-flow capped at &amp;lt;code&amp;gt;(frag_mtu-PCI-4)/8&amp;lt;/code&amp;gt;) || Per-SACK block cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; || 32 || Per-pass staged retransmit cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; || 3 (RFC 8985 default) || Hybrid fast-rxm trigger ([[#8. Retransmission|Section 8]])&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MDEV_MUL&amp;lt;/code&amp;gt; || 2 (build-tunable via &amp;lt;code&amp;gt;FRCT_RTO_MDEV_MULTIPLIER&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; shift in &amp;lt;code&amp;gt;RTO = srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| RTTP nonce || 16 octets || Echoed verbatim&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; || 8 || In-flight probes&lt;br /&gt;
|-&lt;br /&gt;
| RTT clamp || &amp;lt;code&amp;gt;16 * srtt&amp;lt;/code&amp;gt; || Probe-sample upper bound (ACK-derived RTT samples gated by Karn / recovery only)&lt;br /&gt;
|-&lt;br /&gt;
| Cold-probe cadence || 100 ms (rx-driven; see [[#12. RTT estimation|Section 12]]) || Pre-&amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; RTTP rate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt; || 100 ms || RDVS emit cadence&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; || 1 s || RDVS give-up&lt;br /&gt;
|-&lt;br /&gt;
| Delayed-ACK fire || &amp;lt;code&amp;gt;2 * TICTIME&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt; = FRCT tick granularity, default 5 ms; &amp;lt;code&amp;gt;2*TICTIME = 10 ms&amp;lt;/code&amp;gt; by default) || Fired after the first in-order DATA arrival; tick is build-tunable&lt;br /&gt;
|-&lt;br /&gt;
| NACK send cooldown || &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; when an &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; sample exists, else 100 ms || Pre-DRF NACK rate-limit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_SDU&amp;lt;/code&amp;gt; || 1 MiB || Max reassembled SDU; configurable per flow&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The per-flow fragment Maximum Transmission Unit (MTU) is computed&lt;br /&gt;
at flow setup from the lower IPCP&#039;s mtu minus encryption&lt;br /&gt;
&amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; and CRC trailer; there is no FRCT-level default or&lt;br /&gt;
environment-variable override.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 4. Sequence-number rotation (DRF) ==&lt;br /&gt;
&lt;br /&gt;
The DRF (Data Run Flag) bit on an outbound packet means &amp;quot;this is&lt;br /&gt;
the start of a fresh data run&amp;quot; and is set whenever the sender has&lt;br /&gt;
nothing in flight (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Independently of that, if the sender has been idle longer than&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.inact&amp;lt;/code&amp;gt; AND the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;),&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; rolls a random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; before the send and&lt;br /&gt;
resets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    snd_cr.seqno  = random()&lt;br /&gt;
    snd_cr.lwe    = snd_cr.seqno&lt;br /&gt;
    snd_cr.rwe    = snd_cr.seqno + START_WINDOW&lt;br /&gt;
    rtt_lwe       = snd_cr.seqno&lt;br /&gt;
    in_recovery   = false   (recovery state, see Section 8)&lt;br /&gt;
    recovery_high = snd_cr.seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The receiver, on observing rcv-side inactivity&lt;br /&gt;
(&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), requires a DRF on the next&lt;br /&gt;
DATA packet; otherwise it replies with a rate-limited NACK (see&lt;br /&gt;
below).  Non-DATA control packets pass through without the DRF&lt;br /&gt;
requirement.  On DRF the receiver releases the &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots and&lt;br /&gt;
rebases&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    rcv_cr.lwe   = seqno&lt;br /&gt;
    rcv_cr.rwe   = seqno + RQ_SIZE&lt;br /&gt;
    rcv_cr.seqno = seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the inactive packet has DATA but no DRF, a rate-limited NACK is&lt;br /&gt;
fired back to the sender (cooldown per [[#3. Protocol parameters|Section 3]]); non-DATA stale&lt;br /&gt;
arrivals fall through to normal processing (no NACK, no drop).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 5. Send path ==&lt;br /&gt;
&lt;br /&gt;
# If the SDU exceeds &amp;lt;code&amp;gt;(frag_mtu - data_hdr_len)&amp;lt;/code&amp;gt;, the caller (&amp;lt;code&amp;gt;dev.c&amp;lt;/code&amp;gt;) fans it out into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - data_hdr_len))&amp;lt;/code&amp;gt; fragments, each emitted via &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; as its own DATA packet with a per-fragment role ([[#7.2. Fragmentation and reassembly|Section 7.2]]); both FRTX and best-effort flows fragment.  Raw flows (no FRCP engagement, &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) carry no PCI and return &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; for any SDU larger than one packet at the layer below.  An SDU that fits in a single packet is sent as SOLE.  &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; reserves PCI head room; sets DATA, plus DRF when the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
# &amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; if past sender inactivity and the pipe is empty ([[#4. Sequence-number rotation (DRF)|Section 4]]).&lt;br /&gt;
# Advertise FC (&amp;lt;code&amp;gt;pci.window = frcti_advert_rwe(frcti)&amp;lt;/code&amp;gt;, i.e. &amp;lt;code&amp;gt;rcv_cr.rwe&amp;lt;/code&amp;gt; clamped to &amp;lt;code&amp;gt;rcv_cr.lwe + ring_seq_cap&amp;lt;/code&amp;gt; in stream mode) when the receiver side is recent: &amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt; rcv_cr.inact&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Reliable mode (FRTX): leave &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; where it is; reset the slot at &amp;lt;code&amp;gt;RQ_SLOT(seqno)&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;snd_slots[p].time = now&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_slots[p].flags = 0&amp;lt;/code&amp;gt;); queue an &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; (saves a packet copy, arms a wheel timer at &amp;lt;code&amp;gt;now + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;).  Piggyback ACK (&amp;lt;code&amp;gt;pci.ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) while the a-timer for the most recent received DATA packet has not yet expired (&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt;= t_a&amp;lt;/code&amp;gt;); on piggyback, set &amp;lt;code&amp;gt;rcv_cr.seqno = rcv_cr.lwe&amp;lt;/code&amp;gt; so the next delayed-ACK fire is suppressed.  See [[#8. Retransmission|Section 8]] for &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; semantics.&lt;br /&gt;
# Best-effort mode (no FRTX): advance &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; immediately (&amp;lt;code&amp;gt;snd_cr.lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_cr.rwe = snd_cr.lwe + RQ_SIZE&amp;lt;/code&amp;gt;); no retransmit state.  No send-side RTT probe is armed in this mode (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt; requires an in-flight &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, which best-effort never has); the rx-driven cold seeder in &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; is the only probe path.&lt;br /&gt;
# In reliable mode, optionally arm an RTT probe ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 6. Receive path ==&lt;br /&gt;
&lt;br /&gt;
=== 6.1. Early-exit dispatch ===&lt;br /&gt;
&lt;br /&gt;
Keepalive (KA), RTT probe (RTTP), pre-DRF NACK, and rendezvous&lt;br /&gt;
(RDVS) packets short-circuit out of &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; before the locked&lt;br /&gt;
main path; each handler takes its own lock internally.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
      incoming packet&lt;br /&gt;
            |&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | KA?     |---yes--&amp;gt; ka_rcv  ; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RTTP?   |---yes--&amp;gt; rttp_rcv; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | NACK?   |---yes--&amp;gt; nack_rcv; return  (see Section 9)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RDVS?   |---yes--&amp;gt; rdv_rcv ; return  (reply bare FC, ackno=0)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       acquire wrlock; enter locked main path&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt;&lt;br /&gt;
:refresh &amp;lt;code&amp;gt;t_ka_rcv&amp;lt;/code&amp;gt;, honour piggybacked ACK.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt;&lt;br /&gt;
:probe (echo back nonce) or echo (verify nonce, sample RTT).&lt;br /&gt;
;&amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:pre-DRF, sender-side handler.  See [[#9. Pre-DRF NACK|Section 9]].&lt;br /&gt;
;&amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt;&lt;br /&gt;
:reply with a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;); &amp;lt;code&amp;gt;rdlock&amp;lt;/code&amp;gt; only.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 6.2. Locked main path ===&lt;br /&gt;
&lt;br /&gt;
Steps below run with the per-flow &amp;lt;code&amp;gt;frcti.lock&amp;lt;/code&amp;gt; held for writing&lt;br /&gt;
(&amp;lt;code&amp;gt;pthread_rwlock_wrlock&amp;lt;/code&amp;gt;) unless noted.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;&lt;br /&gt;
:Only meaningful when the receive side is stale.  On DRF (Data Run Flag): release &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots, rebase &amp;lt;code&amp;gt;rcv_cr&amp;lt;/code&amp;gt;, continue.  On stale DATA without DRF: fire a pre-DRF NACK if cooldown allows ([[#9. Pre-DRF NACK|Section 9]]), then discard the packet; on cooldown, drop without sending a NACK (a pending cumulative ACK from &amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; may still go out).  Non-DATA, non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; pure-DRF stale arrivals fall through after the DRF rebase branch.&lt;br /&gt;
&lt;br /&gt;
;DATA-only act refresh&lt;br /&gt;
:Refresh &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; only when &amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt; is set, so that non-DATA packets never block the next DRF rebase.&lt;br /&gt;
&lt;br /&gt;
;Wire-dup gate&lt;br /&gt;
:Before flag-driven dispatch, drop wire-duplicate ACKs and wire-duplicate DATA (&amp;lt;code&amp;gt;is_dup_ack&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt;).  The DATA check is bypassed for &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;-bearing arrivals so the piggybacked ACK / SACK / FC carried on a retransmitted DATA at an already-ACK&#039;d &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is still applied; the stale-in-window branch below then drops the packet.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Drop ACKs whose &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; falls outside &amp;lt;code&amp;gt;(snd_cr.lwe, snd_cr.seqno]&amp;lt;/code&amp;gt;.  If &amp;lt;code&amp;gt;ackno == snd_cr.lwe&amp;lt;/code&amp;gt; (non-advancing cumulative ACK), drive RACK fast-retransmit consideration ([[#8. Retransmission|Section 8]]).  Otherwise advance &amp;lt;code&amp;gt;snd_cr.lwe = ackno&amp;lt;/code&amp;gt;, collapse &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; to 0 (Karn-gated by &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; on the just-acknowledged slot, the old head-of-line), reset &amp;lt;code&amp;gt;dup_thresh&amp;lt;/code&amp;gt; to 0, update &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the send-time of the slot at &amp;lt;code&amp;gt;ackno-1&amp;lt;/code&amp;gt; (consumed by RACK and SACK below), decay &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per RFC 8985 sec. 6.2 step 4, exit NewReno-careful recovery (see [[#8. Retransmission|Section 8]]) on &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt;, and feed an RTT sample if eligible ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Walk the block list.  For each block (a present range above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;) NULL out &amp;lt;code&amp;gt;snd_slots[k].rxm&amp;lt;/code&amp;gt;, clear the slot&#039;s per-send flags, and advance &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the latest send-time covered (the Forward Acknowledgement / fack equivalent, Mathis &amp;amp;amp; Mahdavi 1996); the first block whose start clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; skips this fack update so that a head-of-line clamp does not falsely advance fack.  For un-SACKed gaps below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt;, stage a retransmit per slot that is (1) still owned (&amp;lt;code&amp;gt;rxm != NULL&amp;lt;/code&amp;gt;), (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (the RFC 8985 sec. 6.2 hybrid trigger).  Mark the slot &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; and NULL the &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; at stage time.  Capped at &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; staged retransmits per receive pass; what&#039;s left rides the next SACK.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bump &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; (clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt;, never shrinks) and mark window open.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bounds-check &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; against window.  On stale-dup (&amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;), set &amp;lt;code&amp;gt;rcv_cr.seqno = seqno&amp;lt;/code&amp;gt; to force a fresh ACK on the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt;, then drop.  On accept: both FRTX and best-effort stash the packet-buffer index into &amp;lt;code&amp;gt;rq[seqno mod RQ_SIZE]&amp;lt;/code&amp;gt;.  Fragments stash unchanged - the role bits are inspected only at consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]).  On out-of-order arrival, build a SACK reply if not rate-limited (per [[#3. Protocol parameters|Section 3]]) and not deduplicated against the previous &amp;lt;code&amp;gt;(rcv_cr.lwe, n_blocks)&amp;lt;/code&amp;gt; pair; D-SACK reports always bypass the dedup.  If both rate-limit and dedup suppress the reply, neither SACK nor delayed-ACK fires (the sender picks up the gap on its next ACK).  On in-order arrival, arm the delayed-ACK timer.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; exit&lt;br /&gt;
:Releases the per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;), then calls &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; synchronously after the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; release to surface any pending cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 7. Read path and reassembly ==&lt;br /&gt;
&lt;br /&gt;
=== 7.1. Read path ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns a full reassembled SDU (Service Data Unit) via&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; on every FRCP SDU-mode flow (FRTX or best-effort);&lt;br /&gt;
stream-mode is covered in [[#16. Stream-mode flows|Section 16]].  An incomplete head-of-line&lt;br /&gt;
(HoL) run yields &amp;lt;code&amp;gt;-EAGAIN&amp;lt;/code&amp;gt;; an oversized run yields &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; (the&lt;br /&gt;
run is dropped so the flow does not stall).  On best-effort flows,&lt;br /&gt;
a permanently-lost mid-fragment is dropped as soon as a later&lt;br /&gt;
complete SDU becomes visible in the ring ([[#7.2. Fragmentation and reassembly|Section 7.2]] skip-past-&lt;br /&gt;
gap).&lt;br /&gt;
&lt;br /&gt;
Raw flows carry no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt;, so &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns the next pending&lt;br /&gt;
packet-buffer index directly, with no role-bit inspection.  (Raw&lt;br /&gt;
service is selected via &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt; at flow allocation,&lt;br /&gt;
which suppresses &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; creation.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_pdu_ready&amp;lt;/code&amp;gt; is the no-advance peek used by &amp;lt;code&amp;gt;fevent&amp;lt;/code&amp;gt; (the&lt;br /&gt;
Ouroboros flow-event multiplexer, the &amp;lt;code&amp;gt;poll(2)&amp;lt;/code&amp;gt;-equivalent on&lt;br /&gt;
flows).  It returns ready only when the head-of-line run is&lt;br /&gt;
complete and the lead packet (a Protocol Data Unit, here one FRCP&lt;br /&gt;
packet) is present at &amp;lt;code&amp;gt;rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt;; any other state&lt;br /&gt;
(including the best-effort skip-past-gap case) returns not ready,&lt;br /&gt;
and &amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; is left to drop the broken prefix and re-&lt;br /&gt;
inspect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 7.2. Fragmentation and reassembly ===&lt;br /&gt;
&lt;br /&gt;
Send side (&amp;lt;code&amp;gt;flow_write_frag&amp;lt;/code&amp;gt;).  An SDU larger than&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - PCI)&amp;lt;/code&amp;gt; is split into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - PCI))&amp;lt;/code&amp;gt;&lt;br /&gt;
fragments; each fragment is its own FRCP packet with its own&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a per-fragment role flag pair ([[#1.2. Flag bits|Section 1.2]]).  Roles are&lt;br /&gt;
assigned at emit time:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! i !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=0&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=n-1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| else || &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A mid-loop allocation or transmit failure may yield a partial&lt;br /&gt;
write: the call returns the bytes already enqueued (&amp;lt;code&amp;gt;off &amp;amp;gt; 0&amp;lt;/code&amp;gt;) or&lt;br /&gt;
the underlying error (&amp;lt;code&amp;gt;off == 0&amp;lt;/code&amp;gt;).  Best-effort flows fragment&lt;br /&gt;
identically; on the receiver, a partial run with a permanently-&lt;br /&gt;
lost fragment is dropped when a later complete SDU is visible in&lt;br /&gt;
the ring (see skip-past-gap below).  Raw flows carry no PCI and&lt;br /&gt;
refuse anything larger than the layer&#039;s user MTU (&amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Wire-level recovery is fragment-agnostic on FRTX flows: each&lt;br /&gt;
fragment&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; flows through SACK / RACK / RTO / NACK exactly&lt;br /&gt;
as for a SOLE DATA packet, and reassembly does not re-enter the&lt;br /&gt;
loss-detection path.  Best-effort flows run the same &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
machinery (DRF, FC, ACK piggyback, pre-DRF NACK emit) but queue&lt;br /&gt;
no &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; state at the sender, so a lost MID is unrecoverable;&lt;br /&gt;
skip-past-gap handles it (below).&lt;br /&gt;
&lt;br /&gt;
Receive side.  Fragments stash into &amp;lt;code&amp;gt;rq[seqno]&amp;lt;/code&amp;gt; unchanged; role bits&lt;br /&gt;
are read only at consume time.  &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;, called from&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt;, walks the ring starting at the oldest still-&lt;br /&gt;
undelivered &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;base = rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt; (equal to &amp;lt;code&amp;gt;rcv_cr.lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
only when no partial run is in progress; during a partial run &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
has already advanced past &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;).  It produces one of three&lt;br /&gt;
outcomes:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Outcome !! Cause&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELIVER (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]=SOLE&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;), or &amp;lt;code&amp;gt;rq[base]=FIRST&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; follows in slots &amp;lt;code&amp;gt;[base+1..base+n-1]&amp;lt;/code&amp;gt; with all intermediate roles in &amp;lt;code&amp;gt;{MID,FIRST,LAST}&amp;lt;/code&amp;gt; contiguous.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DROP (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; without a preceding &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;); a &amp;lt;code&amp;gt;FIRST..[non-LAST]..new-FIRST&amp;lt;/code&amp;gt; or new-&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; mid-run (drop the broken prefix with &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; = run length minus 1, so the new &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; stays); or, on best-effort flows, a gap at &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; later in the ring (drop up to the new run start).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; absent or &amp;lt;code&amp;gt;FIRST..[non-LAST]&amp;lt;/code&amp;gt; with no later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; in the ring (FRTX waits for retx; best-effort waits for arrival).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DELIVER&amp;lt;/code&amp;gt; triggers &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;: a scatter-gather &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; of the &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;&lt;br /&gt;
consecutive fragments at &amp;lt;code&amp;gt;rq[base..base+n-1]&amp;lt;/code&amp;gt; directly into the&lt;br /&gt;
caller&#039;s buffer; each per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;) is&lt;br /&gt;
released and &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; advances by &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; was already advanced&lt;br /&gt;
incrementally as each contiguous fragment arrived; &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;&lt;br /&gt;
only restores the fixed-width invariant &amp;lt;code&amp;gt;rwe == lwe + RQ_SIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
No intermediate reassembly buffer is allocated.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; past the broken prefix (releasing the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;s)&lt;br /&gt;
and pulls &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; up to the new trailing edge if needed; the next&lt;br /&gt;
consume retries from the new &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;.  Oversize or arithmetically&lt;br /&gt;
overflowing delivery (sum of fragment lengths &amp;amp;gt; &amp;lt;code&amp;gt;max_rcv_sdu&amp;lt;/code&amp;gt;, sum&lt;br /&gt;
&amp;amp;gt; caller&#039;s buffer, or running-sum overflow) also drops the run&lt;br /&gt;
with &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skip-past-gap (best-effort only).  On FRTX, a gap in the run means&lt;br /&gt;
&amp;quot;waiting for retransmit&amp;quot; and &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;.&lt;br /&gt;
On best-effort flows the gap is permanent, so &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;&lt;br /&gt;
scans forward in the ring for the next &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;; if one is&lt;br /&gt;
visible within &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;, it returns &amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; for the broken prefix and&lt;br /&gt;
the consume loop retries at the new &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;.  Memory hold is bounded&lt;br /&gt;
by &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;; the partial releases on the next consume call once a&lt;br /&gt;
later complete run exists.  Voice-like flows (one &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; per SDU)&lt;br /&gt;
see no extra wait: any later &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; makes the prior gap droppable&lt;br /&gt;
immediately.&lt;br /&gt;
&lt;br /&gt;
The choice to defer reassembly to consume time keeps the receive&lt;br /&gt;
path zero-copy: fragments stay in the shared-memory ring until&lt;br /&gt;
the application pulls, and the SDU lands directly in the caller&#039;s&lt;br /&gt;
buffer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 8. Retransmission ==&lt;br /&gt;
&lt;br /&gt;
FRCP is bounded by two delta-t-derived timers (Watson 1981, see&lt;br /&gt;
[[#15. Heritage and adopted techniques|Section 15]]):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer): upper bound on ACK delay.  An ACK for a received DATA packet MUST be emitted within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; of receipt; an attempt to send an ACK after the a-timer has expired is suppressed (the sender&#039;s RTO is already in motion).&lt;br /&gt;
* &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer): upper bound on retransmission.  A given DATA packet MUST NOT be retransmitted after &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; has elapsed since its first send (&amp;lt;code&amp;gt;t0&amp;lt;/code&amp;gt;); when the bound is hit, the flow is declared down (raising the Ouroboros asynchronous flow condition &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt;, which marks the flow dead to both endpoints) rather than retransmitted again.&lt;br /&gt;
&lt;br /&gt;
Each in-flight FRTX &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; owns one &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt;, armed in a hashed&lt;br /&gt;
timing wheel; the wheel deadline is the slot&#039;s next eligible&lt;br /&gt;
retransmit time.&lt;br /&gt;
&lt;br /&gt;
;RTO timer&lt;br /&gt;
:On fire (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;), re-emit with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, mark &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (Karn-suppress next ACK&#039;s RTT sample), and (for the head-of-line (HoL) slot only) bump &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; up to &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt;.  Wheel deadline is &amp;lt;code&amp;gt;t_send + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;.  Re-armed unless consumed.  The RTO timer also clears &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (re-arming fast-retransmit eligibility), resets &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; to 1 on a HoL fire (RFC 8985 sec. 6.2 step 4 reset clause), and marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; if its &amp;lt;code&amp;gt;frct_tx&amp;lt;/code&amp;gt; call fails.&lt;br /&gt;
&lt;br /&gt;
;r-timer guard&lt;br /&gt;
:Before any retransmit attempt, check &amp;lt;code&amp;gt;(now - t0)&amp;lt;/code&amp;gt; against &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;.  If exceeded, the slot is no longer eligible for retransmit.  Only the RTO timer (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;) treats r-timer expiry as terminal: it marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; (peer unreachable).  Fast-retransmit, SACK-driven retransmit, and NACK-driven head-of-line re-emit silently skip aged-out slots and defer the flow-down decision to the next RTO fire.&lt;br /&gt;
&lt;br /&gt;
;Fast retransmit (hybrid trigger, RFC 8985 sec. 6.2)&lt;br /&gt;
:On a non-advancing cumulative ACK with the scoreboard advanced, fire one fast retransmit when EITHER (a) the head-of-line slot&#039;s latest send is older than the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; ([[#3. Protocol parameters|Section 3]]) and not yet aged out, OR (b) the SACK &amp;lt;code&amp;gt;dup-thresh&amp;lt;/code&amp;gt; count above &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; reaches &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; (= 3, RFC 8985 sec. 6.2 step 4).  Fires at most once per non-advancing cumulative-ACK value, gated by &amp;lt;code&amp;gt;rack_fired_lwe&amp;lt;/code&amp;gt; (the &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; at which fast-retransmit last fired).  Set &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; on the slot (one-shot per-slot gate) and enter NewReno-style careful recovery (see NewReno below in this section).&lt;br /&gt;
:The RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; uses the RFC 8985 sec. 6.2 form &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor.  Before the first RTT sample seeds &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; falls back to &amp;lt;code&amp;gt;MIN(reo_wnd_mult * SRTT / 4, SRTT)&amp;lt;/code&amp;gt;, still floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt; (consistent with the windowed-minimum fallback described in [[#12. RTT estimation|Section 12]]).  &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt; is a windowed minimum over the last &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; = 5 min of RTT samples (matches the Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt; default) so a route change to a longer path eventually re-anchors the reorder window without relying on &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; growth alone.&lt;br /&gt;
&lt;br /&gt;
;SACK-driven retransmit&lt;br /&gt;
:For each gap below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt; whose slot is (1) still owned, (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (same hybrid as fast-retransmit, see [[#6.2. Locked main path|Section 6.2]]), re-emit.  Each SACK-driven retransmit re-arms a fresh &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; so a lost retransmit can still be recovered by its own RTO timer.&lt;br /&gt;
&lt;br /&gt;
;NewReno&lt;br /&gt;
:On entry, &amp;lt;code&amp;gt;recovery_high = snd_cr.seqno + RTT_QUARANTINE&amp;lt;/code&amp;gt;.  Exit when &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt; (the latter means everything sent has been acknowledged).  &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; also clears recovery.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 9. Pre-DRF NACK ==&lt;br /&gt;
&lt;br /&gt;
The two sides have different inactivity thresholds (&amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), so a receiver can detect &amp;quot;stale data run&amp;quot; before the sender&#039;s own DRF logic kicks in.  NACK is the receiver-driven nudge that asks the sender to re-transmit the head of the run.&lt;br /&gt;
&lt;br /&gt;
;Send (&amp;lt;code&amp;gt;frcti_nack_snd&amp;lt;/code&amp;gt;, called by &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;FRCT_INACT_NEED_NACK&amp;lt;/code&amp;gt;)&lt;br /&gt;
:When an incoming DATA packet has no DRF and rcv-side activity is older than &amp;lt;code&amp;gt;rcv_cr.inact&amp;lt;/code&amp;gt;, the receiver emits a bare packet with &amp;lt;code&amp;gt;flags = FRCT_NACK&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;seqno = arrival_seqno - 1&amp;lt;/code&amp;gt; (informational only, not consulted by the receive handler).  The cooldown in [[#3. Protocol parameters|Section 3]] rate-limits the burst.  Non-DATA non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; non-DATA DRF still rebases via the DRF branch.&lt;br /&gt;
&lt;br /&gt;
;Receive (&amp;lt;code&amp;gt;frcti_nack_rcv&amp;lt;/code&amp;gt;)&lt;br /&gt;
:Dispatched in the early-exit branch ([[#6.1. Early-exit dispatch|Section 6.1]]), before &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;.  The sender copies the head-of-line (HoL) &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; packet, marks the slot &amp;lt;code&amp;gt;SND_RTX | SND_FAST_RXM&amp;lt;/code&amp;gt; (Karn-suppress next ACK, one-shot fast-rxm gate), sets &amp;lt;code&amp;gt;rtt_lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, and re-emits via &amp;lt;code&amp;gt;fast_rxm_send&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt; and a refreshed &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;.  The original &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed - the NACK emit is additive to the normal retransmit machinery, not a replacement.  No-op if nothing is in flight, the HoL slot has aged past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, or the HoL &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; pointer has been cleared by SACK or RACK.&lt;br /&gt;
&lt;br /&gt;
NACK has exactly one role: lost first-of-run (DRF) packet recovery. Until the DRF packet arrives, the receiver cannot rebase its window, so any subsequent in-flight packets look stale to the receiver. The NACK fires the moment a stale receiver sees DATA without DRF, telling the sender to re-emit the head-of-line (DRF) packet at NACK-cooldown latency rather than waiting for the initial RTO (which is the configured default until &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded by the first probe round-trip).  Mid-stream loss is NOT NACK-driven; it is recovered by the sender&#039;s RTO, fast retransmit, and SACK-driven retransmit paths ([[#8. Retransmission|Section 8]]) only.&lt;br /&gt;
&lt;br /&gt;
The existing &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed on a NACK re-emit, so the RTO path remains the eventual fallback.&lt;br /&gt;
&lt;br /&gt;
== 10. Cumulative + selective ACK ==&lt;br /&gt;
&lt;br /&gt;
Cumulative ACK is &amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;.  On out-of-order arrival the&lt;br /&gt;
receiver also emits a SACK packet ([[#1.3. SACK payload|Section 1.3]]) whose payload lists&lt;br /&gt;
&#039;&#039;present&#039;&#039; blocks above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; (analogous to TCP SACK / QUIC ACK&lt;br /&gt;
ranges).  SACKs are rate-limited per [[#3. Protocol parameters|Section 3]] and suppressed when&lt;br /&gt;
neither &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; nor block count has changed since the last SACK.&lt;br /&gt;
&lt;br /&gt;
D-SACK reports (RFC 2883) are emitted in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; of an&lt;br /&gt;
otherwise normal SACK frame (see [[#1.3. SACK payload|Section 1.3]] for the encoding).&lt;br /&gt;
Two receiver triggers arm a pending D-SACK report (single-slot,&lt;br /&gt;
latest-wins):&lt;br /&gt;
&lt;br /&gt;
* DATA arrival with &amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;, both wire-dup (no RXM, &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt; path) and retransmit (RXM, post-FC branch) (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
* &amp;lt;code&amp;gt;rq_accept&amp;lt;/code&amp;gt; conflict, slot already occupied in &amp;lt;code&amp;gt;[lwe, rwe)&amp;lt;/code&amp;gt; (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
&lt;br /&gt;
When a D-SACK is pending and the standard scoreboard SACK would be&lt;br /&gt;
suppressed by dedup or rate-limit, the report is emitted as a&lt;br /&gt;
stand-alone SACK frame through the normal &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; path; when a&lt;br /&gt;
D-SACK report is pending the path bypasses dedup and the &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;&lt;br /&gt;
rate-limit, but the a-timer suppression on rcv inactivity still&lt;br /&gt;
applies.&lt;br /&gt;
&lt;br /&gt;
Bare ACKs are deferred via a per-flow delayed-ACK timer (one in&lt;br /&gt;
flight at a time, atomic test-and-set dedup; fires per [[#3. Protocol parameters|Section 3]]&lt;br /&gt;
after the first in-order arrival).  Suppressed if (1) no new&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, (2) rcv side is inactive (older than &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt;), or (3) the&lt;br /&gt;
sender just sent within &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;.  A pending D-SACK ride-through&lt;br /&gt;
bypasses (1) and (3); the a-timer gate (2) is unconditional.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 11. Flow control ==&lt;br /&gt;
&lt;br /&gt;
The receiver advertises &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; in every FC field.  The sender treats&lt;br /&gt;
its &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; as the absolute right edge: when&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt;= snd_cr.rwe&amp;lt;/code&amp;gt; the window is closed and &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt;&lt;br /&gt;
yields.  While closed, the sender periodically emits RDVS&lt;br /&gt;
(rendezvous) packets (cadence &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt;); the receiver replies with&lt;br /&gt;
a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;) that reopens the window.  Once the&lt;br /&gt;
window has been closed for longer than &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; the sender stops&lt;br /&gt;
emitting RDVS but does not tear the flow down - the writer keeps&lt;br /&gt;
blocking until either a peer-driven FC arrives or the KA&lt;br /&gt;
(keepalive) / r-timer marks the flow.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt; on receipt and MUST NOT shrink:&lt;br /&gt;
a backward &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is silently clamped to the current &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt;;&lt;br /&gt;
the FC packet still reopens the window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 12. RTT estimation ==&lt;br /&gt;
&lt;br /&gt;
Active RTTP probes ([[#1.4. RTTP payload|Section 1.4]]) carry a 32-bit &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt; (0&lt;br /&gt;
reserved) and a 16-byte random nonce echoed verbatim - defends&lt;br /&gt;
against spoofed replies.  A ring of &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; in-flight probes is&lt;br /&gt;
kept; an echo whose &amp;lt;code&amp;gt;(id, nonce)&amp;lt;/code&amp;gt; doesn&#039;t match the ring slot is&lt;br /&gt;
dropped.  A single RTTP sample is clamped to &amp;lt;code&amp;gt;RTT_CLAMP_MUL * srtt&amp;lt;/code&amp;gt;&lt;br /&gt;
(compile-time &amp;lt;code&amp;gt;RTT_CLAMP_MUL = 16&amp;lt;/code&amp;gt;) once &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded; the first&lt;br /&gt;
cold-probe sample feeds &amp;lt;code&amp;gt;rtt_update&amp;lt;/code&amp;gt; raw.&lt;br /&gt;
&lt;br /&gt;
Probe arming gates:&lt;br /&gt;
&lt;br /&gt;
;Cold (no &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; yet)&lt;br /&gt;
:the receive path arms at most one probe per 100 ms via &amp;lt;code&amp;gt;frcti_rcv_probe&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;PROBE_DUE_COLD&amp;lt;/code&amp;gt;); arming requires an incoming packet.  Active send-path arming bails while &amp;lt;code&amp;gt;srtt == 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
;Warm (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt;, called from &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt;)&lt;br /&gt;
:outstanding data (&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt; snd_cr.lwe&amp;lt;/code&amp;gt;), AND at least &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_rcv_rtt&amp;lt;/code&amp;gt; (last RTT receive of any kind), AND at least &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_snd_probe&amp;lt;/code&amp;gt; (last probe emit).&lt;br /&gt;
&lt;br /&gt;
Sample feeds either Linux&#039;s asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_LINUX_RTT_ESTIMATOR&amp;lt;/code&amp;gt;, default ON) or RFC 6298 symmetric EWMA&lt;br /&gt;
(compile option).  &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is floored at 10 ms when seeded from a&lt;br /&gt;
hint, at 1 us after every update (including the first seeding&lt;br /&gt;
sample); &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; floored at 100 ns.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;RTO = max(rto_min, 2 * srtt, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(the &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; floor is an FRCT addition not in RFC 6298).&lt;br /&gt;
Effective wheel deadline capped per [[#3. Protocol parameters|Section 3]].&lt;br /&gt;
&lt;br /&gt;
ACK-derived samples (&amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;rtt_sample_eligible&amp;lt;/code&amp;gt;), beyond&lt;br /&gt;
the cum-ACK advance gate in &amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno &amp;amp;gt; lwe&amp;lt;/code&amp;gt; and&lt;br /&gt;
&amp;lt;code&amp;gt;ackno &amp;amp;lt;= seqno&amp;lt;/code&amp;gt;), require all of: not in recovery; ACK packet does&lt;br /&gt;
not carry &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;; HoL slot&#039;s &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; bit clear; slot&#039;s &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;&lt;br /&gt;
pointer non-NULL (not SACK-consumed); &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; not below the &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
fence; &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; already seeded by an RTTP probe.  There is no ACK-only&lt;br /&gt;
seeding.&lt;br /&gt;
&lt;br /&gt;
Every eligible sample also feeds &amp;lt;code&amp;gt;RACK.min_RTT&amp;lt;/code&amp;gt; (RFC 8985 sec. 6.2)&lt;br /&gt;
via a windowed minimum: replace whenever the sample is strictly&lt;br /&gt;
smaller OR more than &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; (5 min, matches Linux&lt;br /&gt;
&amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) has elapsed since the current min was set.  The&lt;br /&gt;
downward branch is immediate (faster path picked up at once); the&lt;br /&gt;
upward branch is gated on the window (a transient queue burst does&lt;br /&gt;
not poison the estimate, but a sustained route change to a longer&lt;br /&gt;
path re-anchors &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; after at most one window).  Seeded from&lt;br /&gt;
&amp;lt;code&amp;gt;rtt_hint&amp;lt;/code&amp;gt; at &amp;lt;code&amp;gt;rtt_init&amp;lt;/code&amp;gt;; 0 acts as the unset sentinel and the base&lt;br /&gt;
in &amp;lt;code&amp;gt;rack_reorder_window&amp;lt;/code&amp;gt; falls back from &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt; (so&lt;br /&gt;
&amp;lt;code&amp;gt;R = mult * SRTT/4&amp;lt;/code&amp;gt;, capped at &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;, floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt;)&lt;br /&gt;
until the first sample.  See [[#6.2. Locked main path|Section 6.2]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 13. Liveness (keepalive) ==&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;code&amp;gt;qs.timeout &amp;amp;gt; 0&amp;lt;/code&amp;gt; a per-flow KA (keepalive) timer is armed.&lt;br /&gt;
Arming uses &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; for the deadline computation:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;deadline = min(snd_act + qs.timeout/4, rcv_act + qs.timeout)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(clamped to &amp;lt;code&amp;gt;now + qs.timeout/4&amp;lt;/code&amp;gt; if already past).  The timer fires&lt;br /&gt;
either on sender idleness (to send a KA) or on receiver idleness&lt;br /&gt;
(to declare the peer dead).  On fire (&amp;lt;code&amp;gt;ka_snd&amp;lt;/code&amp;gt;) the peer-dead test&lt;br /&gt;
uses &amp;lt;code&amp;gt;max(rcv_cr.act, t_ka_rcv)&amp;lt;/code&amp;gt; so a recent KA reply counts even&lt;br /&gt;
when no DATA has arrived:&lt;br /&gt;
&lt;br /&gt;
* If &amp;lt;code&amp;gt;now - max(rcv_cr.act, t_ka_rcv) &amp;amp;gt; qs.timeout&amp;lt;/code&amp;gt;, mark the flow &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; and notify the per-process flow-event set (&amp;lt;code&amp;gt;proc.fqset&amp;lt;/code&amp;gt;) with &amp;lt;code&amp;gt;FLOW_PEER&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Else if &amp;lt;code&amp;gt;snd_idle &amp;amp;gt; qs.timeout/4&amp;lt;/code&amp;gt;, emit a bare &amp;lt;code&amp;gt;KA | ACK&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) and re-arm.&lt;br /&gt;
* Else just re-arm.&lt;br /&gt;
&lt;br /&gt;
Note: &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; are the receive and transmit shared-memory&lt;br /&gt;
ring buffers.  The r-timer raises &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; on both (route is&lt;br /&gt;
broken); keepalive raises &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; only and notifies&lt;br /&gt;
the flow-event set (peer is silent, writer keeps &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; usable) -&lt;br /&gt;
distinct ACLs.  &amp;lt;code&amp;gt;qs.timeout == 0&amp;lt;/code&amp;gt; disables keepalive entirely; a&lt;br /&gt;
silent peer crash is then undetected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 14. Linger / teardown ==&lt;br /&gt;
&lt;br /&gt;
On &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt; computes a grace timeout&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;max(rcv_cr.act + rcv_cr.inact, snd_cr.act + snd_cr.inact) - now&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(floored at 0 and converted to seconds) and returns it; &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&lt;br /&gt;
forwards this to the IRMd as the dealloc grace.  The IRMd, not FRCT,&lt;br /&gt;
performs the wait.  Before computing the timeout, FRCT may emit a&lt;br /&gt;
final ACK when &amp;lt;code&amp;gt;rcv_cr.lwe != rcv_cr.seqno&amp;lt;/code&amp;gt; (the peer has not been&lt;br /&gt;
told the most recent cumulative ACK) AND the rcv side has been&lt;br /&gt;
active within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer not aged out).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt; is honoured only when &amp;lt;code&amp;gt;snd_cr.lwe &amp;amp;lt; edge&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;edge =&lt;br /&gt;
snd_fin_seqno&amp;lt;/code&amp;gt; after FIN has been sent in stream mode and&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno&amp;lt;/code&amp;gt; otherwise (data or FIN still in flight).  The drain&lt;br /&gt;
itself runs in &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&#039;s &amp;lt;code&amp;gt;while (FRCTI_LINGERING)&amp;lt;/code&amp;gt; loop, not in&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The fd is single-reader / single-writer (documented in the&lt;br /&gt;
manpages).  &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt; pumps &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; on every call (via&lt;br /&gt;
&amp;lt;code&amp;gt;flow_wait_window&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;flow_drain_rx_nb&amp;lt;/code&amp;gt;) and additionally blocks on&lt;br /&gt;
&amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; when the send window is closed.  A pure-writer thread thus&lt;br /&gt;
consumes ACKs without a dedicated reader.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 15. Heritage and adopted techniques ==&lt;br /&gt;
&lt;br /&gt;
Delta-t (Watson, 1981) is the primary heritage; FRCP descends from&lt;br /&gt;
the delta-t protocol family via the Recursive InterNetwork&lt;br /&gt;
Architecture (RINA; Day, &amp;quot;Patterns in Network Architecture&amp;quot;, 2008,&lt;br /&gt;
ch. 9).  Timer-based connection management&lt;br /&gt;
(no SYN/FIN handshake, per-flow state born on first DATA and&lt;br /&gt;
reclaimed after &amp;lt;code&amp;gt;t_mpl + a + r&amp;lt;/code&amp;gt; of silence), the DRF marker, and the&lt;br /&gt;
&amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; timers all come from delta-t.  See Watson,&lt;br /&gt;
&amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection&lt;br /&gt;
Management&amp;quot;, Computer Networks 5 (1981).&lt;br /&gt;
&lt;br /&gt;
The unified &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive and its&lt;br /&gt;
multi-axis QoS-cube argument ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) also come from RINA&lt;br /&gt;
(Day 2008, ch. 6; Grasa et al., &amp;quot;IRATI: investigating RINA as an&lt;br /&gt;
alternative to TCP/IP&amp;quot;, Computer Networks 92 (2015)) - reliability,&lt;br /&gt;
ordering, CRC presence, and encryption are flow attributes, not&lt;br /&gt;
separate sockets or protocols.&lt;br /&gt;
&lt;br /&gt;
The table below summarises additional adopted techniques and their&lt;br /&gt;
references.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FRCP mechanism !! Heritage !! Reference / note&lt;br /&gt;
|-&lt;br /&gt;
| Random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; || TCP ISN || RFC 6528 (Gont &amp;amp;amp; Bellovin, 2012).  QUIC PN-space reset (RFC 9000 sec. 12.3) is a structural analogue.&lt;br /&gt;
|-&lt;br /&gt;
| Cumulative ACK, left-window-edge advance || TCP || RFC 793 / RFC 9293&lt;br /&gt;
|-&lt;br /&gt;
| Receive window with non-shrink rule || TCP || RFC 793 sec. 3.7 / RFC 9293 sec. 3.8.6; RFC 1122 sec. 4.2.2.16 for the explicit non-shrink prohibition&lt;br /&gt;
|-&lt;br /&gt;
| Modular &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; arithmetic (&amp;lt;code&amp;gt;before&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;after&amp;lt;/code&amp;gt; helpers) || TCP || RFC 793 sec. 3.3 / RFC 9293 sec. 3.4&lt;br /&gt;
|-&lt;br /&gt;
| Selective ACK block list || TCP || RFC 2018 (Mathis et al., 1996).  Encoded as a typed FRCP packet rather than a TCP option, so framing is closer to QUIC ACK frames.  D-SACK (RFC 2883) carried in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt;; see [[#1.3. SACK payload|Section 1.3]].&lt;br /&gt;
|-&lt;br /&gt;
| NewReno-careful recovery with &amp;lt;code&amp;gt;recovery_high&amp;lt;/code&amp;gt; gate || TCP || RFC 6582 (Henderson et al., 2012); QUIC builds on the same model in RFC 9002 sec. 7.3.2.  Cwnd half absent (CC in IPCP).&lt;br /&gt;
|-&lt;br /&gt;
| RACK reordering window for fast retransmit || TCP || RFC 8985 (Cheng et al., 2021).  FRCP &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor against &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; collapse; matches RFC 8985 sec. 6.2 and Linux &amp;lt;code&amp;gt;tcp_rack_reo_wnd&amp;lt;/code&amp;gt;.  DSACK-driven &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; (sec. 6.2 step 4) is adopted; see [[#1.3. SACK payload|Section 1.3]] for the wire encoding.  The hybrid RACK-or-&amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; trigger from RFC 8985 sec. 6.2 step 4 is adopted ([[#8. Retransmission|Section 8]]).  QUIC&#039;s analogue in RFC 9002 sec. 6.1.2 uses &amp;lt;code&amp;gt;max(srtt, latest_rtt)&amp;lt;/code&amp;gt; as the base.&lt;br /&gt;
|-&lt;br /&gt;
| Karn&#039;s algorithm: no RTT sample on retransmits, RTO-collapse freeze || TCP || Karn &amp;amp;amp; Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, SIGCOMM 1987; RFC 6298 sec. 3.&lt;br /&gt;
|-&lt;br /&gt;
| RTO formula &amp;lt;code&amp;gt;RTO = max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt; || TCP || RFC 6298 (Paxson et al., 2011).  &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; = 250 us is below RFC 6298 sec. 2.4&#039;s 1 s SHOULD-floor - a recursive-layer choice.&lt;br /&gt;
|-&lt;br /&gt;
| Linux asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator (default) || Linux kernel || &amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;if(delta&amp;amp;lt;0) m&amp;amp;gt;&amp;amp;gt;=3&amp;lt;/code&amp;gt; dampening is a kernel divergence from RFC 6298.  RFC 6298 EWMA available behind a compile flag.&lt;br /&gt;
|-&lt;br /&gt;
| Delayed ACK with rate suppression || TCP || RFC 813 (Clark, 1982); RFC 1122 sec. 4.2.3.2; RFC 5681 sec. 4.2.  Single-deadline coalescing rather than &amp;quot;ack-every-other-segment&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| Zero-window-probe / persist-timer analogue (RDVS) || TCP || RFC 1122 sec. 4.2.2.17 / RFC 9293 sec. 3.8.6.1.  RDVS solicits an FC reply, distinct from QUIC &amp;lt;code&amp;gt;DATA_BLOCKED&amp;lt;/code&amp;gt; (RFC 9000 sec. 19.12), which is one-way notification.  &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; give-up departs from TCP.&lt;br /&gt;
|-&lt;br /&gt;
| Multiplexed control on a single PCI || SCTP / QUIC || SCTP chunk bundling (RFC 9260 sec. 6.10); QUIC frame multiplexing (RFC 9000 sec. 12.4).  Cleaner fit than TCP&#039;s separate-flag-bits design.&lt;br /&gt;
|-&lt;br /&gt;
| ACK ranges as multiple discontiguous acked blocks || QUIC || QUIC ACK frame (RFC 9000 sec. 19.3).  FRCP SACK is conceptually QUIC-frame-shaped even though encoded as absolute &amp;lt;code&amp;gt;[start,end]&amp;lt;/code&amp;gt; pairs.&lt;br /&gt;
|-&lt;br /&gt;
| Nonce-authenticated active RTT / liveness probing (RTTP) || QUIC &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;PATH_RESPONSE&amp;lt;/code&amp;gt; (RFC 9000 sec. 8.2, sec. 19.17, sec. 19.18).  WebRTC ICE consent-freshness (RFC 7675) is the same pattern.  QUIC&#039;s nonce is 8 octets; FRCP chooses 16.&lt;br /&gt;
|-&lt;br /&gt;
| Probing distinct from keepalive || QUIC || KA timer answers &amp;quot;peer alive?&amp;quot;, RTTP answers &amp;quot;path measurable?&amp;quot;, as in QUIC PING (RFC 9000 sec. 19.2) vs &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| Bare KA + ACK keepalive packets || QUIC / SCTP || QUIC PING (RFC 9000 sec. 19.2); SCTP HEARTBEAT / HEARTBEAT-ACK (RFC 9260 sec. 8.3).  SCTP HEARTBEAT also carries an opaque echoed blob, structurally similar to FRCP RTTP.&lt;br /&gt;
|-&lt;br /&gt;
| (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) fragment-role bits ([[#7.2. Fragmentation and reassembly|Section 7.2]]) || SCTP || RFC 9260 sec. 3.3.1 DATA chunk B/E bits encode the same four states (&amp;lt;code&amp;gt;B+E=SOLE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;B-only=FIRST&amp;lt;/code&amp;gt;, neither=&amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E-only=LAST&amp;lt;/code&amp;gt;).  Each fragment carries its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;/TSN and is independently retransmitted.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-offset reassembly (Sections [[#1.5. Stream PCI extension|1.5]], [[#16. Stream-mode flows|16]]) || QUIC || QUIC STREAM frame (RFC 9000 sec. 19.8) uses Offset + Length varints; FRCP uses fixed 32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;.  One stream per flow vs QUIC&#039;s many streams multiplexed.&lt;br /&gt;
|-&lt;br /&gt;
| FIN end-of-stream marker (Sections [[#1.2. Flag bits|1.2]], [[#16. Stream-mode flows|16]]) || TCP / QUIC || TCP FIN flag (RFC 9293 sec. 3.1) closes one half of the byte stream; QUIC STREAM frame FIN bit (RFC 9000 sec. 19.8) does the same per stream with an immutable final-size invariance (RFC 9000 sec. 4.5: the final size is fixed once observed).  FRCP&#039;s FIN consumes one packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (not one byte of stream space) and is idempotent on the sender side.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-credit flow control ([[#16. Stream-mode flows|Section 16]]) || QUIC || &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; (RFC 9000 sec. 4.1, sec. 19.10).  FRCP projects a per-flow byte budget onto the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;.  Single stream per flow collapses QUIC&#039;s &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.&lt;br /&gt;
|-&lt;br /&gt;
| Header protection (encrypted seqnos) || QUIC || QUIC RFC 9001 sec. 5.4 applies header protection on top of AEAD to mask the packet number.  FRCP&#039;s per-flow AEAD wrap ([[#16. Stream-mode flows|Section 16]]) is wider: it encrypts the entire PCI including &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; because the IPCP below already routes, so no destination connection-ID needs to stay in clear (cf. RFC 9000 sec. 5.2).&lt;br /&gt;
|-&lt;br /&gt;
| Two-bit fragment role polarity || SCTP || The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair follows SCTP B/E (begin = 1 / end = 1) rather than IPv4 MF (RFC 791 sec. 3.2), which has the inverse polarity (MF = 1 means NOT last).&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal reliability / ordering axes ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) || SCTP || PR-SCTP (RFC 3758, per-message partial reliability) and SCTP DATA U-bit (RFC 9260 sec. 3.3.1, per-message unordered) are the closest precedents for decoupling reliability from ordering; FRCP sets them per-flow rather than per-message.&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal CRC (&amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt;) || UDP-Lite || RFC 3828 (Larzon et al., 2004) lets the sender pick a per-packet Checksum Coverage and the receiver enforce a locally configured minimum (no in-band negotiation; sec. 3.1, sec. 3.3).  FRCP gates a full CRC trailer on &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; at flow setup.  Contrast TCP / SCTP (mandatory checksum) and QUIC (AEAD subsumes CRC).&lt;br /&gt;
|-&lt;br /&gt;
| Setup-time service negotiation || DCCP / SCTP / QUIC || DCCP Service Codes (RFC 4340 sec. 8.1.2, RFC 5595); SCTP INIT parameters (RFC 9260 sec. 3.3.2); QUIC transport parameters (RFC 9000 sec. 7.4).  All negotiate service properties at connection setup; only RINA&#039;s QoS cube exposes them as an orthogonal vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.1. Original to FRCP (no clean prior art) ===&lt;br /&gt;
&lt;br /&gt;
* Pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]): receiver-driven nudge exploiting &amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;.  Closest analogues are SCTP Gap Ack Blocks (RFC 9260 sec. 3.3.4) and DCCP Ack Vector (RFC 4340 sec. 11.4) - both let the receiver describe gaps to the sender, but neither targets the cross-epoch / pre-DRF case.&lt;br /&gt;
* &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; window-probe give-up: neither TCP (persist-timer probes until application or R2 abort, RFC 9293 sec. 3.8.6.1) nor QUIC has an explicit FC-give-up counter.  A recursive-network choice: outer layers can drop the flow.&lt;br /&gt;
* Skip-past-gap reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]): SCTP fragments and reassembles every flow regardless of reliability/ordering, using its own per-stream reassembly queue; QUIC fragments via STREAM offsets.  FRCP fragments best-effort flows too, but the receiver drops the broken prefix the moment a later run-start (&amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; role) is visible inside the &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;-wide reorder ring - no IP-frag-style timeout, no SCTP-style explicit abort.  If no later run-start arrives within the ring, &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt; and the partial run keeps its slots; the next inspect retries.  The trade-off: a permanently-lost &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; in a long isolated run holds slots until either a later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; appears in the ring or the writer stops, at which point the slots are reclaimed on flow teardown.&lt;br /&gt;
* Reassembly deferred to consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]), message mode only (&amp;lt;code&amp;gt;qos.service == SVC_MESSAGE&amp;lt;/code&amp;gt;): SCTP (RFC 9260 sec. 6.9), QUIC (RFC 9000 sec. 2.2), and TCP (RFC 9293) all hold reassembly state at the receive boundary.  FRCP message-mode leaves fragments in the shared-memory ring until &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; pulls and lands the SDU directly in the caller&#039;s buffer.  Stream mode ([[#16. Stream-mode flows|Section 16]]) uses the standard QUIC-style direct ring placement on receive and does not defer.  The optimisation is enabled by the Shared-Memory Subsystem (SSM) packet-buffer ring (see &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt; at [[#1.1. PCI header|Section 1.1]]); the analogue is OS-level scatter-gather I/O (&amp;lt;code&amp;gt;recvmsg+iovec&amp;lt;/code&amp;gt;), not a transport-layer prior art.&lt;br /&gt;
* TLP-equivalent tail-loss recovery (RFC 8985 sec. 7; RFC 9002 sec. 6.2): FRCP does not emit an explicit Tail Loss Probe packet, but the same goal is met implicitly by RACK loss detection ([[#8. Retransmission|Section 8]]) firing on a non-advancing cumulative ACK once the head-of-line slot ages past the RACK reorder window &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; - well below &amp;lt;code&amp;gt;RTO = max(2 * SRTT, SRTT + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.  A receiver-driven nudge is also available via the pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.2. Not adopted ===&lt;br /&gt;
&lt;br /&gt;
* Slow start, congestion window (cwnd), Additive Increase / Multiplicative Decrease (AIMD), NewReno cwnd inflation.  Congestion control lives in the IPCP CA policies and is driven by Explicit Congestion Notification (ECN, RFC 3168).&lt;br /&gt;
* Nagle / silly-window-syndrome (SWS) avoidance (RFC 896, RFC 1122 sec. 4.2.3.4).  (Deferred work, not adopted in the current spec.)&lt;br /&gt;
* TCP Timestamps (RFC 7323) / Protection Against Wrapped Sequences (PAWS) - RTT measurement uses RTTP, not per-segment timestamps.  A peer-supplied timestamp echoed on every ACK lets a malicious peer drive the &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; estimate arbitrarily low, collapsing the RTO and triggering a self-inflicted retransmit storm.  RTTP confines RTT measurement to nonce-authenticated probe round-trips, where a forged echo is rejected before it can reach the estimator.&lt;br /&gt;
* ECN (Explicit Congestion Notification) response inside FRCP (consumed by IPCP Congestion Avoidance / CA).&lt;br /&gt;
* IP-style fragment-offset reassembly (RFC 791 sec. 3.2; RFC 8200 sec. 4.5).  Message-mode FRCP relies on the FRCT &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; reorder ring keyed by &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (shared by FRTX and best-effort flows) to put fragments back in order; no separate offset field is needed and no IP-style hole-list reassembly buffer is kept.  Stream-mode FRCP does carry &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte offsets ([[#1.5. Stream PCI extension|Section 1.5]]) for direct ring placement on receive.&lt;br /&gt;
* QUIC STREAM offset+length framing on &#039;&#039;every&#039;&#039; flow (RFC 9000 sec. 19.8).  Message-mode FRCP uses the SCTP-style B/E flag-bit encoding (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) and skips the offsets; stream-mode FRCP adopts the QUIC offset model (heritage table above).&lt;br /&gt;
&lt;br /&gt;
== 16. Stream-mode flows ==&lt;br /&gt;
&lt;br /&gt;
When a flow is allocated with &amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt; both peers&lt;br /&gt;
switch to byte-stream semantics, layered on top of the FRTX reorder&lt;br /&gt;
machinery already described in Sections [[#6. Receive path|6]]-[[#8. Retransmission|8]].&lt;br /&gt;
&lt;br /&gt;
=== 16.1. Send ===&lt;br /&gt;
&lt;br /&gt;
The sender splits the caller&#039;s octets into chunks of at most&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt; octets (Sections [[#1.1. PCI header|1.1]]&lt;br /&gt;
and [[#1.5. Stream PCI extension|1.5]]).  Each chunk is one DATA packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte range copied from a monotonic stream counter.&lt;br /&gt;
In stream mode &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST be transmitted as&lt;br /&gt;
zero; the per-byte position is carried by the &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt;&lt;br /&gt;
extension instead.&lt;br /&gt;
&lt;br /&gt;
End-of-stream is signalled with a 0-byte DATA packet that has &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;&lt;br /&gt;
(bit 12) set, emitted on the FIN triggers listed in [[#1.2. Flag bits|Section 1.2]]&lt;br /&gt;
(WR-half close, &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, and any other path that yields the&lt;br /&gt;
final byte).  The sender MUST emit at most one FIN per flow; its&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; MUST equal &amp;lt;code&amp;gt;[final-byte, final-byte)&amp;lt;/code&amp;gt; (i.e., empty&lt;br /&gt;
interval at the final byte position; final-size invariance,&lt;br /&gt;
analogous to QUIC RFC 9000 sec. 4.5).  Idempotency is enforced by&lt;br /&gt;
an &amp;lt;code&amp;gt;snd_fin_sent&amp;lt;/code&amp;gt; guard.&lt;br /&gt;
&lt;br /&gt;
=== 16.2. Receive ===&lt;br /&gt;
&lt;br /&gt;
On arrival the receiver places the payload directly into a per-flow&lt;br /&gt;
byte-indexed receive ring of width &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; (octets) at the position&lt;br /&gt;
indicated by &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, with a two-segment &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; across the ring&lt;br /&gt;
boundary if needed.  Receipt is recorded in the FRTX reorder&lt;br /&gt;
machinery ([[#6.2. Locked main path|Section 6.2]]) augmented with the packet&#039;s &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;, and&lt;br /&gt;
FIN bit per slot.  When a packet&#039;s &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; front-overlaps&lt;br /&gt;
bytes already at or below the byte high-water mark, the overlap is&lt;br /&gt;
trimmed before placement so the same byte is never written twice.&lt;br /&gt;
After stashing, the receiver advances &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; and the byte high-water&lt;br /&gt;
mark across any newly-contiguous prefix.  Each slot advanced MUST&lt;br /&gt;
satisfy &amp;lt;code&amp;gt;start == the last-delivered slot&#039;s end&amp;lt;/code&amp;gt;; a slot whose&lt;br /&gt;
&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; does not equal that &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; is silently dropped at delivery time&lt;br /&gt;
(the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is consumed, no stream bytes contributed) and the high-&lt;br /&gt;
water mark does not advance past it.  The stream byte-stream&lt;br /&gt;
stalls at that point - there is no flow-tear-down on mismatch.&lt;br /&gt;
This filters spliced or off-path-injected slots that fall in&lt;br /&gt;
window without strong cryptographic authentication.&lt;br /&gt;
&lt;br /&gt;
A FIN slot marks end-of-stream at advance time only if its byte&lt;br /&gt;
position equals the last-delivered slot&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;; otherwise the FIN&lt;br /&gt;
is ignored and the corresponding &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; occupies a slot but&lt;br /&gt;
contributes no stream bytes.  No packet buffer is held after the&lt;br /&gt;
ring copy.&lt;br /&gt;
&lt;br /&gt;
=== 16.3. Read ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns up to &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt; octets from the contiguous prefix&lt;br /&gt;
&amp;lt;code&amp;gt;[next, high-water)&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;next&amp;lt;/code&amp;gt; is the byte the application has&lt;br /&gt;
already consumed up to and &amp;lt;code&amp;gt;high-water&amp;lt;/code&amp;gt; is the rightmost contiguous&lt;br /&gt;
byte received.  When the stream is fully drained AND end-of-stream&lt;br /&gt;
(EOS) was observed (&amp;lt;code&amp;gt;next == EOS&amp;lt;/code&amp;gt; byte position), &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns&lt;br /&gt;
0 (&amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) - the same shape POSIX &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt; uses on TCP after a peer&lt;br /&gt;
FIN.&lt;br /&gt;
&lt;br /&gt;
=== 16.4. Flow control ===&lt;br /&gt;
&lt;br /&gt;
ACK / SACK / RACK / RTO machinery is unchanged; the FRTX reorder&lt;br /&gt;
ring is reused as a per-&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; received-bitmap.  Let &amp;lt;code&amp;gt;per_pkt =&lt;br /&gt;
(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt;, the maximum stream-&lt;br /&gt;
byte payload one DATA packet can carry ([[#16.1. Send|Section 16.1]]).  The&lt;br /&gt;
receive window advertised in FC is clamped so the byte window&lt;br /&gt;
(&amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt;) cannot be overrun: the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is at most&lt;br /&gt;
&amp;lt;code&amp;gt;rcv_cr.lwe + ring_sz / per_pkt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC byte-credit flow-control model&lt;br /&gt;
(&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt;, RFC 9000 sec. 4.1 and sec. 19.10) projected onto&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; space.  With one stream per flow there is no &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; /&lt;br /&gt;
&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.  Receiver-side silly-window-syndrome&lt;br /&gt;
(SWS) avoidance (RFC 9293 sec. 3.8.6.2.2) is achieved by combining&lt;br /&gt;
the consume-time &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; bump with the global non-shrink rule from&lt;br /&gt;
[[#11. Flow control|Section 11]].&lt;br /&gt;
&lt;br /&gt;
=== 16.5. Security considerations ===&lt;br /&gt;
&lt;br /&gt;
Threat model.  An attacker that can observe (on-path passive) or&lt;br /&gt;
predict (off-path blind) the flow&#039;s seqnos and byte offsets on an&lt;br /&gt;
unencrypted stream flow can inject DATA or FIN at any in-window&lt;br /&gt;
position.  The in-line consistency checks above (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; == prior&lt;br /&gt;
&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; on advance; FIN MUST be 0-byte; FIN MUST sit at the final&lt;br /&gt;
byte position) realise the spirit of RFC 5961&#039;s &amp;quot;sequence-window&lt;br /&gt;
plus exact-position match for control bits&amp;quot; without an explicit&lt;br /&gt;
challenge-ACK probe; they make a few specific blind attack shapes&lt;br /&gt;
harder but are not cryptographic authentication. This is&lt;br /&gt;
comparable to TCP without the TCP Authentication Option (TCP-AO,&lt;br /&gt;
RFC 5925), tighter than a&lt;br /&gt;
pre-RFC-5961 TCP stack, and roughly equivalent to a modern&lt;br /&gt;
RFC 5961 stack against blind off-path injection - none of these&lt;br /&gt;
help once the attacker can sniff. TLS over TCP (RFC 8446)&lt;br /&gt;
encrypts only the TCP payload and leaves TCP seqnos, ACKs, FIN,&lt;br /&gt;
and RST in the clear, so TLS does NOT defend against TCP-header-&lt;br /&gt;
level injection; QUIC (RFC 9000) hides packet numbers under&lt;br /&gt;
header protection (RFC 9001 sec. 5.4), so this specific weakness&lt;br /&gt;
does not apply to QUIC.&lt;br /&gt;
&lt;br /&gt;
Mitigation: AEAD.  When the flow has encryption enabled the&lt;br /&gt;
recommended AEAD ciphers (AES-GCM, RFC 5288; or ChaCha20-Poly1305,&lt;br /&gt;
RFC 8439) wrap the entire FRCP packet on the wire - PCI, stream&lt;br /&gt;
extension, body, and the CRC trailer when &amp;lt;code&amp;gt;ber == 0&amp;lt;/code&amp;gt; - under a&lt;br /&gt;
per-flow symmetric key derived from the flow&#039;s own key exchange&lt;br /&gt;
([[#1.1. PCI header|Section 1.1]]).  The AEAD tag (~2^-128 forgery probability)&lt;br /&gt;
dominates the CRC (~2^-32) for integrity in this mode but the CRC&lt;br /&gt;
trailer is currently retained inside the wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
Implementations MUST NOT rely on the security properties below&lt;br /&gt;
when a non-AEAD cipher (e.g. AES-CTR alone) is negotiated; non-&lt;br /&gt;
AEAD modes provide confidentiality only and the threat-model&lt;br /&gt;
claims do not hold.&lt;br /&gt;
&lt;br /&gt;
With an AEAD cipher in use, seqnos, byte offsets, and the FIN bit&lt;br /&gt;
are both authenticated and confidential. Against an off-path or&lt;br /&gt;
on-path-passive attacker this is:&lt;br /&gt;
&lt;br /&gt;
* Stronger than TCP+TLS (TCP header in the clear).&lt;br /&gt;
* Stronger than TCP+TCP-AO (header authenticated but visible).&lt;br /&gt;
* Comparable to IPsec ESP transport mode (RFC 4303), which similarly authenticates and encrypts the upper-layer header plus payload, and to QUIC packet protection (RFC 9001 sec. 5), with the difference that QUIC must leave the destination connection ID in the clear for routing whereas FRCP relies on the IPCP below for delivery and can therefore encrypt its entire PCI.&lt;br /&gt;
&lt;br /&gt;
Keying granularity.  FRCP runs key exchange (&amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt;) per flow, so&lt;br /&gt;
each &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; yields independent symmetric keys.  This is&lt;br /&gt;
finer-grained than QUIC (per-connection, RFC 9001, where one&lt;br /&gt;
handshake covers all multiplexed streams) and finer-grained than&lt;br /&gt;
typical IPsec deployment (per-host-pair Security Associations,&lt;br /&gt;
SAs).  Forward secrecy follows from the &amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt; when an ephemeral&lt;br /&gt;
Diffie-Hellman exchange (DHE), or a hybrid mode (classical DH +&lt;br /&gt;
post-quantum Key Encapsulation Mechanism / KEM), is selected.&lt;br /&gt;
&lt;br /&gt;
Replay protection.  The AEAD layer itself does NOT carry an&lt;br /&gt;
explicit anti-replay window (unlike IPsec ESP, RFC 4303 sec.&lt;br /&gt;
3.4.3, or DTLS, RFC 9147 sec. 4.5.1).  For FRCP-engaged flows the&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space duplicate-suppression in [[#6.2. Locked main path|Section 6.2]] rejects replayed&lt;br /&gt;
DATA after the AEAD strips the wrap, because the AEAD authenticates&lt;br /&gt;
the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a replay re-presents an old &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; that is then&lt;br /&gt;
discarded either as a duplicate (still inside the receive window)&lt;br /&gt;
or as outside the receive window, depending on how far &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; has&lt;br /&gt;
advanced since the original packet was delivered.  RAW&lt;br /&gt;
(&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) flows have no FRCP layer and therefore&lt;br /&gt;
no replay protection at the AEAD layer either; deployments that&lt;br /&gt;
need replay rejection on RAW flows MUST provide it at a higher&lt;br /&gt;
layer.&lt;br /&gt;
&lt;br /&gt;
Layering.  The AEAD wrap sits below FRCP on the data path, so&lt;br /&gt;
RAW best-effort flows (&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;, the UDP-equivalent&lt;br /&gt;
service of [[#2.2. Service modes (orthogonal axes)|Section 2.2]]) inherit the same per-flow integrity +&lt;br /&gt;
confidentiality scope as FRCP-engaged flows - whatever the IPCP&lt;br /&gt;
and FRCP (if any) put on the wire is what the AEAD authenticates.&lt;br /&gt;
No DTLS-equivalent layering is required for confidentiality and&lt;br /&gt;
integrity; replay protection above AEAD is a separate concern as&lt;br /&gt;
noted above.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 17. References ==&lt;br /&gt;
&lt;br /&gt;
This section lists the IETF documents, published works, and&lt;br /&gt;
source-code references cited inline elsewhere in this document.&lt;br /&gt;
IETF documents are cited inline as &amp;quot;RFC NNNN sec. X.Y&amp;quot;; books,&lt;br /&gt;
journal papers, and source-code references are cited inline by&lt;br /&gt;
author and year (or by file and function name) and are listed&lt;br /&gt;
here for convenience.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.1. IETF documents ===&lt;br /&gt;
&lt;br /&gt;
;[RFC 791]&lt;br /&gt;
:J. Postel, &amp;quot;Internet Protocol&amp;quot;, STD 5, RFC 791, September 1981.&lt;br /&gt;
;[RFC 793]&lt;br /&gt;
:J. Postel, &amp;quot;Transmission Control Protocol&amp;quot;, STD 7, RFC 793, September 1981.  Obsoleted by RFC 9293.&lt;br /&gt;
;[RFC 813]&lt;br /&gt;
:D. D. Clark, &amp;quot;Window and Acknowledgement Strategy in TCP&amp;quot;, RFC 813, July 1982.&lt;br /&gt;
;[RFC 896]&lt;br /&gt;
:J. Nagle, &amp;quot;Congestion Control in IP/TCP Internetworks&amp;quot;, RFC 896, January 1984.&lt;br /&gt;
;[RFC 1122]&lt;br /&gt;
:R. Braden (ed.), &amp;quot;Requirements for Internet Hosts -- Communication Layers&amp;quot;, STD 3, RFC 1122, October 1989.&lt;br /&gt;
;[RFC 2018]&lt;br /&gt;
:M. Mathis, J. Mahdavi, S. Floyd, A. Romanow, &amp;quot;TCP Selective Acknowledgment Options&amp;quot;, RFC 2018, October 1996.&lt;br /&gt;
;[RFC 2119]&lt;br /&gt;
:S. Bradner, &amp;quot;Key words for use in RFCs to Indicate Requirement Levels&amp;quot;, BCP 14, RFC 2119, March 1997.&lt;br /&gt;
;[RFC 2883]&lt;br /&gt;
:S. Floyd, J. Mahdavi, M. Mathis, M. Podolsky, &amp;quot;An Extension to the Selective Acknowledgement (SACK) Option for TCP&amp;quot;, RFC 2883, July 2000.&lt;br /&gt;
;[RFC 3758]&lt;br /&gt;
:R. Stewart, M. Ramalho, Q. Xie, M. Tuexen, P. Conrad, &amp;quot;Stream Control Transmission Protocol (SCTP) Partial Reliability Extension&amp;quot;, RFC 3758, May 2004.&lt;br /&gt;
;[RFC 3828]&lt;br /&gt;
:L-A. Larzon, M. Degermark, S. Pink, L-E. Jonsson (ed.), G. Fairhurst (ed.), &amp;quot;The Lightweight User Datagram Protocol (UDP-Lite)&amp;quot;, RFC 3828, July 2004.&lt;br /&gt;
;[RFC 4303]&lt;br /&gt;
:S. Kent, &amp;quot;IP Encapsulating Security Payload (ESP)&amp;quot;, RFC 4303, December 2005.&lt;br /&gt;
;[RFC 4340]&lt;br /&gt;
:E. Kohler, M. Handley, S. Floyd, &amp;quot;Datagram Congestion Control Protocol (DCCP)&amp;quot;, RFC 4340, March 2006.&lt;br /&gt;
;[RFC 5288]&lt;br /&gt;
:J. Salowey, A. Choudhury, D. McGrew, &amp;quot;AES Galois Counter Mode (GCM) Cipher Suites for TLS&amp;quot;, RFC 5288, August 2008.&lt;br /&gt;
;[RFC 5595]&lt;br /&gt;
:G. Fairhurst, &amp;quot;The Datagram Congestion Control Protocol (DCCP) Service Codes&amp;quot;, RFC 5595, September 2009.&lt;br /&gt;
;[RFC 5681]&lt;br /&gt;
:M. Allman, V. Paxson, E. Blanton, &amp;quot;TCP Congestion Control&amp;quot;, RFC 5681, September 2009.&lt;br /&gt;
;[RFC 5925]&lt;br /&gt;
:J. Touch, A. Mankin, R. Bonica, &amp;quot;The TCP Authentication Option&amp;quot;, RFC 5925, June 2010.&lt;br /&gt;
;[RFC 5961]&lt;br /&gt;
:A. Ramaiah, R. Stewart, M. Dalal, &amp;quot;Improving TCP&#039;s Robustness to Blind In-Window Attacks&amp;quot;, RFC 5961, August 2010.&lt;br /&gt;
;[RFC 6298]&lt;br /&gt;
:V. Paxson, M. Allman, J. Chu, M. Sargent, &amp;quot;Computing TCP&#039;s Retransmission Timer&amp;quot;, RFC 6298, June 2011.&lt;br /&gt;
;[RFC 6528]&lt;br /&gt;
:F. Gont, S. Bellovin, &amp;quot;Defending against Sequence Number Attacks&amp;quot;, RFC 6528, February 2012.  Obsoletes RFC 1948.&lt;br /&gt;
;[RFC 6582]&lt;br /&gt;
:T. Henderson, S. Floyd, A. Gurtov, Y. Nishida, &amp;quot;The NewReno Modification to TCP&#039;s Fast Recovery Algorithm&amp;quot;, RFC 6582, April 2012.&lt;br /&gt;
;[RFC 7323]&lt;br /&gt;
:D. Borman, B. Braden, V. Jacobson, R. Scheffenegger (ed.), &amp;quot;TCP Extensions for High Performance&amp;quot;, RFC 7323, September 2014.&lt;br /&gt;
;[RFC 7675]&lt;br /&gt;
:M. Perumal, D. Wing, R. Ravindranath, T. Reddy, M. Thomson, &amp;quot;Session Traversal Utilities for NAT (STUN) Usage for Consent Freshness&amp;quot;, RFC 7675, October 2015.&lt;br /&gt;
;[RFC 8174]&lt;br /&gt;
:B. Leiba, &amp;quot;Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words&amp;quot;, BCP 14, RFC 8174, May 2017.&lt;br /&gt;
;[RFC 8200]&lt;br /&gt;
:S. Deering, R. Hinden, &amp;quot;Internet Protocol, Version 6 (IPv6) Specification&amp;quot;, STD 86, RFC 8200, July 2017.&lt;br /&gt;
;[RFC 8439]&lt;br /&gt;
:Y. Nir, A. Langley, &amp;quot;ChaCha20 and Poly1305 for IETF Protocols&amp;quot;, RFC 8439, June 2018.&lt;br /&gt;
;[RFC 8446]&lt;br /&gt;
:E. Rescorla, &amp;quot;The Transport Layer Security (TLS) Protocol Version 1.3&amp;quot;, RFC 8446, August 2018.&lt;br /&gt;
;[RFC 8985]&lt;br /&gt;
:Y. Cheng, N. Cardwell, N. Dukkipati, P. Jha, &amp;quot;The RACK-TLP Loss Detection Algorithm for TCP&amp;quot;, RFC 8985, February 2021.&lt;br /&gt;
;[RFC 9000]&lt;br /&gt;
:J. Iyengar (ed.), M. Thomson (ed.), &amp;quot;QUIC: A UDP-Based Multiplexed and Secure Transport&amp;quot;, RFC 9000, May 2021.&lt;br /&gt;
;[RFC 9001]&lt;br /&gt;
:M. Thomson (ed.), S. Turner (ed.), &amp;quot;Using TLS to Secure QUIC&amp;quot;, RFC 9001, May 2021.&lt;br /&gt;
;[RFC 9002]&lt;br /&gt;
:J. Iyengar (ed.), I. Swett (ed.), &amp;quot;QUIC Loss Detection and Congestion Control&amp;quot;, RFC 9002, May 2021.&lt;br /&gt;
;[RFC 9147]&lt;br /&gt;
:E. Rescorla, H. Tschofenig, N. Modadugu, &amp;quot;The Datagram Transport Layer Security (DTLS) Protocol Version 1.3&amp;quot;, RFC 9147, April 2022.&lt;br /&gt;
;[RFC 9260]&lt;br /&gt;
:R. Stewart, M. Tuexen, K. Nielsen, &amp;quot;Stream Control Transmission Protocol&amp;quot;, RFC 9260, June 2022.  Obsoletes RFC 4960.&lt;br /&gt;
;[RFC 9293]&lt;br /&gt;
:W. Eddy (ed.), &amp;quot;Transmission Control Protocol (TCP)&amp;quot;, STD 7, RFC 9293, August 2022.  Obsoletes RFC 793 and several follow-ons; updates RFC 1122 and others.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.2. Books and journal papers ===&lt;br /&gt;
&lt;br /&gt;
;[Day08]&lt;br /&gt;
:J. Day, &amp;quot;Patterns in Network Architecture: A Return to Fundamentals&amp;quot;, Prentice Hall, 2008.&lt;br /&gt;
;[Grasa15]&lt;br /&gt;
:E. Grasa et al., &amp;quot;IRATI: investigating RINA as an alternative to TCP/IP&amp;quot;, Computer Networks, Vol. 92, December 2015.&lt;br /&gt;
;[KP87]&lt;br /&gt;
:P. Karn, C. Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, ACM SIGCOMM, August 1987.&lt;br /&gt;
;[Wat81]&lt;br /&gt;
:R. W. Watson, &amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection Management&amp;quot;, Computer Networks, Vol. 5, 1981.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.3. Source-code references ===&lt;br /&gt;
&lt;br /&gt;
;[Linux-RTT]&lt;br /&gt;
:&amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt; of the Linux kernel, defining the asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; variance update used as FRCP&#039;s default RTT estimator ([[#12. RTT estimation|Section 12]]).  Line-stable browseable copy at https://elixir.bootlin.com/linux/latest/source/net/ipv4/tcp_input.c.&lt;br /&gt;
&lt;br /&gt;
[[Category:Protocols]]&lt;br /&gt;
[[Category:Ouroboros internals]]&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1916</id>
		<title>Flow and Retransmission Control Protocol</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1916"/>
		<updated>2026-05-17T14:47:17Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 9. Pre-DRF NACK */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:FRCP - Flow and Retransmission Control Protocol}}&lt;br /&gt;
&lt;br /&gt;
FRCP runs end-to-end between two peers over a flow.  It delivers&lt;br /&gt;
reliability, in-order delivery, flow control, and liveness.&lt;br /&gt;
Congestion Control (CC) is not in FRCP - that lives in the IPC&lt;br /&gt;
Process (IPCP) Congestion Avoidance (CA) policies, orthogonal to&lt;br /&gt;
FRCP.  Flow allocation, naming, and IPCP lifecycle are handled by&lt;br /&gt;
the IPC Resource Manager daemon (IRMd).&lt;br /&gt;
&lt;br /&gt;
FRCT (Flow and Retransmission Control Task) is the libouroboros&lt;br /&gt;
implementation of FRCP; the task lives in &amp;lt;code&amp;gt;src/lib/frct.c&amp;lt;/code&amp;gt;.  The&lt;br /&gt;
remainder of this document describes the FRCP wire protocol and the&lt;br /&gt;
behaviour FRCT realises.  Code symbols retain the &amp;lt;code&amp;gt;FRCT_&amp;lt;/code&amp;gt; prefix&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, ...) because they belong to the implementing&lt;br /&gt;
task; this document references them verbatim.&lt;br /&gt;
&lt;br /&gt;
The keywords &amp;quot;MUST&amp;quot;, &amp;quot;MUST NOT&amp;quot;, &amp;quot;REQUIRED&amp;quot;, &amp;quot;SHALL&amp;quot;, &amp;quot;SHALL NOT&amp;quot;,&lt;br /&gt;
&amp;quot;SHOULD&amp;quot;, &amp;quot;SHOULD NOT&amp;quot;, &amp;quot;RECOMMENDED&amp;quot;, &amp;quot;MAY&amp;quot;, and &amp;quot;OPTIONAL&amp;quot; in&lt;br /&gt;
this document are to be interpreted as described in &amp;lt;code&amp;gt;BCP&amp;lt;/code&amp;gt; 14 (Best&lt;br /&gt;
Current Practice; RFC 2119, RFC 8174) when, and only when, they&lt;br /&gt;
appear in all capitals.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:Unsigned 32-bit / 8-bit integers (kernel-C style).&lt;br /&gt;
;&amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:Nanoseconds.&lt;br /&gt;
&lt;br /&gt;
Modular sequence-number comparators (32-bit, modulo 2^32):&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;before(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;(int32_t)(a - b) &amp;amp;lt; 0&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;after(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;before(b, a)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Used throughout for &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; ordering checks.&lt;br /&gt;
&lt;br /&gt;
Round-Trip Time (RTT) abbreviations used throughout:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;&lt;br /&gt;
:Smoothed RTT estimate (RFC 6298).&lt;br /&gt;
;&amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt;&lt;br /&gt;
:Mean deviation of RTT (Linux variance estimator).&lt;br /&gt;
;&amp;lt;code&amp;gt;EWMA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Exponentially Weighted Moving Average.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTO&amp;lt;/code&amp;gt;&lt;br /&gt;
:Retransmission Timeout, &amp;lt;code&amp;gt;max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Timer-bound symbols &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer, ACK delay) and &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer,&lt;br /&gt;
retransmission window) are defined in [[#8. Retransmission|Section 8]]; &amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; (Maximum&lt;br /&gt;
Packet Lifetime) is introduced in [[#2.1. Per-flow state|Section 2.1]] (the &amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; field)&lt;br /&gt;
with heritage in [[#15. Heritage and adopted techniques|Section 15]].&lt;br /&gt;
&lt;br /&gt;
Wire-format diagrams follow the IETF convention: bit 0 is the&lt;br /&gt;
leftmost (most significant) bit and fields are in network byte&lt;br /&gt;
order unless stated otherwise.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1. Wire format ==&lt;br /&gt;
&lt;br /&gt;
=== 1.1. PCI header ===&lt;br /&gt;
&lt;br /&gt;
Fixed 16-octet base Protocol-Control Information (PCI) header&lt;br /&gt;
prefixed to every FRCP packet (RFC convention: bit 0 leftmost,&lt;br /&gt;
most-significant bit first).  All multi-byte fields except &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;&lt;br /&gt;
are in network byte order; &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt; is an opaque 16-bit value that&lt;br /&gt;
the receiver recomputes from the wire bytes and compares to the&lt;br /&gt;
in-place &amp;lt;code&amp;gt;pci-&amp;gt;hcs&amp;lt;/code&amp;gt; read, so its on-wire byte order need only&lt;br /&gt;
match between peers running compatible builds.  DATA packets on&lt;br /&gt;
stream-mode flows carry an additional 8-octet extension (see&lt;br /&gt;
[[#1.5. Stream PCI extension|Section 1.5]]); SACK and RTTP carry their own payloads after the&lt;br /&gt;
base PCI.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |             flags             |              hcs              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            window                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            seqno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            ackno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                     payload (variable) ...&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt;&lt;br /&gt;
:feature/type bitmap (see [[#1.2. Flag bits|Section 1.2]]).&lt;br /&gt;
;&amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;&lt;br /&gt;
:CRC-16-CCITT-FALSE Header Check Sequence (HCS) over &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; (+ stream extension when present); the two octets of the &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt; field itself are omitted from the CRC input.  Verified on receive before any flag-driven dispatch.&lt;br /&gt;
;&amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt;&lt;br /&gt;
:receiver-advertised right window edge (valid iff FC).&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-flow sequence number.&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;&lt;br /&gt;
:cumulative Acknowledgement (ACK) (valid iff ACK).&lt;br /&gt;
&lt;br /&gt;
A single packet can simultaneously carry DATA + ACK + FC (Flow&lt;br /&gt;
Control) + RXM (Retransmission) by ORing flag bits; the PCI&lt;br /&gt;
multiplexes control on the same wire frame in the spirit of SCTP&lt;br /&gt;
chunk bundling (RFC 9260 sec. 6.10) and QUIC frame multiplexing&lt;br /&gt;
(RFC 9000 sec. 12.4).  DATA-bearing packets carry the caller&#039;s&lt;br /&gt;
payload after the PCI; SACK (Selective Acknowledgement) and RTTP&lt;br /&gt;
(Round-Trip Time Probe) carry their own typed payloads after the&lt;br /&gt;
PCI.&lt;br /&gt;
&lt;br /&gt;
Optional framing (per-flow, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]]).  On the wire, the&lt;br /&gt;
order from inside out is:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Layer !! Scope&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| The FRCP packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body + CRC-32 ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| CRC-32 covers the body only (PCI is in HCS); appended iff &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; on DATA, or on every SACK packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ AEAD-wrap of above ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| Iff Authenticated Encryption with Associated Data (AEAD) is enabled.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* HCS in the PCI covers the header fields on every packet and is verified before any flag-driven dispatch.&lt;br /&gt;
* The CRC-32 trailer (IEEE 802.3 / zlib reflected polynomial &amp;lt;code&amp;gt;0xEDB88320&amp;lt;/code&amp;gt;, init &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;, xor-out &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;) covers the body on DATA when &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; and on every SACK packet; the trailer is written as a raw &amp;lt;code&amp;gt;uint32_t&amp;lt;/code&amp;gt; (the same convention as &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;: opaque on the wire as long as both peers run compatible builds).  The PCI is not under the CRC (Cyclic Redundancy Check) because the HCS already protects it.  It is appended before AEAD encryption and therefore rides inside the AEAD wrap when both are active; the AEAD tag (~2^-128 forgery probability) dominates the CRC (~2^-32) for integrity in that mode but the CRC trailer is currently retained.&lt;br /&gt;
* When encryption is enabled, the entire (possibly-CRC&#039;d) FRCP packet is wrapped with AEAD inside the shared-memory packet buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt;); the packet grows by the AEAD overhead, namely a leading nonce / Initialization Vector (IV) of &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_ivsz&amp;lt;/code&amp;gt;) and a trailing authentication tag of &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_tagsz&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Both CRC and AEAD are layered around the FRCP wire format and are not visible to the FRCP machinery itself.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.2. Flag bits ===&lt;br /&gt;
&lt;br /&gt;
Flag bits are numbered most-significant-bit first to match the wire&lt;br /&gt;
diagram (bit numbering per [[#1.1. PCI header|Section 1.1]]; bit 0 is the MSB of the&lt;br /&gt;
16-bit &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; field and lands at wire-position 0 in network byte&lt;br /&gt;
order).  Bits 13..15 are reserved and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Bit !! Mask !! Name !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| 0 || &amp;lt;code&amp;gt;0x8000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt; || Carries caller payload&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &amp;lt;code&amp;gt;0x4000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DRF&amp;lt;/code&amp;gt; || Data Run Flag: start of a fresh run&lt;br /&gt;
|-&lt;br /&gt;
| 2 || &amp;lt;code&amp;gt;0x2000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt; || Acknowledgement: &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field valid&lt;br /&gt;
|-&lt;br /&gt;
| 3 || &amp;lt;code&amp;gt;0x1000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt; || Negative ACK; &amp;lt;code&amp;gt;seqno = arrival_seqno-1&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || &amp;lt;code&amp;gt;0x0800&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt; || Flow Control: &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; field valid (&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || &amp;lt;code&amp;gt;0x0400&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt; || Rendezvous probe (window-closed)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || &amp;lt;code&amp;gt;0x0200&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; || First Fragment (role bit 0; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || &amp;lt;code&amp;gt;0x0100&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; || Last Fragment (role bit 1; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 8 || &amp;lt;code&amp;gt;0x0080&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RXM&amp;lt;/code&amp;gt; || Retransmission&lt;br /&gt;
|-&lt;br /&gt;
| 9 || &amp;lt;code&amp;gt;0x0040&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt; || Selective ACK block list in payload&lt;br /&gt;
|-&lt;br /&gt;
| 10 || &amp;lt;code&amp;gt;0x0020&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt; || RTT Probe / echo (payload follows)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || &amp;lt;code&amp;gt;0x0010&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt; || Keepalive&lt;br /&gt;
|-&lt;br /&gt;
| 12 || &amp;lt;code&amp;gt;0x0008&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt; || End-of-stream marker (stream mode)&lt;br /&gt;
|-&lt;br /&gt;
| 13-15 || -- || -- || Reserved (MUST be zero)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair encodes the fragment role of a DATA-bearing&lt;br /&gt;
Service Data Unit (SDU), SCTP-style begin/end flags (RFC 9260&lt;br /&gt;
sec. 3.3.1):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FFGM !! LFGM !! Role&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || Sole / un-fragmented SDU (begin AND end)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || First fragment of a multi-fragment SDU&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Middle fragment&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Last fragment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Each fragment is carried in its own FRCP packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;;&lt;br /&gt;
FRTX (the FRCT Retransmission service mode, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]])&lt;br /&gt;
recovers individual fragments via the normal Retransmission Timeout&lt;br /&gt;
(RTO) / SACK / Recent Acknowledgement (RACK, RFC 8985) path.  The&lt;br /&gt;
receiver reassembles the SDU at consume time once the contiguous&lt;br /&gt;
[FIRST .. LAST] run has fully arrived.  On non-DATA packets the role&lt;br /&gt;
bits are unused and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
In stream mode (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;, see [[#16. Stream-mode flows|Section 16]]) there are&lt;br /&gt;
no SDU boundaries to encode, so &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST&lt;br /&gt;
be transmitted as zero.  End-of-stream uses a dedicated bit (&amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;,&lt;br /&gt;
bit 12) carried on a 0-byte DATA packet, emitted at write-half close&lt;br /&gt;
(&amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;FLOWFRDONLY&amp;lt;/code&amp;gt;), during linger drain, and at &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;;&lt;br /&gt;
emission is idempotent (first call wins).  After contiguous delivery&lt;br /&gt;
of the FIN-bearing slot, the receiver latches &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt; at the FIN&#039;s&lt;br /&gt;
start offset; &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns 0 (end-of-file, &amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) once buffered&lt;br /&gt;
bytes have been drained up to &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt;.  Per-byte position is&lt;br /&gt;
carried by the [start, end) extension ([[#1.5. Stream PCI extension|Section 1.5]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.3. SACK payload ===&lt;br /&gt;
&lt;br /&gt;
A SACK packet has the &amp;lt;code&amp;gt;FRCT_ACK | FRCT_FC | FRCT_SACK&amp;lt;/code&amp;gt; flag bits set&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI, the&lt;br /&gt;
payload is a 2-octet block count (network byte order), 2 octets of&lt;br /&gt;
padding to 4-byte align the block list, then &amp;lt;code&amp;gt;n_blocks&amp;lt;/code&amp;gt; pairs of&lt;br /&gt;
32-bit start/end seqnos describing &#039;&#039;present&#039;&#039; (received) ranges&lt;br /&gt;
above the cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |           n_blocks            |        padding (2 octets)     |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[0]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            end[0]                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[1]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
                       ... n_blocks pairs total ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;n_blocks &amp;amp;lt;= SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; (2048).  The per-flow effective cap is&lt;br /&gt;
further bounded by &amp;lt;code&amp;gt;(frag_mtu - PCI - 4) / 8&amp;lt;/code&amp;gt; blocks per packet; SACK&lt;br /&gt;
packets carry no stream extension, so PCI here is the 16-octet base&lt;br /&gt;
header even on stream-mode flows.&lt;br /&gt;
&lt;br /&gt;
Wire invariant: every block produced by the receiver, except an&lt;br /&gt;
optional leading Duplicate SACK (D-SACK) block as described below,&lt;br /&gt;
describes a range strictly above the cumulative ACK carried in the&lt;br /&gt;
PCI &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field (&amp;lt;code&amp;gt;after(start[i], ackno)&amp;lt;/code&amp;gt;).  This makes the D-SACK&lt;br /&gt;
convention below unambiguous; the receiver-side builder MUST&lt;br /&gt;
preserve it.&lt;br /&gt;
&lt;br /&gt;
Duplicate SACK (D-SACK, RFC 2883) is signalled in-band: no flag&lt;br /&gt;
bit, no extra framing.  Modular seqno arithmetic uses the&lt;br /&gt;
&amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators defined in the Notation block.&lt;br /&gt;
&amp;lt;code&amp;gt;Block[0]&amp;lt;/code&amp;gt; carries a D-SACK report when either:&lt;br /&gt;
&lt;br /&gt;
;case 1 (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;before(blocks[0].start, ackno)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ackno - blocks[0].start&amp;lt;/code&amp;gt; is within &amp;lt;code&amp;gt;MAX_DSACK_LAG&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;== RQ_SIZE&amp;lt;/code&amp;gt;).  A single duplicate &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; observed below the cumulative ACK.&lt;br /&gt;
;case 2 (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;blocks[0]&amp;lt;/code&amp;gt; is a sub-range of some &amp;lt;code&amp;gt;blocks[i&amp;gt;0]&amp;lt;/code&amp;gt; (not exactly equal).  Reports a duplicate of an in-window &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; that the same packet&#039;s remaining SACK blocks already describe as received.&lt;br /&gt;
&lt;br /&gt;
Senders that do not implement D-SACK process &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; through the&lt;br /&gt;
normal SACK-mark loop and the existing clamp-and-skip path makes&lt;br /&gt;
case-1 a no-op (&amp;lt;code&amp;gt;start &amp;amp;lt; snd_cr.lwe&amp;lt;/code&amp;gt; clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt;, the inner&lt;br /&gt;
loop then skips &amp;lt;code&amp;gt;k == snd_cr.lwe&amp;lt;/code&amp;gt;) and case-2 idempotent (same slots&lt;br /&gt;
NULL&#039;d twice).  D-SACK-aware senders feed the report into the RACK&lt;br /&gt;
&amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; scaler (RFC 8985 sec. 6.2 step 4): bump on receipt&lt;br /&gt;
(cap 20), halve once per 16 cumulatively-ACK&#039;d seqnos since the&lt;br /&gt;
most recent D-SACK arrival or halve event, reset to 1 on an RTO&lt;br /&gt;
timer fire at the head-of-line.  D-SACK alone never enters&lt;br /&gt;
NewReno-careful recovery (see [[#8. Retransmission|Section 8]]); only non-D-SACK blocks&lt;br /&gt;
count as gaps.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.4. RTTP payload ===&lt;br /&gt;
&lt;br /&gt;
An RTTP (Round-Trip Time Probe) packet has only the &amp;lt;code&amp;gt;FRCT_RTTP&amp;lt;/code&amp;gt; flag&lt;br /&gt;
set (bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI,&lt;br /&gt;
the payload is 24 octets (packed):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          probe_id                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          echo_id                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +                  nonce (16 octets, echoed verbatim)           +&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:sender counter, 0 on reply, 0 reserved.&lt;br /&gt;
;&amp;lt;code&amp;gt;echo_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:peer&#039;s &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;, 0 on outbound probe.&lt;br /&gt;
;&amp;lt;code&amp;gt;nonce&amp;lt;/code&amp;gt;&lt;br /&gt;
:random, echoed unmodified, memcmp&#039;d to defeat spoof.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.5. Stream PCI extension ===&lt;br /&gt;
&lt;br /&gt;
A stream-mode flow (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;) carries an extra&lt;br /&gt;
8-octet extension after the 16-octet base PCI on every DATA packet&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            start                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                             end                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset of the first payload byte in the stream.&lt;br /&gt;
;&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset one past the last payload byte; &amp;lt;code&amp;gt;end - start&amp;lt;/code&amp;gt; equals the on-wire payload length.&lt;br /&gt;
&lt;br /&gt;
Total stream-mode PCI for DATA packets is 24 octets (16 base + 8&lt;br /&gt;
extension); control packets (SACK, RTTP, bare ACK, KA, etc.) retain&lt;br /&gt;
the 16-octet base PCI.  Stream mode MUST be negotiated at flow&lt;br /&gt;
allocation; the extension is present iff stream mode is in use,&lt;br /&gt;
never on a per-packet basis.  Both peers MUST treat &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; as&lt;br /&gt;
monotonic 32-bit byte offsets; when a slot reaches the head of the&lt;br /&gt;
contiguous run with &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; not equal to the prior packet&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; the&lt;br /&gt;
slot is silently dropped at delivery time ([[#16. Stream-mode flows|Section 16]]) rather&lt;br /&gt;
than rejected at stash.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC STREAM-frame reassembly model (RFC 9000 sec. 19.8):&lt;br /&gt;
each packet carries its packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (this PCI&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; field) and a&lt;br /&gt;
separate stream byte position (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;).  Separating the two&lt;br /&gt;
avoids TCP&#039;s conflation of packet identity with byte position which&lt;br /&gt;
forces Karn&#039;s algorithm for Round-Trip Time (RTT) sampling (no RTT&lt;br /&gt;
sample on retransmits, RFC 6298 sec. 3); FRCP applies the&lt;br /&gt;
Karn-equivalent gate via a combination of per-packet &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;,&lt;br /&gt;
per-slot &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; flags, and a sample-fence &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; (see [[#2.1. Per-flow state|Section 2.1]]&lt;br /&gt;
and [[#12. RTT estimation|Section 12]]).  FRCP&#039;s fixed-32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; wrap at 4 GiB of&lt;br /&gt;
wire bytes, narrower than QUIC&#039;s 62-bit varint offset (cf. RFC 9000&lt;br /&gt;
sec. 16); the on-wire wrap is handled by the same modular &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;&lt;br /&gt;
/ &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators ([[#1.3. SACK payload|Section 1.3]]) FRCP uses for seqnos, which&lt;br /&gt;
remain unambiguous as long as the in-flight byte window stays&lt;br /&gt;
strictly under 2 GiB (the half-range of the signed-int32 difference&lt;br /&gt;
in &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;).  The default per-flow ring is 1 MiB; the&lt;br /&gt;
implementation caps &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; at 128 MiB (&amp;lt;code&amp;gt;FRCT_STREAM_RING_SZ_MAX&amp;lt;/code&amp;gt;),&lt;br /&gt;
well below the 2 GiB half-range bound.  The runtime byte counters&lt;br /&gt;
exposed via FUSE (Filesystem in Userspace) in the Ouroboros&lt;br /&gt;
Resource Information Base (RIB, a virtual-filesystem introspection&lt;br /&gt;
bridge) are platform &amp;lt;code&amp;gt;size_t&amp;lt;/code&amp;gt; and do not wrap on 64-bit hosts.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2. Per-flow state and service modes ==&lt;br /&gt;
&lt;br /&gt;
=== 2.1. Per-flow state ===&lt;br /&gt;
&lt;br /&gt;
Each flow keeps a sender control record and a receiver control&lt;br /&gt;
record:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: oldest unacked &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (cumulative ACK boundary as seen by sender); rcv: next in-order &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; expected&lt;br /&gt;
;&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: peer-advertised right window edge; rcv: locally-advertised right window edge&lt;br /&gt;
;&amp;lt;code&amp;gt;cflags&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-direction feature flags: retransmission (&amp;lt;code&amp;gt;FRCTFRTX&amp;lt;/code&amp;gt;), receiver flow control (&amp;lt;code&amp;gt;FRCTFRESCNTL&amp;lt;/code&amp;gt;), linger-on-close (&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt;); see &amp;lt;code&amp;gt;&amp;amp;lt;ouroboros/fccntl.h&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: next &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; to send; rcv: force-ACK trigger - set on a stale or dup DATA so the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; emits a fresh cumulative ACK&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; counter for standalone ACK-bearing control packets (delayed ACK, SACK, final ACK on dealloc); not bumped on piggybacked ACK riding a DATA packet (which uses the DATA &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;).  Used by wire-dup ACK detection; rcv: incoming-ACK dedup tracker&lt;br /&gt;
;&amp;lt;code&amp;gt;act&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:last activity (used by inactivity / DRF)&lt;br /&gt;
;&amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:inactivity threshold; sender = &amp;lt;code&amp;gt;3*mpl + a + r + 1s&amp;lt;/code&amp;gt;, receiver = &amp;lt;code&amp;gt;2*mpl + a + r + 1s&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;mpl&amp;lt;/code&amp;gt; is the Maximum Packet Lifetime (delta-t terminology; see [[#15. Heritage and adopted techniques|Section 15]]); &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; are the FRCT a-timer and r-timer bounds (see [[#8. Retransmission|Section 8]]).  The asymmetry is load-bearing for pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
The sender holds a per-slot ring &amp;lt;code&amp;gt;snd_slots[RQ_SIZE]&amp;lt;/code&amp;gt; keyed by&lt;br /&gt;
&amp;lt;code&amp;gt;(seqno mod RQ_SIZE)&amp;lt;/code&amp;gt;.  Each slot tracks its retransmit entry (&amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;),&lt;br /&gt;
last-send timestamp, and retransmit flag bits: &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (a&lt;br /&gt;
retransmit is pending or has fired, gates the next RTT sample&lt;br /&gt;
under Karn) and &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (one-shot fast-retransmit staged for&lt;br /&gt;
this loss event).&lt;br /&gt;
&lt;br /&gt;
The receiver holds a parallel reorder ring &amp;lt;code&amp;gt;rcv_slots[RQ_SIZE]&amp;lt;/code&amp;gt;&lt;br /&gt;
(referred to as &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; in prose) holding stashed out-of-order&lt;br /&gt;
packet-buffer indexes; both FRTX and best-effort flows share this&lt;br /&gt;
path.  The invariant &amp;lt;code&amp;gt;rwe - lwe &amp;amp;lt;= RQ_SIZE&amp;lt;/code&amp;gt; holds: on each consume&lt;br /&gt;
the receiver advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; by the consumed count, capping the&lt;br /&gt;
receive window at &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; slots.&lt;br /&gt;
&lt;br /&gt;
A separate fence variable &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; is bumped on every retransmit&lt;br /&gt;
(timer-fire, SACK-driven, fast-rxm, NACK-driven) and on every&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; ([[#4. Sequence-number rotation (DRF)|Section 4]]) to mark the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; range whose RTT samples&lt;br /&gt;
MUST be discarded.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 2.2. Service modes (orthogonal axes) ===&lt;br /&gt;
&lt;br /&gt;
FRCP exposes its wire features as a vector of independent QoS&lt;br /&gt;
axes selected at flow allocation time.  All flows go through the&lt;br /&gt;
same &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive; the &amp;lt;code&amp;gt;qosspec_t&amp;lt;/code&amp;gt; passed&lt;br /&gt;
in determines which protocol machinery engages on the wire.  This&lt;br /&gt;
contrasts with the POSIX BSD socket model where TCP and UDP&lt;br /&gt;
require different socket types (&amp;lt;code&amp;gt;SOCK_STREAM&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;SOCK_DGRAM&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
The axes:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = unordered (no FRCP engagement: raw datagrams, no PCI on the wire, UDP-equivalent at this layer); 1 = message-ordered (FRCP engaged; SDU boundaries preserved across fragmentation); 2 = stream (byte-oriented, no SDU boundaries; FRTX required)&lt;br /&gt;
;&amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = lossless service requested: FRTX retransmit machinery engages ([[#8. Retransmission|Section 8]]); MUST be 0 for &amp;lt;code&amp;gt;service=2&amp;lt;/code&amp;gt;.  Non-zero = best-effort, FRTX off.&lt;br /&gt;
;&amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bit Error Rate tolerance.  0 = error-free service requested: a CRC trailer is appended after the body of DATA packets and verified on receive (added / checked outside the FRCP PCI; see [[#1.1. PCI header|Section 1.1]]).  Non-zero = peer accepts errors; trailer omitted.  SACK control packets carry a CRC32 trailer regardless of &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; gate applies to DATA only.&lt;br /&gt;
;&amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt;&lt;br /&gt;
:Peer-timeout (ms); 0 disables the keepalive timer.  Independent of FRCP engagement.&lt;br /&gt;
&lt;br /&gt;
Encryption is a separate per-flow attribute set at flow setup;&lt;br /&gt;
when enabled it wraps the FRCP packet (PCI + body, plus the CRC&lt;br /&gt;
trailer if any) under AEAD, expanding the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; by &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt;&lt;br /&gt;
octets (nonce / tag).  The CRC trailer is currently kept inside&lt;br /&gt;
the AEAD wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
&lt;br /&gt;
Reachable combinations exported by &amp;lt;code&amp;gt;include/ouroboros/qos.h&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Cube !! &amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; !! Engaged&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw&amp;lt;/code&amp;gt; || 0 || 1 || 1 || Raw passthrough&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw_safe&amp;lt;/code&amp;gt; || 0 || 1 || 0 || Raw + CRC trailer&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; || 1 || 1 || 1 || FRCP, no FRTX, no CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt_safe&amp;lt;/code&amp;gt; || 1 || 1 || 0 || FRCP, no FRTX, CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_msg&amp;lt;/code&amp;gt; || 1 || 0 || 0 || FRCP + FRTX&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_stream&amp;lt;/code&amp;gt; || 2 || 0 || 0 || FRCP + FRTX, stream&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Forced couplings actually enforced by the public API:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;service == SVC_STREAM&amp;lt;/code&amp;gt; (2) requires &amp;lt;code&amp;gt;loss == 0&amp;lt;/code&amp;gt;; &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;flow_accept&amp;lt;/code&amp;gt; reject the pair otherwise with &amp;lt;code&amp;gt;-EINVAL&amp;lt;/code&amp;gt;.&lt;br /&gt;
* FRTX requires FRCP engagement (&amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;); requesting &amp;lt;code&amp;gt;loss = 0&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;service = SVC_RAW&amp;lt;/code&amp;gt; is structurally a no-op because no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; is created.&lt;br /&gt;
* The &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; build flag globally forces &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;.  Note: this flag defaults to ON, so default builds ship with CRC disabled until &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; is set to OFF.&lt;br /&gt;
&lt;br /&gt;
Caveat: the API does NOT force &amp;lt;code&amp;gt;ber = 0&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; has &amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;, which means the PCI&lt;br /&gt;
itself is not CRC-protected on that cube; the HCS ([[#1.1. PCI header|Section 1.1]])&lt;br /&gt;
remains the only integrity check on the header.&lt;br /&gt;
&lt;br /&gt;
The FRCP-no-FRTX regime (&amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;loss &amp;amp;gt; 0&amp;lt;/code&amp;gt;) is meaningful&lt;br /&gt;
and live: sequence numbering, in-order delivery, flow-control&lt;br /&gt;
advertisement, KA, DRF rotation, and SDU fragmentation /&lt;br /&gt;
reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]) all run.  Lost packets are dropped&lt;br /&gt;
rather than retransmitted; a permanently-lost mid-fragment is&lt;br /&gt;
dropped via skip-past-gap once a later SDU is visible in the&lt;br /&gt;
reorder ring.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. Protocol parameters ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Slot ring / rcv window width&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;START_WINDOW&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Initial &amp;lt;code&amp;gt;rwe-lwe&amp;lt;/code&amp;gt; after rotate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MAX(250 us build-tunable, 1&amp;amp;lt;&amp;amp;lt;RXMQ_RES)&amp;lt;/code&amp;gt;; per-flow via &amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;FRCTSRTOMIN&amp;lt;/code&amp;gt;).  Default ~1 ms with &amp;lt;code&amp;gt;RXMQ_RES=20&amp;lt;/code&amp;gt;. || RTO floor; also floored at the retransmit-wheel resolution (~1 ms by default).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt; || 20 || Backoff shift cap&lt;br /&gt;
|-&lt;br /&gt;
| RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MIN(reo_wnd_mult * min_RTT/4, SRTT)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; scales on D-SACK, cap 20 || Reorder window; per RFC 8985 sec. 6.2; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per sec. 6.2 step 4&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; || 300 s (5 min, Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; windowed re-anchor&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_WND_MULT_MAX&amp;lt;/code&amp;gt; || 20 (RFC 8985 sec. 6.2 step 4) || &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_DECAY_PKTS&amp;lt;/code&amp;gt; || 16 (RFC 8985 sec. 6.2 step 4 / &amp;lt;code&amp;gt;RACK.reo_wnd_persist&amp;lt;/code&amp;gt;) || Fresh-ACK&#039;d seq count per halving&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_DSACK_LAG&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || D-SACK sanity cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTT_QUARANTINE&amp;lt;/code&amp;gt; || 32 (&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; steps) || NewReno gate pad&lt;br /&gt;
|-&lt;br /&gt;
| SACK rate-limit || &amp;lt;code&amp;gt;SACK_MIN_GAP_NS&amp;lt;/code&amp;gt; (250 us, fixed) || Min SACK gap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; || 2048 (wire cap; per-flow capped at &amp;lt;code&amp;gt;(frag_mtu-PCI-4)/8&amp;lt;/code&amp;gt;) || Per-SACK block cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; || 32 || Per-pass staged retransmit cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; || 3 (RFC 8985 default) || Hybrid fast-rxm trigger ([[#8. Retransmission|Section 8]])&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MDEV_MUL&amp;lt;/code&amp;gt; || 2 (build-tunable via &amp;lt;code&amp;gt;FRCT_RTO_MDEV_MULTIPLIER&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; shift in &amp;lt;code&amp;gt;RTO = srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| RTTP nonce || 16 octets || Echoed verbatim&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; || 8 || In-flight probes&lt;br /&gt;
|-&lt;br /&gt;
| RTT clamp || &amp;lt;code&amp;gt;16 * srtt&amp;lt;/code&amp;gt; || Probe-sample upper bound (ACK-derived RTT samples gated by Karn / recovery only)&lt;br /&gt;
|-&lt;br /&gt;
| Cold-probe cadence || 100 ms (rx-driven; see [[#12. RTT estimation|Section 12]]) || Pre-&amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; RTTP rate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt; || 100 ms || RDVS emit cadence&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; || 1 s || RDVS give-up&lt;br /&gt;
|-&lt;br /&gt;
| Delayed-ACK fire || &amp;lt;code&amp;gt;2 * TICTIME&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt; = FRCT tick granularity, default 5 ms; &amp;lt;code&amp;gt;2*TICTIME = 10 ms&amp;lt;/code&amp;gt; by default) || Fired after the first in-order DATA arrival; tick is build-tunable&lt;br /&gt;
|-&lt;br /&gt;
| NACK send cooldown || &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; when an &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; sample exists, else 100 ms || Pre-DRF NACK rate-limit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_SDU&amp;lt;/code&amp;gt; || 1 MiB || Max reassembled SDU; configurable per flow&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The per-flow fragment Maximum Transmission Unit (MTU) is computed&lt;br /&gt;
at flow setup from the lower IPCP&#039;s mtu minus encryption&lt;br /&gt;
&amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; and CRC trailer; there is no FRCT-level default or&lt;br /&gt;
environment-variable override.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 4. Sequence-number rotation (DRF) ==&lt;br /&gt;
&lt;br /&gt;
The DRF (Data Run Flag) bit on an outbound packet means &amp;quot;this is&lt;br /&gt;
the start of a fresh data run&amp;quot; and is set whenever the sender has&lt;br /&gt;
nothing in flight (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Independently of that, if the sender has been idle longer than&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.inact&amp;lt;/code&amp;gt; AND the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;),&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; rolls a random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; before the send and&lt;br /&gt;
resets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    snd_cr.seqno  = random()&lt;br /&gt;
    snd_cr.lwe    = snd_cr.seqno&lt;br /&gt;
    snd_cr.rwe    = snd_cr.seqno + START_WINDOW&lt;br /&gt;
    rtt_lwe       = snd_cr.seqno&lt;br /&gt;
    in_recovery   = false   (recovery state, see Section 8)&lt;br /&gt;
    recovery_high = snd_cr.seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The receiver, on observing rcv-side inactivity&lt;br /&gt;
(&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), requires a DRF on the next&lt;br /&gt;
DATA packet; otherwise it replies with a rate-limited NACK (see&lt;br /&gt;
below).  Non-DATA control packets pass through without the DRF&lt;br /&gt;
requirement.  On DRF the receiver releases the &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots and&lt;br /&gt;
rebases&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    rcv_cr.lwe   = seqno&lt;br /&gt;
    rcv_cr.rwe   = seqno + RQ_SIZE&lt;br /&gt;
    rcv_cr.seqno = seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the inactive packet has DATA but no DRF, a rate-limited NACK is&lt;br /&gt;
fired back to the sender (cooldown per [[#3. Protocol parameters|Section 3]]); non-DATA stale&lt;br /&gt;
arrivals fall through to normal processing (no NACK, no drop).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 5. Send path ==&lt;br /&gt;
&lt;br /&gt;
# If the SDU exceeds &amp;lt;code&amp;gt;(frag_mtu - data_hdr_len)&amp;lt;/code&amp;gt;, the caller (&amp;lt;code&amp;gt;dev.c&amp;lt;/code&amp;gt;) fans it out into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - data_hdr_len))&amp;lt;/code&amp;gt; fragments, each emitted via &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; as its own DATA packet with a per-fragment role ([[#7.2. Fragmentation and reassembly|Section 7.2]]); both FRTX and best-effort flows fragment.  Raw flows (no FRCP engagement, &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) carry no PCI and return &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; for any SDU larger than one packet at the layer below.  An SDU that fits in a single packet is sent as SOLE.  &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; reserves PCI head room; sets DATA, plus DRF when the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
# &amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; if past sender inactivity and the pipe is empty ([[#4. Sequence-number rotation (DRF)|Section 4]]).&lt;br /&gt;
# Advertise FC (&amp;lt;code&amp;gt;pci.window = frcti_advert_rwe(frcti)&amp;lt;/code&amp;gt;, i.e. &amp;lt;code&amp;gt;rcv_cr.rwe&amp;lt;/code&amp;gt; clamped to &amp;lt;code&amp;gt;rcv_cr.lwe + ring_seq_cap&amp;lt;/code&amp;gt; in stream mode) when the receiver side is recent: &amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt; rcv_cr.inact&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Reliable mode (FRTX): leave &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; where it is; reset the slot at &amp;lt;code&amp;gt;RQ_SLOT(seqno)&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;snd_slots[p].time = now&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_slots[p].flags = 0&amp;lt;/code&amp;gt;); queue an &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; (saves a packet copy, arms a wheel timer at &amp;lt;code&amp;gt;now + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;).  Piggyback ACK (&amp;lt;code&amp;gt;pci.ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) while the a-timer for the most recent received DATA packet has not yet expired (&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt;= t_a&amp;lt;/code&amp;gt;); on piggyback, set &amp;lt;code&amp;gt;rcv_cr.seqno = rcv_cr.lwe&amp;lt;/code&amp;gt; so the next delayed-ACK fire is suppressed.  See [[#8. Retransmission|Section 8]] for &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; semantics.&lt;br /&gt;
# Best-effort mode (no FRTX): advance &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; immediately (&amp;lt;code&amp;gt;snd_cr.lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_cr.rwe = snd_cr.lwe + RQ_SIZE&amp;lt;/code&amp;gt;); no retransmit state.  No send-side RTT probe is armed in this mode (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt; requires an in-flight &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, which best-effort never has); the rx-driven cold seeder in &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; is the only probe path.&lt;br /&gt;
# In reliable mode, optionally arm an RTT probe ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 6. Receive path ==&lt;br /&gt;
&lt;br /&gt;
=== 6.1. Early-exit dispatch ===&lt;br /&gt;
&lt;br /&gt;
Keepalive (KA), RTT probe (RTTP), pre-DRF NACK, and rendezvous&lt;br /&gt;
(RDVS) packets short-circuit out of &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; before the locked&lt;br /&gt;
main path; each handler takes its own lock internally.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
      incoming packet&lt;br /&gt;
            |&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | KA?     |---yes--&amp;gt; ka_rcv  ; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RTTP?   |---yes--&amp;gt; rttp_rcv; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | NACK?   |---yes--&amp;gt; nack_rcv; return  (see Section 9)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RDVS?   |---yes--&amp;gt; rdv_rcv ; return  (reply bare FC, ackno=0)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       acquire wrlock; enter locked main path&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt;&lt;br /&gt;
:refresh &amp;lt;code&amp;gt;t_ka_rcv&amp;lt;/code&amp;gt;, honour piggybacked ACK.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt;&lt;br /&gt;
:probe (echo back nonce) or echo (verify nonce, sample RTT).&lt;br /&gt;
;&amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:pre-DRF, sender-side handler.  See [[#9. Pre-DRF NACK|Section 9]].&lt;br /&gt;
;&amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt;&lt;br /&gt;
:reply with a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;); &amp;lt;code&amp;gt;rdlock&amp;lt;/code&amp;gt; only.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 6.2. Locked main path ===&lt;br /&gt;
&lt;br /&gt;
Steps below run with the per-flow &amp;lt;code&amp;gt;frcti.lock&amp;lt;/code&amp;gt; held for writing&lt;br /&gt;
(&amp;lt;code&amp;gt;pthread_rwlock_wrlock&amp;lt;/code&amp;gt;) unless noted.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;&lt;br /&gt;
:Only meaningful when the receive side is stale.  On DRF (Data Run Flag): release &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots, rebase &amp;lt;code&amp;gt;rcv_cr&amp;lt;/code&amp;gt;, continue.  On stale DATA without DRF: fire a pre-DRF NACK if cooldown allows ([[#9. Pre-DRF NACK|Section 9]]), then discard the packet; on cooldown, drop without sending a NACK (a pending cumulative ACK from &amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; may still go out).  Non-DATA, non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; pure-DRF stale arrivals fall through after the DRF rebase branch.&lt;br /&gt;
&lt;br /&gt;
;DATA-only act refresh&lt;br /&gt;
:Refresh &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; only when &amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt; is set, so that non-DATA packets never block the next DRF rebase.&lt;br /&gt;
&lt;br /&gt;
;Wire-dup gate&lt;br /&gt;
:Before flag-driven dispatch, drop wire-duplicate ACKs and wire-duplicate DATA (&amp;lt;code&amp;gt;is_dup_ack&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt;).  The DATA check is bypassed for &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;-bearing arrivals so the piggybacked ACK / SACK / FC carried on a retransmitted DATA at an already-ACK&#039;d &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is still applied; the stale-in-window branch below then drops the packet.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Drop ACKs whose &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; falls outside &amp;lt;code&amp;gt;(snd_cr.lwe, snd_cr.seqno]&amp;lt;/code&amp;gt;.  If &amp;lt;code&amp;gt;ackno == snd_cr.lwe&amp;lt;/code&amp;gt; (non-advancing cumulative ACK), drive RACK fast-retransmit consideration ([[#8. Retransmission|Section 8]]).  Otherwise advance &amp;lt;code&amp;gt;snd_cr.lwe = ackno&amp;lt;/code&amp;gt;, collapse &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; to 0 (Karn-gated by &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; on the just-acknowledged slot, the old head-of-line), reset &amp;lt;code&amp;gt;dup_thresh&amp;lt;/code&amp;gt; to 0, update &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the send-time of the slot at &amp;lt;code&amp;gt;ackno-1&amp;lt;/code&amp;gt; (consumed by RACK and SACK below), decay &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per RFC 8985 sec. 6.2 step 4, exit NewReno-careful recovery (see [[#8. Retransmission|Section 8]]) on &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt;, and feed an RTT sample if eligible ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Walk the block list.  For each block (a present range above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;) NULL out &amp;lt;code&amp;gt;snd_slots[k].rxm&amp;lt;/code&amp;gt;, clear the slot&#039;s per-send flags, and advance &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the latest send-time covered (the Forward Acknowledgement / fack equivalent, Mathis &amp;amp;amp; Mahdavi 1996); the first block whose start clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; skips this fack update so that a head-of-line clamp does not falsely advance fack.  For un-SACKed gaps below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt;, stage a retransmit per slot that is (1) still owned (&amp;lt;code&amp;gt;rxm != NULL&amp;lt;/code&amp;gt;), (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (the RFC 8985 sec. 6.2 hybrid trigger).  Mark the slot &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; and NULL the &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; at stage time.  Capped at &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; staged retransmits per receive pass; what&#039;s left rides the next SACK.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bump &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; (clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt;, never shrinks) and mark window open.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bounds-check &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; against window.  On stale-dup (&amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;), set &amp;lt;code&amp;gt;rcv_cr.seqno = seqno&amp;lt;/code&amp;gt; to force a fresh ACK on the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt;, then drop.  On accept: both FRTX and best-effort stash the packet-buffer index into &amp;lt;code&amp;gt;rq[seqno mod RQ_SIZE]&amp;lt;/code&amp;gt;.  Fragments stash unchanged - the role bits are inspected only at consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]).  On out-of-order arrival, build a SACK reply if not rate-limited (per [[#3. Protocol parameters|Section 3]]) and not deduplicated against the previous &amp;lt;code&amp;gt;(rcv_cr.lwe, n_blocks)&amp;lt;/code&amp;gt; pair; D-SACK reports always bypass the dedup.  If both rate-limit and dedup suppress the reply, neither SACK nor delayed-ACK fires (the sender picks up the gap on its next ACK).  On in-order arrival, arm the delayed-ACK timer.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; exit&lt;br /&gt;
:Releases the per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;), then calls &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; synchronously after the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; release to surface any pending cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 7. Read path and reassembly ==&lt;br /&gt;
&lt;br /&gt;
=== 7.1. Read path ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns a full reassembled SDU (Service Data Unit) via&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; on every FRCP SDU-mode flow (FRTX or best-effort);&lt;br /&gt;
stream-mode is covered in [[#16. Stream-mode flows|Section 16]].  An incomplete head-of-line&lt;br /&gt;
(HoL) run yields &amp;lt;code&amp;gt;-EAGAIN&amp;lt;/code&amp;gt;; an oversized run yields &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; (the&lt;br /&gt;
run is dropped so the flow does not stall).  On best-effort flows,&lt;br /&gt;
a permanently-lost mid-fragment is dropped as soon as a later&lt;br /&gt;
complete SDU becomes visible in the ring ([[#7.2. Fragmentation and reassembly|Section 7.2]] skip-past-&lt;br /&gt;
gap).&lt;br /&gt;
&lt;br /&gt;
Raw flows carry no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt;, so &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns the next pending&lt;br /&gt;
packet-buffer index directly, with no role-bit inspection.  (Raw&lt;br /&gt;
service is selected via &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt; at flow allocation,&lt;br /&gt;
which suppresses &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; creation.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_pdu_ready&amp;lt;/code&amp;gt; is the no-advance peek used by &amp;lt;code&amp;gt;fevent&amp;lt;/code&amp;gt; (the&lt;br /&gt;
Ouroboros flow-event multiplexer, the &amp;lt;code&amp;gt;poll(2)&amp;lt;/code&amp;gt;-equivalent on&lt;br /&gt;
flows).  It returns ready only when the head-of-line run is&lt;br /&gt;
complete and the lead packet (a Protocol Data Unit, here one FRCP&lt;br /&gt;
packet) is present at &amp;lt;code&amp;gt;rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt;; any other state&lt;br /&gt;
(including the best-effort skip-past-gap case) returns not ready,&lt;br /&gt;
and &amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; is left to drop the broken prefix and re-&lt;br /&gt;
inspect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 7.2. Fragmentation and reassembly ===&lt;br /&gt;
&lt;br /&gt;
Send side (&amp;lt;code&amp;gt;flow_write_frag&amp;lt;/code&amp;gt;).  An SDU larger than&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - PCI)&amp;lt;/code&amp;gt; is split into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - PCI))&amp;lt;/code&amp;gt;&lt;br /&gt;
fragments; each fragment is its own FRCP packet with its own&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a per-fragment role flag pair ([[#1.2. Flag bits|Section 1.2]]).  Roles are&lt;br /&gt;
assigned at emit time:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! i !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=0&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=n-1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| else || &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A mid-loop allocation or transmit failure may yield a partial&lt;br /&gt;
write: the call returns the bytes already enqueued (&amp;lt;code&amp;gt;off &amp;amp;gt; 0&amp;lt;/code&amp;gt;) or&lt;br /&gt;
the underlying error (&amp;lt;code&amp;gt;off == 0&amp;lt;/code&amp;gt;).  Best-effort flows fragment&lt;br /&gt;
identically; on the receiver, a partial run with a permanently-&lt;br /&gt;
lost fragment is dropped when a later complete SDU is visible in&lt;br /&gt;
the ring (see skip-past-gap below).  Raw flows carry no PCI and&lt;br /&gt;
refuse anything larger than the layer&#039;s user MTU (&amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Wire-level recovery is fragment-agnostic on FRTX flows: each&lt;br /&gt;
fragment&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; flows through SACK / RACK / RTO / NACK exactly&lt;br /&gt;
as for a SOLE DATA packet, and reassembly does not re-enter the&lt;br /&gt;
loss-detection path.  Best-effort flows run the same &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
machinery (DRF, FC, ACK piggyback, pre-DRF NACK emit) but queue&lt;br /&gt;
no &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; state at the sender, so a lost MID is unrecoverable;&lt;br /&gt;
skip-past-gap handles it (below).&lt;br /&gt;
&lt;br /&gt;
Receive side.  Fragments stash into &amp;lt;code&amp;gt;rq[seqno]&amp;lt;/code&amp;gt; unchanged; role bits&lt;br /&gt;
are read only at consume time.  &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;, called from&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt;, walks the ring starting at the oldest still-&lt;br /&gt;
undelivered &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;base = rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt; (equal to &amp;lt;code&amp;gt;rcv_cr.lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
only when no partial run is in progress; during a partial run &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
has already advanced past &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;).  It produces one of three&lt;br /&gt;
outcomes:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Outcome !! Cause&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELIVER (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]=SOLE&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;), or &amp;lt;code&amp;gt;rq[base]=FIRST&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; follows in slots &amp;lt;code&amp;gt;[base+1..base+n-1]&amp;lt;/code&amp;gt; with all intermediate roles in &amp;lt;code&amp;gt;{MID,FIRST,LAST}&amp;lt;/code&amp;gt; contiguous.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DROP (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; without a preceding &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;); a &amp;lt;code&amp;gt;FIRST..[non-LAST]..new-FIRST&amp;lt;/code&amp;gt; or new-&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; mid-run (drop the broken prefix with &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; = run length minus 1, so the new &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; stays); or, on best-effort flows, a gap at &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; later in the ring (drop up to the new run start).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; absent or &amp;lt;code&amp;gt;FIRST..[non-LAST]&amp;lt;/code&amp;gt; with no later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; in the ring (FRTX waits for retx; best-effort waits for arrival).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DELIVER&amp;lt;/code&amp;gt; triggers &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;: a scatter-gather &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; of the &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;&lt;br /&gt;
consecutive fragments at &amp;lt;code&amp;gt;rq[base..base+n-1]&amp;lt;/code&amp;gt; directly into the&lt;br /&gt;
caller&#039;s buffer; each per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;) is&lt;br /&gt;
released and &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; advances by &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; was already advanced&lt;br /&gt;
incrementally as each contiguous fragment arrived; &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;&lt;br /&gt;
only restores the fixed-width invariant &amp;lt;code&amp;gt;rwe == lwe + RQ_SIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
No intermediate reassembly buffer is allocated.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; past the broken prefix (releasing the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;s)&lt;br /&gt;
and pulls &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; up to the new trailing edge if needed; the next&lt;br /&gt;
consume retries from the new &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;.  Oversize or arithmetically&lt;br /&gt;
overflowing delivery (sum of fragment lengths &amp;amp;gt; &amp;lt;code&amp;gt;max_rcv_sdu&amp;lt;/code&amp;gt;, sum&lt;br /&gt;
&amp;amp;gt; caller&#039;s buffer, or running-sum overflow) also drops the run&lt;br /&gt;
with &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skip-past-gap (best-effort only).  On FRTX, a gap in the run means&lt;br /&gt;
&amp;quot;waiting for retransmit&amp;quot; and &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;.&lt;br /&gt;
On best-effort flows the gap is permanent, so &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;&lt;br /&gt;
scans forward in the ring for the next &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;; if one is&lt;br /&gt;
visible within &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;, it returns &amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; for the broken prefix and&lt;br /&gt;
the consume loop retries at the new &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;.  Memory hold is bounded&lt;br /&gt;
by &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;; the partial releases on the next consume call once a&lt;br /&gt;
later complete run exists.  Voice-like flows (one &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; per SDU)&lt;br /&gt;
see no extra wait: any later &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; makes the prior gap droppable&lt;br /&gt;
immediately.&lt;br /&gt;
&lt;br /&gt;
The choice to defer reassembly to consume time keeps the receive&lt;br /&gt;
path zero-copy: fragments stay in the shared-memory ring until&lt;br /&gt;
the application pulls, and the SDU lands directly in the caller&#039;s&lt;br /&gt;
buffer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 8. Retransmission ==&lt;br /&gt;
&lt;br /&gt;
FRCP is bounded by two delta-t-derived timers (Watson 1981, see&lt;br /&gt;
[[#15. Heritage and adopted techniques|Section 15]]):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer): upper bound on ACK delay.  An ACK for a received DATA packet MUST be emitted within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; of receipt; an attempt to send an ACK after the a-timer has expired is suppressed (the sender&#039;s RTO is already in motion).&lt;br /&gt;
* &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer): upper bound on retransmission.  A given DATA packet MUST NOT be retransmitted after &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; has elapsed since its first send (&amp;lt;code&amp;gt;t0&amp;lt;/code&amp;gt;); when the bound is hit, the flow is declared down (raising the Ouroboros asynchronous flow condition &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt;, which marks the flow dead to both endpoints) rather than retransmitted again.&lt;br /&gt;
&lt;br /&gt;
Each in-flight FRTX &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; owns one &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt;, armed in a hashed&lt;br /&gt;
timing wheel; the wheel deadline is the slot&#039;s next eligible&lt;br /&gt;
retransmit time.&lt;br /&gt;
&lt;br /&gt;
;RTO timer&lt;br /&gt;
:On fire (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;), re-emit with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, mark &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (Karn-suppress next ACK&#039;s RTT sample), and (for the head-of-line (HoL) slot only) bump &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; up to &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt;.  Wheel deadline is &amp;lt;code&amp;gt;t_send + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;.  Re-armed unless consumed.  The RTO timer also clears &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (re-arming fast-retransmit eligibility), resets &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; to 1 on a HoL fire (RFC 8985 sec. 6.2 step 4 reset clause), and marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; if its &amp;lt;code&amp;gt;frct_tx&amp;lt;/code&amp;gt; call fails.&lt;br /&gt;
&lt;br /&gt;
;r-timer guard&lt;br /&gt;
:Before any retransmit attempt, check &amp;lt;code&amp;gt;(now - t0)&amp;lt;/code&amp;gt; against &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;.  If exceeded, the slot is no longer eligible for retransmit.  Only the RTO timer (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;) treats r-timer expiry as terminal: it marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; (peer unreachable).  Fast-retransmit, SACK-driven retransmit, and NACK-driven head-of-line re-emit silently skip aged-out slots and defer the flow-down decision to the next RTO fire.&lt;br /&gt;
&lt;br /&gt;
;Fast retransmit (hybrid trigger, RFC 8985 sec. 6.2)&lt;br /&gt;
:On a non-advancing cumulative ACK with the scoreboard advanced, fire one fast retransmit when EITHER (a) the head-of-line slot&#039;s latest send is older than the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; ([[#3. Protocol parameters|Section 3]]) and not yet aged out, OR (b) the SACK &amp;lt;code&amp;gt;dup-thresh&amp;lt;/code&amp;gt; count above &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; reaches &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; (= 3, RFC 8985 sec. 6.2 step 4).  Fires at most once per non-advancing cumulative-ACK value, gated by &amp;lt;code&amp;gt;rack_fired_lwe&amp;lt;/code&amp;gt; (the &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; at which fast-retransmit last fired).  Set &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; on the slot (one-shot per-slot gate) and enter NewReno-style careful recovery (see NewReno below in this section).&lt;br /&gt;
:The RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; uses the RFC 8985 sec. 6.2 form &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor.  Before the first RTT sample seeds &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; falls back to &amp;lt;code&amp;gt;MIN(reo_wnd_mult * SRTT / 4, SRTT)&amp;lt;/code&amp;gt;, still floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt; (consistent with the windowed-minimum fallback described in [[#12. RTT estimation|Section 12]]).  &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt; is a windowed minimum over the last &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; = 5 min of RTT samples (matches the Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt; default) so a route change to a longer path eventually re-anchors the reorder window without relying on &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; growth alone.&lt;br /&gt;
&lt;br /&gt;
;SACK-driven retransmit&lt;br /&gt;
:For each gap below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt; whose slot is (1) still owned, (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (same hybrid as fast-retransmit, see [[#6.2. Locked main path|Section 6.2]]), re-emit.  Each SACK-driven retransmit re-arms a fresh &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; so a lost retransmit can still be recovered by its own RTO timer.&lt;br /&gt;
&lt;br /&gt;
;NewReno&lt;br /&gt;
:On entry, &amp;lt;code&amp;gt;recovery_high = snd_cr.seqno + RTT_QUARANTINE&amp;lt;/code&amp;gt;.  Exit when &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt; (the latter means everything sent has been acknowledged).  &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; also clears recovery.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 9. Pre-DRF NACK ==&lt;br /&gt;
&lt;br /&gt;
The two sides have different inactivity thresholds (&amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), so a receiver can detect &amp;quot;stale data run&amp;quot; before the sender&#039;s own DRF logic kicks in.  NACK is the receiver-driven nudge that asks the sender to re-transmit the head of the run.&lt;br /&gt;
&lt;br /&gt;
;Send (&amp;lt;code&amp;gt;frcti_nack_snd&amp;lt;/code&amp;gt;, called by &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;FRCT_INACT_NEED_NACK&amp;lt;/code&amp;gt;)&lt;br /&gt;
:When an incoming DATA packet has no DRF and rcv-side activity is older than &amp;lt;code&amp;gt;rcv_cr.inact&amp;lt;/code&amp;gt;, the receiver emits a bare packet with &amp;lt;code&amp;gt;flags = FRCT_NACK&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;seqno = arrival_seqno - 1&amp;lt;/code&amp;gt; (informational only, not consulted by the receive handler).  The cooldown in [[#3. Protocol parameters|Section 3]] rate-limits the burst.  Non-DATA non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; non-DATA DRF still rebases via the DRF branch.&lt;br /&gt;
&lt;br /&gt;
;Receive (&amp;lt;code&amp;gt;frcti_nack_rcv&amp;lt;/code&amp;gt;)&lt;br /&gt;
:Dispatched in the early-exit branch ([[#6.1. Early-exit dispatch|Section 6.1]]), before &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;.  The sender copies the head-of-line (HoL) &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; packet, marks the slot &amp;lt;code&amp;gt;SND_RTX | SND_FAST_RXM&amp;lt;/code&amp;gt; (Karn-suppress next ACK, one-shot fast-rxm gate), sets &amp;lt;code&amp;gt;rtt_lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, and re-emits via &amp;lt;code&amp;gt;fast_rxm_send&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt; and a refreshed &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;.  The original &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed - the NACK emit is additive to the normal retransmit machinery, not a replacement.  No-op if nothing is in flight, the HoL slot has aged past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, or the HoL &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; pointer has been cleared by SACK or RACK.&lt;br /&gt;
&lt;br /&gt;
NACK has exactly one role: lost first-of-run (DRF) packet recovery. Until the DRF packet arrives, the receiver cannot rebase its window, so any subsequent in-flight packets look stale to the receiver. The NACK fires the moment a stale receiver sees DATA without DRF, telling the sender to re-emit the head-of-line (DRF) packet at NACK-cooldown latency rather than waiting for the initial RTO (which is the configured default until &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded by the first probe round-trip).  Mid-stream loss is NOT NACK-driven; it is recovered by the sender&#039;s RTO, fast retransmit, and SACK-driven retransmit paths ([[#8. Retransmission|Section 8]]) only.&lt;br /&gt;
&lt;br /&gt;
The existing &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed on a NACK re-emit, so the RTO path remains the eventual fallback.&lt;br /&gt;
&lt;br /&gt;
== 10. Cumulative + selective ACK ==&lt;br /&gt;
&lt;br /&gt;
Cumulative ACK is &amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;.  On out-of-order arrival the&lt;br /&gt;
receiver also emits a SACK packet ([[#1.3. SACK payload|Section 1.3]]) whose payload lists&lt;br /&gt;
&#039;&#039;present&#039;&#039; blocks above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; (analogous to TCP SACK / QUIC ACK&lt;br /&gt;
ranges).  SACKs are rate-limited per [[#3. Protocol parameters|Section 3]] and suppressed when&lt;br /&gt;
neither &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; nor block count has changed since the last SACK.&lt;br /&gt;
&lt;br /&gt;
D-SACK reports (RFC 2883) are emitted in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; of an&lt;br /&gt;
otherwise normal SACK frame (see [[#1.3. SACK payload|Section 1.3]] for the encoding).&lt;br /&gt;
Two receiver triggers arm a pending D-SACK report (single-slot,&lt;br /&gt;
latest-wins):&lt;br /&gt;
&lt;br /&gt;
* DATA arrival with &amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;, both wire-dup (no RXM, &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt; path) and retransmit (RXM, post-FC branch) (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
* &amp;lt;code&amp;gt;rq_accept&amp;lt;/code&amp;gt; conflict, slot already occupied in &amp;lt;code&amp;gt;[lwe, rwe)&amp;lt;/code&amp;gt; (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
&lt;br /&gt;
When a D-SACK is pending and the standard scoreboard SACK would be&lt;br /&gt;
suppressed by dedup or rate-limit, the report is emitted as a&lt;br /&gt;
stand-alone SACK frame through the normal &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; path; when a&lt;br /&gt;
D-SACK report is pending the path bypasses dedup and the &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;&lt;br /&gt;
rate-limit, but the a-timer suppression on rcv inactivity still&lt;br /&gt;
applies.&lt;br /&gt;
&lt;br /&gt;
Bare ACKs are deferred via a per-flow delayed-ACK timer (one in&lt;br /&gt;
flight at a time, atomic test-and-set dedup; fires per [[#3. Protocol parameters|Section 3]]&lt;br /&gt;
after the first in-order arrival).  Suppressed if (1) no new&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, (2) rcv side is inactive (older than &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt;), or (3) the&lt;br /&gt;
sender just sent within &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;.  A pending D-SACK ride-through&lt;br /&gt;
bypasses (1) and (3); the a-timer gate (2) is unconditional.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 11. Flow control ==&lt;br /&gt;
&lt;br /&gt;
The receiver advertises &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; in every FC field.  The sender treats&lt;br /&gt;
its &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; as the absolute right edge: when&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt;= snd_cr.rwe&amp;lt;/code&amp;gt; the window is closed and &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt;&lt;br /&gt;
yields.  While closed, the sender periodically emits RDVS&lt;br /&gt;
(rendezvous) packets (cadence &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt;); the receiver replies with&lt;br /&gt;
a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;) that reopens the window.  Once the&lt;br /&gt;
window has been closed for longer than &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; the sender stops&lt;br /&gt;
emitting RDVS but does not tear the flow down - the writer keeps&lt;br /&gt;
blocking until either a peer-driven FC arrives or the KA&lt;br /&gt;
(keepalive) / r-timer marks the flow.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt; on receipt and MUST NOT shrink:&lt;br /&gt;
a backward &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is silently clamped to the current &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt;;&lt;br /&gt;
the FC packet still reopens the window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 12. RTT estimation ==&lt;br /&gt;
&lt;br /&gt;
Active RTTP probes ([[#1.4. RTTP payload|Section 1.4]]) carry a 32-bit &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt; (0&lt;br /&gt;
reserved) and a 16-byte random nonce echoed verbatim - defends&lt;br /&gt;
against spoofed replies.  A ring of &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; in-flight probes is&lt;br /&gt;
kept; an echo whose &amp;lt;code&amp;gt;(id, nonce)&amp;lt;/code&amp;gt; doesn&#039;t match the ring slot is&lt;br /&gt;
dropped.  A single RTTP sample is clamped to &amp;lt;code&amp;gt;RTT_CLAMP_MUL * srtt&amp;lt;/code&amp;gt;&lt;br /&gt;
(compile-time &amp;lt;code&amp;gt;RTT_CLAMP_MUL = 16&amp;lt;/code&amp;gt;) once &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded; the first&lt;br /&gt;
cold-probe sample feeds &amp;lt;code&amp;gt;rtt_update&amp;lt;/code&amp;gt; raw.&lt;br /&gt;
&lt;br /&gt;
Probe arming gates:&lt;br /&gt;
&lt;br /&gt;
;Cold (no &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; yet)&lt;br /&gt;
:the receive path arms at most one probe per 100 ms via &amp;lt;code&amp;gt;frcti_rcv_probe&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;PROBE_DUE_COLD&amp;lt;/code&amp;gt;); arming requires an incoming packet.  Active send-path arming bails while &amp;lt;code&amp;gt;srtt == 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
;Warm (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt;, called from &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt;)&lt;br /&gt;
:outstanding data (&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt; snd_cr.lwe&amp;lt;/code&amp;gt;), AND at least &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_rcv_rtt&amp;lt;/code&amp;gt; (last RTT receive of any kind), AND at least &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_snd_probe&amp;lt;/code&amp;gt; (last probe emit).&lt;br /&gt;
&lt;br /&gt;
Sample feeds either Linux&#039;s asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_LINUX_RTT_ESTIMATOR&amp;lt;/code&amp;gt;, default ON) or RFC 6298 symmetric EWMA&lt;br /&gt;
(compile option).  &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is floored at 10 ms when seeded from a&lt;br /&gt;
hint, at 1 us after every update (including the first seeding&lt;br /&gt;
sample); &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; floored at 100 ns.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;RTO = max(rto_min, 2 * srtt, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(the &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; floor is an FRCT addition not in RFC 6298).&lt;br /&gt;
Effective wheel deadline capped per [[#3. Protocol parameters|Section 3]].&lt;br /&gt;
&lt;br /&gt;
ACK-derived samples (&amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;rtt_sample_eligible&amp;lt;/code&amp;gt;), beyond&lt;br /&gt;
the cum-ACK advance gate in &amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno &amp;amp;gt; lwe&amp;lt;/code&amp;gt; and&lt;br /&gt;
&amp;lt;code&amp;gt;ackno &amp;amp;lt;= seqno&amp;lt;/code&amp;gt;), require all of: not in recovery; ACK packet does&lt;br /&gt;
not carry &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;; HoL slot&#039;s &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; bit clear; slot&#039;s &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;&lt;br /&gt;
pointer non-NULL (not SACK-consumed); &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; not below the &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
fence; &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; already seeded by an RTTP probe.  There is no ACK-only&lt;br /&gt;
seeding.&lt;br /&gt;
&lt;br /&gt;
Every eligible sample also feeds &amp;lt;code&amp;gt;RACK.min_RTT&amp;lt;/code&amp;gt; (RFC 8985 sec. 6.2)&lt;br /&gt;
via a windowed minimum: replace whenever the sample is strictly&lt;br /&gt;
smaller OR more than &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; (5 min, matches Linux&lt;br /&gt;
&amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) has elapsed since the current min was set.  The&lt;br /&gt;
downward branch is immediate (faster path picked up at once); the&lt;br /&gt;
upward branch is gated on the window (a transient queue burst does&lt;br /&gt;
not poison the estimate, but a sustained route change to a longer&lt;br /&gt;
path re-anchors &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; after at most one window).  Seeded from&lt;br /&gt;
&amp;lt;code&amp;gt;rtt_hint&amp;lt;/code&amp;gt; at &amp;lt;code&amp;gt;rtt_init&amp;lt;/code&amp;gt;; 0 acts as the unset sentinel and the base&lt;br /&gt;
in &amp;lt;code&amp;gt;rack_reorder_window&amp;lt;/code&amp;gt; falls back from &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt; (so&lt;br /&gt;
&amp;lt;code&amp;gt;R = mult * SRTT/4&amp;lt;/code&amp;gt;, capped at &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;, floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt;)&lt;br /&gt;
until the first sample.  See [[#6.2. Locked main path|Section 6.2]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 13. Liveness (keepalive) ==&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;code&amp;gt;qs.timeout &amp;amp;gt; 0&amp;lt;/code&amp;gt; a per-flow KA (keepalive) timer is armed.&lt;br /&gt;
Arming uses &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; for the deadline computation:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;deadline = min(snd_act + qs.timeout/4, rcv_act + qs.timeout)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(clamped to &amp;lt;code&amp;gt;now + qs.timeout/4&amp;lt;/code&amp;gt; if already past).  The timer fires&lt;br /&gt;
either on sender idleness (to send a KA) or on receiver idleness&lt;br /&gt;
(to declare the peer dead).  On fire (&amp;lt;code&amp;gt;ka_snd&amp;lt;/code&amp;gt;) the peer-dead test&lt;br /&gt;
uses &amp;lt;code&amp;gt;max(rcv_cr.act, t_ka_rcv)&amp;lt;/code&amp;gt; so a recent KA reply counts even&lt;br /&gt;
when no DATA has arrived:&lt;br /&gt;
&lt;br /&gt;
* If &amp;lt;code&amp;gt;now - max(rcv_cr.act, t_ka_rcv) &amp;amp;gt; qs.timeout&amp;lt;/code&amp;gt;, mark the flow &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; and notify the per-process flow-event set (&amp;lt;code&amp;gt;proc.fqset&amp;lt;/code&amp;gt;) with &amp;lt;code&amp;gt;FLOW_PEER&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Else if &amp;lt;code&amp;gt;snd_idle &amp;amp;gt; qs.timeout/4&amp;lt;/code&amp;gt;, emit a bare &amp;lt;code&amp;gt;KA | ACK&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) and re-arm.&lt;br /&gt;
* Else just re-arm.&lt;br /&gt;
&lt;br /&gt;
Note: &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; are the receive and transmit shared-memory&lt;br /&gt;
ring buffers.  The r-timer raises &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; on both (route is&lt;br /&gt;
broken); keepalive raises &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; only and notifies&lt;br /&gt;
the flow-event set (peer is silent, writer keeps &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; usable) -&lt;br /&gt;
distinct ACLs.  &amp;lt;code&amp;gt;qs.timeout == 0&amp;lt;/code&amp;gt; disables keepalive entirely; a&lt;br /&gt;
silent peer crash is then undetected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 14. Linger / teardown ==&lt;br /&gt;
&lt;br /&gt;
On &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt; computes a grace timeout&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;max(rcv_cr.act + rcv_cr.inact, snd_cr.act + snd_cr.inact) - now&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(floored at 0 and converted to seconds) and returns it; &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&lt;br /&gt;
forwards this to the IRMd as the dealloc grace.  The IRMd, not FRCT,&lt;br /&gt;
performs the wait.  Before computing the timeout, FRCT may emit a&lt;br /&gt;
final ACK when &amp;lt;code&amp;gt;rcv_cr.lwe != rcv_cr.seqno&amp;lt;/code&amp;gt; (the peer has not been&lt;br /&gt;
told the most recent cumulative ACK) AND the rcv side has been&lt;br /&gt;
active within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer not aged out).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt; is honoured only when &amp;lt;code&amp;gt;snd_cr.lwe &amp;amp;lt; edge&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;edge =&lt;br /&gt;
snd_fin_seqno&amp;lt;/code&amp;gt; after FIN has been sent in stream mode and&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno&amp;lt;/code&amp;gt; otherwise (data or FIN still in flight).  The drain&lt;br /&gt;
itself runs in &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&#039;s &amp;lt;code&amp;gt;while (FRCTI_LINGERING)&amp;lt;/code&amp;gt; loop, not in&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The fd is single-reader / single-writer (documented in the&lt;br /&gt;
manpages).  &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt; pumps &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; on every call (via&lt;br /&gt;
&amp;lt;code&amp;gt;flow_wait_window&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;flow_drain_rx_nb&amp;lt;/code&amp;gt;) and additionally blocks on&lt;br /&gt;
&amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; when the send window is closed.  A pure-writer thread thus&lt;br /&gt;
consumes ACKs without a dedicated reader.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 15. Heritage and adopted techniques ==&lt;br /&gt;
&lt;br /&gt;
Delta-t (Watson, 1981) is the primary heritage; FRCP descends from&lt;br /&gt;
the delta-t protocol family via the Recursive InterNetwork&lt;br /&gt;
Architecture (RINA; Day, &amp;quot;Patterns in Network Architecture&amp;quot;, 2008,&lt;br /&gt;
ch. 9).  Timer-based connection management&lt;br /&gt;
(no SYN/FIN handshake, per-flow state born on first DATA and&lt;br /&gt;
reclaimed after &amp;lt;code&amp;gt;t_mpl + a + r&amp;lt;/code&amp;gt; of silence), the DRF marker, and the&lt;br /&gt;
&amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; timers all come from delta-t.  See Watson,&lt;br /&gt;
&amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection&lt;br /&gt;
Management&amp;quot;, Computer Networks 5 (1981).&lt;br /&gt;
&lt;br /&gt;
The unified &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive and its&lt;br /&gt;
multi-axis QoS-cube argument ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) also come from RINA&lt;br /&gt;
(Day 2008, ch. 6; Grasa et al., &amp;quot;IRATI: investigating RINA as an&lt;br /&gt;
alternative to TCP/IP&amp;quot;, Computer Networks 92 (2015)) - reliability,&lt;br /&gt;
ordering, CRC presence, and encryption are flow attributes, not&lt;br /&gt;
separate sockets or protocols.&lt;br /&gt;
&lt;br /&gt;
The table below summarises additional adopted techniques and their&lt;br /&gt;
references.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FRCP mechanism !! Heritage !! Reference / note&lt;br /&gt;
|-&lt;br /&gt;
| Random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; || TCP ISN || RFC 6528 (Gont &amp;amp;amp; Bellovin, 2012).  QUIC PN-space reset (RFC 9000 sec. 12.3) is a structural analogue.&lt;br /&gt;
|-&lt;br /&gt;
| Cumulative ACK, left-window-edge advance || TCP || RFC 793 / RFC 9293&lt;br /&gt;
|-&lt;br /&gt;
| Receive window with non-shrink rule || TCP || RFC 793 sec. 3.7 / RFC 9293 sec. 3.8.6; RFC 1122 sec. 4.2.2.16 for the explicit non-shrink prohibition&lt;br /&gt;
|-&lt;br /&gt;
| Modular &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; arithmetic (&amp;lt;code&amp;gt;before&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;after&amp;lt;/code&amp;gt; helpers) || TCP || RFC 793 sec. 3.3 / RFC 9293 sec. 3.4&lt;br /&gt;
|-&lt;br /&gt;
| Selective ACK block list || TCP || RFC 2018 (Mathis et al., 1996).  Encoded as a typed FRCP packet rather than a TCP option, so framing is closer to QUIC ACK frames.  D-SACK (RFC 2883) carried in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt;; see [[#1.3. SACK payload|Section 1.3]].&lt;br /&gt;
|-&lt;br /&gt;
| NewReno-careful recovery with &amp;lt;code&amp;gt;recovery_high&amp;lt;/code&amp;gt; gate || TCP || RFC 6582 (Henderson et al., 2012); QUIC builds on the same model in RFC 9002 sec. 7.3.2.  Cwnd half absent (CC in IPCP).&lt;br /&gt;
|-&lt;br /&gt;
| RACK reordering window for fast retransmit || TCP || RFC 8985 (Cheng et al., 2021).  FRCP &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor against &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; collapse; matches RFC 8985 sec. 6.2 and Linux &amp;lt;code&amp;gt;tcp_rack_reo_wnd&amp;lt;/code&amp;gt;.  DSACK-driven &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; (sec. 6.2 step 4) is adopted; see [[#1.3. SACK payload|Section 1.3]] for the wire encoding.  The hybrid RACK-or-&amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; trigger from RFC 8985 sec. 6.2 step 4 is adopted ([[#8. Retransmission|Section 8]]).  QUIC&#039;s analogue in RFC 9002 sec. 6.1.2 uses &amp;lt;code&amp;gt;max(srtt, latest_rtt)&amp;lt;/code&amp;gt; as the base.&lt;br /&gt;
|-&lt;br /&gt;
| Karn&#039;s algorithm: no RTT sample on retransmits, RTO-collapse freeze || TCP || Karn &amp;amp;amp; Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, SIGCOMM 1987; RFC 6298 sec. 3.&lt;br /&gt;
|-&lt;br /&gt;
| RTO formula &amp;lt;code&amp;gt;RTO = max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt; || TCP || RFC 6298 (Paxson et al., 2011).  &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; = 5 ms is below RFC 6298 sec. 2.4&#039;s 1 s SHOULD-floor - a recursive-layer choice.&lt;br /&gt;
|-&lt;br /&gt;
| Linux asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator (default) || Linux kernel || &amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;if(delta&amp;amp;lt;0) m&amp;amp;gt;&amp;amp;gt;=3&amp;lt;/code&amp;gt; dampening is a kernel divergence from RFC 6298.  RFC 6298 EWMA available behind a compile flag.&lt;br /&gt;
|-&lt;br /&gt;
| Delayed ACK with rate suppression || TCP || RFC 813 (Clark, 1982); RFC 1122 sec. 4.2.3.2; RFC 5681 sec. 4.2.  Single-deadline coalescing rather than &amp;quot;ack-every-other-segment&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| Zero-window-probe / persist-timer analogue (RDVS) || TCP || RFC 1122 sec. 4.2.2.17 / RFC 9293 sec. 3.8.6.1.  RDVS solicits an FC reply, distinct from QUIC &amp;lt;code&amp;gt;DATA_BLOCKED&amp;lt;/code&amp;gt; (RFC 9000 sec. 19.12), which is one-way notification.  &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; give-up departs from TCP.&lt;br /&gt;
|-&lt;br /&gt;
| Multiplexed control on a single PCI || SCTP / QUIC || SCTP chunk bundling (RFC 9260 sec. 6.10); QUIC frame multiplexing (RFC 9000 sec. 12.4).  Cleaner fit than TCP&#039;s separate-flag-bits design.&lt;br /&gt;
|-&lt;br /&gt;
| ACK ranges as multiple discontiguous acked blocks || QUIC || QUIC ACK frame (RFC 9000 sec. 19.3).  FRCP SACK is conceptually QUIC-frame-shaped even though encoded as absolute &amp;lt;code&amp;gt;[start,end]&amp;lt;/code&amp;gt; pairs.&lt;br /&gt;
|-&lt;br /&gt;
| Nonce-authenticated active RTT / liveness probing (RTTP) || QUIC &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;PATH_RESPONSE&amp;lt;/code&amp;gt; (RFC 9000 sec. 8.2, sec. 19.17, sec. 19.18).  WebRTC ICE consent-freshness (RFC 7675) is the same pattern.  QUIC&#039;s nonce is 8 octets; FRCP chooses 16.&lt;br /&gt;
|-&lt;br /&gt;
| Probing distinct from keepalive || QUIC || KA timer answers &amp;quot;peer alive?&amp;quot;, RTTP answers &amp;quot;path measurable?&amp;quot;, as in QUIC PING (RFC 9000 sec. 19.2) vs &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| Bare KA + ACK keepalive packets || QUIC / SCTP || QUIC PING (RFC 9000 sec. 19.2); SCTP HEARTBEAT / HEARTBEAT-ACK (RFC 9260 sec. 8.3).  SCTP HEARTBEAT also carries an opaque echoed blob, structurally similar to FRCP RTTP.&lt;br /&gt;
|-&lt;br /&gt;
| (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) fragment-role bits ([[#7.2. Fragmentation and reassembly|Section 7.2]]) || SCTP || RFC 9260 sec. 3.3.1 DATA chunk B/E bits encode the same four states (&amp;lt;code&amp;gt;B+E=SOLE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;B-only=FIRST&amp;lt;/code&amp;gt;, neither=&amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E-only=LAST&amp;lt;/code&amp;gt;).  Each fragment carries its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;/TSN and is independently retransmitted.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-offset reassembly (Sections [[#1.5. Stream PCI extension|1.5]], [[#16. Stream-mode flows|16]]) || QUIC || QUIC STREAM frame (RFC 9000 sec. 19.8) uses Offset + Length varints; FRCP uses fixed 32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;.  One stream per flow vs QUIC&#039;s many streams multiplexed.&lt;br /&gt;
|-&lt;br /&gt;
| FIN end-of-stream marker (Sections [[#1.2. Flag bits|1.2]], [[#16. Stream-mode flows|16]]) || TCP / QUIC || TCP FIN flag (RFC 9293 sec. 3.1) closes one half of the byte stream; QUIC STREAM frame FIN bit (RFC 9000 sec. 19.8) does the same per stream with an immutable final-size invariance (RFC 9000 sec. 4.5: the final size is fixed once observed).  FRCP&#039;s FIN consumes one packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (not one byte of stream space) and is idempotent on the sender side.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-credit flow control ([[#16. Stream-mode flows|Section 16]]) || QUIC || &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; (RFC 9000 sec. 4.1, sec. 19.10).  FRCP projects a per-flow byte budget onto the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;.  Single stream per flow collapses QUIC&#039;s &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.&lt;br /&gt;
|-&lt;br /&gt;
| Header protection (encrypted seqnos) || QUIC || QUIC RFC 9001 sec. 5.4 applies header protection on top of AEAD to mask the packet number.  FRCP&#039;s per-flow AEAD wrap ([[#16. Stream-mode flows|Section 16]]) is wider: it encrypts the entire PCI including &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; because the IPCP below already routes, so no destination connection-ID needs to stay in clear (cf. RFC 9000 sec. 5.2).&lt;br /&gt;
|-&lt;br /&gt;
| Two-bit fragment role polarity || SCTP || The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair follows SCTP B/E (begin = 1 / end = 1) rather than IPv4 MF (RFC 791 sec. 3.2), which has the inverse polarity (MF = 1 means NOT last).&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal reliability / ordering axes ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) || SCTP || PR-SCTP (RFC 3758, per-message partial reliability) and SCTP DATA U-bit (RFC 9260 sec. 3.3.1, per-message unordered) are the closest precedents for decoupling reliability from ordering; FRCP sets them per-flow rather than per-message.&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal CRC (&amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt;) || UDP-Lite || RFC 3828 (Larzon et al., 2004) lets the sender pick a per-packet Checksum Coverage and the receiver enforce a locally configured minimum (no in-band negotiation; sec. 3.1, sec. 3.3).  FRCP gates a full CRC trailer on &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; at flow setup.  Contrast TCP / SCTP (mandatory checksum) and QUIC (AEAD subsumes CRC).&lt;br /&gt;
|-&lt;br /&gt;
| Setup-time service negotiation || DCCP / SCTP / QUIC || DCCP Service Codes (RFC 4340 sec. 8.1.2, RFC 5595); SCTP INIT parameters (RFC 9260 sec. 3.3.2); QUIC transport parameters (RFC 9000 sec. 7.4).  All negotiate service properties at connection setup; only RINA&#039;s QoS cube exposes them as an orthogonal vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.1. Original to FRCP (no clean prior art) ===&lt;br /&gt;
&lt;br /&gt;
* Pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]): receiver-driven nudge exploiting &amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;.  Closest analogues are SCTP Gap Ack Blocks (RFC 9260 sec. 3.3.4) and DCCP Ack Vector (RFC 4340 sec. 11.4) - both let the receiver describe gaps to the sender, but neither targets the cross-epoch / pre-DRF case.&lt;br /&gt;
* &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; window-probe give-up: neither TCP (persist-timer probes until application or R2 abort, RFC 9293 sec. 3.8.6.1) nor QUIC has an explicit FC-give-up counter.  A recursive-network choice: outer layers can drop the flow.&lt;br /&gt;
* Skip-past-gap reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]): SCTP fragments and reassembles every flow regardless of reliability/ordering, using its own per-stream reassembly queue; QUIC fragments via STREAM offsets.  FRCP fragments best-effort flows too, but the receiver drops the broken prefix the moment a later run-start (&amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; role) is visible inside the &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;-wide reorder ring - no IP-frag-style timeout, no SCTP-style explicit abort.  If no later run-start arrives within the ring, &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt; and the partial run keeps its slots; the next inspect retries.  The trade-off: a permanently-lost &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; in a long isolated run holds slots until either a later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; appears in the ring or the writer stops, at which point the slots are reclaimed on flow teardown.&lt;br /&gt;
* Reassembly deferred to consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]), message mode only (&amp;lt;code&amp;gt;qos.service == SVC_MESSAGE&amp;lt;/code&amp;gt;): SCTP (RFC 9260 sec. 6.9), QUIC (RFC 9000 sec. 2.2), and TCP (RFC 9293) all hold reassembly state at the receive boundary.  FRCP message-mode leaves fragments in the shared-memory ring until &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; pulls and lands the SDU directly in the caller&#039;s buffer.  Stream mode ([[#16. Stream-mode flows|Section 16]]) uses the standard QUIC-style direct ring placement on receive and does not defer.  The optimisation is enabled by the Shared-Memory Subsystem (SSM) packet-buffer ring (see &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt; at [[#1.1. PCI header|Section 1.1]]); the analogue is OS-level scatter-gather I/O (&amp;lt;code&amp;gt;recvmsg+iovec&amp;lt;/code&amp;gt;), not a transport-layer prior art.&lt;br /&gt;
* TLP-equivalent tail-loss recovery (RFC 8985 sec. 7; RFC 9002 sec. 6.2): FRCP does not emit an explicit Tail Loss Probe packet, but the same goal is met implicitly by RACK loss detection ([[#8. Retransmission|Section 8]]) firing on a non-advancing cumulative ACK once the head-of-line slot ages past the RACK reorder window &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; - well below &amp;lt;code&amp;gt;RTO = max(2 * SRTT, SRTT + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.  A receiver-driven nudge is also available via the pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.2. Not adopted ===&lt;br /&gt;
&lt;br /&gt;
* Slow start, congestion window (cwnd), Additive Increase / Multiplicative Decrease (AIMD), NewReno cwnd inflation.  Congestion control lives in the IPCP CA policies and is driven by Explicit Congestion Notification (ECN, RFC 3168).&lt;br /&gt;
* Nagle / silly-window-syndrome (SWS) avoidance (RFC 896, RFC 1122 sec. 4.2.3.4).  (Deferred work, not adopted in the current spec.)&lt;br /&gt;
* TCP Timestamps (RFC 7323) / Protection Against Wrapped Sequences (PAWS) - RTT measurement uses RTTP, not per-segment timestamps.  A peer-supplied timestamp echoed on every ACK lets a malicious peer drive the &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; estimate arbitrarily low, collapsing the RTO and triggering a self-inflicted retransmit storm.  RTTP confines RTT measurement to nonce-authenticated probe round-trips, where a forged echo is rejected before it can reach the estimator.&lt;br /&gt;
* ECN (Explicit Congestion Notification) response inside FRCP (consumed by IPCP Congestion Avoidance / CA).&lt;br /&gt;
* IP-style fragment-offset reassembly (RFC 791 sec. 3.2; RFC 8200 sec. 4.5).  Message-mode FRCP relies on the FRCT &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; reorder ring keyed by &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (shared by FRTX and best-effort flows) to put fragments back in order; no separate offset field is needed and no IP-style hole-list reassembly buffer is kept.  Stream-mode FRCP does carry &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte offsets ([[#1.5. Stream PCI extension|Section 1.5]]) for direct ring placement on receive.&lt;br /&gt;
* QUIC STREAM offset+length framing on &#039;&#039;every&#039;&#039; flow (RFC 9000 sec. 19.8).  Message-mode FRCP uses the SCTP-style B/E flag-bit encoding (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) and skips the offsets; stream-mode FRCP adopts the QUIC offset model (heritage table above).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 16. Stream-mode flows ==&lt;br /&gt;
&lt;br /&gt;
When a flow is allocated with &amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt; both peers&lt;br /&gt;
switch to byte-stream semantics, layered on top of the FRTX reorder&lt;br /&gt;
machinery already described in Sections [[#6. Receive path|6]]-[[#8. Retransmission|8]].&lt;br /&gt;
&lt;br /&gt;
=== 16.1. Send ===&lt;br /&gt;
&lt;br /&gt;
The sender splits the caller&#039;s octets into chunks of at most&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt; octets (Sections [[#1.1. PCI header|1.1]]&lt;br /&gt;
and [[#1.5. Stream PCI extension|1.5]]).  Each chunk is one DATA packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte range copied from a monotonic stream counter.&lt;br /&gt;
In stream mode &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST be transmitted as&lt;br /&gt;
zero; the per-byte position is carried by the &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt;&lt;br /&gt;
extension instead.&lt;br /&gt;
&lt;br /&gt;
End-of-stream is signalled with a 0-byte DATA packet that has &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;&lt;br /&gt;
(bit 12) set, emitted on the FIN triggers listed in [[#1.2. Flag bits|Section 1.2]]&lt;br /&gt;
(WR-half close, &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, and any other path that yields the&lt;br /&gt;
final byte).  The sender MUST emit at most one FIN per flow; its&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; MUST equal &amp;lt;code&amp;gt;[final-byte, final-byte)&amp;lt;/code&amp;gt; (i.e., empty&lt;br /&gt;
interval at the final byte position; final-size invariance,&lt;br /&gt;
analogous to QUIC RFC 9000 sec. 4.5).  Idempotency is enforced by&lt;br /&gt;
an &amp;lt;code&amp;gt;snd_fin_sent&amp;lt;/code&amp;gt; guard.&lt;br /&gt;
&lt;br /&gt;
=== 16.2. Receive ===&lt;br /&gt;
&lt;br /&gt;
On arrival the receiver places the payload directly into a per-flow&lt;br /&gt;
byte-indexed receive ring of width &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; (octets) at the position&lt;br /&gt;
indicated by &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, with a two-segment &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; across the ring&lt;br /&gt;
boundary if needed.  Receipt is recorded in the FRTX reorder&lt;br /&gt;
machinery ([[#6.2. Locked main path|Section 6.2]]) augmented with the packet&#039;s &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;, and&lt;br /&gt;
FIN bit per slot.  When a packet&#039;s &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; front-overlaps&lt;br /&gt;
bytes already at or below the byte high-water mark, the overlap is&lt;br /&gt;
trimmed before placement so the same byte is never written twice.&lt;br /&gt;
After stashing, the receiver advances &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; and the byte high-water&lt;br /&gt;
mark across any newly-contiguous prefix.  Each slot advanced MUST&lt;br /&gt;
satisfy &amp;lt;code&amp;gt;start == the last-delivered slot&#039;s end&amp;lt;/code&amp;gt;; a slot whose&lt;br /&gt;
&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; does not equal that &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; is silently dropped at delivery time&lt;br /&gt;
(the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is consumed, no stream bytes contributed) and the high-&lt;br /&gt;
water mark does not advance past it.  The stream byte-stream&lt;br /&gt;
stalls at that point - there is no flow-tear-down on mismatch.&lt;br /&gt;
This filters spliced or off-path-injected slots that fall in&lt;br /&gt;
window without strong cryptographic authentication.&lt;br /&gt;
&lt;br /&gt;
A FIN slot marks end-of-stream at advance time only if its byte&lt;br /&gt;
position equals the last-delivered slot&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;; otherwise the FIN&lt;br /&gt;
is ignored and the corresponding &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; occupies a slot but&lt;br /&gt;
contributes no stream bytes.  No packet buffer is held after the&lt;br /&gt;
ring copy.&lt;br /&gt;
&lt;br /&gt;
=== 16.3. Read ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns up to &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt; octets from the contiguous prefix&lt;br /&gt;
&amp;lt;code&amp;gt;[next, high-water)&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;next&amp;lt;/code&amp;gt; is the byte the application has&lt;br /&gt;
already consumed up to and &amp;lt;code&amp;gt;high-water&amp;lt;/code&amp;gt; is the rightmost contiguous&lt;br /&gt;
byte received.  When the stream is fully drained AND end-of-stream&lt;br /&gt;
(EOS) was observed (&amp;lt;code&amp;gt;next == EOS&amp;lt;/code&amp;gt; byte position), &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns&lt;br /&gt;
0 (&amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) - the same shape POSIX &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt; uses on TCP after a peer&lt;br /&gt;
FIN.&lt;br /&gt;
&lt;br /&gt;
=== 16.4. Flow control ===&lt;br /&gt;
&lt;br /&gt;
ACK / SACK / RACK / RTO machinery is unchanged; the FRTX reorder&lt;br /&gt;
ring is reused as a per-&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; received-bitmap.  Let &amp;lt;code&amp;gt;per_pkt =&lt;br /&gt;
(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt;, the maximum stream-&lt;br /&gt;
byte payload one DATA packet can carry ([[#16.1. Send|Section 16.1]]).  The&lt;br /&gt;
receive window advertised in FC is clamped so the byte window&lt;br /&gt;
(&amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt;) cannot be overrun: the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is at most&lt;br /&gt;
&amp;lt;code&amp;gt;rcv_cr.lwe + ring_sz / per_pkt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC byte-credit flow-control model&lt;br /&gt;
(&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt;, RFC 9000 sec. 4.1 and sec. 19.10) projected onto&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; space.  With one stream per flow there is no &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; /&lt;br /&gt;
&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.  Receiver-side silly-window-syndrome&lt;br /&gt;
(SWS) avoidance (RFC 9293 sec. 3.8.6.2.2) is achieved by combining&lt;br /&gt;
the consume-time &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; bump with the global non-shrink rule from&lt;br /&gt;
[[#11. Flow control|Section 11]].&lt;br /&gt;
&lt;br /&gt;
=== 16.5. Security considerations ===&lt;br /&gt;
&lt;br /&gt;
Threat model.  An attacker that can observe (on-path passive) or&lt;br /&gt;
predict (off-path blind) the flow&#039;s seqnos and byte offsets on an&lt;br /&gt;
unencrypted stream flow can inject DATA or FIN at any in-window&lt;br /&gt;
position.  The in-line consistency checks above (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; == prior&lt;br /&gt;
&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; on advance; FIN MUST be 0-byte; FIN MUST sit at the final&lt;br /&gt;
byte position) realise the spirit of RFC 5961&#039;s &amp;quot;sequence-window&lt;br /&gt;
plus exact-position match for control bits&amp;quot; without an explicit&lt;br /&gt;
challenge-ACK probe; they make a few specific blind attack shapes&lt;br /&gt;
harder but are not cryptographic authentication. This is&lt;br /&gt;
comparable to TCP without the TCP Authentication Option (TCP-AO,&lt;br /&gt;
RFC 5925), tighter than a&lt;br /&gt;
pre-RFC-5961 TCP stack, and roughly equivalent to a modern&lt;br /&gt;
RFC 5961 stack against blind off-path injection - none of these&lt;br /&gt;
help once the attacker can sniff. TLS over TCP (RFC 8446)&lt;br /&gt;
encrypts only the TCP payload and leaves TCP seqnos, ACKs, FIN,&lt;br /&gt;
and RST in the clear, so TLS does NOT defend against TCP-header-&lt;br /&gt;
level injection; QUIC (RFC 9000) hides packet numbers under&lt;br /&gt;
header protection (RFC 9001 sec. 5.4), so this specific weakness&lt;br /&gt;
does not apply to QUIC.&lt;br /&gt;
&lt;br /&gt;
Mitigation: AEAD.  When the flow has encryption enabled the&lt;br /&gt;
recommended AEAD ciphers (AES-GCM, RFC 5288; or ChaCha20-Poly1305,&lt;br /&gt;
RFC 8439) wrap the entire FRCP packet on the wire - PCI, stream&lt;br /&gt;
extension, body, and the CRC trailer when &amp;lt;code&amp;gt;ber == 0&amp;lt;/code&amp;gt; - under a&lt;br /&gt;
per-flow symmetric key derived from the flow&#039;s own key exchange&lt;br /&gt;
([[#1.1. PCI header|Section 1.1]]).  The AEAD tag (~2^-128 forgery probability)&lt;br /&gt;
dominates the CRC (~2^-32) for integrity in this mode but the CRC&lt;br /&gt;
trailer is currently retained inside the wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
Implementations MUST NOT rely on the security properties below&lt;br /&gt;
when a non-AEAD cipher (e.g. AES-CTR alone) is negotiated; non-&lt;br /&gt;
AEAD modes provide confidentiality only and the threat-model&lt;br /&gt;
claims do not hold.&lt;br /&gt;
&lt;br /&gt;
With an AEAD cipher in use, seqnos, byte offsets, and the FIN bit&lt;br /&gt;
are both authenticated and confidential. Against an off-path or&lt;br /&gt;
on-path-passive attacker this is:&lt;br /&gt;
&lt;br /&gt;
* Stronger than TCP+TLS (TCP header in the clear).&lt;br /&gt;
* Stronger than TCP+TCP-AO (header authenticated but visible).&lt;br /&gt;
* Comparable to IPsec ESP transport mode (RFC 4303), which similarly authenticates and encrypts the upper-layer header plus payload, and to QUIC packet protection (RFC 9001 sec. 5), with the difference that QUIC must leave the destination connection ID in the clear for routing whereas FRCP relies on the IPCP below for delivery and can therefore encrypt its entire PCI.&lt;br /&gt;
&lt;br /&gt;
Keying granularity.  FRCP runs key exchange (&amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt;) per flow, so&lt;br /&gt;
each &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; yields independent symmetric keys.  This is&lt;br /&gt;
finer-grained than QUIC (per-connection, RFC 9001, where one&lt;br /&gt;
handshake covers all multiplexed streams) and finer-grained than&lt;br /&gt;
typical IPsec deployment (per-host-pair Security Associations,&lt;br /&gt;
SAs).  Forward secrecy follows from the &amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt; when an ephemeral&lt;br /&gt;
Diffie-Hellman exchange (DHE), or a hybrid mode (classical DH +&lt;br /&gt;
post-quantum Key Encapsulation Mechanism / KEM), is selected.&lt;br /&gt;
&lt;br /&gt;
Replay protection.  The AEAD layer itself does NOT carry an&lt;br /&gt;
explicit anti-replay window (unlike IPsec ESP, RFC 4303 sec.&lt;br /&gt;
3.4.3, or DTLS, RFC 9147 sec. 4.5.1).  For FRCP-engaged flows the&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space duplicate-suppression in [[#6.2. Locked main path|Section 6.2]] rejects replayed&lt;br /&gt;
DATA after the AEAD strips the wrap, because the AEAD authenticates&lt;br /&gt;
the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a replay re-presents an old &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; that is then&lt;br /&gt;
discarded either as a duplicate (still inside the receive window)&lt;br /&gt;
or as outside the receive window, depending on how far &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; has&lt;br /&gt;
advanced since the original packet was delivered.  RAW&lt;br /&gt;
(&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) flows have no FRCP layer and therefore&lt;br /&gt;
no replay protection at the AEAD layer either; deployments that&lt;br /&gt;
need replay rejection on RAW flows MUST provide it at a higher&lt;br /&gt;
layer.&lt;br /&gt;
&lt;br /&gt;
Layering.  The AEAD wrap sits below FRCP on the data path, so&lt;br /&gt;
RAW best-effort flows (&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;, the UDP-equivalent&lt;br /&gt;
service of [[#2.2. Service modes (orthogonal axes)|Section 2.2]]) inherit the same per-flow integrity +&lt;br /&gt;
confidentiality scope as FRCP-engaged flows - whatever the IPCP&lt;br /&gt;
and FRCP (if any) put on the wire is what the AEAD authenticates.&lt;br /&gt;
No DTLS-equivalent layering is required for confidentiality and&lt;br /&gt;
integrity; replay protection above AEAD is a separate concern as&lt;br /&gt;
noted above.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 17. References ==&lt;br /&gt;
&lt;br /&gt;
This section lists the IETF documents, published works, and&lt;br /&gt;
source-code references cited inline elsewhere in this document.&lt;br /&gt;
IETF documents are cited inline as &amp;quot;RFC NNNN sec. X.Y&amp;quot;; books,&lt;br /&gt;
journal papers, and source-code references are cited inline by&lt;br /&gt;
author and year (or by file and function name) and are listed&lt;br /&gt;
here for convenience.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.1. IETF documents ===&lt;br /&gt;
&lt;br /&gt;
;[RFC 791]&lt;br /&gt;
:J. Postel, &amp;quot;Internet Protocol&amp;quot;, STD 5, RFC 791, September 1981.&lt;br /&gt;
;[RFC 793]&lt;br /&gt;
:J. Postel, &amp;quot;Transmission Control Protocol&amp;quot;, STD 7, RFC 793, September 1981.  Obsoleted by RFC 9293.&lt;br /&gt;
;[RFC 813]&lt;br /&gt;
:D. D. Clark, &amp;quot;Window and Acknowledgement Strategy in TCP&amp;quot;, RFC 813, July 1982.&lt;br /&gt;
;[RFC 896]&lt;br /&gt;
:J. Nagle, &amp;quot;Congestion Control in IP/TCP Internetworks&amp;quot;, RFC 896, January 1984.&lt;br /&gt;
;[RFC 1122]&lt;br /&gt;
:R. Braden (ed.), &amp;quot;Requirements for Internet Hosts -- Communication Layers&amp;quot;, STD 3, RFC 1122, October 1989.&lt;br /&gt;
;[RFC 2018]&lt;br /&gt;
:M. Mathis, J. Mahdavi, S. Floyd, A. Romanow, &amp;quot;TCP Selective Acknowledgment Options&amp;quot;, RFC 2018, October 1996.&lt;br /&gt;
;[RFC 2119]&lt;br /&gt;
:S. Bradner, &amp;quot;Key words for use in RFCs to Indicate Requirement Levels&amp;quot;, BCP 14, RFC 2119, March 1997.&lt;br /&gt;
;[RFC 2883]&lt;br /&gt;
:S. Floyd, J. Mahdavi, M. Mathis, M. Podolsky, &amp;quot;An Extension to the Selective Acknowledgement (SACK) Option for TCP&amp;quot;, RFC 2883, July 2000.&lt;br /&gt;
;[RFC 3758]&lt;br /&gt;
:R. Stewart, M. Ramalho, Q. Xie, M. Tuexen, P. Conrad, &amp;quot;Stream Control Transmission Protocol (SCTP) Partial Reliability Extension&amp;quot;, RFC 3758, May 2004.&lt;br /&gt;
;[RFC 3828]&lt;br /&gt;
:L-A. Larzon, M. Degermark, S. Pink, L-E. Jonsson (ed.), G. Fairhurst (ed.), &amp;quot;The Lightweight User Datagram Protocol (UDP-Lite)&amp;quot;, RFC 3828, July 2004.&lt;br /&gt;
;[RFC 4303]&lt;br /&gt;
:S. Kent, &amp;quot;IP Encapsulating Security Payload (ESP)&amp;quot;, RFC 4303, December 2005.&lt;br /&gt;
;[RFC 4340]&lt;br /&gt;
:E. Kohler, M. Handley, S. Floyd, &amp;quot;Datagram Congestion Control Protocol (DCCP)&amp;quot;, RFC 4340, March 2006.&lt;br /&gt;
;[RFC 5288]&lt;br /&gt;
:J. Salowey, A. Choudhury, D. McGrew, &amp;quot;AES Galois Counter Mode (GCM) Cipher Suites for TLS&amp;quot;, RFC 5288, August 2008.&lt;br /&gt;
;[RFC 5595]&lt;br /&gt;
:G. Fairhurst, &amp;quot;The Datagram Congestion Control Protocol (DCCP) Service Codes&amp;quot;, RFC 5595, September 2009.&lt;br /&gt;
;[RFC 5681]&lt;br /&gt;
:M. Allman, V. Paxson, E. Blanton, &amp;quot;TCP Congestion Control&amp;quot;, RFC 5681, September 2009.&lt;br /&gt;
;[RFC 5925]&lt;br /&gt;
:J. Touch, A. Mankin, R. Bonica, &amp;quot;The TCP Authentication Option&amp;quot;, RFC 5925, June 2010.&lt;br /&gt;
;[RFC 5961]&lt;br /&gt;
:A. Ramaiah, R. Stewart, M. Dalal, &amp;quot;Improving TCP&#039;s Robustness to Blind In-Window Attacks&amp;quot;, RFC 5961, August 2010.&lt;br /&gt;
;[RFC 6298]&lt;br /&gt;
:V. Paxson, M. Allman, J. Chu, M. Sargent, &amp;quot;Computing TCP&#039;s Retransmission Timer&amp;quot;, RFC 6298, June 2011.&lt;br /&gt;
;[RFC 6528]&lt;br /&gt;
:F. Gont, S. Bellovin, &amp;quot;Defending against Sequence Number Attacks&amp;quot;, RFC 6528, February 2012.  Obsoletes RFC 1948.&lt;br /&gt;
;[RFC 6582]&lt;br /&gt;
:T. Henderson, S. Floyd, A. Gurtov, Y. Nishida, &amp;quot;The NewReno Modification to TCP&#039;s Fast Recovery Algorithm&amp;quot;, RFC 6582, April 2012.&lt;br /&gt;
;[RFC 7323]&lt;br /&gt;
:D. Borman, B. Braden, V. Jacobson, R. Scheffenegger (ed.), &amp;quot;TCP Extensions for High Performance&amp;quot;, RFC 7323, September 2014.&lt;br /&gt;
;[RFC 7675]&lt;br /&gt;
:M. Perumal, D. Wing, R. Ravindranath, T. Reddy, M. Thomson, &amp;quot;Session Traversal Utilities for NAT (STUN) Usage for Consent Freshness&amp;quot;, RFC 7675, October 2015.&lt;br /&gt;
;[RFC 8174]&lt;br /&gt;
:B. Leiba, &amp;quot;Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words&amp;quot;, BCP 14, RFC 8174, May 2017.&lt;br /&gt;
;[RFC 8200]&lt;br /&gt;
:S. Deering, R. Hinden, &amp;quot;Internet Protocol, Version 6 (IPv6) Specification&amp;quot;, STD 86, RFC 8200, July 2017.&lt;br /&gt;
;[RFC 8439]&lt;br /&gt;
:Y. Nir, A. Langley, &amp;quot;ChaCha20 and Poly1305 for IETF Protocols&amp;quot;, RFC 8439, June 2018.&lt;br /&gt;
;[RFC 8446]&lt;br /&gt;
:E. Rescorla, &amp;quot;The Transport Layer Security (TLS) Protocol Version 1.3&amp;quot;, RFC 8446, August 2018.&lt;br /&gt;
;[RFC 8985]&lt;br /&gt;
:Y. Cheng, N. Cardwell, N. Dukkipati, P. Jha, &amp;quot;The RACK-TLP Loss Detection Algorithm for TCP&amp;quot;, RFC 8985, February 2021.&lt;br /&gt;
;[RFC 9000]&lt;br /&gt;
:J. Iyengar (ed.), M. Thomson (ed.), &amp;quot;QUIC: A UDP-Based Multiplexed and Secure Transport&amp;quot;, RFC 9000, May 2021.&lt;br /&gt;
;[RFC 9001]&lt;br /&gt;
:M. Thomson (ed.), S. Turner (ed.), &amp;quot;Using TLS to Secure QUIC&amp;quot;, RFC 9001, May 2021.&lt;br /&gt;
;[RFC 9002]&lt;br /&gt;
:J. Iyengar (ed.), I. Swett (ed.), &amp;quot;QUIC Loss Detection and Congestion Control&amp;quot;, RFC 9002, May 2021.&lt;br /&gt;
;[RFC 9147]&lt;br /&gt;
:E. Rescorla, H. Tschofenig, N. Modadugu, &amp;quot;The Datagram Transport Layer Security (DTLS) Protocol Version 1.3&amp;quot;, RFC 9147, April 2022.&lt;br /&gt;
;[RFC 9260]&lt;br /&gt;
:R. Stewart, M. Tuexen, K. Nielsen, &amp;quot;Stream Control Transmission Protocol&amp;quot;, RFC 9260, June 2022.  Obsoletes RFC 4960.&lt;br /&gt;
;[RFC 9293]&lt;br /&gt;
:W. Eddy (ed.), &amp;quot;Transmission Control Protocol (TCP)&amp;quot;, STD 7, RFC 9293, August 2022.  Obsoletes RFC 793 and several follow-ons; updates RFC 1122 and others.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.2. Books and journal papers ===&lt;br /&gt;
&lt;br /&gt;
;[Day08]&lt;br /&gt;
:J. Day, &amp;quot;Patterns in Network Architecture: A Return to Fundamentals&amp;quot;, Prentice Hall, 2008.&lt;br /&gt;
;[Grasa15]&lt;br /&gt;
:E. Grasa et al., &amp;quot;IRATI: investigating RINA as an alternative to TCP/IP&amp;quot;, Computer Networks, Vol. 92, December 2015.&lt;br /&gt;
;[KP87]&lt;br /&gt;
:P. Karn, C. Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, ACM SIGCOMM, August 1987.&lt;br /&gt;
;[Wat81]&lt;br /&gt;
:R. W. Watson, &amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection Management&amp;quot;, Computer Networks, Vol. 5, 1981.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.3. Source-code references ===&lt;br /&gt;
&lt;br /&gt;
;[Linux-RTT]&lt;br /&gt;
:&amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt; of the Linux kernel, defining the asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; variance update used as FRCP&#039;s default RTT estimator ([[#12. RTT estimation|Section 12]]).  Line-stable browseable copy at https://elixir.bootlin.com/linux/latest/source/net/ipv4/tcp_input.c.&lt;br /&gt;
&lt;br /&gt;
[[Category:Protocols]]&lt;br /&gt;
[[Category:Ouroboros internals]]&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1915</id>
		<title>Flow and Retransmission Control Protocol</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1915"/>
		<updated>2026-05-17T14:40:10Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:FRCP - Flow and Retransmission Control Protocol}}&lt;br /&gt;
&lt;br /&gt;
FRCP runs end-to-end between two peers over a flow.  It delivers&lt;br /&gt;
reliability, in-order delivery, flow control, and liveness.&lt;br /&gt;
Congestion Control (CC) is not in FRCP - that lives in the IPC&lt;br /&gt;
Process (IPCP) Congestion Avoidance (CA) policies, orthogonal to&lt;br /&gt;
FRCP.  Flow allocation, naming, and IPCP lifecycle are handled by&lt;br /&gt;
the IPC Resource Manager daemon (IRMd).&lt;br /&gt;
&lt;br /&gt;
FRCT (Flow and Retransmission Control Task) is the libouroboros&lt;br /&gt;
implementation of FRCP; the task lives in &amp;lt;code&amp;gt;src/lib/frct.c&amp;lt;/code&amp;gt;.  The&lt;br /&gt;
remainder of this document describes the FRCP wire protocol and the&lt;br /&gt;
behaviour FRCT realises.  Code symbols retain the &amp;lt;code&amp;gt;FRCT_&amp;lt;/code&amp;gt; prefix&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, ...) because they belong to the implementing&lt;br /&gt;
task; this document references them verbatim.&lt;br /&gt;
&lt;br /&gt;
The keywords &amp;quot;MUST&amp;quot;, &amp;quot;MUST NOT&amp;quot;, &amp;quot;REQUIRED&amp;quot;, &amp;quot;SHALL&amp;quot;, &amp;quot;SHALL NOT&amp;quot;,&lt;br /&gt;
&amp;quot;SHOULD&amp;quot;, &amp;quot;SHOULD NOT&amp;quot;, &amp;quot;RECOMMENDED&amp;quot;, &amp;quot;MAY&amp;quot;, and &amp;quot;OPTIONAL&amp;quot; in&lt;br /&gt;
this document are to be interpreted as described in &amp;lt;code&amp;gt;BCP&amp;lt;/code&amp;gt; 14 (Best&lt;br /&gt;
Current Practice; RFC 2119, RFC 8174) when, and only when, they&lt;br /&gt;
appear in all capitals.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:Unsigned 32-bit / 8-bit integers (kernel-C style).&lt;br /&gt;
;&amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:Nanoseconds.&lt;br /&gt;
&lt;br /&gt;
Modular sequence-number comparators (32-bit, modulo 2^32):&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;before(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;(int32_t)(a - b) &amp;amp;lt; 0&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;after(a, b)&amp;lt;/code&amp;gt;&lt;br /&gt;
:&amp;lt;code&amp;gt;before(b, a)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Used throughout for &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; ordering checks.&lt;br /&gt;
&lt;br /&gt;
Round-Trip Time (RTT) abbreviations used throughout:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;&lt;br /&gt;
:Smoothed RTT estimate (RFC 6298).&lt;br /&gt;
;&amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt;&lt;br /&gt;
:Mean deviation of RTT (Linux variance estimator).&lt;br /&gt;
;&amp;lt;code&amp;gt;EWMA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Exponentially Weighted Moving Average.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTO&amp;lt;/code&amp;gt;&lt;br /&gt;
:Retransmission Timeout, &amp;lt;code&amp;gt;max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Timer-bound symbols &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer, ACK delay) and &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer,&lt;br /&gt;
retransmission window) are defined in [[#8. Retransmission|Section 8]]; &amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; (Maximum&lt;br /&gt;
Packet Lifetime) is introduced in [[#2.1. Per-flow state|Section 2.1]] (the &amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; field)&lt;br /&gt;
with heritage in [[#15. Heritage and adopted techniques|Section 15]].&lt;br /&gt;
&lt;br /&gt;
Wire-format diagrams follow the IETF convention: bit 0 is the&lt;br /&gt;
leftmost (most significant) bit and fields are in network byte&lt;br /&gt;
order unless stated otherwise.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1. Wire format ==&lt;br /&gt;
&lt;br /&gt;
=== 1.1. PCI header ===&lt;br /&gt;
&lt;br /&gt;
Fixed 16-octet base Protocol-Control Information (PCI) header&lt;br /&gt;
prefixed to every FRCP packet (RFC convention: bit 0 leftmost,&lt;br /&gt;
most-significant bit first).  All multi-byte fields except &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;&lt;br /&gt;
are in network byte order; &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt; is an opaque 16-bit value that&lt;br /&gt;
the receiver recomputes from the wire bytes and compares to the&lt;br /&gt;
in-place &amp;lt;code&amp;gt;pci-&amp;gt;hcs&amp;lt;/code&amp;gt; read, so its on-wire byte order need only&lt;br /&gt;
match between peers running compatible builds.  DATA packets on&lt;br /&gt;
stream-mode flows carry an additional 8-octet extension (see&lt;br /&gt;
[[#1.5. Stream PCI extension|Section 1.5]]); SACK and RTTP carry their own payloads after the&lt;br /&gt;
base PCI.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |             flags             |              hcs              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            window                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            seqno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            ackno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                     payload (variable) ...&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt;&lt;br /&gt;
:feature/type bitmap (see [[#1.2. Flag bits|Section 1.2]]).&lt;br /&gt;
;&amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;&lt;br /&gt;
:CRC-16-CCITT-FALSE Header Check Sequence (HCS) over &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; (+ stream extension when present); the two octets of the &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt; field itself are omitted from the CRC input.  Verified on receive before any flag-driven dispatch.&lt;br /&gt;
;&amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt;&lt;br /&gt;
:receiver-advertised right window edge (valid iff FC).&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-flow sequence number.&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;&lt;br /&gt;
:cumulative Acknowledgement (ACK) (valid iff ACK).&lt;br /&gt;
&lt;br /&gt;
A single packet can simultaneously carry DATA + ACK + FC (Flow&lt;br /&gt;
Control) + RXM (Retransmission) by ORing flag bits; the PCI&lt;br /&gt;
multiplexes control on the same wire frame in the spirit of SCTP&lt;br /&gt;
chunk bundling (RFC 9260 sec. 6.10) and QUIC frame multiplexing&lt;br /&gt;
(RFC 9000 sec. 12.4).  DATA-bearing packets carry the caller&#039;s&lt;br /&gt;
payload after the PCI; SACK (Selective Acknowledgement) and RTTP&lt;br /&gt;
(Round-Trip Time Probe) carry their own typed payloads after the&lt;br /&gt;
PCI.&lt;br /&gt;
&lt;br /&gt;
Optional framing (per-flow, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]]).  On the wire, the&lt;br /&gt;
order from inside out is:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Layer !! Scope&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| The FRCP packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ PCI + body + CRC-32 ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| CRC-32 covers the body only (PCI is in HCS); appended iff &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; on DATA, or on every SACK packet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;[ AEAD-wrap of above ]&amp;lt;/code&amp;gt;&lt;br /&gt;
| Iff Authenticated Encryption with Associated Data (AEAD) is enabled.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* HCS in the PCI covers the header fields on every packet and is verified before any flag-driven dispatch.&lt;br /&gt;
* The CRC-32 trailer (IEEE 802.3 / zlib reflected polynomial &amp;lt;code&amp;gt;0xEDB88320&amp;lt;/code&amp;gt;, init &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;, xor-out &amp;lt;code&amp;gt;0xFFFFFFFF&amp;lt;/code&amp;gt;) covers the body on DATA when &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; and on every SACK packet; the trailer is written as a raw &amp;lt;code&amp;gt;uint32_t&amp;lt;/code&amp;gt; (the same convention as &amp;lt;code&amp;gt;hcs&amp;lt;/code&amp;gt;: opaque on the wire as long as both peers run compatible builds).  The PCI is not under the CRC (Cyclic Redundancy Check) because the HCS already protects it.  It is appended before AEAD encryption and therefore rides inside the AEAD wrap when both are active; the AEAD tag (~2^-128 forgery probability) dominates the CRC (~2^-32) for integrity in that mode but the CRC trailer is currently retained.&lt;br /&gt;
* When encryption is enabled, the entire (possibly-CRC&#039;d) FRCP packet is wrapped with AEAD inside the shared-memory packet buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt;); the packet grows by the AEAD overhead, namely a leading nonce / Initialization Vector (IV) of &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_ivsz&amp;lt;/code&amp;gt;) and a trailing authentication tag of &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; bytes (&amp;lt;code&amp;gt;crypt_get_tagsz&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Both CRC and AEAD are layered around the FRCP wire format and are not visible to the FRCP machinery itself.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.2. Flag bits ===&lt;br /&gt;
&lt;br /&gt;
Flag bits are numbered most-significant-bit first to match the wire&lt;br /&gt;
diagram (bit numbering per [[#1.1. PCI header|Section 1.1]]; bit 0 is the MSB of the&lt;br /&gt;
16-bit &amp;lt;code&amp;gt;flags&amp;lt;/code&amp;gt; field and lands at wire-position 0 in network byte&lt;br /&gt;
order).  Bits 13..15 are reserved and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Bit !! Mask !! Name !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| 0 || &amp;lt;code&amp;gt;0x8000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt; || Carries caller payload&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &amp;lt;code&amp;gt;0x4000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;DRF&amp;lt;/code&amp;gt; || Data Run Flag: start of a fresh run&lt;br /&gt;
|-&lt;br /&gt;
| 2 || &amp;lt;code&amp;gt;0x2000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt; || Acknowledgement: &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field valid&lt;br /&gt;
|-&lt;br /&gt;
| 3 || &amp;lt;code&amp;gt;0x1000&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt; || Negative ACK; &amp;lt;code&amp;gt;seqno = arrival_seqno-1&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || &amp;lt;code&amp;gt;0x0800&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt; || Flow Control: &amp;lt;code&amp;gt;window&amp;lt;/code&amp;gt; field valid (&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| 5 || &amp;lt;code&amp;gt;0x0400&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt; || Rendezvous probe (window-closed)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || &amp;lt;code&amp;gt;0x0200&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; || First Fragment (role bit 0; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 7 || &amp;lt;code&amp;gt;0x0100&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; || Last Fragment (role bit 1; see below)&lt;br /&gt;
|-&lt;br /&gt;
| 8 || &amp;lt;code&amp;gt;0x0080&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RXM&amp;lt;/code&amp;gt; || Retransmission&lt;br /&gt;
|-&lt;br /&gt;
| 9 || &amp;lt;code&amp;gt;0x0040&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt; || Selective ACK block list in payload&lt;br /&gt;
|-&lt;br /&gt;
| 10 || &amp;lt;code&amp;gt;0x0020&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt; || RTT Probe / echo (payload follows)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || &amp;lt;code&amp;gt;0x0010&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt; || Keepalive&lt;br /&gt;
|-&lt;br /&gt;
| 12 || &amp;lt;code&amp;gt;0x0008&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt; || End-of-stream marker (stream mode)&lt;br /&gt;
|-&lt;br /&gt;
| 13-15 || -- || -- || Reserved (MUST be zero)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair encodes the fragment role of a DATA-bearing&lt;br /&gt;
Service Data Unit (SDU), SCTP-style begin/end flags (RFC 9260&lt;br /&gt;
sec. 3.3.1):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FFGM !! LFGM !! Role&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 1 || Sole / un-fragmented SDU (begin AND end)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0 || First fragment of a multi-fragment SDU&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 0 || Middle fragment&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || Last fragment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Each fragment is carried in its own FRCP packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;;&lt;br /&gt;
FRTX (the FRCT Retransmission service mode, see [[#2.2. Service modes (orthogonal axes)|Section 2.2]])&lt;br /&gt;
recovers individual fragments via the normal Retransmission Timeout&lt;br /&gt;
(RTO) / SACK / Recent Acknowledgement (RACK, RFC 8985) path.  The&lt;br /&gt;
receiver reassembles the SDU at consume time once the contiguous&lt;br /&gt;
[FIRST .. LAST] run has fully arrived.  On non-DATA packets the role&lt;br /&gt;
bits are unused and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
In stream mode (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;, see [[#16. Stream-mode flows|Section 16]]) there are&lt;br /&gt;
no SDU boundaries to encode, so &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST&lt;br /&gt;
be transmitted as zero.  End-of-stream uses a dedicated bit (&amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;,&lt;br /&gt;
bit 12) carried on a 0-byte DATA packet, emitted at write-half close&lt;br /&gt;
(&amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;FLOWFRDONLY&amp;lt;/code&amp;gt;), during linger drain, and at &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;;&lt;br /&gt;
emission is idempotent (first call wins).  After contiguous delivery&lt;br /&gt;
of the FIN-bearing slot, the receiver latches &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt; at the FIN&#039;s&lt;br /&gt;
start offset; &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns 0 (end-of-file, &amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) once buffered&lt;br /&gt;
bytes have been drained up to &amp;lt;code&amp;gt;byte_fin&amp;lt;/code&amp;gt;.  Per-byte position is&lt;br /&gt;
carried by the [start, end) extension ([[#1.5. Stream PCI extension|Section 1.5]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.3. SACK payload ===&lt;br /&gt;
&lt;br /&gt;
A SACK packet has the &amp;lt;code&amp;gt;FRCT_ACK | FRCT_FC | FRCT_SACK&amp;lt;/code&amp;gt; flag bits set&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI, the&lt;br /&gt;
payload is a 2-octet block count (network byte order), 2 octets of&lt;br /&gt;
padding to 4-byte align the block list, then &amp;lt;code&amp;gt;n_blocks&amp;lt;/code&amp;gt; pairs of&lt;br /&gt;
32-bit start/end seqnos describing &#039;&#039;present&#039;&#039; (received) ranges&lt;br /&gt;
above the cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |           n_blocks            |        padding (2 octets)     |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[0]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            end[0]                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[1]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
                       ... n_blocks pairs total ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;n_blocks &amp;amp;lt;= SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; (2048).  The per-flow effective cap is&lt;br /&gt;
further bounded by &amp;lt;code&amp;gt;(frag_mtu - PCI - 4) / 8&amp;lt;/code&amp;gt; blocks per packet; SACK&lt;br /&gt;
packets carry no stream extension, so PCI here is the 16-octet base&lt;br /&gt;
header even on stream-mode flows.&lt;br /&gt;
&lt;br /&gt;
Wire invariant: every block produced by the receiver, except an&lt;br /&gt;
optional leading Duplicate SACK (D-SACK) block as described below,&lt;br /&gt;
describes a range strictly above the cumulative ACK carried in the&lt;br /&gt;
PCI &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; field (&amp;lt;code&amp;gt;after(start[i], ackno)&amp;lt;/code&amp;gt;).  This makes the D-SACK&lt;br /&gt;
convention below unambiguous; the receiver-side builder MUST&lt;br /&gt;
preserve it.&lt;br /&gt;
&lt;br /&gt;
Duplicate SACK (D-SACK, RFC 2883) is signalled in-band: no flag&lt;br /&gt;
bit, no extra framing.  Modular seqno arithmetic uses the&lt;br /&gt;
&amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators defined in the Notation block.&lt;br /&gt;
&amp;lt;code&amp;gt;Block[0]&amp;lt;/code&amp;gt; carries a D-SACK report when either:&lt;br /&gt;
&lt;br /&gt;
;case 1 (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;before(blocks[0].start, ackno)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;ackno - blocks[0].start&amp;lt;/code&amp;gt; is within &amp;lt;code&amp;gt;MAX_DSACK_LAG&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;== RQ_SIZE&amp;lt;/code&amp;gt;).  A single duplicate &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; observed below the cumulative ACK.&lt;br /&gt;
;case 2 (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
:&amp;lt;code&amp;gt;blocks[0]&amp;lt;/code&amp;gt; is a sub-range of some &amp;lt;code&amp;gt;blocks[i&amp;gt;0]&amp;lt;/code&amp;gt; (not exactly equal).  Reports a duplicate of an in-window &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; that the same packet&#039;s remaining SACK blocks already describe as received.&lt;br /&gt;
&lt;br /&gt;
Senders that do not implement D-SACK process &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; through the&lt;br /&gt;
normal SACK-mark loop and the existing clamp-and-skip path makes&lt;br /&gt;
case-1 a no-op (&amp;lt;code&amp;gt;start &amp;amp;lt; snd_cr.lwe&amp;lt;/code&amp;gt; clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt;, the inner&lt;br /&gt;
loop then skips &amp;lt;code&amp;gt;k == snd_cr.lwe&amp;lt;/code&amp;gt;) and case-2 idempotent (same slots&lt;br /&gt;
NULL&#039;d twice).  D-SACK-aware senders feed the report into the RACK&lt;br /&gt;
&amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; scaler (RFC 8985 sec. 6.2 step 4): bump on receipt&lt;br /&gt;
(cap 20), halve once per 16 cumulatively-ACK&#039;d seqnos since the&lt;br /&gt;
most recent D-SACK arrival or halve event, reset to 1 on an RTO&lt;br /&gt;
timer fire at the head-of-line.  D-SACK alone never enters&lt;br /&gt;
NewReno-careful recovery (see [[#8. Retransmission|Section 8]]); only non-D-SACK blocks&lt;br /&gt;
count as gaps.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.4. RTTP payload ===&lt;br /&gt;
&lt;br /&gt;
An RTTP (Round-Trip Time Probe) packet has only the &amp;lt;code&amp;gt;FRCT_RTTP&amp;lt;/code&amp;gt; flag&lt;br /&gt;
set (bit numbering per [[#1.1. PCI header|Section 1.1]]).  Following the 16-octet PCI,&lt;br /&gt;
the payload is 24 octets (packed):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          probe_id                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          echo_id                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +                  nonce (16 octets, echoed verbatim)           +&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:sender counter, 0 on reply, 0 reserved.&lt;br /&gt;
;&amp;lt;code&amp;gt;echo_id&amp;lt;/code&amp;gt;&lt;br /&gt;
:peer&#039;s &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt;, 0 on outbound probe.&lt;br /&gt;
;&amp;lt;code&amp;gt;nonce&amp;lt;/code&amp;gt;&lt;br /&gt;
:random, echoed unmodified, memcmp&#039;d to defeat spoof.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.5. Stream PCI extension ===&lt;br /&gt;
&lt;br /&gt;
A stream-mode flow (&amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt;) carries an extra&lt;br /&gt;
8-octet extension after the 16-octet base PCI on every DATA packet&lt;br /&gt;
(bit numbering per [[#1.1. PCI header|Section 1.1]]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            start                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                             end                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset of the first payload byte in the stream.&lt;br /&gt;
;&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;&lt;br /&gt;
:octet offset one past the last payload byte; &amp;lt;code&amp;gt;end - start&amp;lt;/code&amp;gt; equals the on-wire payload length.&lt;br /&gt;
&lt;br /&gt;
Total stream-mode PCI for DATA packets is 24 octets (16 base + 8&lt;br /&gt;
extension); control packets (SACK, RTTP, bare ACK, KA, etc.) retain&lt;br /&gt;
the 16-octet base PCI.  Stream mode MUST be negotiated at flow&lt;br /&gt;
allocation; the extension is present iff stream mode is in use,&lt;br /&gt;
never on a per-packet basis.  Both peers MUST treat &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; as&lt;br /&gt;
monotonic 32-bit byte offsets; when a slot reaches the head of the&lt;br /&gt;
contiguous run with &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; not equal to the prior packet&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; the&lt;br /&gt;
slot is silently dropped at delivery time ([[#16. Stream-mode flows|Section 16]]) rather&lt;br /&gt;
than rejected at stash.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC STREAM-frame reassembly model (RFC 9000 sec. 19.8):&lt;br /&gt;
each packet carries its packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (this PCI&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; field) and a&lt;br /&gt;
separate stream byte position (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;).  Separating the two&lt;br /&gt;
avoids TCP&#039;s conflation of packet identity with byte position which&lt;br /&gt;
forces Karn&#039;s algorithm for Round-Trip Time (RTT) sampling (no RTT&lt;br /&gt;
sample on retransmits, RFC 6298 sec. 3); FRCP applies the&lt;br /&gt;
Karn-equivalent gate via a combination of per-packet &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;,&lt;br /&gt;
per-slot &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; flags, and a sample-fence &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; (see [[#2.1. Per-flow state|Section 2.1]]&lt;br /&gt;
and [[#12. RTT estimation|Section 12]]).  FRCP&#039;s fixed-32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; wrap at 4 GiB of&lt;br /&gt;
wire bytes, narrower than QUIC&#039;s 62-bit varint offset (cf. RFC 9000&lt;br /&gt;
sec. 16); the on-wire wrap is handled by the same modular &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;&lt;br /&gt;
/ &amp;lt;code&amp;gt;after()&amp;lt;/code&amp;gt; comparators ([[#1.3. SACK payload|Section 1.3]]) FRCP uses for seqnos, which&lt;br /&gt;
remain unambiguous as long as the in-flight byte window stays&lt;br /&gt;
strictly under 2 GiB (the half-range of the signed-int32 difference&lt;br /&gt;
in &amp;lt;code&amp;gt;before()&amp;lt;/code&amp;gt;).  The default per-flow ring is 1 MiB; the&lt;br /&gt;
implementation caps &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; at 128 MiB (&amp;lt;code&amp;gt;FRCT_STREAM_RING_SZ_MAX&amp;lt;/code&amp;gt;),&lt;br /&gt;
well below the 2 GiB half-range bound.  The runtime byte counters&lt;br /&gt;
exposed via FUSE (Filesystem in Userspace) in the Ouroboros&lt;br /&gt;
Resource Information Base (RIB, a virtual-filesystem introspection&lt;br /&gt;
bridge) are platform &amp;lt;code&amp;gt;size_t&amp;lt;/code&amp;gt; and do not wrap on 64-bit hosts.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2. Per-flow state and service modes ==&lt;br /&gt;
&lt;br /&gt;
=== 2.1. Per-flow state ===&lt;br /&gt;
&lt;br /&gt;
Each flow keeps a sender control record and a receiver control&lt;br /&gt;
record:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: oldest unacked &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (cumulative ACK boundary as seen by sender); rcv: next in-order &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; expected&lt;br /&gt;
;&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: peer-advertised right window edge; rcv: locally-advertised right window edge&lt;br /&gt;
;&amp;lt;code&amp;gt;cflags&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u8&amp;lt;/code&amp;gt;&lt;br /&gt;
:per-direction feature flags: retransmission (&amp;lt;code&amp;gt;FRCTFRTX&amp;lt;/code&amp;gt;), receiver flow control (&amp;lt;code&amp;gt;FRCTFRESCNTL&amp;lt;/code&amp;gt;), linger-on-close (&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt;); see &amp;lt;code&amp;gt;&amp;amp;lt;ouroboros/fccntl.h&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
;&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: next &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; to send; rcv: force-ACK trigger - set on a stale or dup DATA so the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; emits a fresh cumulative ACK&lt;br /&gt;
;&amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;u32&amp;lt;/code&amp;gt;&lt;br /&gt;
:snd: &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; counter for standalone ACK-bearing control packets (delayed ACK, SACK, final ACK on dealloc); not bumped on piggybacked ACK riding a DATA packet (which uses the DATA &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;).  Used by wire-dup ACK detection; rcv: incoming-ACK dedup tracker&lt;br /&gt;
;&amp;lt;code&amp;gt;act&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:last activity (used by inactivity / DRF)&lt;br /&gt;
;&amp;lt;code&amp;gt;inact&amp;lt;/code&amp;gt; : &amp;lt;code&amp;gt;ns&amp;lt;/code&amp;gt;&lt;br /&gt;
:inactivity threshold; sender = &amp;lt;code&amp;gt;3*mpl + a + r + 1s&amp;lt;/code&amp;gt;, receiver = &amp;lt;code&amp;gt;2*mpl + a + r + 1s&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;mpl&amp;lt;/code&amp;gt; is the Maximum Packet Lifetime (delta-t terminology; see [[#15. Heritage and adopted techniques|Section 15]]); &amp;lt;code&amp;gt;a&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;r&amp;lt;/code&amp;gt; are the FRCT a-timer and r-timer bounds (see [[#8. Retransmission|Section 8]]).  The asymmetry is load-bearing for pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
The sender holds a per-slot ring &amp;lt;code&amp;gt;snd_slots[RQ_SIZE]&amp;lt;/code&amp;gt; keyed by&lt;br /&gt;
&amp;lt;code&amp;gt;(seqno mod RQ_SIZE)&amp;lt;/code&amp;gt;.  Each slot tracks its retransmit entry (&amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;),&lt;br /&gt;
last-send timestamp, and retransmit flag bits: &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (a&lt;br /&gt;
retransmit is pending or has fired, gates the next RTT sample&lt;br /&gt;
under Karn) and &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (one-shot fast-retransmit staged for&lt;br /&gt;
this loss event).&lt;br /&gt;
&lt;br /&gt;
The receiver holds a parallel reorder ring &amp;lt;code&amp;gt;rcv_slots[RQ_SIZE]&amp;lt;/code&amp;gt;&lt;br /&gt;
(referred to as &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; in prose) holding stashed out-of-order&lt;br /&gt;
packet-buffer indexes; both FRTX and best-effort flows share this&lt;br /&gt;
path.  The invariant &amp;lt;code&amp;gt;rwe - lwe &amp;amp;lt;= RQ_SIZE&amp;lt;/code&amp;gt; holds: on each consume&lt;br /&gt;
the receiver advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; by the consumed count, capping the&lt;br /&gt;
receive window at &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; slots.&lt;br /&gt;
&lt;br /&gt;
A separate fence variable &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt; is bumped on every retransmit&lt;br /&gt;
(timer-fire, SACK-driven, fast-rxm, NACK-driven) and on every&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; ([[#4. Sequence-number rotation (DRF)|Section 4]]) to mark the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; range whose RTT samples&lt;br /&gt;
MUST be discarded.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 2.2. Service modes (orthogonal axes) ===&lt;br /&gt;
&lt;br /&gt;
FRCP exposes its wire features as a vector of independent QoS&lt;br /&gt;
axes selected at flow allocation time.  All flows go through the&lt;br /&gt;
same &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive; the &amp;lt;code&amp;gt;qosspec_t&amp;lt;/code&amp;gt; passed&lt;br /&gt;
in determines which protocol machinery engages on the wire.  This&lt;br /&gt;
contrasts with the POSIX BSD socket model where TCP and UDP&lt;br /&gt;
require different socket types (&amp;lt;code&amp;gt;SOCK_STREAM&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;SOCK_DGRAM&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
The axes:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = unordered (no FRCP engagement: raw datagrams, no PCI on the wire, UDP-equivalent at this layer); 1 = message-ordered (FRCP engaged; SDU boundaries preserved across fragmentation); 2 = stream (byte-oriented, no SDU boundaries; FRTX required)&lt;br /&gt;
;&amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt;&lt;br /&gt;
:0 = lossless service requested: FRTX retransmit machinery engages ([[#8. Retransmission|Section 8]]); MUST be 0 for &amp;lt;code&amp;gt;service=2&amp;lt;/code&amp;gt;.  Non-zero = best-effort, FRTX off.&lt;br /&gt;
;&amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bit Error Rate tolerance.  0 = error-free service requested: a CRC trailer is appended after the body of DATA packets and verified on receive (added / checked outside the FRCP PCI; see [[#1.1. PCI header|Section 1.1]]).  Non-zero = peer accepts errors; trailer omitted.  SACK control packets carry a CRC32 trailer regardless of &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; gate applies to DATA only.&lt;br /&gt;
;&amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt;&lt;br /&gt;
:Peer-timeout (ms); 0 disables the keepalive timer.  Independent of FRCP engagement.&lt;br /&gt;
&lt;br /&gt;
Encryption is a separate per-flow attribute set at flow setup;&lt;br /&gt;
when enabled it wraps the FRCP packet (PCI + body, plus the CRC&lt;br /&gt;
trailer if any) under AEAD, expanding the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; by &amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; + &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt;&lt;br /&gt;
octets (nonce / tag).  The CRC trailer is currently kept inside&lt;br /&gt;
the AEAD wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
&lt;br /&gt;
Reachable combinations exported by &amp;lt;code&amp;gt;include/ouroboros/qos.h&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Cube !! &amp;lt;code&amp;gt;service&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;loss&amp;lt;/code&amp;gt; !! &amp;lt;code&amp;gt;ber&amp;lt;/code&amp;gt; !! Engaged&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw&amp;lt;/code&amp;gt; || 0 || 1 || 1 || Raw passthrough&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_raw_safe&amp;lt;/code&amp;gt; || 0 || 1 || 0 || Raw + CRC trailer&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; || 1 || 1 || 1 || FRCP, no FRTX, no CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_rt_safe&amp;lt;/code&amp;gt; || 1 || 1 || 0 || FRCP, no FRTX, CRC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_msg&amp;lt;/code&amp;gt; || 1 || 0 || 0 || FRCP + FRTX&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;qos_stream&amp;lt;/code&amp;gt; || 2 || 0 || 0 || FRCP + FRTX, stream&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Forced couplings actually enforced by the public API:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;service == SVC_STREAM&amp;lt;/code&amp;gt; (2) requires &amp;lt;code&amp;gt;loss == 0&amp;lt;/code&amp;gt;; &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;flow_accept&amp;lt;/code&amp;gt; reject the pair otherwise with &amp;lt;code&amp;gt;-EINVAL&amp;lt;/code&amp;gt;.&lt;br /&gt;
* FRTX requires FRCP engagement (&amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;); requesting &amp;lt;code&amp;gt;loss = 0&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;service = SVC_RAW&amp;lt;/code&amp;gt; is structurally a no-op because no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; is created.&lt;br /&gt;
* The &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; build flag globally forces &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;.  Note: this flag defaults to ON, so default builds ship with CRC disabled until &amp;lt;code&amp;gt;QOS_DISABLE_CRC&amp;lt;/code&amp;gt; is set to OFF.&lt;br /&gt;
&lt;br /&gt;
Caveat: the API does NOT force &amp;lt;code&amp;gt;ber = 0&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;service != SVC_RAW&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;code&amp;gt;qos_rt&amp;lt;/code&amp;gt; has &amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;ber = 1&amp;lt;/code&amp;gt;, which means the PCI&lt;br /&gt;
itself is not CRC-protected on that cube; the HCS ([[#1.1. PCI header|Section 1.1]])&lt;br /&gt;
remains the only integrity check on the header.&lt;br /&gt;
&lt;br /&gt;
The FRCP-no-FRTX regime (&amp;lt;code&amp;gt;service = SVC_MESSAGE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;loss &amp;amp;gt; 0&amp;lt;/code&amp;gt;) is meaningful&lt;br /&gt;
and live: sequence numbering, in-order delivery, flow-control&lt;br /&gt;
advertisement, KA, DRF rotation, and SDU fragmentation /&lt;br /&gt;
reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]) all run.  Lost packets are dropped&lt;br /&gt;
rather than retransmitted; a permanently-lost mid-fragment is&lt;br /&gt;
dropped via skip-past-gap once a later SDU is visible in the&lt;br /&gt;
reorder ring.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. Protocol parameters ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Parameter !! Value !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Slot ring / rcv window width&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;START_WINDOW&amp;lt;/code&amp;gt; || compile-time, power of 2 (default 128) || Initial &amp;lt;code&amp;gt;rwe-lwe&amp;lt;/code&amp;gt; after rotate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MAX(250 us build-tunable, 1&amp;amp;lt;&amp;amp;lt;RXMQ_RES)&amp;lt;/code&amp;gt;; per-flow via &amp;lt;code&amp;gt;fccntl&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;FRCTSRTOMIN&amp;lt;/code&amp;gt;).  Default ~1 ms with &amp;lt;code&amp;gt;RXMQ_RES=20&amp;lt;/code&amp;gt;. || RTO floor; also floored at the retransmit-wheel resolution (~1 ms by default).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt; || 20 || Backoff shift cap&lt;br /&gt;
|-&lt;br /&gt;
| RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;MIN(reo_wnd_mult * min_RTT/4, SRTT)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; scales on D-SACK, cap 20 || Reorder window; per RFC 8985 sec. 6.2; &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per sec. 6.2 step 4&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; || 300 s (5 min, Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; windowed re-anchor&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_WND_MULT_MAX&amp;lt;/code&amp;gt; || 20 (RFC 8985 sec. 6.2 step 4) || &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;REO_DECAY_PKTS&amp;lt;/code&amp;gt; || 16 (RFC 8985 sec. 6.2 step 4 / &amp;lt;code&amp;gt;RACK.reo_wnd_persist&amp;lt;/code&amp;gt;) || Fresh-ACK&#039;d seq count per halving&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_DSACK_LAG&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt; || D-SACK sanity cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTT_QUARANTINE&amp;lt;/code&amp;gt; || 32 (&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; steps) || NewReno gate pad&lt;br /&gt;
|-&lt;br /&gt;
| SACK rate-limit || &amp;lt;code&amp;gt;SACK_MIN_GAP_NS&amp;lt;/code&amp;gt; (250 us, fixed) || Min SACK gap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_MAX_BLOCKS&amp;lt;/code&amp;gt; || 2048 (wire cap; per-flow capped at &amp;lt;code&amp;gt;(frag_mtu-PCI-4)/8&amp;lt;/code&amp;gt;) || Per-SACK block cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; || 32 || Per-pass staged retransmit cap&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; || 3 (RFC 8985 default) || Hybrid fast-rxm trigger ([[#8. Retransmission|Section 8]])&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MDEV_MUL&amp;lt;/code&amp;gt; || 2 (build-tunable via &amp;lt;code&amp;gt;FRCT_RTO_MDEV_MULTIPLIER&amp;lt;/code&amp;gt;) || &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; shift in &amp;lt;code&amp;gt;RTO = srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| RTTP nonce || 16 octets || Echoed verbatim&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; || 8 || In-flight probes&lt;br /&gt;
|-&lt;br /&gt;
| RTT clamp || &amp;lt;code&amp;gt;16 * srtt&amp;lt;/code&amp;gt; || Probe-sample upper bound (ACK-derived RTT samples gated by Karn / recovery only)&lt;br /&gt;
|-&lt;br /&gt;
| Cold-probe cadence || 100 ms (rx-driven; see [[#12. RTT estimation|Section 12]]) || Pre-&amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; RTTP rate&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt; || 100 ms || RDVS emit cadence&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; || 1 s || RDVS give-up&lt;br /&gt;
|-&lt;br /&gt;
| Delayed-ACK fire || &amp;lt;code&amp;gt;2 * TICTIME&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt; = FRCT tick granularity, default 5 ms; &amp;lt;code&amp;gt;2*TICTIME = 10 ms&amp;lt;/code&amp;gt; by default) || Fired after the first in-order DATA arrival; tick is build-tunable&lt;br /&gt;
|-&lt;br /&gt;
| NACK send cooldown || &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; when an &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; sample exists, else 100 ms || Pre-DRF NACK rate-limit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;MAX_SDU&amp;lt;/code&amp;gt; || 1 MiB || Max reassembled SDU; configurable per flow&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The per-flow fragment Maximum Transmission Unit (MTU) is computed&lt;br /&gt;
at flow setup from the lower IPCP&#039;s mtu minus encryption&lt;br /&gt;
&amp;lt;code&amp;gt;headsz&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;tailsz&amp;lt;/code&amp;gt; and CRC trailer; there is no FRCT-level default or&lt;br /&gt;
environment-variable override.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 4. Sequence-number rotation (DRF) ==&lt;br /&gt;
&lt;br /&gt;
The DRF (Data Run Flag) bit on an outbound packet means &amp;quot;this is&lt;br /&gt;
the start of a fresh data run&amp;quot; and is set whenever the sender has&lt;br /&gt;
nothing in flight (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Independently of that, if the sender has been idle longer than&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.inact&amp;lt;/code&amp;gt; AND the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;),&lt;br /&gt;
&amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; rolls a random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; before the send and&lt;br /&gt;
resets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    snd_cr.seqno  = random()&lt;br /&gt;
    snd_cr.lwe    = snd_cr.seqno&lt;br /&gt;
    snd_cr.rwe    = snd_cr.seqno + START_WINDOW&lt;br /&gt;
    rtt_lwe       = snd_cr.seqno&lt;br /&gt;
    in_recovery   = false   (recovery state, see Section 8)&lt;br /&gt;
    recovery_high = snd_cr.seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The receiver, on observing rcv-side inactivity&lt;br /&gt;
(&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), requires a DRF on the next&lt;br /&gt;
DATA packet; otherwise it replies with a rate-limited NACK (see&lt;br /&gt;
below).  Non-DATA control packets pass through without the DRF&lt;br /&gt;
requirement.  On DRF the receiver releases the &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots and&lt;br /&gt;
rebases&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
    rcv_cr.lwe   = seqno&lt;br /&gt;
    rcv_cr.rwe   = seqno + RQ_SIZE&lt;br /&gt;
    rcv_cr.seqno = seqno&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the inactive packet has DATA but no DRF, a rate-limited NACK is&lt;br /&gt;
fired back to the sender (cooldown per [[#3. Protocol parameters|Section 3]]); non-DATA stale&lt;br /&gt;
arrivals fall through to normal processing (no NACK, no drop).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 5. Send path ==&lt;br /&gt;
&lt;br /&gt;
# If the SDU exceeds &amp;lt;code&amp;gt;(frag_mtu - data_hdr_len)&amp;lt;/code&amp;gt;, the caller (&amp;lt;code&amp;gt;dev.c&amp;lt;/code&amp;gt;) fans it out into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - data_hdr_len))&amp;lt;/code&amp;gt; fragments, each emitted via &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; as its own DATA packet with a per-fragment role ([[#7.2. Fragmentation and reassembly|Section 7.2]]); both FRTX and best-effort flows fragment.  Raw flows (no FRCP engagement, &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) carry no PCI and return &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; for any SDU larger than one packet at the layer below.  An SDU that fits in a single packet is sent as SOLE.  &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt; reserves PCI head room; sets DATA, plus DRF when the pipe is empty (&amp;lt;code&amp;gt;snd_cr.seqno == snd_cr.lwe&amp;lt;/code&amp;gt;).&lt;br /&gt;
# &amp;lt;code&amp;gt;seqno_rotate()&amp;lt;/code&amp;gt; if past sender inactivity and the pipe is empty ([[#4. Sequence-number rotation (DRF)|Section 4]]).&lt;br /&gt;
# Advertise FC (&amp;lt;code&amp;gt;pci.window = frcti_advert_rwe(frcti)&amp;lt;/code&amp;gt;, i.e. &amp;lt;code&amp;gt;rcv_cr.rwe&amp;lt;/code&amp;gt; clamped to &amp;lt;code&amp;gt;rcv_cr.lwe + ring_seq_cap&amp;lt;/code&amp;gt; in stream mode) when the receiver side is recent: &amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt; rcv_cr.inact&amp;lt;/code&amp;gt;.&lt;br /&gt;
# Reliable mode (FRTX): leave &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; where it is; reset the slot at &amp;lt;code&amp;gt;RQ_SLOT(seqno)&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;snd_slots[p].time = now&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_slots[p].flags = 0&amp;lt;/code&amp;gt;); queue an &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; (saves a packet copy, arms a wheel timer at &amp;lt;code&amp;gt;now + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;).  Piggyback ACK (&amp;lt;code&amp;gt;pci.ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) while the a-timer for the most recent received DATA packet has not yet expired (&amp;lt;code&amp;gt;now - rcv_cr.act &amp;amp;lt;= t_a&amp;lt;/code&amp;gt;); on piggyback, set &amp;lt;code&amp;gt;rcv_cr.seqno = rcv_cr.lwe&amp;lt;/code&amp;gt; so the next delayed-ACK fire is suppressed.  See [[#8. Retransmission|Section 8]] for &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; semantics.&lt;br /&gt;
# Best-effort mode (no FRTX): advance &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; immediately (&amp;lt;code&amp;gt;snd_cr.lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;snd_cr.rwe = snd_cr.lwe + RQ_SIZE&amp;lt;/code&amp;gt;); no retransmit state.  No send-side RTT probe is armed in this mode (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt; requires an in-flight &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, which best-effort never has); the rx-driven cold seeder in &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; is the only probe path.&lt;br /&gt;
# In reliable mode, optionally arm an RTT probe ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 6. Receive path ==&lt;br /&gt;
&lt;br /&gt;
=== 6.1. Early-exit dispatch ===&lt;br /&gt;
&lt;br /&gt;
Keepalive (KA), RTT probe (RTTP), pre-DRF NACK, and rendezvous&lt;br /&gt;
(RDVS) packets short-circuit out of &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; before the locked&lt;br /&gt;
main path; each handler takes its own lock internally.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
      incoming packet&lt;br /&gt;
            |&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | KA?     |---yes--&amp;gt; ka_rcv  ; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RTTP?   |---yes--&amp;gt; rttp_rcv; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | NACK?   |---yes--&amp;gt; nack_rcv; return  (see Section 9)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RDVS?   |---yes--&amp;gt; rdv_rcv ; return  (reply bare FC, ackno=0)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       acquire wrlock; enter locked main path&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;KA&amp;lt;/code&amp;gt;&lt;br /&gt;
:refresh &amp;lt;code&amp;gt;t_ka_rcv&amp;lt;/code&amp;gt;, honour piggybacked ACK.&lt;br /&gt;
;&amp;lt;code&amp;gt;RTTP&amp;lt;/code&amp;gt;&lt;br /&gt;
:probe (echo back nonce) or echo (verify nonce, sample RTT).&lt;br /&gt;
;&amp;lt;code&amp;gt;NACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:pre-DRF, sender-side handler.  See [[#9. Pre-DRF NACK|Section 9]].&lt;br /&gt;
;&amp;lt;code&amp;gt;RDVS&amp;lt;/code&amp;gt;&lt;br /&gt;
:reply with a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;); &amp;lt;code&amp;gt;rdlock&amp;lt;/code&amp;gt; only.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 6.2. Locked main path ===&lt;br /&gt;
&lt;br /&gt;
Steps below run with the per-flow &amp;lt;code&amp;gt;frcti.lock&amp;lt;/code&amp;gt; held for writing&lt;br /&gt;
(&amp;lt;code&amp;gt;pthread_rwlock_wrlock&amp;lt;/code&amp;gt;) unless noted.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;&lt;br /&gt;
:Only meaningful when the receive side is stale.  On DRF (Data Run Flag): release &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; slots, rebase &amp;lt;code&amp;gt;rcv_cr&amp;lt;/code&amp;gt;, continue.  On stale DATA without DRF: fire a pre-DRF NACK if cooldown allows ([[#9. Pre-DRF NACK|Section 9]]), then discard the packet; on cooldown, drop without sending a NACK (a pending cumulative ACK from &amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; may still go out).  Non-DATA, non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; pure-DRF stale arrivals fall through after the DRF rebase branch.&lt;br /&gt;
&lt;br /&gt;
;DATA-only act refresh&lt;br /&gt;
:Refresh &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; only when &amp;lt;code&amp;gt;FRCT_DATA&amp;lt;/code&amp;gt; is set, so that non-DATA packets never block the next DRF rebase.&lt;br /&gt;
&lt;br /&gt;
;Wire-dup gate&lt;br /&gt;
:Before flag-driven dispatch, drop wire-duplicate ACKs and wire-duplicate DATA (&amp;lt;code&amp;gt;is_dup_ack&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt;).  The DATA check is bypassed for &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;-bearing arrivals so the piggybacked ACK / SACK / FC carried on a retransmitted DATA at an already-ACK&#039;d &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is still applied; the stale-in-window branch below then drops the packet.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;ACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Drop ACKs whose &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt; falls outside &amp;lt;code&amp;gt;(snd_cr.lwe, snd_cr.seqno]&amp;lt;/code&amp;gt;.  If &amp;lt;code&amp;gt;ackno == snd_cr.lwe&amp;lt;/code&amp;gt; (non-advancing cumulative ACK), drive RACK fast-retransmit consideration ([[#8. Retransmission|Section 8]]).  Otherwise advance &amp;lt;code&amp;gt;snd_cr.lwe = ackno&amp;lt;/code&amp;gt;, collapse &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; to 0 (Karn-gated by &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; on the just-acknowledged slot, the old head-of-line), reset &amp;lt;code&amp;gt;dup_thresh&amp;lt;/code&amp;gt; to 0, update &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the send-time of the slot at &amp;lt;code&amp;gt;ackno-1&amp;lt;/code&amp;gt; (consumed by RACK and SACK below), decay &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; per RFC 8985 sec. 6.2 step 4, exit NewReno-careful recovery (see [[#8. Retransmission|Section 8]]) on &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt;, and feed an RTT sample if eligible ([[#12. RTT estimation|Section 12]]).&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;SACK&amp;lt;/code&amp;gt;&lt;br /&gt;
:Walk the block list.  For each block (a present range above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;) NULL out &amp;lt;code&amp;gt;snd_slots[k].rxm&amp;lt;/code&amp;gt;, clear the slot&#039;s per-send flags, and advance &amp;lt;code&amp;gt;t_latest_ack&amp;lt;/code&amp;gt; to the latest send-time covered (the Forward Acknowledgement / fack equivalent, Mathis &amp;amp;amp; Mahdavi 1996); the first block whose start clamps to &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; skips this fack update so that a head-of-line clamp does not falsely advance fack.  For un-SACKed gaps below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt;, stage a retransmit per slot that is (1) still owned (&amp;lt;code&amp;gt;rxm != NULL&amp;lt;/code&amp;gt;), (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (the RFC 8985 sec. 6.2 hybrid trigger).  Mark the slot &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; and NULL the &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; at stage time.  Capped at &amp;lt;code&amp;gt;SACK_RXM_MAX&amp;lt;/code&amp;gt; staged retransmits per receive pass; what&#039;s left rides the next SACK.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;FC&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bump &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; (clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt;, never shrinks) and mark window open.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;DATA&amp;lt;/code&amp;gt;&lt;br /&gt;
:Bounds-check &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; against window.  On stale-dup (&amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;), set &amp;lt;code&amp;gt;rcv_cr.seqno = seqno&amp;lt;/code&amp;gt; to force a fresh ACK on the next &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt;, then drop.  On accept: both FRTX and best-effort stash the packet-buffer index into &amp;lt;code&amp;gt;rq[seqno mod RQ_SIZE]&amp;lt;/code&amp;gt;.  Fragments stash unchanged - the role bits are inspected only at consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]).  On out-of-order arrival, build a SACK reply if not rate-limited (per [[#3. Protocol parameters|Section 3]]) and not deduplicated against the previous &amp;lt;code&amp;gt;(rcv_cr.lwe, n_blocks)&amp;lt;/code&amp;gt; pair; D-SACK reports always bypass the dedup.  If both rate-limit and dedup suppress the reply, neither SACK nor delayed-ACK fires (the sender picks up the gap on its next ACK).  On in-order arrival, arm the delayed-ACK timer.&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;code&amp;gt;drop_packet&amp;lt;/code&amp;gt; exit&lt;br /&gt;
:Releases the per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;), then calls &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; synchronously after the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt; release to surface any pending cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 7. Read path and reassembly ==&lt;br /&gt;
&lt;br /&gt;
=== 7.1. Read path ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns a full reassembled SDU (Service Data Unit) via&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; on every FRCP SDU-mode flow (FRTX or best-effort);&lt;br /&gt;
stream-mode is covered in [[#16. Stream-mode flows|Section 16]].  An incomplete head-of-line&lt;br /&gt;
(HoL) run yields &amp;lt;code&amp;gt;-EAGAIN&amp;lt;/code&amp;gt;; an oversized run yields &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt; (the&lt;br /&gt;
run is dropped so the flow does not stall).  On best-effort flows,&lt;br /&gt;
a permanently-lost mid-fragment is dropped as soon as a later&lt;br /&gt;
complete SDU becomes visible in the ring ([[#7.2. Fragmentation and reassembly|Section 7.2]] skip-past-&lt;br /&gt;
gap).&lt;br /&gt;
&lt;br /&gt;
Raw flows carry no &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt;, so &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns the next pending&lt;br /&gt;
packet-buffer index directly, with no role-bit inspection.  (Raw&lt;br /&gt;
service is selected via &amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt; at flow allocation,&lt;br /&gt;
which suppresses &amp;lt;code&amp;gt;frcti&amp;lt;/code&amp;gt; creation.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_pdu_ready&amp;lt;/code&amp;gt; is the no-advance peek used by &amp;lt;code&amp;gt;fevent&amp;lt;/code&amp;gt; (the&lt;br /&gt;
Ouroboros flow-event multiplexer, the &amp;lt;code&amp;gt;poll(2)&amp;lt;/code&amp;gt;-equivalent on&lt;br /&gt;
flows).  It returns ready only when the head-of-line run is&lt;br /&gt;
complete and the lead packet (a Protocol Data Unit, here one FRCP&lt;br /&gt;
packet) is present at &amp;lt;code&amp;gt;rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt;; any other state&lt;br /&gt;
(including the best-effort skip-past-gap case) returns not ready,&lt;br /&gt;
and &amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt; is left to drop the broken prefix and re-&lt;br /&gt;
inspect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 7.2. Fragmentation and reassembly ===&lt;br /&gt;
&lt;br /&gt;
Send side (&amp;lt;code&amp;gt;flow_write_frag&amp;lt;/code&amp;gt;).  An SDU larger than&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - PCI)&amp;lt;/code&amp;gt; is split into &amp;lt;code&amp;gt;ceil(count / (frag_mtu - PCI))&amp;lt;/code&amp;gt;&lt;br /&gt;
fragments; each fragment is its own FRCP packet with its own&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a per-fragment role flag pair ([[#1.2. Flag bits|Section 1.2]]).  Roles are&lt;br /&gt;
assigned at emit time:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! i !! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=0&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;i=n-1&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| else || &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A mid-loop allocation or transmit failure may yield a partial&lt;br /&gt;
write: the call returns the bytes already enqueued (&amp;lt;code&amp;gt;off &amp;amp;gt; 0&amp;lt;/code&amp;gt;) or&lt;br /&gt;
the underlying error (&amp;lt;code&amp;gt;off == 0&amp;lt;/code&amp;gt;).  Best-effort flows fragment&lt;br /&gt;
identically; on the receiver, a partial run with a permanently-&lt;br /&gt;
lost fragment is dropped when a later complete SDU is visible in&lt;br /&gt;
the ring (see skip-past-gap below).  Raw flows carry no PCI and&lt;br /&gt;
refuse anything larger than the layer&#039;s user MTU (&amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Wire-level recovery is fragment-agnostic on FRTX flows: each&lt;br /&gt;
fragment&#039;s &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; flows through SACK / RACK / RTO / NACK exactly&lt;br /&gt;
as for a SOLE DATA packet, and reassembly does not re-enter the&lt;br /&gt;
loss-detection path.  Best-effort flows run the same &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;&lt;br /&gt;
machinery (DRF, FC, ACK piggyback, pre-DRF NACK emit) but queue&lt;br /&gt;
no &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; state at the sender, so a lost MID is unrecoverable;&lt;br /&gt;
skip-past-gap handles it (below).&lt;br /&gt;
&lt;br /&gt;
Receive side.  Fragments stash into &amp;lt;code&amp;gt;rq[seqno]&amp;lt;/code&amp;gt; unchanged; role bits&lt;br /&gt;
are read only at consume time.  &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;, called from&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_consume&amp;lt;/code&amp;gt;, walks the ring starting at the oldest still-&lt;br /&gt;
undelivered &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;base = rcv_cr.rwe - RQ_SIZE&amp;lt;/code&amp;gt; (equal to &amp;lt;code&amp;gt;rcv_cr.lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
only when no partial run is in progress; during a partial run &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
has already advanced past &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;).  It produces one of three&lt;br /&gt;
outcomes:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Outcome !! Cause&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DELIVER (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]=SOLE&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;), or &amp;lt;code&amp;gt;rq[base]=FIRST&amp;lt;/code&amp;gt; and a &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; follows in slots &amp;lt;code&amp;gt;[base+1..base+n-1]&amp;lt;/code&amp;gt; with all intermediate roles in &amp;lt;code&amp;gt;{MID,FIRST,LAST}&amp;lt;/code&amp;gt; contiguous.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DROP (n)&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;LAST&amp;lt;/code&amp;gt; without a preceding &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;n=1&amp;lt;/code&amp;gt;); a &amp;lt;code&amp;gt;FIRST..[non-LAST]..new-FIRST&amp;lt;/code&amp;gt; or new-&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; mid-run (drop the broken prefix with &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt; = run length minus 1, so the new &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; stays); or, on best-effort flows, a gap at &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; later in the ring (drop up to the new run start).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;rq[base]&amp;lt;/code&amp;gt; absent or &amp;lt;code&amp;gt;FIRST..[non-LAST]&amp;lt;/code&amp;gt; with no later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; in the ring (FRTX waits for retx; best-effort waits for arrival).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DELIVER&amp;lt;/code&amp;gt; triggers &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;: a scatter-gather &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; of the &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;&lt;br /&gt;
consecutive fragments at &amp;lt;code&amp;gt;rq[base..base+n-1]&amp;lt;/code&amp;gt; directly into the&lt;br /&gt;
caller&#039;s buffer; each per-packet shared-memory buffer (&amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;) is&lt;br /&gt;
released and &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; advances by &amp;lt;code&amp;gt;n&amp;lt;/code&amp;gt;.  &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; was already advanced&lt;br /&gt;
incrementally as each contiguous fragment arrived; &amp;lt;code&amp;gt;frag_gather&amp;lt;/code&amp;gt;&lt;br /&gt;
only restores the fixed-width invariant &amp;lt;code&amp;gt;rwe == lwe + RQ_SIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
No intermediate reassembly buffer is allocated.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; advances &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; past the broken prefix (releasing the &amp;lt;code&amp;gt;spb&amp;lt;/code&amp;gt;s)&lt;br /&gt;
and pulls &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; up to the new trailing edge if needed; the next&lt;br /&gt;
consume retries from the new &amp;lt;code&amp;gt;base&amp;lt;/code&amp;gt;.  Oversize or arithmetically&lt;br /&gt;
overflowing delivery (sum of fragment lengths &amp;amp;gt; &amp;lt;code&amp;gt;max_rcv_sdu&amp;lt;/code&amp;gt;, sum&lt;br /&gt;
&amp;amp;gt; caller&#039;s buffer, or running-sum overflow) also drops the run&lt;br /&gt;
with &amp;lt;code&amp;gt;-EMSGSIZE&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skip-past-gap (best-effort only).  On FRTX, a gap in the run means&lt;br /&gt;
&amp;quot;waiting for retransmit&amp;quot; and &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt;.&lt;br /&gt;
On best-effort flows the gap is permanent, so &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt;&lt;br /&gt;
scans forward in the ring for the next &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt;; if one is&lt;br /&gt;
visible within &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;, it returns &amp;lt;code&amp;gt;DROP&amp;lt;/code&amp;gt; for the broken prefix and&lt;br /&gt;
the consume loop retries at the new &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt;.  Memory hold is bounded&lt;br /&gt;
by &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;; the partial releases on the next consume call once a&lt;br /&gt;
later complete run exists.  Voice-like flows (one &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; per SDU)&lt;br /&gt;
see no extra wait: any later &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; makes the prior gap droppable&lt;br /&gt;
immediately.&lt;br /&gt;
&lt;br /&gt;
The choice to defer reassembly to consume time keeps the receive&lt;br /&gt;
path zero-copy: fragments stay in the shared-memory ring until&lt;br /&gt;
the application pulls, and the SDU lands directly in the caller&#039;s&lt;br /&gt;
buffer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 8. Retransmission ==&lt;br /&gt;
&lt;br /&gt;
FRCP is bounded by two delta-t-derived timers (Watson 1981, see&lt;br /&gt;
[[#15. Heritage and adopted techniques|Section 15]]):&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer): upper bound on ACK delay.  An ACK for a received DATA packet MUST be emitted within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; of receipt; an attempt to send an ACK after the a-timer has expired is suppressed (the sender&#039;s RTO is already in motion).&lt;br /&gt;
* &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; (r-timer): upper bound on retransmission.  A given DATA packet MUST NOT be retransmitted after &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; has elapsed since its first send (&amp;lt;code&amp;gt;t0&amp;lt;/code&amp;gt;); when the bound is hit, the flow is declared down (raising the Ouroboros asynchronous flow condition &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt;, which marks the flow dead to both endpoints) rather than retransmitted again.&lt;br /&gt;
&lt;br /&gt;
Each in-flight FRTX &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; owns one &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt;, armed in a hashed&lt;br /&gt;
timing wheel; the wheel deadline is the slot&#039;s next eligible&lt;br /&gt;
retransmit time.&lt;br /&gt;
&lt;br /&gt;
;RTO timer&lt;br /&gt;
:On fire (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;), re-emit with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;, mark &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; (Karn-suppress next ACK&#039;s RTT sample), and (for the head-of-line (HoL) slot only) bump &amp;lt;code&amp;gt;rto_mul&amp;lt;/code&amp;gt; up to &amp;lt;code&amp;gt;MAX_RTO_MUL&amp;lt;/code&amp;gt;.  Wheel deadline is &amp;lt;code&amp;gt;t_send + (rto &amp;amp;lt;&amp;amp;lt; rto_mul)&amp;lt;/code&amp;gt;.  Re-armed unless consumed.  The RTO timer also clears &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; (re-arming fast-retransmit eligibility), resets &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; to 1 on a HoL fire (RFC 8985 sec. 6.2 step 4 reset clause), and marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; if its &amp;lt;code&amp;gt;frct_tx&amp;lt;/code&amp;gt; call fails.&lt;br /&gt;
&lt;br /&gt;
;r-timer guard&lt;br /&gt;
:Before any retransmit attempt, check &amp;lt;code&amp;gt;(now - t0)&amp;lt;/code&amp;gt; against &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;.  If exceeded, the slot is no longer eligible for retransmit.  Only the RTO timer (&amp;lt;code&amp;gt;rxm_due&amp;lt;/code&amp;gt;) treats r-timer expiry as terminal: it marks the flow &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; (peer unreachable).  Fast-retransmit, SACK-driven retransmit, and NACK-driven head-of-line re-emit silently skip aged-out slots and defer the flow-down decision to the next RTO fire.&lt;br /&gt;
&lt;br /&gt;
;Fast retransmit (hybrid trigger, RFC 8985 sec. 6.2)&lt;br /&gt;
:On a non-advancing cumulative ACK with the scoreboard advanced, fire one fast retransmit when EITHER (a) the head-of-line slot&#039;s latest send is older than the RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; ([[#3. Protocol parameters|Section 3]]) and not yet aged out, OR (b) the SACK &amp;lt;code&amp;gt;dup-thresh&amp;lt;/code&amp;gt; count above &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; reaches &amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; (= 3, RFC 8985 sec. 6.2 step 4).  Fires at most once per non-advancing cumulative-ACK value, gated by &amp;lt;code&amp;gt;rack_fired_lwe&amp;lt;/code&amp;gt; (the &amp;lt;code&amp;gt;snd_cr.lwe&amp;lt;/code&amp;gt; at which fast-retransmit last fired).  Set &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt; on the slot (one-shot per-slot gate) and enter NewReno-style careful recovery (see NewReno below in this section).&lt;br /&gt;
:The RACK reorder window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; uses the RFC 8985 sec. 6.2 form &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor.  Before the first RTT sample seeds &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; falls back to &amp;lt;code&amp;gt;MIN(reo_wnd_mult * SRTT / 4, SRTT)&amp;lt;/code&amp;gt;, still floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt; (consistent with the windowed-minimum fallback described in [[#12. RTT estimation|Section 12]]).  &amp;lt;code&amp;gt;min_rtt&amp;lt;/code&amp;gt; is a windowed minimum over the last &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; = 5 min of RTT samples (matches the Linux &amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt; default) so a route change to a longer path eventually re-anchors the reorder window without relying on &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; growth alone.&lt;br /&gt;
&lt;br /&gt;
;SACK-driven retransmit&lt;br /&gt;
:For each gap below &amp;lt;code&amp;gt;hi_sacked&amp;lt;/code&amp;gt; whose slot is (1) still owned, (2) not already &amp;lt;code&amp;gt;SND_FAST_RXM&amp;lt;/code&amp;gt;, (3) not aged out past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, and (4) either outside the RACK window &amp;lt;code&amp;gt;R&amp;lt;/code&amp;gt; OR with &amp;lt;code&amp;gt;dup_thresh &amp;amp;gt;= DUP_THRESH&amp;lt;/code&amp;gt; (same hybrid as fast-retransmit, see [[#6.2. Locked main path|Section 6.2]]), re-emit.  Each SACK-driven retransmit re-arms a fresh &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; so a lost retransmit can still be recovered by its own RTO timer.&lt;br /&gt;
&lt;br /&gt;
;NewReno&lt;br /&gt;
:On entry, &amp;lt;code&amp;gt;recovery_high = snd_cr.seqno + RTT_QUARANTINE&amp;lt;/code&amp;gt;.  Exit when &amp;lt;code&amp;gt;ackno &amp;amp;gt;= recovery_high&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;ackno == snd_cr.seqno&amp;lt;/code&amp;gt; (the latter means everything sent has been acknowledged).  &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; also clears recovery.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 9. Pre-DRF NACK ==&lt;br /&gt;
&lt;br /&gt;
The two sides have different inactivity thresholds&lt;br /&gt;
(&amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;), so a receiver can detect &amp;quot;stale data&lt;br /&gt;
run&amp;quot; before the sender&#039;s own DRF logic kicks in.  NACK is the&lt;br /&gt;
receiver-driven nudge that asks the sender to re-transmit the head&lt;br /&gt;
of the run.&lt;br /&gt;
&lt;br /&gt;
;Send (&amp;lt;code&amp;gt;frcti_nack_snd&amp;lt;/code&amp;gt;, called by &amp;lt;code&amp;gt;frcti_rcv&amp;lt;/code&amp;gt; when &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;FRCT_INACT_NEED_NACK&amp;lt;/code&amp;gt;)&lt;br /&gt;
:When an incoming DATA packet has no DRF and rcv-side activity is older than &amp;lt;code&amp;gt;rcv_cr.inact&amp;lt;/code&amp;gt;, the receiver emits a bare packet with &amp;lt;code&amp;gt;flags = FRCT_NACK&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;seqno = arrival_seqno - 1&amp;lt;/code&amp;gt; (informational only, not consulted by the receive handler).  The cooldown in [[#3. Protocol parameters|Section 3]] rate-limits the burst.  Non-DATA non-DRF arrivals bypass &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt; entirely; non-DATA DRF still rebases via the DRF branch.&lt;br /&gt;
&lt;br /&gt;
;Receive (&amp;lt;code&amp;gt;frcti_nack_rcv&amp;lt;/code&amp;gt;)&lt;br /&gt;
:Dispatched in the early-exit branch ([[#6.1. Early-exit dispatch|Section 6.1]]), before &amp;lt;code&amp;gt;rcv_inact_check&amp;lt;/code&amp;gt;.  The sender copies the head-of-line (HoL) &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; packet, marks the slot &amp;lt;code&amp;gt;SND_RTX | SND_FAST_RXM&amp;lt;/code&amp;gt; (Karn-suppress next ACK, one-shot fast-rxm gate), sets &amp;lt;code&amp;gt;rtt_lwe = snd_cr.lwe + 1&amp;lt;/code&amp;gt;, and re-emits via &amp;lt;code&amp;gt;fast_rxm_send&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt; and a refreshed &amp;lt;code&amp;gt;ackno&amp;lt;/code&amp;gt;.  The original &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left armed - the NACK emit is additive to the normal retransmit machinery, not a replacement.  No-op if nothing is in flight, the HoL slot has aged past &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt;, or the HoL &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt; pointer has been cleared by SACK or RACK.&lt;br /&gt;
&lt;br /&gt;
NACK serves two roles:&lt;br /&gt;
&lt;br /&gt;
# Lost first-of-run (DRF) packet recovery.  Required.  Until the DRF packet arrives, the receiver cannot rebase its window, so any subsequent in-flight packets look stale to the receiver.  The NACK fires the moment the second packet arrives at a stale receiver, telling the sender to re-emit the HoL (DRF) packet at NACK-cooldown latency rather than waiting for the initial RTO (which is the configured default until &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded by the first probe round-trip).&lt;br /&gt;
# General loss-recovery accelerator.  When loss is detected receiver-first, the NACK skips one RTO of latency relative to waiting for the sender&#039;s RTO to fire.&lt;br /&gt;
&lt;br /&gt;
In both cases the existing &amp;lt;code&amp;gt;rxm_entry&amp;lt;/code&amp;gt; and its RTO timer are left&lt;br /&gt;
armed, so the RTO path remains the eventual fallback.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 10. Cumulative + selective ACK ==&lt;br /&gt;
&lt;br /&gt;
Cumulative ACK is &amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;.  On out-of-order arrival the&lt;br /&gt;
receiver also emits a SACK packet ([[#1.3. SACK payload|Section 1.3]]) whose payload lists&lt;br /&gt;
&#039;&#039;present&#039;&#039; blocks above &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; (analogous to TCP SACK / QUIC ACK&lt;br /&gt;
ranges).  SACKs are rate-limited per [[#3. Protocol parameters|Section 3]] and suppressed when&lt;br /&gt;
neither &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; nor block count has changed since the last SACK.&lt;br /&gt;
&lt;br /&gt;
D-SACK reports (RFC 2883) are emitted in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt; of an&lt;br /&gt;
otherwise normal SACK frame (see [[#1.3. SACK payload|Section 1.3]] for the encoding).&lt;br /&gt;
Two receiver triggers arm a pending D-SACK report (single-slot,&lt;br /&gt;
latest-wins):&lt;br /&gt;
&lt;br /&gt;
* DATA arrival with &amp;lt;code&amp;gt;seqno &amp;amp;lt; rcv_cr.lwe&amp;lt;/code&amp;gt;, both wire-dup (no RXM, &amp;lt;code&amp;gt;is_dup_data&amp;lt;/code&amp;gt; path) and retransmit (RXM, post-FC branch) (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
* &amp;lt;code&amp;gt;rq_accept&amp;lt;/code&amp;gt; conflict, slot already occupied in &amp;lt;code&amp;gt;[lwe, rwe)&amp;lt;/code&amp;gt; (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
&lt;br /&gt;
When a D-SACK is pending and the standard scoreboard SACK would be&lt;br /&gt;
suppressed by dedup or rate-limit, the report is emitted as a&lt;br /&gt;
stand-alone SACK frame through the normal &amp;lt;code&amp;gt;ack_snd&amp;lt;/code&amp;gt; path; when a&lt;br /&gt;
D-SACK report is pending the path bypasses dedup and the &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;&lt;br /&gt;
rate-limit, but the a-timer suppression on rcv inactivity still&lt;br /&gt;
applies.&lt;br /&gt;
&lt;br /&gt;
Bare ACKs are deferred via a per-flow delayed-ACK timer (one in&lt;br /&gt;
flight at a time, atomic test-and-set dedup; fires per [[#3. Protocol parameters|Section 3]]&lt;br /&gt;
after the first in-order arrival).  Suppressed if (1) no new&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;, (2) rcv side is inactive (older than &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt;), or (3) the&lt;br /&gt;
sender just sent within &amp;lt;code&amp;gt;TICTIME&amp;lt;/code&amp;gt;.  A pending D-SACK ride-through&lt;br /&gt;
bypasses (1) and (3); the a-timer gate (2) is unconditional.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 11. Flow control ==&lt;br /&gt;
&lt;br /&gt;
The receiver advertises &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; in every FC field.  The sender treats&lt;br /&gt;
its &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt; as the absolute right edge: when&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt;= snd_cr.rwe&amp;lt;/code&amp;gt; the window is closed and &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt;&lt;br /&gt;
yields.  While closed, the sender periodically emits RDVS&lt;br /&gt;
(rendezvous) packets (cadence &amp;lt;code&amp;gt;DELT_RDV&amp;lt;/code&amp;gt;); the receiver replies with&lt;br /&gt;
a bare FC packet (&amp;lt;code&amp;gt;ackno = 0&amp;lt;/code&amp;gt;) that reopens the window.  Once the&lt;br /&gt;
window has been closed for longer than &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; the sender stops&lt;br /&gt;
emitting RDVS but does not tear the flow down - the writer keeps&lt;br /&gt;
blocking until either a peer-driven FC arrives or the KA&lt;br /&gt;
(keepalive) / r-timer marks the flow.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is clamped to &amp;lt;code&amp;gt;lwe + RQ_SIZE&amp;lt;/code&amp;gt; on receipt and MUST NOT shrink:&lt;br /&gt;
a backward &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is silently clamped to the current &amp;lt;code&amp;gt;snd_cr.rwe&amp;lt;/code&amp;gt;;&lt;br /&gt;
the FC packet still reopens the window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 12. RTT estimation ==&lt;br /&gt;
&lt;br /&gt;
Active RTTP probes ([[#1.4. RTTP payload|Section 1.4]]) carry a 32-bit &amp;lt;code&amp;gt;probe_id&amp;lt;/code&amp;gt; (0&lt;br /&gt;
reserved) and a 16-byte random nonce echoed verbatim - defends&lt;br /&gt;
against spoofed replies.  A ring of &amp;lt;code&amp;gt;RTTP_RING&amp;lt;/code&amp;gt; in-flight probes is&lt;br /&gt;
kept; an echo whose &amp;lt;code&amp;gt;(id, nonce)&amp;lt;/code&amp;gt; doesn&#039;t match the ring slot is&lt;br /&gt;
dropped.  A single RTTP sample is clamped to &amp;lt;code&amp;gt;RTT_CLAMP_MUL * srtt&amp;lt;/code&amp;gt;&lt;br /&gt;
(compile-time &amp;lt;code&amp;gt;RTT_CLAMP_MUL = 16&amp;lt;/code&amp;gt;) once &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is seeded; the first&lt;br /&gt;
cold-probe sample feeds &amp;lt;code&amp;gt;rtt_update&amp;lt;/code&amp;gt; raw.&lt;br /&gt;
&lt;br /&gt;
Probe arming gates:&lt;br /&gt;
&lt;br /&gt;
;Cold (no &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; yet)&lt;br /&gt;
:the receive path arms at most one probe per 100 ms via &amp;lt;code&amp;gt;frcti_rcv_probe&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;PROBE_DUE_COLD&amp;lt;/code&amp;gt;); arming requires an incoming packet.  Active send-path arming bails while &amp;lt;code&amp;gt;srtt == 0&amp;lt;/code&amp;gt;.&lt;br /&gt;
;Warm (&amp;lt;code&amp;gt;rtt_probe_arm&amp;lt;/code&amp;gt;, called from &amp;lt;code&amp;gt;frcti_snd&amp;lt;/code&amp;gt;)&lt;br /&gt;
:outstanding data (&amp;lt;code&amp;gt;snd_cr.seqno &amp;amp;gt; snd_cr.lwe&amp;lt;/code&amp;gt;), AND at least &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_rcv_rtt&amp;lt;/code&amp;gt; (last RTT receive of any kind), AND at least &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; since &amp;lt;code&amp;gt;t_snd_probe&amp;lt;/code&amp;gt; (last probe emit).&lt;br /&gt;
&lt;br /&gt;
Sample feeds either Linux&#039;s asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator&lt;br /&gt;
(&amp;lt;code&amp;gt;FRCT_LINUX_RTT_ESTIMATOR&amp;lt;/code&amp;gt;, default ON) or RFC 6298 symmetric EWMA&lt;br /&gt;
(compile option).  &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; is floored at 10 ms when seeded from a&lt;br /&gt;
hint, at 1 us after every update (including the first seeding&lt;br /&gt;
sample); &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; floored at 100 ns.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;RTO = max(rto_min, 2 * srtt, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(the &amp;lt;code&amp;gt;2 * srtt&amp;lt;/code&amp;gt; floor is an FRCT addition not in RFC 6298).&lt;br /&gt;
Effective wheel deadline capped per [[#3. Protocol parameters|Section 3]].&lt;br /&gt;
&lt;br /&gt;
ACK-derived samples (&amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;rtt_sample_eligible&amp;lt;/code&amp;gt;), beyond&lt;br /&gt;
the cum-ACK advance gate in &amp;lt;code&amp;gt;frcti_ack_rcv&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno &amp;amp;gt; lwe&amp;lt;/code&amp;gt; and&lt;br /&gt;
&amp;lt;code&amp;gt;ackno &amp;amp;lt;= seqno&amp;lt;/code&amp;gt;), require all of: not in recovery; ACK packet does&lt;br /&gt;
not carry &amp;lt;code&amp;gt;FRCT_RXM&amp;lt;/code&amp;gt;; HoL slot&#039;s &amp;lt;code&amp;gt;SND_RTX&amp;lt;/code&amp;gt; bit clear; slot&#039;s &amp;lt;code&amp;gt;rxm&amp;lt;/code&amp;gt;&lt;br /&gt;
pointer non-NULL (not SACK-consumed); &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; not below the &amp;lt;code&amp;gt;rtt_lwe&amp;lt;/code&amp;gt;&lt;br /&gt;
fence; &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; already seeded by an RTTP probe.  There is no ACK-only&lt;br /&gt;
seeding.&lt;br /&gt;
&lt;br /&gt;
Every eligible sample also feeds &amp;lt;code&amp;gt;RACK.min_RTT&amp;lt;/code&amp;gt; (RFC 8985 sec. 6.2)&lt;br /&gt;
via a windowed minimum: replace whenever the sample is strictly&lt;br /&gt;
smaller OR more than &amp;lt;code&amp;gt;MIN_RTT_WIN_NS&amp;lt;/code&amp;gt; (5 min, matches Linux&lt;br /&gt;
&amp;lt;code&amp;gt;tcp_min_rtt_wlen&amp;lt;/code&amp;gt;) has elapsed since the current min was set.  The&lt;br /&gt;
downward branch is immediate (faster path picked up at once); the&lt;br /&gt;
upward branch is gated on the window (a transient queue burst does&lt;br /&gt;
not poison the estimate, but a sustained route change to a longer&lt;br /&gt;
path re-anchors &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; after at most one window).  Seeded from&lt;br /&gt;
&amp;lt;code&amp;gt;rtt_hint&amp;lt;/code&amp;gt; at &amp;lt;code&amp;gt;rtt_init&amp;lt;/code&amp;gt;; 0 acts as the unset sentinel and the base&lt;br /&gt;
in &amp;lt;code&amp;gt;rack_reorder_window&amp;lt;/code&amp;gt; falls back from &amp;lt;code&amp;gt;min_RTT&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt; (so&lt;br /&gt;
&amp;lt;code&amp;gt;R = mult * SRTT/4&amp;lt;/code&amp;gt;, capped at &amp;lt;code&amp;gt;SRTT&amp;lt;/code&amp;gt;, floored at &amp;lt;code&amp;gt;MIN_REORDER_NS&amp;lt;/code&amp;gt;)&lt;br /&gt;
until the first sample.  See [[#6.2. Locked main path|Section 6.2]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 13. Liveness (keepalive) ==&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;code&amp;gt;qs.timeout &amp;amp;gt; 0&amp;lt;/code&amp;gt; a per-flow KA (keepalive) timer is armed.&lt;br /&gt;
Arming uses &amp;lt;code&amp;gt;rcv_cr.act&amp;lt;/code&amp;gt; for the deadline computation:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;deadline = min(snd_act + qs.timeout/4, rcv_act + qs.timeout)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(clamped to &amp;lt;code&amp;gt;now + qs.timeout/4&amp;lt;/code&amp;gt; if already past).  The timer fires&lt;br /&gt;
either on sender idleness (to send a KA) or on receiver idleness&lt;br /&gt;
(to declare the peer dead).  On fire (&amp;lt;code&amp;gt;ka_snd&amp;lt;/code&amp;gt;) the peer-dead test&lt;br /&gt;
uses &amp;lt;code&amp;gt;max(rcv_cr.act, t_ka_rcv)&amp;lt;/code&amp;gt; so a recent KA reply counts even&lt;br /&gt;
when no DATA has arrived:&lt;br /&gt;
&lt;br /&gt;
* If &amp;lt;code&amp;gt;now - max(rcv_cr.act, t_ka_rcv) &amp;amp;gt; qs.timeout&amp;lt;/code&amp;gt;, mark the flow &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; and notify the per-process flow-event set (&amp;lt;code&amp;gt;proc.fqset&amp;lt;/code&amp;gt;) with &amp;lt;code&amp;gt;FLOW_PEER&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Else if &amp;lt;code&amp;gt;snd_idle &amp;amp;gt; qs.timeout/4&amp;lt;/code&amp;gt;, emit a bare &amp;lt;code&amp;gt;KA | ACK&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;ackno = rcv_cr.lwe&amp;lt;/code&amp;gt;) and re-arm.&lt;br /&gt;
* Else just re-arm.&lt;br /&gt;
&lt;br /&gt;
Note: &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; are the receive and transmit shared-memory&lt;br /&gt;
ring buffers.  The r-timer raises &amp;lt;code&amp;gt;ACL_FLOWDOWN&amp;lt;/code&amp;gt; on both (route is&lt;br /&gt;
broken); keepalive raises &amp;lt;code&amp;gt;ACL_FLOWPEER&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; only and notifies&lt;br /&gt;
the flow-event set (peer is silent, writer keeps &amp;lt;code&amp;gt;tx_rb&amp;lt;/code&amp;gt; usable) -&lt;br /&gt;
distinct ACLs.  &amp;lt;code&amp;gt;qs.timeout == 0&amp;lt;/code&amp;gt; disables keepalive entirely; a&lt;br /&gt;
silent peer crash is then undetected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 14. Linger / teardown ==&lt;br /&gt;
&lt;br /&gt;
On &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt; computes a grace timeout&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;max(rcv_cr.act + rcv_cr.inact, snd_cr.act + snd_cr.inact) - now&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(floored at 0 and converted to seconds) and returns it; &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&lt;br /&gt;
forwards this to the IRMd as the dealloc grace.  The IRMd, not FRCT,&lt;br /&gt;
performs the wait.  Before computing the timeout, FRCT may emit a&lt;br /&gt;
final ACK when &amp;lt;code&amp;gt;rcv_cr.lwe != rcv_cr.seqno&amp;lt;/code&amp;gt; (the peer has not been&lt;br /&gt;
told the most recent cumulative ACK) AND the rcv side has been&lt;br /&gt;
active within &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; (a-timer not aged out).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;FRCTFLINGER&amp;lt;/code&amp;gt; is honoured only when &amp;lt;code&amp;gt;snd_cr.lwe &amp;amp;lt; edge&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;edge =&lt;br /&gt;
snd_fin_seqno&amp;lt;/code&amp;gt; after FIN has been sent in stream mode and&lt;br /&gt;
&amp;lt;code&amp;gt;snd_cr.seqno&amp;lt;/code&amp;gt; otherwise (data or FIN still in flight).  The drain&lt;br /&gt;
itself runs in &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;&#039;s &amp;lt;code&amp;gt;while (FRCTI_LINGERING)&amp;lt;/code&amp;gt; loop, not in&lt;br /&gt;
&amp;lt;code&amp;gt;frcti_dealloc&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The fd is single-reader / single-writer (documented in the&lt;br /&gt;
manpages).  &amp;lt;code&amp;gt;flow_write&amp;lt;/code&amp;gt; pumps &amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; on every call (via&lt;br /&gt;
&amp;lt;code&amp;gt;flow_wait_window&amp;lt;/code&amp;gt; -&amp;amp;gt; &amp;lt;code&amp;gt;flow_drain_rx_nb&amp;lt;/code&amp;gt;) and additionally blocks on&lt;br /&gt;
&amp;lt;code&amp;gt;rx_rb&amp;lt;/code&amp;gt; when the send window is closed.  A pure-writer thread thus&lt;br /&gt;
consumes ACKs without a dedicated reader.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 15. Heritage and adopted techniques ==&lt;br /&gt;
&lt;br /&gt;
Delta-t (Watson, 1981) is the primary heritage; FRCP descends from&lt;br /&gt;
the delta-t protocol family via the Recursive InterNetwork&lt;br /&gt;
Architecture (RINA; Day, &amp;quot;Patterns in Network Architecture&amp;quot;, 2008,&lt;br /&gt;
ch. 9).  Timer-based connection management&lt;br /&gt;
(no SYN/FIN handshake, per-flow state born on first DATA and&lt;br /&gt;
reclaimed after &amp;lt;code&amp;gt;t_mpl + a + r&amp;lt;/code&amp;gt; of silence), the DRF marker, and the&lt;br /&gt;
&amp;lt;code&amp;gt;t_mpl&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_a&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;t_r&amp;lt;/code&amp;gt; timers all come from delta-t.  See Watson,&lt;br /&gt;
&amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection&lt;br /&gt;
Management&amp;quot;, Computer Networks 5 (1981).&lt;br /&gt;
&lt;br /&gt;
The unified &amp;lt;code&amp;gt;flow_alloc(name, qos, ...)&amp;lt;/code&amp;gt; primitive and its&lt;br /&gt;
multi-axis QoS-cube argument ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) also come from RINA&lt;br /&gt;
(Day 2008, ch. 6; Grasa et al., &amp;quot;IRATI: investigating RINA as an&lt;br /&gt;
alternative to TCP/IP&amp;quot;, Computer Networks 92 (2015)) - reliability,&lt;br /&gt;
ordering, CRC presence, and encryption are flow attributes, not&lt;br /&gt;
separate sockets or protocols.&lt;br /&gt;
&lt;br /&gt;
The table below summarises additional adopted techniques and their&lt;br /&gt;
references.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! FRCP mechanism !! Heritage !! Reference / note&lt;br /&gt;
|-&lt;br /&gt;
| Random new &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;seqno_rotate&amp;lt;/code&amp;gt; || TCP ISN || RFC 6528 (Gont &amp;amp;amp; Bellovin, 2012).  QUIC PN-space reset (RFC 9000 sec. 12.3) is a structural analogue.&lt;br /&gt;
|-&lt;br /&gt;
| Cumulative ACK, left-window-edge advance || TCP || RFC 793 / RFC 9293&lt;br /&gt;
|-&lt;br /&gt;
| Receive window with non-shrink rule || TCP || RFC 793 sec. 3.7 / RFC 9293 sec. 3.8.6; RFC 1122 sec. 4.2.2.16 for the explicit non-shrink prohibition&lt;br /&gt;
|-&lt;br /&gt;
| Modular &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; arithmetic (&amp;lt;code&amp;gt;before&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;after&amp;lt;/code&amp;gt; helpers) || TCP || RFC 793 sec. 3.3 / RFC 9293 sec. 3.4&lt;br /&gt;
|-&lt;br /&gt;
| Selective ACK block list || TCP || RFC 2018 (Mathis et al., 1996).  Encoded as a typed FRCP packet rather than a TCP option, so framing is closer to QUIC ACK frames.  D-SACK (RFC 2883) carried in-band as &amp;lt;code&amp;gt;block[0]&amp;lt;/code&amp;gt;; see [[#1.3. SACK payload|Section 1.3]].&lt;br /&gt;
|-&lt;br /&gt;
| NewReno-careful recovery with &amp;lt;code&amp;gt;recovery_high&amp;lt;/code&amp;gt; gate || TCP || RFC 6582 (Henderson et al., 2012); QUIC builds on the same model in RFC 9002 sec. 7.3.2.  Cwnd half absent (CC in IPCP).&lt;br /&gt;
|-&lt;br /&gt;
| RACK reordering window for fast retransmit || TCP || RFC 8985 (Cheng et al., 2021).  FRCP &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; with a &amp;lt;code&amp;gt;MIN_REORDER_NS = 250 us&amp;lt;/code&amp;gt; floor against &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; collapse; matches RFC 8985 sec. 6.2 and Linux &amp;lt;code&amp;gt;tcp_rack_reo_wnd&amp;lt;/code&amp;gt;.  DSACK-driven &amp;lt;code&amp;gt;reo_wnd_mult&amp;lt;/code&amp;gt; (sec. 6.2 step 4) is adopted; see [[#1.3. SACK payload|Section 1.3]] for the wire encoding.  The hybrid RACK-or-&amp;lt;code&amp;gt;DUP_THRESH&amp;lt;/code&amp;gt; trigger from RFC 8985 sec. 6.2 step 4 is adopted ([[#8. Retransmission|Section 8]]).  QUIC&#039;s analogue in RFC 9002 sec. 6.1.2 uses &amp;lt;code&amp;gt;max(srtt, latest_rtt)&amp;lt;/code&amp;gt; as the base.&lt;br /&gt;
|-&lt;br /&gt;
| Karn&#039;s algorithm: no RTT sample on retransmits, RTO-collapse freeze || TCP || Karn &amp;amp;amp; Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, SIGCOMM 1987; RFC 6298 sec. 3.&lt;br /&gt;
|-&lt;br /&gt;
| RTO formula &amp;lt;code&amp;gt;RTO = max(RTO_MIN, srtt + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt; || TCP || RFC 6298 (Paxson et al., 2011).  &amp;lt;code&amp;gt;RTO_MIN&amp;lt;/code&amp;gt; = 5 ms is below RFC 6298 sec. 2.4&#039;s 1 s SHOULD-floor - a recursive-layer choice.&lt;br /&gt;
|-&lt;br /&gt;
| Linux asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; estimator (default) || Linux kernel || &amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt;; the &amp;lt;code&amp;gt;if(delta&amp;amp;lt;0) m&amp;amp;gt;&amp;amp;gt;=3&amp;lt;/code&amp;gt; dampening is a kernel divergence from RFC 6298.  RFC 6298 EWMA available behind a compile flag.&lt;br /&gt;
|-&lt;br /&gt;
| Delayed ACK with rate suppression || TCP || RFC 813 (Clark, 1982); RFC 1122 sec. 4.2.3.2; RFC 5681 sec. 4.2.  Single-deadline coalescing rather than &amp;quot;ack-every-other-segment&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| Zero-window-probe / persist-timer analogue (RDVS) || TCP || RFC 1122 sec. 4.2.2.17 / RFC 9293 sec. 3.8.6.1.  RDVS solicits an FC reply, distinct from QUIC &amp;lt;code&amp;gt;DATA_BLOCKED&amp;lt;/code&amp;gt; (RFC 9000 sec. 19.12), which is one-way notification.  &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; give-up departs from TCP.&lt;br /&gt;
|-&lt;br /&gt;
| Multiplexed control on a single PCI || SCTP / QUIC || SCTP chunk bundling (RFC 9260 sec. 6.10); QUIC frame multiplexing (RFC 9000 sec. 12.4).  Cleaner fit than TCP&#039;s separate-flag-bits design.&lt;br /&gt;
|-&lt;br /&gt;
| ACK ranges as multiple discontiguous acked blocks || QUIC || QUIC ACK frame (RFC 9000 sec. 19.3).  FRCP SACK is conceptually QUIC-frame-shaped even though encoded as absolute &amp;lt;code&amp;gt;[start,end]&amp;lt;/code&amp;gt; pairs.&lt;br /&gt;
|-&lt;br /&gt;
| Nonce-authenticated active RTT / liveness probing (RTTP) || QUIC &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;PATH_RESPONSE&amp;lt;/code&amp;gt; (RFC 9000 sec. 8.2, sec. 19.17, sec. 19.18).  WebRTC ICE consent-freshness (RFC 7675) is the same pattern.  QUIC&#039;s nonce is 8 octets; FRCP chooses 16.&lt;br /&gt;
|-&lt;br /&gt;
| Probing distinct from keepalive || QUIC || KA timer answers &amp;quot;peer alive?&amp;quot;, RTTP answers &amp;quot;path measurable?&amp;quot;, as in QUIC PING (RFC 9000 sec. 19.2) vs &amp;lt;code&amp;gt;PATH_CHALLENGE&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| Bare KA + ACK keepalive packets || QUIC / SCTP || QUIC PING (RFC 9000 sec. 19.2); SCTP HEARTBEAT / HEARTBEAT-ACK (RFC 9260 sec. 8.3).  SCTP HEARTBEAT also carries an opaque echoed blob, structurally similar to FRCP RTTP.&lt;br /&gt;
|-&lt;br /&gt;
| (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) fragment-role bits ([[#7.2. Fragmentation and reassembly|Section 7.2]]) || SCTP || RFC 9260 sec. 3.3.1 DATA chunk B/E bits encode the same four states (&amp;lt;code&amp;gt;B+E=SOLE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;B-only=FIRST&amp;lt;/code&amp;gt;, neither=&amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;E-only=LAST&amp;lt;/code&amp;gt;).  Each fragment carries its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;/TSN and is independently retransmitted.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-offset reassembly (Sections [[#1.5. Stream PCI extension|1.5]], [[#16. Stream-mode flows|16]]) || QUIC || QUIC STREAM frame (RFC 9000 sec. 19.8) uses Offset + Length varints; FRCP uses fixed 32-bit &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;.  One stream per flow vs QUIC&#039;s many streams multiplexed.&lt;br /&gt;
|-&lt;br /&gt;
| FIN end-of-stream marker (Sections [[#1.2. Flag bits|1.2]], [[#16. Stream-mode flows|16]]) || TCP / QUIC || TCP FIN flag (RFC 9293 sec. 3.1) closes one half of the byte stream; QUIC STREAM frame FIN bit (RFC 9000 sec. 19.8) does the same per stream with an immutable final-size invariance (RFC 9000 sec. 4.5: the final size is fixed once observed).  FRCP&#039;s FIN consumes one packet &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (not one byte of stream space) and is idempotent on the sender side.&lt;br /&gt;
|-&lt;br /&gt;
| Stream byte-credit flow control ([[#16. Stream-mode flows|Section 16]]) || QUIC || &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; (RFC 9000 sec. 4.1, sec. 19.10).  FRCP projects a per-flow byte budget onto the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt;.  Single stream per flow collapses QUIC&#039;s &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.&lt;br /&gt;
|-&lt;br /&gt;
| Header protection (encrypted seqnos) || QUIC || QUIC RFC 9001 sec. 5.4 applies header protection on top of AEAD to mask the packet number.  FRCP&#039;s per-flow AEAD wrap ([[#16. Stream-mode flows|Section 16]]) is wider: it encrypts the entire PCI including &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; because the IPCP below already routes, so no destination connection-ID needs to stay in clear (cf. RFC 9000 sec. 5.2).&lt;br /&gt;
|-&lt;br /&gt;
| Two-bit fragment role polarity || SCTP || The (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) pair follows SCTP B/E (begin = 1 / end = 1) rather than IPv4 MF (RFC 791 sec. 3.2), which has the inverse polarity (MF = 1 means NOT last).&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal reliability / ordering axes ([[#2.2. Service modes (orthogonal axes)|Section 2.2]]) || SCTP || PR-SCTP (RFC 3758, per-message partial reliability) and SCTP DATA U-bit (RFC 9260 sec. 3.3.1, per-message unordered) are the closest precedents for decoupling reliability from ordering; FRCP sets them per-flow rather than per-message.&lt;br /&gt;
|-&lt;br /&gt;
| Orthogonal CRC (&amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt;) || UDP-Lite || RFC 3828 (Larzon et al., 2004) lets the sender pick a per-packet Checksum Coverage and the receiver enforce a locally configured minimum (no in-band negotiation; sec. 3.1, sec. 3.3).  FRCP gates a full CRC trailer on &amp;lt;code&amp;gt;qs.ber == 0&amp;lt;/code&amp;gt; at flow setup.  Contrast TCP / SCTP (mandatory checksum) and QUIC (AEAD subsumes CRC).&lt;br /&gt;
|-&lt;br /&gt;
| Setup-time service negotiation || DCCP / SCTP / QUIC || DCCP Service Codes (RFC 4340 sec. 8.1.2, RFC 5595); SCTP INIT parameters (RFC 9260 sec. 3.3.2); QUIC transport parameters (RFC 9000 sec. 7.4).  All negotiate service properties at connection setup; only RINA&#039;s QoS cube exposes them as an orthogonal vector.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.1. Original to FRCP (no clean prior art) ===&lt;br /&gt;
&lt;br /&gt;
* Pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]): receiver-driven nudge exploiting &amp;lt;code&amp;gt;snd_cr.inact &amp;amp;gt; rcv_cr.inact&amp;lt;/code&amp;gt;.  Closest analogues are SCTP Gap Ack Blocks (RFC 9260 sec. 3.3.4) and DCCP Ack Vector (RFC 4340 sec. 11.4) - both let the receiver describe gaps to the sender, but neither targets the cross-epoch / pre-DRF case.&lt;br /&gt;
* &amp;lt;code&amp;gt;MAX_RDV&amp;lt;/code&amp;gt; window-probe give-up: neither TCP (persist-timer probes until application or R2 abort, RFC 9293 sec. 3.8.6.1) nor QUIC has an explicit FC-give-up counter.  A recursive-network choice: outer layers can drop the flow.&lt;br /&gt;
* Skip-past-gap reassembly ([[#7.2. Fragmentation and reassembly|Section 7.2]]): SCTP fragments and reassembles every flow regardless of reliability/ordering, using its own per-stream reassembly queue; QUIC fragments via STREAM offsets.  FRCP fragments best-effort flows too, but the receiver drops the broken prefix the moment a later run-start (&amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; role) is visible inside the &amp;lt;code&amp;gt;RQ_SIZE&amp;lt;/code&amp;gt;-wide reorder ring - no IP-frag-style timeout, no SCTP-style explicit abort.  If no later run-start arrives within the ring, &amp;lt;code&amp;gt;frag_run_inspect&amp;lt;/code&amp;gt; returns &amp;lt;code&amp;gt;NOT_READY&amp;lt;/code&amp;gt; and the partial run keeps its slots; the next inspect retries.  The trade-off: a permanently-lost &amp;lt;code&amp;gt;MID&amp;lt;/code&amp;gt; in a long isolated run holds slots until either a later &amp;lt;code&amp;gt;FIRST&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;SOLE&amp;lt;/code&amp;gt; appears in the ring or the writer stops, at which point the slots are reclaimed on flow teardown.&lt;br /&gt;
* Reassembly deferred to consume time ([[#7.2. Fragmentation and reassembly|Section 7.2]]), message mode only (&amp;lt;code&amp;gt;qos.service == SVC_MESSAGE&amp;lt;/code&amp;gt;): SCTP (RFC 9260 sec. 6.9), QUIC (RFC 9000 sec. 2.2), and TCP (RFC 9293) all hold reassembly state at the receive boundary.  FRCP message-mode leaves fragments in the shared-memory ring until &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; pulls and lands the SDU directly in the caller&#039;s buffer.  Stream mode ([[#16. Stream-mode flows|Section 16]]) uses the standard QUIC-style direct ring placement on receive and does not defer.  The optimisation is enabled by the Shared-Memory Subsystem (SSM) packet-buffer ring (see &amp;lt;code&amp;gt;struct ssm_pk_buff&amp;lt;/code&amp;gt; at [[#1.1. PCI header|Section 1.1]]); the analogue is OS-level scatter-gather I/O (&amp;lt;code&amp;gt;recvmsg+iovec&amp;lt;/code&amp;gt;), not a transport-layer prior art.&lt;br /&gt;
* TLP-equivalent tail-loss recovery (RFC 8985 sec. 7; RFC 9002 sec. 6.2): FRCP does not emit an explicit Tail Loss Probe packet, but the same goal is met implicitly by RACK loss detection ([[#8. Retransmission|Section 8]]) firing on a non-advancing cumulative ACK once the head-of-line slot ages past the RACK reorder window &amp;lt;code&amp;gt;R = MIN(reo_wnd_mult * min_RTT / 4, SRTT)&amp;lt;/code&amp;gt; - well below &amp;lt;code&amp;gt;RTO = max(2 * SRTT, SRTT + (mdev &amp;amp;lt;&amp;amp;lt; MDEV_MUL))&amp;lt;/code&amp;gt;.  A receiver-driven nudge is also available via the pre-DRF NACK ([[#9. Pre-DRF NACK|Section 9]]).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.2. Not adopted ===&lt;br /&gt;
&lt;br /&gt;
* Slow start, congestion window (cwnd), Additive Increase / Multiplicative Decrease (AIMD), NewReno cwnd inflation.  Congestion control lives in the IPCP CA policies and is driven by Explicit Congestion Notification (ECN, RFC 3168).&lt;br /&gt;
* Nagle / silly-window-syndrome (SWS) avoidance (RFC 896, RFC 1122 sec. 4.2.3.4).  (Deferred work, not adopted in the current spec.)&lt;br /&gt;
* TCP Timestamps (RFC 7323) / Protection Against Wrapped Sequences (PAWS) - RTT measurement uses RTTP, not per-segment timestamps.  A peer-supplied timestamp echoed on every ACK lets a malicious peer drive the &amp;lt;code&amp;gt;srtt&amp;lt;/code&amp;gt; estimate arbitrarily low, collapsing the RTO and triggering a self-inflicted retransmit storm.  RTTP confines RTT measurement to nonce-authenticated probe round-trips, where a forged echo is rejected before it can reach the estimator.&lt;br /&gt;
* ECN (Explicit Congestion Notification) response inside FRCP (consumed by IPCP Congestion Avoidance / CA).&lt;br /&gt;
* IP-style fragment-offset reassembly (RFC 791 sec. 3.2; RFC 8200 sec. 4.5).  Message-mode FRCP relies on the FRCT &amp;lt;code&amp;gt;rq[]&amp;lt;/code&amp;gt; reorder ring keyed by &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; (shared by FRTX and best-effort flows) to put fragments back in order; no separate offset field is needed and no IP-style hole-list reassembly buffer is kept.  Stream-mode FRCP does carry &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte offsets ([[#1.5. Stream PCI extension|Section 1.5]]) for direct ring placement on receive.&lt;br /&gt;
* QUIC STREAM offset+length framing on &#039;&#039;every&#039;&#039; flow (RFC 9000 sec. 19.8).  Message-mode FRCP uses the SCTP-style B/E flag-bit encoding (&amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt;) and skips the offsets; stream-mode FRCP adopts the QUIC offset model (heritage table above).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 16. Stream-mode flows ==&lt;br /&gt;
&lt;br /&gt;
When a flow is allocated with &amp;lt;code&amp;gt;qos.service == SVC_STREAM&amp;lt;/code&amp;gt; both peers&lt;br /&gt;
switch to byte-stream semantics, layered on top of the FRTX reorder&lt;br /&gt;
machinery already described in Sections [[#6. Receive path|6]]-[[#8. Retransmission|8]].&lt;br /&gt;
&lt;br /&gt;
=== 16.1. Send ===&lt;br /&gt;
&lt;br /&gt;
The sender splits the caller&#039;s octets into chunks of at most&lt;br /&gt;
&amp;lt;code&amp;gt;(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt; octets (Sections [[#1.1. PCI header|1.1]]&lt;br /&gt;
and [[#1.5. Stream PCI extension|1.5]]).  Each chunk is one DATA packet with its own &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; byte range copied from a monotonic stream counter.&lt;br /&gt;
In stream mode &amp;lt;code&amp;gt;FFGM&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;LFGM&amp;lt;/code&amp;gt; are unused and MUST be transmitted as&lt;br /&gt;
zero; the per-byte position is carried by the &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt;&lt;br /&gt;
extension instead.&lt;br /&gt;
&lt;br /&gt;
End-of-stream is signalled with a 0-byte DATA packet that has &amp;lt;code&amp;gt;FIN&amp;lt;/code&amp;gt;&lt;br /&gt;
(bit 12) set, emitted on the FIN triggers listed in [[#1.2. Flag bits|Section 1.2]]&lt;br /&gt;
(WR-half close, &amp;lt;code&amp;gt;flow_dealloc&amp;lt;/code&amp;gt;, and any other path that yields the&lt;br /&gt;
final byte).  The sender MUST emit at most one FIN per flow; its&lt;br /&gt;
&amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; MUST equal &amp;lt;code&amp;gt;[final-byte, final-byte)&amp;lt;/code&amp;gt; (i.e., empty&lt;br /&gt;
interval at the final byte position; final-size invariance,&lt;br /&gt;
analogous to QUIC RFC 9000 sec. 4.5).  Idempotency is enforced by&lt;br /&gt;
an &amp;lt;code&amp;gt;snd_fin_sent&amp;lt;/code&amp;gt; guard.&lt;br /&gt;
&lt;br /&gt;
=== 16.2. Receive ===&lt;br /&gt;
&lt;br /&gt;
On arrival the receiver places the payload directly into a per-flow&lt;br /&gt;
byte-indexed receive ring of width &amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt; (octets) at the position&lt;br /&gt;
indicated by &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, with a two-segment &amp;lt;code&amp;gt;memcpy&amp;lt;/code&amp;gt; across the ring&lt;br /&gt;
boundary if needed.  Receipt is recorded in the FRTX reorder&lt;br /&gt;
machinery ([[#6.2. Locked main path|Section 6.2]]) augmented with the packet&#039;s &amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;, and&lt;br /&gt;
FIN bit per slot.  When a packet&#039;s &amp;lt;code&amp;gt;[start, end)&amp;lt;/code&amp;gt; front-overlaps&lt;br /&gt;
bytes already at or below the byte high-water mark, the overlap is&lt;br /&gt;
trimmed before placement so the same byte is never written twice.&lt;br /&gt;
After stashing, the receiver advances &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; and the byte high-water&lt;br /&gt;
mark across any newly-contiguous prefix.  Each slot advanced MUST&lt;br /&gt;
satisfy &amp;lt;code&amp;gt;start == the last-delivered slot&#039;s end&amp;lt;/code&amp;gt;; a slot whose&lt;br /&gt;
&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; does not equal that &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; is silently dropped at delivery time&lt;br /&gt;
(the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; is consumed, no stream bytes contributed) and the high-&lt;br /&gt;
water mark does not advance past it.  The stream byte-stream&lt;br /&gt;
stalls at that point - there is no flow-tear-down on mismatch.&lt;br /&gt;
This filters spliced or off-path-injected slots that fall in&lt;br /&gt;
window without strong cryptographic authentication.&lt;br /&gt;
&lt;br /&gt;
A FIN slot marks end-of-stream at advance time only if its byte&lt;br /&gt;
position equals the last-delivered slot&#039;s &amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt;; otherwise the FIN&lt;br /&gt;
is ignored and the corresponding &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; occupies a slot but&lt;br /&gt;
contributes no stream bytes.  No packet buffer is held after the&lt;br /&gt;
ring copy.&lt;br /&gt;
&lt;br /&gt;
=== 16.3. Read ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns up to &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt; octets from the contiguous prefix&lt;br /&gt;
&amp;lt;code&amp;gt;[next, high-water)&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;next&amp;lt;/code&amp;gt; is the byte the application has&lt;br /&gt;
already consumed up to and &amp;lt;code&amp;gt;high-water&amp;lt;/code&amp;gt; is the rightmost contiguous&lt;br /&gt;
byte received.  When the stream is fully drained AND end-of-stream&lt;br /&gt;
(EOS) was observed (&amp;lt;code&amp;gt;next == EOS&amp;lt;/code&amp;gt; byte position), &amp;lt;code&amp;gt;flow_read&amp;lt;/code&amp;gt; returns&lt;br /&gt;
0 (&amp;lt;code&amp;gt;EOF&amp;lt;/code&amp;gt;) - the same shape POSIX &amp;lt;code&amp;gt;read(2)&amp;lt;/code&amp;gt; uses on TCP after a peer&lt;br /&gt;
FIN.&lt;br /&gt;
&lt;br /&gt;
=== 16.4. Flow control ===&lt;br /&gt;
&lt;br /&gt;
ACK / SACK / RACK / RTO machinery is unchanged; the FRTX reorder&lt;br /&gt;
ring is reused as a per-&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; received-bitmap.  Let &amp;lt;code&amp;gt;per_pkt =&lt;br /&gt;
(frag_mtu - base PCI - stream PCI extension)&amp;lt;/code&amp;gt;, the maximum stream-&lt;br /&gt;
byte payload one DATA packet can carry ([[#16.1. Send|Section 16.1]]).  The&lt;br /&gt;
receive window advertised in FC is clamped so the byte window&lt;br /&gt;
(&amp;lt;code&amp;gt;ring_sz&amp;lt;/code&amp;gt;) cannot be overrun: the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; is at most&lt;br /&gt;
&amp;lt;code&amp;gt;rcv_cr.lwe + ring_sz / per_pkt&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC byte-credit flow-control model&lt;br /&gt;
(&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt;, RFC 9000 sec. 4.1 and sec. 19.10) projected onto&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; space.  With one stream per flow there is no &amp;lt;code&amp;gt;MAX_DATA&amp;lt;/code&amp;gt; /&lt;br /&gt;
&amp;lt;code&amp;gt;MAX_STREAM_DATA&amp;lt;/code&amp;gt; distinction.  Receiver-side silly-window-syndrome&lt;br /&gt;
(SWS) avoidance (RFC 9293 sec. 3.8.6.2.2) is achieved by combining&lt;br /&gt;
the consume-time &amp;lt;code&amp;gt;rwe&amp;lt;/code&amp;gt; bump with the global non-shrink rule from&lt;br /&gt;
[[#11. Flow control|Section 11]].&lt;br /&gt;
&lt;br /&gt;
=== 16.5. Security considerations ===&lt;br /&gt;
&lt;br /&gt;
Threat model.  An attacker that can observe (on-path passive) or&lt;br /&gt;
predict (off-path blind) the flow&#039;s seqnos and byte offsets on an&lt;br /&gt;
unencrypted stream flow can inject DATA or FIN at any in-window&lt;br /&gt;
position.  The in-line consistency checks above (&amp;lt;code&amp;gt;start&amp;lt;/code&amp;gt; == prior&lt;br /&gt;
&amp;lt;code&amp;gt;end&amp;lt;/code&amp;gt; on advance; FIN MUST be 0-byte; FIN MUST sit at the final&lt;br /&gt;
byte position) realise the spirit of RFC 5961&#039;s &amp;quot;sequence-window&lt;br /&gt;
plus exact-position match for control bits&amp;quot; without an explicit&lt;br /&gt;
challenge-ACK probe; they make a few specific blind attack shapes&lt;br /&gt;
harder but are not cryptographic authentication. This is&lt;br /&gt;
comparable to TCP without the TCP Authentication Option (TCP-AO,&lt;br /&gt;
RFC 5925), tighter than a&lt;br /&gt;
pre-RFC-5961 TCP stack, and roughly equivalent to a modern&lt;br /&gt;
RFC 5961 stack against blind off-path injection - none of these&lt;br /&gt;
help once the attacker can sniff. TLS over TCP (RFC 8446)&lt;br /&gt;
encrypts only the TCP payload and leaves TCP seqnos, ACKs, FIN,&lt;br /&gt;
and RST in the clear, so TLS does NOT defend against TCP-header-&lt;br /&gt;
level injection; QUIC (RFC 9000) hides packet numbers under&lt;br /&gt;
header protection (RFC 9001 sec. 5.4), so this specific weakness&lt;br /&gt;
does not apply to QUIC.&lt;br /&gt;
&lt;br /&gt;
Mitigation: AEAD.  When the flow has encryption enabled the&lt;br /&gt;
recommended AEAD ciphers (AES-GCM, RFC 5288; or ChaCha20-Poly1305,&lt;br /&gt;
RFC 8439) wrap the entire FRCP packet on the wire - PCI, stream&lt;br /&gt;
extension, body, and the CRC trailer when &amp;lt;code&amp;gt;ber == 0&amp;lt;/code&amp;gt; - under a&lt;br /&gt;
per-flow symmetric key derived from the flow&#039;s own key exchange&lt;br /&gt;
([[#1.1. PCI header|Section 1.1]]).  The AEAD tag (~2^-128 forgery probability)&lt;br /&gt;
dominates the CRC (~2^-32) for integrity in this mode but the CRC&lt;br /&gt;
trailer is currently retained inside the wrap (see [[#1.1. PCI header|Section 1.1]]).&lt;br /&gt;
Implementations MUST NOT rely on the security properties below&lt;br /&gt;
when a non-AEAD cipher (e.g. AES-CTR alone) is negotiated; non-&lt;br /&gt;
AEAD modes provide confidentiality only and the threat-model&lt;br /&gt;
claims do not hold.&lt;br /&gt;
&lt;br /&gt;
With an AEAD cipher in use, seqnos, byte offsets, and the FIN bit&lt;br /&gt;
are both authenticated and confidential. Against an off-path or&lt;br /&gt;
on-path-passive attacker this is:&lt;br /&gt;
&lt;br /&gt;
* Stronger than TCP+TLS (TCP header in the clear).&lt;br /&gt;
* Stronger than TCP+TCP-AO (header authenticated but visible).&lt;br /&gt;
* Comparable to IPsec ESP transport mode (RFC 4303), which similarly authenticates and encrypts the upper-layer header plus payload, and to QUIC packet protection (RFC 9001 sec. 5), with the difference that QUIC must leave the destination connection ID in the clear for routing whereas FRCP relies on the IPCP below for delivery and can therefore encrypt its entire PCI.&lt;br /&gt;
&lt;br /&gt;
Keying granularity.  FRCP runs key exchange (&amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt;) per flow, so&lt;br /&gt;
each &amp;lt;code&amp;gt;flow_alloc&amp;lt;/code&amp;gt; yields independent symmetric keys.  This is&lt;br /&gt;
finer-grained than QUIC (per-connection, RFC 9001, where one&lt;br /&gt;
handshake covers all multiplexed streams) and finer-grained than&lt;br /&gt;
typical IPsec deployment (per-host-pair Security Associations,&lt;br /&gt;
SAs).  Forward secrecy follows from the &amp;lt;code&amp;gt;kex&amp;lt;/code&amp;gt; when an ephemeral&lt;br /&gt;
Diffie-Hellman exchange (DHE), or a hybrid mode (classical DH +&lt;br /&gt;
post-quantum Key Encapsulation Mechanism / KEM), is selected.&lt;br /&gt;
&lt;br /&gt;
Replay protection.  The AEAD layer itself does NOT carry an&lt;br /&gt;
explicit anti-replay window (unlike IPsec ESP, RFC 4303 sec.&lt;br /&gt;
3.4.3, or DTLS, RFC 9147 sec. 4.5.1).  For FRCP-engaged flows the&lt;br /&gt;
&amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt;-space duplicate-suppression in [[#6.2. Locked main path|Section 6.2]] rejects replayed&lt;br /&gt;
DATA after the AEAD strips the wrap, because the AEAD authenticates&lt;br /&gt;
the &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; and a replay re-presents an old &amp;lt;code&amp;gt;seqno&amp;lt;/code&amp;gt; that is then&lt;br /&gt;
discarded either as a duplicate (still inside the receive window)&lt;br /&gt;
or as outside the receive window, depending on how far &amp;lt;code&amp;gt;lwe&amp;lt;/code&amp;gt; has&lt;br /&gt;
advanced since the original packet was delivered.  RAW&lt;br /&gt;
(&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;) flows have no FRCP layer and therefore&lt;br /&gt;
no replay protection at the AEAD layer either; deployments that&lt;br /&gt;
need replay rejection on RAW flows MUST provide it at a higher&lt;br /&gt;
layer.&lt;br /&gt;
&lt;br /&gt;
Layering.  The AEAD wrap sits below FRCP on the data path, so&lt;br /&gt;
RAW best-effort flows (&amp;lt;code&amp;gt;qos.service == SVC_RAW&amp;lt;/code&amp;gt;, the UDP-equivalent&lt;br /&gt;
service of [[#2.2. Service modes (orthogonal axes)|Section 2.2]]) inherit the same per-flow integrity +&lt;br /&gt;
confidentiality scope as FRCP-engaged flows - whatever the IPCP&lt;br /&gt;
and FRCP (if any) put on the wire is what the AEAD authenticates.&lt;br /&gt;
No DTLS-equivalent layering is required for confidentiality and&lt;br /&gt;
integrity; replay protection above AEAD is a separate concern as&lt;br /&gt;
noted above.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 17. References ==&lt;br /&gt;
&lt;br /&gt;
This section lists the IETF documents, published works, and&lt;br /&gt;
source-code references cited inline elsewhere in this document.&lt;br /&gt;
IETF documents are cited inline as &amp;quot;RFC NNNN sec. X.Y&amp;quot;; books,&lt;br /&gt;
journal papers, and source-code references are cited inline by&lt;br /&gt;
author and year (or by file and function name) and are listed&lt;br /&gt;
here for convenience.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.1. IETF documents ===&lt;br /&gt;
&lt;br /&gt;
;[RFC 791]&lt;br /&gt;
:J. Postel, &amp;quot;Internet Protocol&amp;quot;, STD 5, RFC 791, September 1981.&lt;br /&gt;
;[RFC 793]&lt;br /&gt;
:J. Postel, &amp;quot;Transmission Control Protocol&amp;quot;, STD 7, RFC 793, September 1981.  Obsoleted by RFC 9293.&lt;br /&gt;
;[RFC 813]&lt;br /&gt;
:D. D. Clark, &amp;quot;Window and Acknowledgement Strategy in TCP&amp;quot;, RFC 813, July 1982.&lt;br /&gt;
;[RFC 896]&lt;br /&gt;
:J. Nagle, &amp;quot;Congestion Control in IP/TCP Internetworks&amp;quot;, RFC 896, January 1984.&lt;br /&gt;
;[RFC 1122]&lt;br /&gt;
:R. Braden (ed.), &amp;quot;Requirements for Internet Hosts -- Communication Layers&amp;quot;, STD 3, RFC 1122, October 1989.&lt;br /&gt;
;[RFC 2018]&lt;br /&gt;
:M. Mathis, J. Mahdavi, S. Floyd, A. Romanow, &amp;quot;TCP Selective Acknowledgment Options&amp;quot;, RFC 2018, October 1996.&lt;br /&gt;
;[RFC 2119]&lt;br /&gt;
:S. Bradner, &amp;quot;Key words for use in RFCs to Indicate Requirement Levels&amp;quot;, BCP 14, RFC 2119, March 1997.&lt;br /&gt;
;[RFC 2883]&lt;br /&gt;
:S. Floyd, J. Mahdavi, M. Mathis, M. Podolsky, &amp;quot;An Extension to the Selective Acknowledgement (SACK) Option for TCP&amp;quot;, RFC 2883, July 2000.&lt;br /&gt;
;[RFC 3758]&lt;br /&gt;
:R. Stewart, M. Ramalho, Q. Xie, M. Tuexen, P. Conrad, &amp;quot;Stream Control Transmission Protocol (SCTP) Partial Reliability Extension&amp;quot;, RFC 3758, May 2004.&lt;br /&gt;
;[RFC 3828]&lt;br /&gt;
:L-A. Larzon, M. Degermark, S. Pink, L-E. Jonsson (ed.), G. Fairhurst (ed.), &amp;quot;The Lightweight User Datagram Protocol (UDP-Lite)&amp;quot;, RFC 3828, July 2004.&lt;br /&gt;
;[RFC 4303]&lt;br /&gt;
:S. Kent, &amp;quot;IP Encapsulating Security Payload (ESP)&amp;quot;, RFC 4303, December 2005.&lt;br /&gt;
;[RFC 4340]&lt;br /&gt;
:E. Kohler, M. Handley, S. Floyd, &amp;quot;Datagram Congestion Control Protocol (DCCP)&amp;quot;, RFC 4340, March 2006.&lt;br /&gt;
;[RFC 5288]&lt;br /&gt;
:J. Salowey, A. Choudhury, D. McGrew, &amp;quot;AES Galois Counter Mode (GCM) Cipher Suites for TLS&amp;quot;, RFC 5288, August 2008.&lt;br /&gt;
;[RFC 5595]&lt;br /&gt;
:G. Fairhurst, &amp;quot;The Datagram Congestion Control Protocol (DCCP) Service Codes&amp;quot;, RFC 5595, September 2009.&lt;br /&gt;
;[RFC 5681]&lt;br /&gt;
:M. Allman, V. Paxson, E. Blanton, &amp;quot;TCP Congestion Control&amp;quot;, RFC 5681, September 2009.&lt;br /&gt;
;[RFC 5925]&lt;br /&gt;
:J. Touch, A. Mankin, R. Bonica, &amp;quot;The TCP Authentication Option&amp;quot;, RFC 5925, June 2010.&lt;br /&gt;
;[RFC 5961]&lt;br /&gt;
:A. Ramaiah, R. Stewart, M. Dalal, &amp;quot;Improving TCP&#039;s Robustness to Blind In-Window Attacks&amp;quot;, RFC 5961, August 2010.&lt;br /&gt;
;[RFC 6298]&lt;br /&gt;
:V. Paxson, M. Allman, J. Chu, M. Sargent, &amp;quot;Computing TCP&#039;s Retransmission Timer&amp;quot;, RFC 6298, June 2011.&lt;br /&gt;
;[RFC 6528]&lt;br /&gt;
:F. Gont, S. Bellovin, &amp;quot;Defending against Sequence Number Attacks&amp;quot;, RFC 6528, February 2012.  Obsoletes RFC 1948.&lt;br /&gt;
;[RFC 6582]&lt;br /&gt;
:T. Henderson, S. Floyd, A. Gurtov, Y. Nishida, &amp;quot;The NewReno Modification to TCP&#039;s Fast Recovery Algorithm&amp;quot;, RFC 6582, April 2012.&lt;br /&gt;
;[RFC 7323]&lt;br /&gt;
:D. Borman, B. Braden, V. Jacobson, R. Scheffenegger (ed.), &amp;quot;TCP Extensions for High Performance&amp;quot;, RFC 7323, September 2014.&lt;br /&gt;
;[RFC 7675]&lt;br /&gt;
:M. Perumal, D. Wing, R. Ravindranath, T. Reddy, M. Thomson, &amp;quot;Session Traversal Utilities for NAT (STUN) Usage for Consent Freshness&amp;quot;, RFC 7675, October 2015.&lt;br /&gt;
;[RFC 8174]&lt;br /&gt;
:B. Leiba, &amp;quot;Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words&amp;quot;, BCP 14, RFC 8174, May 2017.&lt;br /&gt;
;[RFC 8200]&lt;br /&gt;
:S. Deering, R. Hinden, &amp;quot;Internet Protocol, Version 6 (IPv6) Specification&amp;quot;, STD 86, RFC 8200, July 2017.&lt;br /&gt;
;[RFC 8439]&lt;br /&gt;
:Y. Nir, A. Langley, &amp;quot;ChaCha20 and Poly1305 for IETF Protocols&amp;quot;, RFC 8439, June 2018.&lt;br /&gt;
;[RFC 8446]&lt;br /&gt;
:E. Rescorla, &amp;quot;The Transport Layer Security (TLS) Protocol Version 1.3&amp;quot;, RFC 8446, August 2018.&lt;br /&gt;
;[RFC 8985]&lt;br /&gt;
:Y. Cheng, N. Cardwell, N. Dukkipati, P. Jha, &amp;quot;The RACK-TLP Loss Detection Algorithm for TCP&amp;quot;, RFC 8985, February 2021.&lt;br /&gt;
;[RFC 9000]&lt;br /&gt;
:J. Iyengar (ed.), M. Thomson (ed.), &amp;quot;QUIC: A UDP-Based Multiplexed and Secure Transport&amp;quot;, RFC 9000, May 2021.&lt;br /&gt;
;[RFC 9001]&lt;br /&gt;
:M. Thomson (ed.), S. Turner (ed.), &amp;quot;Using TLS to Secure QUIC&amp;quot;, RFC 9001, May 2021.&lt;br /&gt;
;[RFC 9002]&lt;br /&gt;
:J. Iyengar (ed.), I. Swett (ed.), &amp;quot;QUIC Loss Detection and Congestion Control&amp;quot;, RFC 9002, May 2021.&lt;br /&gt;
;[RFC 9147]&lt;br /&gt;
:E. Rescorla, H. Tschofenig, N. Modadugu, &amp;quot;The Datagram Transport Layer Security (DTLS) Protocol Version 1.3&amp;quot;, RFC 9147, April 2022.&lt;br /&gt;
;[RFC 9260]&lt;br /&gt;
:R. Stewart, M. Tuexen, K. Nielsen, &amp;quot;Stream Control Transmission Protocol&amp;quot;, RFC 9260, June 2022.  Obsoletes RFC 4960.&lt;br /&gt;
;[RFC 9293]&lt;br /&gt;
:W. Eddy (ed.), &amp;quot;Transmission Control Protocol (TCP)&amp;quot;, STD 7, RFC 9293, August 2022.  Obsoletes RFC 793 and several follow-ons; updates RFC 1122 and others.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.2. Books and journal papers ===&lt;br /&gt;
&lt;br /&gt;
;[Day08]&lt;br /&gt;
:J. Day, &amp;quot;Patterns in Network Architecture: A Return to Fundamentals&amp;quot;, Prentice Hall, 2008.&lt;br /&gt;
;[Grasa15]&lt;br /&gt;
:E. Grasa et al., &amp;quot;IRATI: investigating RINA as an alternative to TCP/IP&amp;quot;, Computer Networks, Vol. 92, December 2015.&lt;br /&gt;
;[KP87]&lt;br /&gt;
:P. Karn, C. Partridge, &amp;quot;Improving Round-Trip Time Estimates in Reliable Transport Protocols&amp;quot;, ACM SIGCOMM, August 1987.&lt;br /&gt;
;[Wat81]&lt;br /&gt;
:R. W. Watson, &amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection Management&amp;quot;, Computer Networks, Vol. 5, 1981.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.3. Source-code references ===&lt;br /&gt;
&lt;br /&gt;
;[Linux-RTT]&lt;br /&gt;
:&amp;lt;code&amp;gt;tcp_rtt_estimator()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;net/ipv4/tcp_input.c&amp;lt;/code&amp;gt; of the Linux kernel, defining the asymmetric &amp;lt;code&amp;gt;mdev&amp;lt;/code&amp;gt; variance update used as FRCP&#039;s default RTT estimator ([[#12. RTT estimation|Section 12]]).  Line-stable browseable copy at https://elixir.bootlin.com/linux/latest/source/net/ipv4/tcp_input.c.&lt;br /&gt;
&lt;br /&gt;
[[Category:Protocols]]&lt;br /&gt;
[[Category:Ouroboros internals]]&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Protocols&amp;diff=1914</id>
		<title>Ouroboros Protocols</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Protocols&amp;diff=1914"/>
		<updated>2026-05-17T13:52:13Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* Flow and Retransmission Control Protocol */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Under construction}}&lt;br /&gt;
&lt;br /&gt;
The protocols in Ouroboros are designed with strong adherence to the principles of [https://en.wikipedia.org/wiki/Separation_of_concerns separation of concerns] and [https://en.wikipedia.org/wiki/Separation_of_mechanism_and_policy separation of mechanism and policy] in mind to prevent network ossification and protocol ossification from occuring.&lt;br /&gt;
&lt;br /&gt;
There are 5 core protocols in Ouroboros.&lt;br /&gt;
&lt;br /&gt;
== Data Transfer Protocol ==&lt;br /&gt;
Main page: [[Ouroboros Data Transfer Protocol]]&lt;br /&gt;
&lt;br /&gt;
The Data Transfer Protocol resides in the [[Ouroboros Functional Layering|network forwarding layer]]. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
  0                   1                   2                   3&lt;br /&gt;
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                      Destination Address                      +&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |  Time-to-Live |      QoS      |      ECN      |    PADDING    |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                              EID                              +&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 |                                                               |&lt;br /&gt;
 +                           N + 1 Data                          +&lt;br /&gt;
 .                                                               .&lt;br /&gt;
 .                                                               .&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Flow and Retransmission Control Protocol ==&lt;br /&gt;
Main page: [[Flow and Retransmission Control Protocol]]&lt;br /&gt;
&lt;br /&gt;
The Flow and Retransmission Control Protocol resides in the [[Ouroboros Functional Layering|application end-to-end layer]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |             flags             |              hcs              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            window                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            seqno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            ackno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                     payload (variable) ...&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&lt;br /&gt;
  flags   - feature/type bitmap (see 1.2).&lt;br /&gt;
  hcs     - CRC-16-CCITT-FALSE Header Check Sequence (HCS) over&lt;br /&gt;
            flags + window + seqno + ackno (+ stream extension when&lt;br /&gt;
            present); the two octets of the hcs field itself are&lt;br /&gt;
            omitted from the CRC input.  Verified on receive before&lt;br /&gt;
            any flag-driven dispatch.&lt;br /&gt;
  window  - receiver-advertised right window edge (valid iff FC).&lt;br /&gt;
  seqno   - per-flow sequence number.&lt;br /&gt;
  ackno   - cumulative Acknowledgement (ACK) (valid iff ACK).&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Flow Allocation Protocol ==&lt;br /&gt;
Main page: [[Ouroboros Flow Allocation Protocol]]&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 *  0                   1                   2                   3&lt;br /&gt;
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ---+&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                      id (128 bits)                            +    |&lt;br /&gt;
 * |                  Unique flow allocation ID                    |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                     timestamp (64 bits)                       +    |&lt;br /&gt;
 * |                UTC nanoseconds since epoch                    |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |         crt_len (16 bits)     |                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                  certificate (variable)                       +    |&lt;br /&gt;
 * |               X.509 certificate, DER encoded                  |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |F|R|     kex_len (14 bits)     |                               |    | Signed&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +    |&lt;br /&gt;
 * |                                                               |    | Region&lt;br /&gt;
 * +                 kex_data (variable)                           +    |&lt;br /&gt;
 * |      public key (DER/raw) or ciphertext (KEM)                 |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |       cipher_len (16 bits)    |                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                    cipher (variable)                          +    |&lt;br /&gt;
 * |               symmetric cipher name (UTF-8)                   |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |&lt;br /&gt;
 * |        data_len (16 bits)     |                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +                    data (variable)                            +    |&lt;br /&gt;
 * |              Piggybacked application data                     |    |&lt;br /&gt;
 * +                                                               +    |&lt;br /&gt;
 * |                                                               |    |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ---+&lt;br /&gt;
 * |        sig_len (16 bits)      |                               |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               +&lt;br /&gt;
 * |                                                               |&lt;br /&gt;
 * +                     signature (variable)                      +&lt;br /&gt;
 * |                  DSA signature over signed region             |&lt;br /&gt;
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
 *&lt;br /&gt;
 * kex_len field bit layout:&lt;br /&gt;
 *   F (bit 15): Format - 0 = X.509 DER, 1 = Raw/Hybrid&lt;br /&gt;
 *   R (bit 14): Role   - 0 = Server encaps, 1 = Client encaps&lt;br /&gt;
 *               (R is ignored for non-KEM algorithms)&lt;br /&gt;
 *   Bits 0-13:  Length (0-16383 bytes)&lt;br /&gt;
 */&lt;br /&gt;
The Flow Allocation Protocol resides in the [[Ouroboros Functional Layering|network end-to-end layer]].&lt;br /&gt;
&lt;br /&gt;
== Connection Establishment Protocol ==&lt;br /&gt;
Main page: [[Ouroboros Connection Establishment Protocol]]&lt;br /&gt;
&lt;br /&gt;
== Enrollment Protocol ==&lt;br /&gt;
Main page: [[Ouroboros Enrolment Protocol]]&lt;br /&gt;
&lt;br /&gt;
The Enrollment protocol is best seen as one of the application protocols for IPCPs.&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1913</id>
		<title>Flow and Retransmission Control Protocol</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1913"/>
		<updated>2026-05-17T13:51:36Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 1.1. PCI header */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= FRCP - Flow and Retransmission Control Protocol =&lt;br /&gt;
&lt;br /&gt;
FRCP runs end-to-end between two peers over a flow.  It delivers&lt;br /&gt;
reliability, in-order delivery, flow control, and liveness.&lt;br /&gt;
Congestion Control (CC) is not in FRCP - that lives in the IPC&lt;br /&gt;
Process (IPCP) Congestion Avoidance (CA) policies, orthogonal to&lt;br /&gt;
FRCP.  Flow allocation, naming, and IPCP lifecycle are handled by&lt;br /&gt;
the IPC Resource Manager daemon (IRMd).&lt;br /&gt;
&lt;br /&gt;
FRCT (Flow and Retransmission Control Task) is the libouroboros&lt;br /&gt;
implementation of FRCP; the task lives in src/lib/frct.c.  The&lt;br /&gt;
remainder of this document describes the FRCP wire protocol and the&lt;br /&gt;
behaviour FRCT realises.  Code symbols retain the FRCT_ prefix&lt;br /&gt;
(FRCT_DATA, FRCT_RXM, ...) because they belong to the implementing&lt;br /&gt;
task; this document references them verbatim.&lt;br /&gt;
&lt;br /&gt;
The keywords &amp;quot;MUST&amp;quot;, &amp;quot;MUST NOT&amp;quot;, &amp;quot;REQUIRED&amp;quot;, &amp;quot;SHALL&amp;quot;, &amp;quot;SHALL NOT&amp;quot;,&lt;br /&gt;
&amp;quot;SHOULD&amp;quot;, &amp;quot;SHOULD NOT&amp;quot;, &amp;quot;RECOMMENDED&amp;quot;, &amp;quot;MAY&amp;quot;, and &amp;quot;OPTIONAL&amp;quot; in&lt;br /&gt;
this document are to be interpreted as described in BCP 14 (Best&lt;br /&gt;
Current Practice; RFC 2119, RFC 8174) when, and only when, they&lt;br /&gt;
appear in all capitals.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  u32, u8       Unsigned 32-bit / 8-bit integers (kernel-C style).&lt;br /&gt;
  ns            Nanoseconds.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Modular sequence-number comparators (32-bit, modulo 2^32):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    before(a, b)  ==  (int32_t)(a - b) &amp;lt; 0&lt;br /&gt;
    after(a, b)   ==  before(b, a)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Used throughout for ackno / seqno ordering checks.&lt;br /&gt;
&lt;br /&gt;
Round-Trip Time (RTT) abbreviations used throughout:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    SRTT          Smoothed RTT estimate (RFC 6298).&lt;br /&gt;
    mdev          Mean deviation of RTT (Linux variance estimator).&lt;br /&gt;
    EWMA          Exponentially Weighted Moving Average.&lt;br /&gt;
    RTO           Retransmission Timeout, max(RTO_MIN,&lt;br /&gt;
                  srtt + (mdev &amp;lt;&amp;lt; MDEV_MUL)).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Timer-bound symbols t_a (a-timer, ACK delay) and t_r (r-timer,&lt;br /&gt;
retransmission window) are defined in Section 8; t_mpl (Maximum&lt;br /&gt;
Packet Lifetime) is introduced in Section 2.1 (the inact field)&lt;br /&gt;
with heritage in Section 15.&lt;br /&gt;
&lt;br /&gt;
Wire-format diagrams follow the IETF convention: bit 0 is the&lt;br /&gt;
leftmost (most significant) bit and fields are in network byte&lt;br /&gt;
order unless stated otherwise.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1. Wire format ==&lt;br /&gt;
&lt;br /&gt;
=== 1.1. PCI header ===&lt;br /&gt;
&lt;br /&gt;
Fixed 16-octet base Protocol-Control Information (PCI) header&lt;br /&gt;
prefixed to every FRCP packet (RFC convention: bit 0 leftmost,&lt;br /&gt;
most-significant bit first).  All multi-byte fields except hcs&lt;br /&gt;
are in network byte order; hcs is an opaque 16-bit value that&lt;br /&gt;
the receiver recomputes from the wire bytes and compares to the&lt;br /&gt;
in-place pci-&amp;gt;hcs read, so its on-wire byte order need only&lt;br /&gt;
match between peers running compatible builds.  DATA packets on&lt;br /&gt;
stream-mode flows carry an additional 8-octet extension (see&lt;br /&gt;
Section 1.5); SACK and RTTP carry their own payloads after the&lt;br /&gt;
base PCI.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |             flags             |              hcs              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            window                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            seqno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            ackno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                     payload (variable) ...&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  flags   - feature/type bitmap (see 1.2).&lt;br /&gt;
  hcs     - CRC-16-CCITT-FALSE Header Check Sequence (HCS) over&lt;br /&gt;
            flags + window + seqno + ackno (+ stream extension when&lt;br /&gt;
            present); the two octets of the hcs field itself are&lt;br /&gt;
            omitted from the CRC input.  Verified on receive before&lt;br /&gt;
            any flag-driven dispatch.&lt;br /&gt;
  window  - receiver-advertised right window edge (valid iff FC).&lt;br /&gt;
  seqno   - per-flow sequence number.&lt;br /&gt;
  ackno   - cumulative Acknowledgement (ACK) (valid iff ACK).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A single packet can simultaneously carry DATA + ACK + FC (Flow&lt;br /&gt;
Control) + RXM (Retransmission) by ORing flag bits; the PCI&lt;br /&gt;
multiplexes control on the same wire frame in the spirit of SCTP&lt;br /&gt;
chunk bundling (RFC 9260 sec. 6.10) and QUIC frame multiplexing&lt;br /&gt;
(RFC 9000 sec. 12.4).  DATA-bearing packets carry the caller&#039;s&lt;br /&gt;
payload after the PCI; SACK (Selective Acknowledgement) and RTTP&lt;br /&gt;
(Round-Trip Time Probe) carry their own typed payloads after the&lt;br /&gt;
PCI.&lt;br /&gt;
&lt;br /&gt;
Optional framing (per-flow, see Section 2.2).  On the wire, the&lt;br /&gt;
order from inside out is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    [   PCI + body          ]    -- the FRCP packet&lt;br /&gt;
    [   PCI + body + CRC-32 ]    -- CRC-32 covers the body only (PCI&lt;br /&gt;
                                    is in HCS); appended iff qs.ber&lt;br /&gt;
                                    == 0 on DATA, or on every SACK&lt;br /&gt;
                                    packet&lt;br /&gt;
    [ AEAD-wrap of above    ]    -- iff Authenticated Encryption&lt;br /&gt;
                                    with Associated Data (AEAD) is&lt;br /&gt;
                                    enabled&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - HCS in the PCI covers the header fields on every packet and is&lt;br /&gt;
    verified before any flag-driven dispatch.&lt;br /&gt;
  - The CRC-32 trailer (IEEE 802.3 / zlib reflected polynomial&lt;br /&gt;
    0xEDB88320, init 0xFFFFFFFF, xor-out 0xFFFFFFFF) covers the&lt;br /&gt;
    body on DATA when qs.ber == 0 and on every SACK packet; the&lt;br /&gt;
    trailer is written as a raw uint32_t (the same convention as&lt;br /&gt;
    hcs: opaque on the wire as long as both peers run compatible&lt;br /&gt;
    builds).  The PCI is not under the CRC (Cyclic Redundancy&lt;br /&gt;
    Check) because the HCS already protects it.  It is&lt;br /&gt;
    appended before AEAD encryption and therefore rides inside the&lt;br /&gt;
    AEAD wrap when both are active; the AEAD tag (~2^-128 forgery&lt;br /&gt;
    probability) dominates the CRC (~2^-32) for integrity in that&lt;br /&gt;
    mode but the CRC trailer is currently retained.&lt;br /&gt;
  - When encryption is enabled, the entire (possibly-CRC&#039;d) FRCP&lt;br /&gt;
    packet is wrapped with AEAD inside the shared-memory packet&lt;br /&gt;
    buffer (spb, struct ssm_pk_buff); the packet grows by the AEAD&lt;br /&gt;
    overhead, namely a leading nonce / Initialization Vector (IV)&lt;br /&gt;
    of headsz bytes (crypt_get_ivsz) and a trailing authentication&lt;br /&gt;
    tag of tailsz bytes (crypt_get_tagsz).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Both CRC and AEAD are layered around the FRCP wire format and&lt;br /&gt;
are not visible to the FRCP machinery itself.&lt;br /&gt;
&lt;br /&gt;
=== 1.2. Flag bits ===&lt;br /&gt;
&lt;br /&gt;
Flag bits are numbered most-significant-bit first to match the wire&lt;br /&gt;
diagram (bit numbering per Section 1.1; bit 0 is the MSB of the&lt;br /&gt;
16-bit flags field and lands at wire-position 0 in network byte&lt;br /&gt;
order).  Bits 13..15 are reserved and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    +------+--------+--------+----------------------------------------+&lt;br /&gt;
    | Bit  | Mask   | Name   | Meaning                                |&lt;br /&gt;
    +------+--------+--------+----------------------------------------+&lt;br /&gt;
    |   0  | 0x8000 | DATA   | Carries caller payload                 |&lt;br /&gt;
    |   1  | 0x4000 | DRF    | Data Run Flag: start of a fresh run    |&lt;br /&gt;
    |   2  | 0x2000 | ACK    | Acknowledgement: ackno field valid     |&lt;br /&gt;
    |   3  | 0x1000 | NACK   | Negative ACK; seqno = arrival_seqno-1  |&lt;br /&gt;
    |   4  | 0x0800 | FC     | Flow Control: window field valid (rwe) |&lt;br /&gt;
    |   5  | 0x0400 | RDVS   | Rendezvous probe (window-closed)       |&lt;br /&gt;
    |   6  | 0x0200 | FFGM   | First Fragment (role bit 0; see below) |&lt;br /&gt;
    |   7  | 0x0100 | LFGM   | Last Fragment (role bit 1; see below)  |&lt;br /&gt;
    |   8  | 0x0080 | RXM    | Retransmission                         |&lt;br /&gt;
    |   9  | 0x0040 | SACK   | Selective ACK block list in payload    |&lt;br /&gt;
    |  10  | 0x0020 | RTTP   | RTT Probe / echo (payload follows)     |&lt;br /&gt;
    |  11  | 0x0010 | KA     | Keepalive                              |&lt;br /&gt;
    |  12  | 0x0008 | FIN    | End-of-stream marker (stream mode)     |&lt;br /&gt;
    | 13-15|   --   |  --    | Reserved (MUST be zero)                |&lt;br /&gt;
    +------+--------+--------+----------------------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The (FFGM, LFGM) pair encodes the fragment role of a DATA-bearing&lt;br /&gt;
Service Data Unit (SDU), SCTP-style begin/end flags (RFC 9260&lt;br /&gt;
sec. 3.3.1):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    +-----------+-------------------------------------------------+&lt;br /&gt;
    | FFGM LFGM | Role                                            |&lt;br /&gt;
    +-----------+-------------------------------------------------+&lt;br /&gt;
    |   1   1   | Sole / un-fragmented SDU (begin AND end)        |&lt;br /&gt;
    |   1   0   | First fragment of a multi-fragment SDU          |&lt;br /&gt;
    |   0   0   | Middle fragment                                 |&lt;br /&gt;
    |   0   1   | Last fragment                                   |&lt;br /&gt;
    +-----------+-------------------------------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each fragment is carried in its own FRCP packet with its own seqno;&lt;br /&gt;
FRTX (the FRCT Retransmission service mode, see Section 2.2)&lt;br /&gt;
recovers individual fragments via the normal Retransmission Timeout&lt;br /&gt;
(RTO) / SACK / Recent Acknowledgement (RACK, RFC 8985) path.  The&lt;br /&gt;
receiver reassembles the SDU at consume time once the contiguous&lt;br /&gt;
[FIRST .. LAST] run has fully arrived.  On non-DATA packets the role&lt;br /&gt;
bits are unused and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
In stream mode (qos.service == SVC_STREAM, see Section 16) there are&lt;br /&gt;
no SDU boundaries to encode, so FFGM and LFGM are unused and MUST&lt;br /&gt;
be transmitted as zero.  End-of-stream uses a dedicated bit (FIN,&lt;br /&gt;
bit 12) carried on a 0-byte DATA packet, emitted at write-half close&lt;br /&gt;
(fccntl to FLOWFRDONLY), during linger drain, and at flow_dealloc;&lt;br /&gt;
emission is idempotent (first call wins).  After contiguous delivery&lt;br /&gt;
of the FIN-bearing slot, the receiver latches byte_fin at the FIN&#039;s&lt;br /&gt;
start offset; flow_read returns 0 (end-of-file, EOF) once buffered&lt;br /&gt;
bytes have been drained up to byte_fin.  Per-byte position is&lt;br /&gt;
carried by the [start, end) extension (Section 1.5).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.3. SACK payload ===&lt;br /&gt;
&lt;br /&gt;
A SACK packet has the FRCT_ACK | FRCT_FC | FRCT_SACK flag bits set&lt;br /&gt;
(bit numbering per Section 1.1).  Following the 16-octet PCI, the&lt;br /&gt;
payload is a 2-octet block count (network byte order), 2 octets of&lt;br /&gt;
padding to 4-byte align the block list, then n_blocks pairs of&lt;br /&gt;
32-bit start/end seqnos describing *present* (received) ranges&lt;br /&gt;
above the cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |           n_blocks            |        padding (2 octets)     |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[0]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            end[0]                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[1]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
                       ... n_blocks pairs total ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
n_blocks &amp;lt;= SACK_MAX_BLOCKS (2048).  The per-flow effective cap is&lt;br /&gt;
further bounded by (frag_mtu - PCI - 4) / 8 blocks per packet; SACK&lt;br /&gt;
packets carry no stream extension, so PCI here is the 16-octet base&lt;br /&gt;
header even on stream-mode flows.&lt;br /&gt;
&lt;br /&gt;
Wire invariant: every block produced by the receiver, except an&lt;br /&gt;
optional leading Duplicate SACK (D-SACK) block as described below,&lt;br /&gt;
describes a range strictly above the cumulative ACK carried in the&lt;br /&gt;
PCI ackno field (after(start[i], ackno)).  This makes the D-SACK&lt;br /&gt;
convention below unambiguous; the receiver-side builder MUST&lt;br /&gt;
preserve it.&lt;br /&gt;
&lt;br /&gt;
Duplicate SACK (D-SACK, RFC 2883) is signalled in-band: no flag&lt;br /&gt;
bit, no extra framing.  Modular seqno arithmetic uses the&lt;br /&gt;
before() / after() comparators defined in the Notation block.&lt;br /&gt;
Block[0] carries a D-SACK report when either:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  case 1 (RFC 2883 sec. 4.1.1, full duplicate):&lt;br /&gt;
      before(blocks[0].start, ackno) and ackno - blocks[0].start is&lt;br /&gt;
      within MAX_DSACK_LAG (== RQ_SIZE).  A single duplicate seqno&lt;br /&gt;
      observed below the cumulative ACK.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  case 2 (RFC 2883 sec. 4.1.2, partial duplicate):&lt;br /&gt;
      blocks[0] is a sub-range of some blocks[i&amp;gt;0] (not exactly&lt;br /&gt;
      equal).  Reports a duplicate of an in-window seqno that the&lt;br /&gt;
      same packet&#039;s remaining SACK blocks already describe as&lt;br /&gt;
      received.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Senders that do not implement D-SACK process block[0] through the&lt;br /&gt;
normal SACK-mark loop and the existing clamp-and-skip path makes&lt;br /&gt;
case-1 a no-op (start &amp;lt; snd_cr.lwe clamps to snd_cr.lwe, the inner&lt;br /&gt;
loop then skips k == snd_cr.lwe) and case-2 idempotent (same slots&lt;br /&gt;
NULL&#039;d twice).  D-SACK-aware senders feed the report into the RACK&lt;br /&gt;
reo_wnd_mult scaler (RFC 8985 sec. 6.2 step 4): bump on receipt&lt;br /&gt;
(cap 20), halve once per 16 cumulatively-ACK&#039;d seqnos since the&lt;br /&gt;
most recent D-SACK arrival or halve event, reset to 1 on an RTO&lt;br /&gt;
timer fire at the head-of-line.  D-SACK alone never enters&lt;br /&gt;
NewReno-careful recovery (see Section 8); only non-D-SACK blocks&lt;br /&gt;
count as gaps.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.4. RTTP payload ===&lt;br /&gt;
&lt;br /&gt;
An RTTP (Round-Trip Time Probe) packet has only the FRCT_RTTP flag&lt;br /&gt;
set (bit numbering per Section 1.1).  Following the 16-octet PCI,&lt;br /&gt;
the payload is 24 octets (packed):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          probe_id                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          echo_id                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +                  nonce (16 octets, echoed verbatim)           +&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  probe_id - sender counter, 0 on reply, 0 reserved.&lt;br /&gt;
  echo_id  - peer&#039;s probe_id, 0 on outbound probe.&lt;br /&gt;
  nonce    - random, echoed unmodified, memcmp&#039;d to defeat spoof.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.5. Stream PCI extension ===&lt;br /&gt;
&lt;br /&gt;
A stream-mode flow (qos.service == SVC_STREAM) carries an extra&lt;br /&gt;
8-octet extension after the 16-octet base PCI on every DATA packet&lt;br /&gt;
(bit numbering per Section 1.1):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            start                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                             end                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  start - octet offset of the first payload byte in the stream.&lt;br /&gt;
  end   - octet offset one past the last payload byte;&lt;br /&gt;
          end - start equals the on-wire payload length.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total stream-mode PCI for DATA packets is 24 octets (16 base + 8&lt;br /&gt;
extension); control packets (SACK, RTTP, bare ACK, KA, etc.) retain&lt;br /&gt;
the 16-octet base PCI.  Stream mode MUST be negotiated at flow&lt;br /&gt;
allocation; the extension is present iff stream mode is in use,&lt;br /&gt;
never on a per-packet basis.  Both peers MUST treat start/end as&lt;br /&gt;
monotonic 32-bit byte offsets; when a slot reaches the head of the&lt;br /&gt;
contiguous run with start not equal to the prior packet&#039;s end the&lt;br /&gt;
slot is silently dropped at delivery time (Section 16) rather&lt;br /&gt;
than rejected at stash.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC STREAM-frame reassembly model (RFC 9000 sec. 19.8):&lt;br /&gt;
each packet carries its packet seqno (this PCI&#039;s seqno field) and a&lt;br /&gt;
separate stream byte position (start/end).  Separating the two&lt;br /&gt;
avoids TCP&#039;s conflation of packet identity with byte position which&lt;br /&gt;
forces Karn&#039;s algorithm for Round-Trip Time (RTT) sampling (no RTT&lt;br /&gt;
sample on retransmits, RFC 6298 sec. 3); FRCP applies the&lt;br /&gt;
Karn-equivalent gate via a combination of per-packet FRCT_RXM,&lt;br /&gt;
per-slot SND_RTX flags, and a sample-fence rtt_lwe (see Section 2.1&lt;br /&gt;
and Section 12).  FRCP&#039;s fixed-32-bit start/end wrap at 4 GiB of&lt;br /&gt;
wire bytes, narrower than QUIC&#039;s 62-bit varint offset (cf. RFC 9000&lt;br /&gt;
sec. 16); the on-wire wrap is handled by the same modular before()&lt;br /&gt;
/ after() comparators (Section 1.3) FRCP uses for seqnos, which&lt;br /&gt;
remain unambiguous as long as the in-flight byte window stays&lt;br /&gt;
strictly under 2 GiB (the half-range of the signed-int32 difference&lt;br /&gt;
in before()).  The default per-flow ring is 1 MiB; the&lt;br /&gt;
implementation caps ring_sz at 128 MiB (FRCT_STREAM_RING_SZ_MAX),&lt;br /&gt;
well below the 2 GiB half-range bound.  The runtime byte counters&lt;br /&gt;
exposed via FUSE (Filesystem in Userspace) in the Ouroboros&lt;br /&gt;
Resource Information Base (RIB, a virtual-filesystem introspection&lt;br /&gt;
bridge) are platform size_t and do not wrap on 64-bit hosts.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2. Per-flow state and service modes ==&lt;br /&gt;
&lt;br /&gt;
=== 2.1. Per-flow state ===&lt;br /&gt;
&lt;br /&gt;
Each flow keeps a sender control record and a receiver control&lt;br /&gt;
record:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    lwe    : u32  snd: oldest unacked seqno (cumulative ACK&lt;br /&gt;
                  boundary as seen by sender);&lt;br /&gt;
                  rcv: next in-order seqno expected&lt;br /&gt;
    rwe    : u32  snd: peer-advertised right window edge;&lt;br /&gt;
                  rcv: locally-advertised right window edge&lt;br /&gt;
    cflags : u8   per-direction feature flags: retransmission&lt;br /&gt;
                  (FRCTFRTX), receiver flow control&lt;br /&gt;
                  (FRCTFRESCNTL), linger-on-close (FRCTFLINGER);&lt;br /&gt;
                  see &amp;lt;ouroboros/fccntl.h&amp;gt;&lt;br /&gt;
    seqno  : u32  snd: next seqno to send;&lt;br /&gt;
                  rcv: force-ACK trigger - set on a stale or dup&lt;br /&gt;
                  DATA so the next ack_snd emits a fresh&lt;br /&gt;
                  cumulative ACK&lt;br /&gt;
    ackno  : u32  snd: outbound ACK-packet seqno counter,&lt;br /&gt;
                  incremented for every ACK-bearing packet (bare&lt;br /&gt;
                  ACK, delayed ACK, SACK); used by wire-dup ACK&lt;br /&gt;
                  detection;&lt;br /&gt;
                  rcv: incoming-ACK dedup tracker&lt;br /&gt;
    act    : ns   last activity (used by inactivity / DRF)&lt;br /&gt;
    inact  : ns   inactivity threshold; sender = 3*mpl + a + r + 1s,&lt;br /&gt;
                  receiver = 2*mpl + a + r + 1s.  mpl is the&lt;br /&gt;
                  Maximum Packet Lifetime (delta-t terminology;&lt;br /&gt;
                  see Section 15); a and r are the FRCT a-timer&lt;br /&gt;
                  and r-timer bounds (see Section 8).  The&lt;br /&gt;
                  asymmetry is load-bearing for pre-DRF NACK&lt;br /&gt;
                  (Section 9).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sender holds a per-slot ring snd_slots[RQ_SIZE] keyed by&lt;br /&gt;
(seqno mod RQ_SIZE).  Each slot tracks its retransmit entry (rxm),&lt;br /&gt;
last-send timestamp, and retransmit flag bits: SND_RTX (a&lt;br /&gt;
retransmit is pending or has fired, gates the next RTT sample&lt;br /&gt;
under Karn) and SND_FAST_RXM (one-shot fast-retransmit staged for&lt;br /&gt;
this loss event).&lt;br /&gt;
&lt;br /&gt;
The receiver holds a parallel reorder ring rcv_slots[RQ_SIZE]&lt;br /&gt;
(referred to as rq[] in prose) holding stashed out-of-order&lt;br /&gt;
packet-buffer indexes; both FRTX and best-effort flows share this&lt;br /&gt;
path.  The invariant rwe - lwe &amp;lt;= RQ_SIZE holds: on each consume&lt;br /&gt;
the receiver advances rwe by the consumed count, capping the&lt;br /&gt;
receive window at RQ_SIZE seqno slots.&lt;br /&gt;
&lt;br /&gt;
A separate fence variable rtt_lwe is bumped on every retransmit&lt;br /&gt;
(timer-fire, SACK-driven, fast-rxm, NACK-driven) and on every&lt;br /&gt;
seqno_rotate (Section 4) to mark the seqno range whose RTT samples&lt;br /&gt;
MUST be discarded.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 2.2. Service modes (orthogonal axes) ===&lt;br /&gt;
&lt;br /&gt;
FRCP exposes its wire features as a vector of independent QoS&lt;br /&gt;
axes selected at flow allocation time.  All flows go through the&lt;br /&gt;
same flow_alloc(name, qos, ...) primitive; the qosspec_t passed&lt;br /&gt;
in determines which protocol machinery engages on the wire.  This&lt;br /&gt;
contrasts with the POSIX BSD socket model where TCP and UDP&lt;br /&gt;
require different socket types (SOCK_STREAM / SOCK_DGRAM).&lt;br /&gt;
&lt;br /&gt;
The axes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  service   0 = unordered (no FRCP engagement: raw datagrams,&lt;br /&gt;
              no PCI on the wire, UDP-equivalent at this layer)&lt;br /&gt;
            1 = message-ordered (FRCP engaged; SDU boundaries&lt;br /&gt;
              preserved across fragmentation)&lt;br /&gt;
            2 = stream (byte-oriented, no SDU boundaries; FRTX&lt;br /&gt;
              required)&lt;br /&gt;
  loss      0 = lossless service requested: FRTX retransmit&lt;br /&gt;
              machinery engages (Section 8); MUST be 0 for&lt;br /&gt;
              service=2.  Non-zero = best-effort, FRTX off.&lt;br /&gt;
  ber       Bit Error Rate tolerance.&lt;br /&gt;
            0 = error-free service requested: a CRC trailer is&lt;br /&gt;
              appended after the body of DATA packets and verified&lt;br /&gt;
              on receive (added / checked outside the FRCP PCI;&lt;br /&gt;
              see Section 1.1).  Non-zero = peer accepts errors;&lt;br /&gt;
              trailer omitted.  SACK control packets carry a&lt;br /&gt;
              CRC32 trailer regardless of ber; the ber gate&lt;br /&gt;
              applies to DATA only.&lt;br /&gt;
  timeout   Peer-timeout (ms); 0 disables the keepalive timer.&lt;br /&gt;
              Independent of FRCP engagement.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Encryption is a separate per-flow attribute set at flow setup;&lt;br /&gt;
when enabled it wraps the FRCP packet (PCI + body, plus the CRC&lt;br /&gt;
trailer if any) under AEAD, expanding the spb by headsz + tailsz&lt;br /&gt;
octets (nonce / tag).  The CRC trailer is currently kept inside&lt;br /&gt;
the AEAD wrap (see Section 1.1).&lt;br /&gt;
&lt;br /&gt;
Reachable combinations exported by include/ouroboros/qos.h:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  +-----------------+---------+------+-----+-----------------------+&lt;br /&gt;
  | Cube            | service | loss | ber | Engaged               |&lt;br /&gt;
  +-----------------+---------+------+-----+-----------------------+&lt;br /&gt;
  | qos_raw         |    0    |   1  |   1 | Raw passthrough       |&lt;br /&gt;
  | qos_raw_safe    |    0    |   1  |   0 | Raw + CRC trailer     |&lt;br /&gt;
  | qos_rt          |    1    |   1  |   1 | FRCP, no FRTX, no CRC |&lt;br /&gt;
  | qos_rt_safe     |    1    |   1  |   0 | FRCP, no FRTX, CRC    |&lt;br /&gt;
  | qos_msg         |    1    |   0  |   0 | FRCP + FRTX           |&lt;br /&gt;
  | qos_stream      |    2    |   0  |   0 | FRCP + FRTX, stream   |&lt;br /&gt;
  +-----------------+---------+------+-----+-----------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Forced couplings actually enforced by the public API:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - service == SVC_STREAM (2) requires loss == 0; flow_alloc /&lt;br /&gt;
    flow_accept reject the pair otherwise with -EINVAL.&lt;br /&gt;
  - FRTX requires FRCP engagement (service != SVC_RAW); requesting&lt;br /&gt;
    loss = 0 with service = SVC_RAW is structurally a no-op&lt;br /&gt;
    because no frcti is created.&lt;br /&gt;
  - The QOS_DISABLE_CRC build flag globally forces ber = 1.&lt;br /&gt;
    Note: this flag defaults to ON, so default builds ship with&lt;br /&gt;
    CRC disabled until QOS_DISABLE_CRC is set to OFF.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Caveat: the API does NOT force ber = 0 when service != SVC_RAW.&lt;br /&gt;
qos_rt has service = SVC_MESSAGE with ber = 1, which means the PCI&lt;br /&gt;
itself is not CRC-protected on that cube; the HCS (Section 1.1)&lt;br /&gt;
remains the only integrity check on the header.&lt;br /&gt;
&lt;br /&gt;
The FRCP-no-FRTX regime (service = SVC_MESSAGE, loss &amp;gt; 0) is meaningful&lt;br /&gt;
and live: sequence numbering, in-order delivery, flow-control&lt;br /&gt;
advertisement, KA, DRF rotation, and SDU fragmentation /&lt;br /&gt;
reassembly (Section 7.2) all run.  Lost packets are dropped&lt;br /&gt;
rather than retransmitted; a permanently-lost mid-fragment is&lt;br /&gt;
dropped via skip-past-gap once a later SDU is visible in the&lt;br /&gt;
reorder ring.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. Protocol parameters ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    +--------------------+------------------------+-------------------+&lt;br /&gt;
    | Parameter          | Value                  | Role              |&lt;br /&gt;
    +--------------------+------------------------+-------------------+&lt;br /&gt;
    | RQ_SIZE            | compile-time, power of | Slot ring / rcv   |&lt;br /&gt;
    |                    |  2 (default 128)       | window width      |&lt;br /&gt;
    | START_WINDOW       | compile-time, power of | Initial rwe-lwe   |&lt;br /&gt;
    |                    |  2 (default 128)       | after rotate      |&lt;br /&gt;
    | RTO_MIN            | MAX(250 us build-tun-  | RTO floor; also   |&lt;br /&gt;
    |                    |  able, 1&amp;lt;&amp;lt;RXMQ_RES);   | floored at the    |&lt;br /&gt;
    |                    |  per-flow via fccntl   | retransmit-wheel  |&lt;br /&gt;
    |                    |  (FRCTSRTOMIN).        | resolution        |&lt;br /&gt;
    |                    |  Default ~1 ms with    | (~1 ms by         |&lt;br /&gt;
    |                    |  RXMQ_RES=20.          | default).         |&lt;br /&gt;
    | MAX_RTO_MUL        | 20                     | Backoff shift cap |&lt;br /&gt;
    | RACK window R      | MIN(reo_wnd_mult       | Reorder window;   |&lt;br /&gt;
    |                    |  * min_RTT/4, SRTT)    | per RFC 8985      |&lt;br /&gt;
    |                    |  with MIN_REORDER_NS   | sec. 6.2;         |&lt;br /&gt;
    |                    |  = 250 us floor;       | reo_wnd_mult per  |&lt;br /&gt;
    |                    |  reo_wnd_mult scales   | sec. 6.2 step 4   |&lt;br /&gt;
    |                    |  on D-SACK, cap 20     |                   |&lt;br /&gt;
    | MIN_RTT_WIN_NS     | 300 s (5 min, Linux    | min_RTT windowed  |&lt;br /&gt;
    |                    |  tcp_min_rtt_wlen)     | re-anchor         |&lt;br /&gt;
    | REO_WND_MULT_MAX   | 20 (RFC 8985 sec.      | reo_wnd_mult cap  |&lt;br /&gt;
    |                    |  6.2 step 4)           |                   |&lt;br /&gt;
    | REO_DECAY_PKTS     | 16 (RFC 8985 sec.      | Fresh-ACK&#039;d seq   |&lt;br /&gt;
    |                    |  6.2 step 4 /          | count per halving |&lt;br /&gt;
    |                    |  RACK.reo_wnd_persist) |                   |&lt;br /&gt;
    | MAX_DSACK_LAG      | RQ_SIZE                | D-SACK sanity cap |&lt;br /&gt;
    | RTT_QUARANTINE     | 32 (seqno steps)       | NewReno gate pad  |&lt;br /&gt;
    | SACK rate-limit    | SACK_MIN_GAP_NS        | Min SACK gap      |&lt;br /&gt;
    |                    |  (250 us, fixed)       |                   |&lt;br /&gt;
    | SACK_MAX_BLOCKS    | 2048 (wire cap; per-   | Per-SACK block    |&lt;br /&gt;
    |                    |  flow capped at        | cap               |&lt;br /&gt;
    |                    |  (frag_mtu-PCI-4)/8)   |                   |&lt;br /&gt;
    | SACK_RXM_MAX       | 32                     | Per-pass staged   |&lt;br /&gt;
    |                    |                        | retransmit cap    |&lt;br /&gt;
    | DUP_THRESH         | 3 (RFC 8985 default)   | Hybrid fast-rxm   |&lt;br /&gt;
    |                    |                        | trigger (Sec. 8)  |&lt;br /&gt;
    | MDEV_MUL           | 2 (build-tunable via   | mdev shift in     |&lt;br /&gt;
    |                    |  FRCT_RTO_MDEV_-       | RTO = srtt +      |&lt;br /&gt;
    |                    |  MULTIPLIER)           | (mdev &amp;lt;&amp;lt; MDEV_MUL)|&lt;br /&gt;
    | RTTP nonce         | 16 octets              | Echoed verbatim   |&lt;br /&gt;
    | RTTP_RING          | 8                      | In-flight probes  |&lt;br /&gt;
    | RTT clamp          | 16 * srtt              | Probe-sample      |&lt;br /&gt;
    |                    |                        | upper bound       |&lt;br /&gt;
    |                    |                        | (ACK-derived RTT  |&lt;br /&gt;
    |                    |                        | samples gated by  |&lt;br /&gt;
    |                    |                        | Karn / recovery   |&lt;br /&gt;
    |                    |                        | only)             |&lt;br /&gt;
    | Cold-probe cadence | 100 ms (rx-driven;     | Pre-srtt RTTP     |&lt;br /&gt;
    |                    |  see Section 12)       | rate              |&lt;br /&gt;
    | DELT_RDV           | 100 ms                 | RDVS emit cadence |&lt;br /&gt;
    | MAX_RDV            | 1 s                    | RDVS give-up      |&lt;br /&gt;
    | Delayed-ACK fire   | 2 * TICTIME (TICTIME   | Fired after the   |&lt;br /&gt;
    |                    |  = FRCT tick gran-     | first in-order    |&lt;br /&gt;
    |                    |  ularity, default      | DATA arrival;     |&lt;br /&gt;
    |                    |  5 ms; 2*TICTIME       | tick is build-    |&lt;br /&gt;
    |                    |  = 10 ms by default)   | tunable           |&lt;br /&gt;
    | NACK send cooldown | srtt when an srtt      | Pre-DRF NACK      |&lt;br /&gt;
    |                    |  sample exists, else   | rate-limit        |&lt;br /&gt;
    |                    |  100 ms                |                   |&lt;br /&gt;
    | MAX_SDU            | 1 MiB                  | Max reassembled   |&lt;br /&gt;
    |                    |                        | SDU; configurable |&lt;br /&gt;
    |                    |                        | per flow          |&lt;br /&gt;
    +--------------------+------------------------+-------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The per-flow fragment Maximum Transmission Unit (MTU) is computed&lt;br /&gt;
at flow setup from the lower IPCP&#039;s mtu minus encryption&lt;br /&gt;
headsz / tailsz and CRC trailer; there is no FRCT-level default or&lt;br /&gt;
environment-variable override.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 4. Sequence-number rotation (DRF) ==&lt;br /&gt;
&lt;br /&gt;
The DRF (Data Run Flag) bit on an outbound packet means &amp;quot;this is&lt;br /&gt;
the start of a fresh data run&amp;quot; and is set whenever the sender has&lt;br /&gt;
nothing in flight (snd_cr.seqno == snd_cr.lwe).&lt;br /&gt;
&lt;br /&gt;
Independently of that, if the sender has been idle longer than&lt;br /&gt;
snd_cr.inact AND the pipe is empty (snd_cr.seqno == snd_cr.lwe),&lt;br /&gt;
seqno_rotate() rolls a random new seqno before the send and&lt;br /&gt;
resets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    snd_cr.seqno  = random()&lt;br /&gt;
    snd_cr.lwe    = snd_cr.seqno&lt;br /&gt;
    snd_cr.rwe    = snd_cr.seqno + START_WINDOW&lt;br /&gt;
    rtt_lwe       = snd_cr.seqno&lt;br /&gt;
    in_recovery   = false   (recovery state, see Section 8)&lt;br /&gt;
    recovery_high = snd_cr.seqno&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The receiver, on observing rcv-side inactivity&lt;br /&gt;
(now - rcv_cr.act &amp;gt; rcv_cr.inact), requires a DRF on the next&lt;br /&gt;
DATA packet; otherwise it replies with a rate-limited NACK (see&lt;br /&gt;
below).  Non-DATA control packets pass through without the DRF&lt;br /&gt;
requirement.  On DRF the receiver releases the rq[] slots and&lt;br /&gt;
rebases&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    rcv_cr.lwe   = seqno&lt;br /&gt;
    rcv_cr.rwe   = seqno + RQ_SIZE&lt;br /&gt;
    rcv_cr.seqno = seqno&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the inactive packet has DATA but no DRF, a rate-limited NACK is&lt;br /&gt;
fired back to the sender (cooldown per Section 3); non-DATA stale&lt;br /&gt;
arrivals fall through to normal processing (no NACK, no drop).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 5. Send path ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    1. If the SDU exceeds (frag_mtu - data_hdr_len), the caller&lt;br /&gt;
       (dev.c) fans it out into ceil(count / (frag_mtu -&lt;br /&gt;
       data_hdr_len)) fragments, each emitted via frcti_snd as its&lt;br /&gt;
       own DATA packet with a per-fragment role (Section 7.2);&lt;br /&gt;
       both FRTX and best-effort flows fragment.  Raw flows (no&lt;br /&gt;
       FRCP engagement, qos.service == SVC_RAW) carry no PCI and&lt;br /&gt;
       return -EMSGSIZE for any SDU larger than one packet at the&lt;br /&gt;
       layer below.  An SDU that fits in a single packet is sent&lt;br /&gt;
       as SOLE.  frcti_snd reserves PCI head room; sets DATA, plus&lt;br /&gt;
       DRF when the pipe is empty (snd_cr.seqno == snd_cr.lwe).&lt;br /&gt;
    2. seqno_rotate() if past sender inactivity and the pipe is&lt;br /&gt;
       empty (Section 4).&lt;br /&gt;
    3. Advertise FC (pci.window = frcti_advert_rwe(frcti), i.e.&lt;br /&gt;
       rcv_cr.rwe clamped to rcv_cr.lwe + ring_seq_cap in stream&lt;br /&gt;
       mode) when the receiver side is recent: now - rcv_cr.act&lt;br /&gt;
       &amp;lt; rcv_cr.inact.&lt;br /&gt;
    4. Reliable mode (FRTX): leave snd_cr.lwe where it is; reset&lt;br /&gt;
       the slot at RQ_SLOT(seqno) (snd_slots[p].time = now,&lt;br /&gt;
       snd_slots[p].flags = 0); queue an rxm_entry (saves a packet&lt;br /&gt;
       copy, arms a wheel timer at now + (rto &amp;lt;&amp;lt; rto_mul)).&lt;br /&gt;
       Piggyback ACK (pci.ackno = rcv_cr.lwe) while the a-timer&lt;br /&gt;
       for the most recent received DATA packet has not yet&lt;br /&gt;
       expired (now - rcv_cr.act &amp;lt;= t_a); on piggyback, set&lt;br /&gt;
       rcv_cr.seqno = rcv_cr.lwe so the next delayed-ACK fire is&lt;br /&gt;
       suppressed.  See Section 8 for t_a / t_r semantics.&lt;br /&gt;
    5. Best-effort mode (no FRTX): advance snd_cr.lwe immediately&lt;br /&gt;
       (snd_cr.lwe = snd_cr.lwe + 1, snd_cr.rwe = snd_cr.lwe +&lt;br /&gt;
       RQ_SIZE); no retransmit state.  No send-side RTT probe is&lt;br /&gt;
       armed in this mode (rtt_probe_arm requires an in-flight&lt;br /&gt;
       seqno, which best-effort never has); the rx-driven cold&lt;br /&gt;
       seeder in frcti_rcv is the only probe path.&lt;br /&gt;
    6. In reliable mode, optionally arm an RTT probe (Section 12).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 6. Receive path ==&lt;br /&gt;
&lt;br /&gt;
=== 6.1. Early-exit dispatch ===&lt;br /&gt;
&lt;br /&gt;
Keepalive (KA), RTT probe (RTTP), pre-DRF NACK, and rendezvous&lt;br /&gt;
(RDVS) packets short-circuit out of frcti_rcv before the locked&lt;br /&gt;
main path; each handler takes its own lock internally.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      incoming packet&lt;br /&gt;
            |&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | KA?     |---yes--&amp;gt; ka_rcv  ; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RTTP?   |---yes--&amp;gt; rttp_rcv; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | NACK?   |---yes--&amp;gt; nack_rcv; return  (see Section 9)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RDVS?   |---yes--&amp;gt; rdv_rcv ; return  (reply bare FC, ackno=0)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       acquire wrlock; enter locked main path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - KA   : refresh t_ka_rcv, honour piggybacked ACK.&lt;br /&gt;
  - RTTP : probe (echo back nonce) or echo (verify nonce, sample&lt;br /&gt;
           RTT).&lt;br /&gt;
  - NACK : pre-DRF, sender-side handler.  See Section 9.&lt;br /&gt;
  - RDVS : reply with a bare FC packet (ackno = 0); rdlock only.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 6.2. Locked main path ===&lt;br /&gt;
&lt;br /&gt;
Steps below run with the per-flow frcti.lock held for writing&lt;br /&gt;
(pthread_rwlock_wrlock) unless noted.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  rcv_inact_check&lt;br /&gt;
      Only meaningful when the receive side is stale.  On DRF&lt;br /&gt;
      (Data Run Flag): release rq[] slots, rebase rcv_cr, continue.&lt;br /&gt;
      On stale DATA without DRF: fire a pre-DRF NACK if cooldown&lt;br /&gt;
      allows (Section 9), then discard the packet; on cooldown,&lt;br /&gt;
      drop without sending a NACK (a pending cumulative ACK from&lt;br /&gt;
      drop_packet may still go out).  Non-DATA, non-DRF arrivals&lt;br /&gt;
      bypass rcv_inact_check entirely; pure-DRF stale arrivals fall&lt;br /&gt;
      through after the DRF rebase branch.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  DATA-only act refresh&lt;br /&gt;
      Refresh rcv_cr.act only when FRCT_DATA is set, so that non-DATA&lt;br /&gt;
      packets never block the next DRF rebase.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  Wire-dup gate&lt;br /&gt;
      Before flag-driven dispatch, drop wire-duplicate ACKs and&lt;br /&gt;
      wire-duplicate DATA (is_dup_ack / is_dup_data).  The DATA&lt;br /&gt;
      check is bypassed for FRCT_RXM-bearing arrivals so the&lt;br /&gt;
      piggybacked ACK / SACK / FC carried on a retransmitted DATA&lt;br /&gt;
      at an already-ACK&#039;d seqno is still applied; the stale-in-&lt;br /&gt;
      window branch below then drops the packet.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  ACK&lt;br /&gt;
      Drop ACKs whose ackno falls outside (snd_cr.lwe, snd_cr.seqno].&lt;br /&gt;
      If ackno == snd_cr.lwe (non-advancing cumulative ACK), drive&lt;br /&gt;
      RACK fast-retransmit consideration (Section 8).  Otherwise&lt;br /&gt;
      advance snd_cr.lwe = ackno, collapse rto_mul to 0 (Karn-gated&lt;br /&gt;
      by SND_RTX on the just-acknowledged slot, the old head-of-&lt;br /&gt;
      line), reset dup_thresh to 0, update t_latest_ack to the&lt;br /&gt;
      send-time of the slot at ackno-1 (consumed by RACK and SACK&lt;br /&gt;
      below), decay reo_wnd_mult per RFC 8985 sec. 6.2 step 4,&lt;br /&gt;
      exit NewReno-careful recovery (see Section 8) on&lt;br /&gt;
      ackno &amp;gt;= recovery_high or ackno == snd_cr.seqno, and feed an&lt;br /&gt;
      RTT sample if eligible (Section 12).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  SACK&lt;br /&gt;
      Walk the block list.  For each block (a present range above&lt;br /&gt;
      lwe) NULL out snd_slots[k].rxm, clear the slot&#039;s per-send&lt;br /&gt;
      flags, and advance t_latest_ack to the latest send-time&lt;br /&gt;
      covered (the Forward Acknowledgement / fack equivalent,&lt;br /&gt;
      Mathis &amp;amp; Mahdavi 1996); the first block whose start&lt;br /&gt;
      clamps to snd_cr.lwe skips this fack update so that a head-&lt;br /&gt;
      of-line clamp does not falsely advance fack.  For un-SACKed&lt;br /&gt;
      gaps below hi_sacked, stage a retransmit per slot that is&lt;br /&gt;
      (1) still owned (rxm != NULL), (2) not already SND_FAST_RXM,&lt;br /&gt;
      (3) not aged out past t_r, and (4) either outside the RACK&lt;br /&gt;
      reorder window R OR with dup_thresh &amp;gt;= DUP_THRESH (the RFC&lt;br /&gt;
      8985 sec. 6.2 hybrid trigger).  Mark the slot SND_FAST_RXM&lt;br /&gt;
      and NULL the rxm at stage time.  Capped at SACK_RXM_MAX&lt;br /&gt;
      staged retransmits per receive pass; what&#039;s left rides the&lt;br /&gt;
      next SACK.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  FC&lt;br /&gt;
      Bump snd_cr.rwe (clamped to lwe + RQ_SIZE, never shrinks)&lt;br /&gt;
      and mark window open.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  DATA&lt;br /&gt;
      Bounds-check seqno against window.  On stale-dup&lt;br /&gt;
      (seqno &amp;lt; rcv_cr.lwe), set rcv_cr.seqno = seqno to force a&lt;br /&gt;
      fresh ACK on the next ack_snd, then drop.  On accept: both&lt;br /&gt;
      FRTX and best-effort stash the packet-buffer index into&lt;br /&gt;
      rq[seqno mod RQ_SIZE].  Fragments stash unchanged - the role&lt;br /&gt;
      bits are inspected only at consume time (Section 7.2).  On&lt;br /&gt;
      out-of-order arrival, build a SACK reply if not rate-limited&lt;br /&gt;
      (per Section 3) and not deduplicated against the previous&lt;br /&gt;
      (rcv_cr.lwe, n_blocks) pair; D-SACK reports always bypass the&lt;br /&gt;
      dedup.  If both rate-limit and dedup suppress the reply,&lt;br /&gt;
      neither SACK nor delayed-ACK fires (the sender picks up the&lt;br /&gt;
      gap on its next ACK).  On in-order arrival, arm the delayed-&lt;br /&gt;
      ACK timer.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  drop_packet exit&lt;br /&gt;
      Releases the per-packet shared-memory buffer (spb), then&lt;br /&gt;
      calls ack_snd synchronously after the spb release to surface&lt;br /&gt;
      any pending cumulative ACK.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 7. Read path and reassembly ==&lt;br /&gt;
&lt;br /&gt;
=== 7.1. Read path ===&lt;br /&gt;
&lt;br /&gt;
flow_read returns a full reassembled SDU (Service Data Unit) via&lt;br /&gt;
frcti_consume on every FRCP SDU-mode flow (FRTX or best-effort);&lt;br /&gt;
stream-mode is covered in Section 16.  An incomplete head-of-line&lt;br /&gt;
(HoL) run yields -EAGAIN; an oversized run yields -EMSGSIZE (the&lt;br /&gt;
run is dropped so the flow does not stall).  On best-effort flows,&lt;br /&gt;
a permanently-lost mid-fragment is dropped as soon as a later&lt;br /&gt;
complete SDU becomes visible in the ring (Section 7.2 skip-past-&lt;br /&gt;
gap).&lt;br /&gt;
&lt;br /&gt;
Raw flows carry no frcti, so flow_read returns the next pending&lt;br /&gt;
packet-buffer index directly, with no role-bit inspection.  (Raw&lt;br /&gt;
service is selected via qos.service == SVC_RAW at flow allocation,&lt;br /&gt;
which suppresses frcti creation.)&lt;br /&gt;
&lt;br /&gt;
frcti_pdu_ready is the no-advance peek used by fevent (the&lt;br /&gt;
Ouroboros flow-event multiplexer, the poll(2)-equivalent on&lt;br /&gt;
flows).  It returns ready only when the head-of-line run is&lt;br /&gt;
complete and the lead packet (a Protocol Data Unit, here one FRCP&lt;br /&gt;
packet) is present at rcv_cr.rwe - RQ_SIZE; any other state&lt;br /&gt;
(including the best-effort skip-past-gap case) returns not ready,&lt;br /&gt;
and frcti_consume is left to drop the broken prefix and re-&lt;br /&gt;
inspect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 7.2. Fragmentation and reassembly ===&lt;br /&gt;
&lt;br /&gt;
Send side (flow_write_frag).  An SDU larger than&lt;br /&gt;
(frag_mtu - PCI) is split into ceil(count / (frag_mtu - PCI))&lt;br /&gt;
fragments; each fragment is its own FRCP packet with its own&lt;br /&gt;
seqno and a per-fragment role flag pair (Section 1.2).  Roles are&lt;br /&gt;
assigned at emit time:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    +------+--------+&lt;br /&gt;
    | i    | Role   |&lt;br /&gt;
    +------+--------+&lt;br /&gt;
    | n=1  | SOLE   |&lt;br /&gt;
    | i=0  | FIRST  |&lt;br /&gt;
    | i=n-1| LAST   |&lt;br /&gt;
    | else | MID    |&lt;br /&gt;
    +------+--------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A mid-loop allocation or transmit failure may yield a partial&lt;br /&gt;
write: the call returns the bytes already enqueued (off &amp;gt; 0) or&lt;br /&gt;
the underlying error (off == 0).  Best-effort flows fragment&lt;br /&gt;
identically; on the receiver, a partial run with a permanently-&lt;br /&gt;
lost fragment is dropped when a later complete SDU is visible in&lt;br /&gt;
the ring (see skip-past-gap below).  Raw flows carry no PCI and&lt;br /&gt;
refuse anything larger than the layer&#039;s user MTU (-EMSGSIZE).&lt;br /&gt;
&lt;br /&gt;
Wire-level recovery is fragment-agnostic on FRTX flows: each&lt;br /&gt;
fragment&#039;s seqno flows through SACK / RACK / RTO / NACK exactly&lt;br /&gt;
as for a SOLE DATA packet, and reassembly does not re-enter the&lt;br /&gt;
loss-detection path.  Best-effort flows run the same seqno&lt;br /&gt;
machinery (DRF, FC, ACK piggyback, pre-DRF NACK emit) but queue&lt;br /&gt;
no rxm state at the sender, so a lost MID is unrecoverable;&lt;br /&gt;
skip-past-gap handles it (below).&lt;br /&gt;
&lt;br /&gt;
Receive side.  Fragments stash into rq[seqno] unchanged; role bits&lt;br /&gt;
are read only at consume time.  frag_run_inspect, called from&lt;br /&gt;
frcti_consume, walks the ring starting at the oldest still-&lt;br /&gt;
undelivered seqno base = rcv_cr.rwe - RQ_SIZE (equal to rcv_cr.lwe&lt;br /&gt;
only when no partial run is in progress; during a partial run lwe&lt;br /&gt;
has already advanced past base).  It produces one of three&lt;br /&gt;
outcomes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    +---------------+---------------------------------------------+&lt;br /&gt;
    | Outcome       | Cause                                       |&lt;br /&gt;
    +---------------+---------------------------------------------+&lt;br /&gt;
    | DELIVER (n)   | rq[base]=SOLE (n=1), or rq[base]=FIRST and  |&lt;br /&gt;
    |               | a LAST follows in slots [base+1..base+n-1]  |&lt;br /&gt;
    |               | with all intermediate roles in {MID,FIRST,  |&lt;br /&gt;
    |               | LAST} contiguous.                           |&lt;br /&gt;
    | DROP (n)      | rq[base] is MID or LAST without a preceding |&lt;br /&gt;
    |               | FIRST (n=1); a FIRST..[non-LAST]..new-FIRST |&lt;br /&gt;
    |               | or new-SOLE mid-run (drop the broken prefix |&lt;br /&gt;
    |               | with n = run length minus 1, so the new     |&lt;br /&gt;
    |               | FIRST/SOLE stays); or, on best-effort       |&lt;br /&gt;
    |               | flows, a gap at base with a FIRST/SOLE      |&lt;br /&gt;
    |               | later in the ring (drop up to the new run   |&lt;br /&gt;
    |               | start).                                     |&lt;br /&gt;
    | NOT_READY     | rq[base] absent or FIRST..[non-LAST] with   |&lt;br /&gt;
    |               | no later FIRST/SOLE in the ring (FRTX waits |&lt;br /&gt;
    |               | for retx; best-effort waits for arrival).   |&lt;br /&gt;
    +---------------+---------------------------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DELIVER triggers frag_gather: a scatter-gather memcpy of the n&lt;br /&gt;
consecutive fragments at rq[base..base+n-1] directly into the&lt;br /&gt;
caller&#039;s buffer; each per-packet shared-memory buffer (spb) is&lt;br /&gt;
released and rwe advances by n.  lwe was already advanced&lt;br /&gt;
incrementally as each contiguous fragment arrived; frag_gather&lt;br /&gt;
only restores the fixed-width invariant rwe == lwe + RQ_SIZE.&lt;br /&gt;
No intermediate reassembly buffer is allocated.&lt;br /&gt;
&lt;br /&gt;
DROP advances rwe past the broken prefix (releasing the spbs)&lt;br /&gt;
and pulls lwe up to the new trailing edge if needed; the next&lt;br /&gt;
consume retries from the new base.  Oversize or arithmetically&lt;br /&gt;
overflowing delivery (sum of fragment lengths &amp;gt; max_rcv_sdu, sum&lt;br /&gt;
&amp;gt; caller&#039;s buffer, or running-sum overflow) also drops the run&lt;br /&gt;
with -EMSGSIZE.&lt;br /&gt;
&lt;br /&gt;
Skip-past-gap (best-effort only).  On FRTX, a gap in the run means&lt;br /&gt;
&amp;quot;waiting for retransmit&amp;quot; and frag_run_inspect returns NOT_READY.&lt;br /&gt;
On best-effort flows the gap is permanent, so frag_run_inspect&lt;br /&gt;
scans forward in the ring for the next FIRST or SOLE; if one is&lt;br /&gt;
visible within RQ_SIZE, it returns DROP for the broken prefix and&lt;br /&gt;
the consume loop retries at the new lwe.  Memory hold is bounded&lt;br /&gt;
by RQ_SIZE; the partial releases on the next consume call once a&lt;br /&gt;
later complete run exists.  Voice-like flows (one SOLE per SDU)&lt;br /&gt;
see no extra wait: any later SOLE makes the prior gap droppable&lt;br /&gt;
immediately.&lt;br /&gt;
&lt;br /&gt;
The choice to defer reassembly to consume time keeps the receive&lt;br /&gt;
path zero-copy: fragments stay in the shared-memory ring until&lt;br /&gt;
the application pulls, and the SDU lands directly in the caller&#039;s&lt;br /&gt;
buffer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 8. Retransmission ==&lt;br /&gt;
&lt;br /&gt;
FRCP is bounded by two delta-t-derived timers (Watson 1981, see&lt;br /&gt;
Section 15):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - t_a (a-timer): upper bound on ACK delay.  An ACK for a received&lt;br /&gt;
    DATA packet MUST be emitted within t_a of receipt; an attempt&lt;br /&gt;
    to send an ACK after the a-timer has expired is suppressed&lt;br /&gt;
    (the sender&#039;s RTO is already in motion).&lt;br /&gt;
  - t_r (r-timer): upper bound on retransmission.  A given DATA&lt;br /&gt;
    packet MUST NOT be retransmitted after t_r has elapsed since&lt;br /&gt;
    its first send (t0); when the bound is hit, the flow is&lt;br /&gt;
    declared down (raising the Ouroboros asynchronous flow&lt;br /&gt;
    condition ACL_FLOWDOWN, which marks the flow dead to both&lt;br /&gt;
    endpoints) rather than retransmitted again.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each in-flight FRTX seqno owns one rxm_entry, armed in a hashed&lt;br /&gt;
timing wheel; the wheel deadline is the slot&#039;s next eligible&lt;br /&gt;
retransmit time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  RTO timer&lt;br /&gt;
      On fire (rxm_due), re-emit with FRCT_RXM, mark SND_RTX&lt;br /&gt;
      (Karn-suppress next ACK&#039;s RTT sample), and (for the head-of-&lt;br /&gt;
      line (HoL) slot only) bump rto_mul up to MAX_RTO_MUL.  Wheel&lt;br /&gt;
      deadline is t_send + (rto &amp;lt;&amp;lt; rto_mul).  Re-armed unless&lt;br /&gt;
      consumed.  The RTO timer also clears SND_FAST_RXM (re-arming&lt;br /&gt;
      fast-retransmit eligibility), resets reo_wnd_mult to 1 on a&lt;br /&gt;
      HoL fire (RFC 8985 sec. 6.2 step 4 reset clause), and marks&lt;br /&gt;
      the flow ACL_FLOWDOWN if its frct_tx call fails.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  r-timer guard&lt;br /&gt;
      Before any retransmit attempt, check (now - t0) against t_r.&lt;br /&gt;
      If exceeded, the slot is no longer eligible for retransmit.&lt;br /&gt;
      Only the RTO timer (rxm_due) treats r-timer expiry as&lt;br /&gt;
      terminal: it marks the flow ACL_FLOWDOWN (peer unreachable).&lt;br /&gt;
      Fast-retransmit, SACK-driven retransmit, and NACK-driven&lt;br /&gt;
      head-of-line re-emit silently skip aged-out slots and defer&lt;br /&gt;
      the flow-down decision to the next RTO fire.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  Fast retransmit (hybrid trigger, RFC 8985 sec. 6.2)&lt;br /&gt;
      On a non-advancing cumulative ACK with the scoreboard&lt;br /&gt;
      advanced, fire one fast retransmit when EITHER (a) the head-&lt;br /&gt;
      of-line slot&#039;s latest send is older than the RACK reorder&lt;br /&gt;
      window R (Section 3) and not yet aged out, OR (b) the SACK&lt;br /&gt;
      dup-thresh count above snd_cr.lwe reaches DUP_THRESH (= 3,&lt;br /&gt;
      RFC 8985 sec. 6.2 step 4).  Fires at most once per non-&lt;br /&gt;
      advancing cumulative-ACK value, gated by rack_fired_lwe (the&lt;br /&gt;
      snd_cr.lwe at which fast-retransmit last fired).  Set&lt;br /&gt;
      SND_FAST_RXM on the slot (one-shot per-slot gate) and enter&lt;br /&gt;
      NewReno-style careful recovery (see NewReno below in this&lt;br /&gt;
      section).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      The RACK reorder window R uses the RFC 8985 sec. 6.2 form&lt;br /&gt;
      R = MIN(reo_wnd_mult * min_RTT / 4, SRTT) with a&lt;br /&gt;
      MIN_REORDER_NS = 250 us floor.  Before the first RTT sample&lt;br /&gt;
      seeds min_rtt, R falls back to MIN(reo_wnd_mult * SRTT / 4,&lt;br /&gt;
      SRTT), still floored at MIN_REORDER_NS (consistent with the&lt;br /&gt;
      windowed-minimum fallback described in Section 12).  min_rtt&lt;br /&gt;
      is a windowed minimum over the last MIN_RTT_WIN_NS = 5 min of&lt;br /&gt;
      RTT samples (matches the Linux tcp_min_rtt_wlen default) so a&lt;br /&gt;
      route change to a longer path eventually re-anchors the&lt;br /&gt;
      reorder window without relying on reo_wnd_mult growth alone.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  SACK-driven retransmit&lt;br /&gt;
      For each gap below hi_sacked whose slot is (1) still owned,&lt;br /&gt;
      (2) not already SND_FAST_RXM, (3) not aged out past t_r, and&lt;br /&gt;
      (4) either outside the RACK window R OR with dup_thresh &amp;gt;=&lt;br /&gt;
      DUP_THRESH (same hybrid as fast-retransmit, see Section 6.2),&lt;br /&gt;
      re-emit.  Each SACK-driven retransmit re-arms a fresh rxm so&lt;br /&gt;
      a lost retransmit can still be recovered by its own RTO&lt;br /&gt;
      timer.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  NewReno&lt;br /&gt;
      On entry, recovery_high = snd_cr.seqno + RTT_QUARANTINE.&lt;br /&gt;
      Exit when ackno &amp;gt;= recovery_high or ackno == snd_cr.seqno&lt;br /&gt;
      (the latter means everything sent has been acknowledged).&lt;br /&gt;
      seqno_rotate also clears recovery.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 9. Pre-DRF NACK ==&lt;br /&gt;
&lt;br /&gt;
The two sides have different inactivity thresholds&lt;br /&gt;
(snd_cr.inact &amp;gt; rcv_cr.inact), so a receiver can detect &amp;quot;stale data&lt;br /&gt;
run&amp;quot; before the sender&#039;s own DRF logic kicks in.  NACK is the&lt;br /&gt;
receiver-driven nudge that asks the sender to re-transmit the head&lt;br /&gt;
of the run.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  Send (frcti_nack_snd, called by frcti_rcv when rcv_inact_check&lt;br /&gt;
        returns FRCT_INACT_NEED_NACK)&lt;br /&gt;
      When an incoming DATA packet has no DRF and rcv-side activity&lt;br /&gt;
      is older than rcv_cr.inact, the receiver emits a bare packet&lt;br /&gt;
      with flags = FRCT_NACK and seqno = arrival_seqno - 1&lt;br /&gt;
      (informational only, not consulted by the receive handler).&lt;br /&gt;
      The cooldown in Section 3 rate-limits the burst.  Non-DATA&lt;br /&gt;
      non-DRF arrivals bypass rcv_inact_check entirely; non-DATA&lt;br /&gt;
      DRF still rebases via the DRF branch.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  Receive (frcti_nack_rcv)&lt;br /&gt;
      Dispatched in the early-exit branch (Section 6.1), before&lt;br /&gt;
      rcv_inact_check.  The sender copies the head-of-line (HoL)&lt;br /&gt;
      rxm packet, marks the slot SND_RTX | SND_FAST_RXM (Karn-&lt;br /&gt;
      suppress next ACK, one-shot fast-rxm gate), sets rtt_lwe =&lt;br /&gt;
      snd_cr.lwe + 1, and re-emits via fast_rxm_send with FRCT_RXM&lt;br /&gt;
      and a refreshed ackno.  The original rxm_entry and its RTO&lt;br /&gt;
      timer are left armed - the NACK emit is additive to the&lt;br /&gt;
      normal retransmit machinery, not a replacement.  No-op if&lt;br /&gt;
      nothing is in flight, the HoL slot has aged past t_r, or&lt;br /&gt;
      the HoL rxm pointer has been cleared by SACK or RACK.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NACK serves two roles:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  1. Lost first-of-run (DRF) packet recovery.  Required.  Until&lt;br /&gt;
     the DRF packet arrives, the receiver cannot rebase its&lt;br /&gt;
     window, so any subsequent in-flight packets look stale to&lt;br /&gt;
     the receiver.  The NACK fires the moment the second&lt;br /&gt;
     packet arrives at a stale receiver, telling the sender to&lt;br /&gt;
     re-emit the HoL (DRF) packet at NACK-cooldown latency rather&lt;br /&gt;
     than waiting for the initial RTO (which is the configured&lt;br /&gt;
     default until srtt is seeded by the first probe round-trip).&lt;br /&gt;
  2. General loss-recovery accelerator.  When loss is detected&lt;br /&gt;
     receiver-first, the NACK skips one RTO of latency relative to&lt;br /&gt;
     waiting for the sender&#039;s RTO to fire.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In both cases the existing rxm_entry and its RTO timer are left&lt;br /&gt;
armed, so the RTO path remains the eventual fallback.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 10. Cumulative + selective ACK ==&lt;br /&gt;
&lt;br /&gt;
Cumulative ACK is ackno = rcv_cr.lwe.  On out-of-order arrival the&lt;br /&gt;
receiver also emits a SACK packet (Section 1.3) whose payload lists&lt;br /&gt;
*present* blocks above lwe (analogous to TCP SACK / QUIC ACK&lt;br /&gt;
ranges).  SACKs are rate-limited per Section 3 and suppressed when&lt;br /&gt;
neither lwe nor block count has changed since the last SACK.&lt;br /&gt;
&lt;br /&gt;
D-SACK reports (RFC 2883) are emitted in-band as block[0] of an&lt;br /&gt;
otherwise normal SACK frame (see Section 1.3 for the encoding).&lt;br /&gt;
Two receiver triggers arm a pending D-SACK report (single-slot,&lt;br /&gt;
latest-wins):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - DATA arrival with seqno &amp;lt; rcv_cr.lwe, both wire-dup (no RXM,&lt;br /&gt;
    is_dup_data path) and retransmit (RXM, post-FC branch)&lt;br /&gt;
    (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
  - rq_accept conflict, slot already occupied in [lwe, rwe)&lt;br /&gt;
    (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a D-SACK is pending and the standard scoreboard SACK would be&lt;br /&gt;
suppressed by dedup or rate-limit, the report is emitted as a&lt;br /&gt;
stand-alone SACK frame through the normal ack_snd path; when a&lt;br /&gt;
D-SACK report is pending the path bypasses dedup and the TICTIME&lt;br /&gt;
rate-limit, but the a-timer suppression on rcv inactivity still&lt;br /&gt;
applies.&lt;br /&gt;
&lt;br /&gt;
Bare ACKs are deferred via a per-flow delayed-ACK timer (one in&lt;br /&gt;
flight at a time, atomic test-and-set dedup; fires per Section 3&lt;br /&gt;
after the first in-order arrival).  Suppressed if (1) no new&lt;br /&gt;
seqno, (2) rcv side is inactive (older than t_a), or (3) the&lt;br /&gt;
sender just sent within TICTIME.  A pending D-SACK ride-through&lt;br /&gt;
bypasses (1) and (3); the a-timer gate (2) is unconditional.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 11. Flow control ==&lt;br /&gt;
&lt;br /&gt;
The receiver advertises rwe in every FC field.  The sender treats&lt;br /&gt;
its snd_cr.rwe as the absolute right edge: when&lt;br /&gt;
snd_cr.seqno &amp;gt;= snd_cr.rwe the window is closed and flow_write&lt;br /&gt;
yields.  While closed, the sender periodically emits RDVS&lt;br /&gt;
(rendezvous) packets (cadence DELT_RDV); the receiver replies with&lt;br /&gt;
a bare FC packet (ackno = 0) that reopens the window.  Once the&lt;br /&gt;
window has been closed for longer than MAX_RDV the sender stops&lt;br /&gt;
emitting RDVS but does not tear the flow down - the writer keeps&lt;br /&gt;
blocking until either a peer-driven FC arrives or the KA&lt;br /&gt;
(keepalive) / r-timer marks the flow.&lt;br /&gt;
&lt;br /&gt;
rwe is clamped to lwe + RQ_SIZE on receipt and MUST NOT shrink:&lt;br /&gt;
a backward rwe is silently clamped to the current snd_cr.rwe;&lt;br /&gt;
the FC packet still reopens the window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 12. RTT estimation ==&lt;br /&gt;
&lt;br /&gt;
Active RTTP probes (Section 1.4) carry a 32-bit probe_id (0&lt;br /&gt;
reserved) and a 16-byte random nonce echoed verbatim - defends&lt;br /&gt;
against spoofed replies.  A ring of RTTP_RING in-flight probes is&lt;br /&gt;
kept; an echo whose (id, nonce) doesn&#039;t match the ring slot is&lt;br /&gt;
dropped.  A single RTTP sample is clamped to RTT_CLAMP_MUL * srtt&lt;br /&gt;
(compile-time RTT_CLAMP_MUL = 16) once srtt is seeded; the first&lt;br /&gt;
cold-probe sample feeds rtt_update raw.&lt;br /&gt;
&lt;br /&gt;
Probe arming gates:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - Cold (no srtt yet): the receive path arms at most one probe&lt;br /&gt;
    per 100 ms via frcti_rcv_probe (PROBE_DUE_COLD); arming&lt;br /&gt;
    requires an incoming packet.  Active send-path arming bails&lt;br /&gt;
    while srtt == 0.&lt;br /&gt;
  - Warm (rtt_probe_arm, called from frcti_snd): outstanding&lt;br /&gt;
    data (snd_cr.seqno &amp;gt; snd_cr.lwe), AND at least 2 * srtt&lt;br /&gt;
    since t_rcv_rtt (last RTT receive of any kind), AND at&lt;br /&gt;
    least srtt since t_snd_probe (last probe emit).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sample feeds either Linux&#039;s asymmetric mdev estimator&lt;br /&gt;
(FRCT_LINUX_RTT_ESTIMATOR, default ON) or RFC 6298 symmetric EWMA&lt;br /&gt;
(compile option).  srtt is floored at 10 ms when seeded from a&lt;br /&gt;
hint, at 1 us after every update (including the first seeding&lt;br /&gt;
sample); mdev floored at 100 ns.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    RTO = max(rto_min, 2 * srtt, srtt + (mdev &amp;lt;&amp;lt; MDEV_MUL))&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(the 2 * srtt floor is an FRCT addition not in RFC 6298).&lt;br /&gt;
Effective wheel deadline capped per Section 3.&lt;br /&gt;
&lt;br /&gt;
ACK-derived samples (frcti_ack_rcv -&amp;gt; rtt_sample_eligible), beyond&lt;br /&gt;
the cum-ACK advance gate in frcti_ack_rcv (ackno &amp;gt; lwe and&lt;br /&gt;
ackno &amp;lt;= seqno), require all of: not in recovery; ACK packet does&lt;br /&gt;
not carry FRCT_RXM; HoL slot&#039;s SND_RTX bit clear; slot&#039;s rxm&lt;br /&gt;
pointer non-NULL (not SACK-consumed); lwe not below the rtt_lwe&lt;br /&gt;
fence; srtt already seeded by an RTTP probe.  There is no ACK-only&lt;br /&gt;
seeding.&lt;br /&gt;
&lt;br /&gt;
Every eligible sample also feeds RACK.min_RTT (RFC 8985 sec. 6.2)&lt;br /&gt;
via a windowed minimum: replace whenever the sample is strictly&lt;br /&gt;
smaller OR more than MIN_RTT_WIN_NS (5 min, matches Linux&lt;br /&gt;
tcp_min_rtt_wlen) has elapsed since the current min was set.  The&lt;br /&gt;
downward branch is immediate (faster path picked up at once); the&lt;br /&gt;
upward branch is gated on the window (a transient queue burst does&lt;br /&gt;
not poison the estimate, but a sustained route change to a longer&lt;br /&gt;
path re-anchors min_RTT after at most one window).  Seeded from&lt;br /&gt;
rtt_hint at rtt_init; 0 acts as the unset sentinel and the base&lt;br /&gt;
in rack_reorder_window falls back from min_RTT to SRTT (so&lt;br /&gt;
R = mult * SRTT/4, capped at SRTT, floored at MIN_REORDER_NS)&lt;br /&gt;
until the first sample.  See Section 6.2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 13. Liveness (keepalive) ==&lt;br /&gt;
&lt;br /&gt;
When qs.timeout &amp;gt; 0 a per-flow KA (keepalive) timer is armed.&lt;br /&gt;
Arming uses rcv_cr.act for the deadline computation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    deadline = min(snd_act + qs.timeout/4,&lt;br /&gt;
                   rcv_act + qs.timeout)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(clamped to now + qs.timeout/4 if already past).  The timer fires&lt;br /&gt;
either on sender idleness (to send a KA) or on receiver idleness&lt;br /&gt;
(to declare the peer dead).  On fire (ka_snd) the peer-dead test&lt;br /&gt;
uses max(rcv_cr.act, t_ka_rcv) so a recent KA reply counts even&lt;br /&gt;
when no DATA has arrived:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - If now - max(rcv_cr.act, t_ka_rcv) &amp;gt; qs.timeout, mark the flow&lt;br /&gt;
    ACL_FLOWPEER and notify the per-process flow-event set&lt;br /&gt;
    (proc.fqset) with FLOW_PEER.&lt;br /&gt;
  - Else if snd_idle &amp;gt; qs.timeout/4, emit a bare KA | ACK&lt;br /&gt;
    (ackno = rcv_cr.lwe) and re-arm.&lt;br /&gt;
  - Else just re-arm.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: rx_rb and tx_rb are the receive and transmit shared-memory&lt;br /&gt;
ring buffers.  The r-timer raises ACL_FLOWDOWN on both (route is&lt;br /&gt;
broken); keepalive raises ACL_FLOWPEER on rx_rb only and notifies&lt;br /&gt;
the flow-event set (peer is silent, writer keeps tx_rb usable) -&lt;br /&gt;
distinct ACLs.  qs.timeout == 0 disables keepalive entirely; a&lt;br /&gt;
silent peer crash is then undetected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 14. Linger / teardown ==&lt;br /&gt;
&lt;br /&gt;
On flow_dealloc, frcti_dealloc computes a grace timeout&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    max(rcv_cr.act + rcv_cr.inact, snd_cr.act + snd_cr.inact) - now&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(floored at 0 and converted to seconds) and returns it; flow_dealloc&lt;br /&gt;
forwards this to the IRMd as the dealloc grace.  The IRMd, not FRCT,&lt;br /&gt;
performs the wait.  Before computing the timeout, FRCT may emit a&lt;br /&gt;
final ACK when rcv_cr.lwe != rcv_cr.seqno (the peer has not been&lt;br /&gt;
told the most recent cumulative ACK) AND the rcv side has been&lt;br /&gt;
active within t_a (a-timer not aged out).&lt;br /&gt;
&lt;br /&gt;
FRCTFLINGER is honoured only when snd_cr.lwe &amp;lt; edge, where edge =&lt;br /&gt;
snd_fin_seqno after FIN has been sent in stream mode and&lt;br /&gt;
snd_cr.seqno otherwise (data or FIN still in flight).  The drain&lt;br /&gt;
itself runs in flow_dealloc&#039;s while (FRCTI_LINGERING) loop, not in&lt;br /&gt;
frcti_dealloc.&lt;br /&gt;
&lt;br /&gt;
The fd is single-reader / single-writer (documented in the&lt;br /&gt;
manpages).  flow_write pumps rx_rb on every call (via&lt;br /&gt;
flow_wait_window -&amp;gt; flow_drain_rx_nb) and additionally blocks on&lt;br /&gt;
rx_rb when the send window is closed.  A pure-writer thread thus&lt;br /&gt;
consumes ACKs without a dedicated reader.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 15. Heritage and adopted techniques ==&lt;br /&gt;
&lt;br /&gt;
Delta-t (Watson, 1981) is the primary heritage; FRCP descends from&lt;br /&gt;
the delta-t protocol family via the Recursive InterNetwork&lt;br /&gt;
Architecture (RINA; Day, &amp;quot;Patterns in Network Architecture&amp;quot;, 2008,&lt;br /&gt;
ch. 9).  Timer-based connection management&lt;br /&gt;
(no SYN/FIN handshake, per-flow state born on first DATA and&lt;br /&gt;
reclaimed after t_mpl + a + r of silence), the DRF marker, and the&lt;br /&gt;
t_mpl / t_a / t_r timers all come from delta-t.  See Watson,&lt;br /&gt;
&amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection&lt;br /&gt;
Management&amp;quot;, Computer Networks 5 (1981).&lt;br /&gt;
&lt;br /&gt;
The unified `flow_alloc(name, qos, ...)` primitive and its&lt;br /&gt;
multi-axis QoS-cube argument (Section 2.2) also come from RINA&lt;br /&gt;
(Day 2008, ch. 6; Grasa et al., &amp;quot;IRATI: investigating RINA as an&lt;br /&gt;
alternative to TCP/IP&amp;quot;, Computer Networks 92 (2015)) - reliability,&lt;br /&gt;
ordering, CRC presence, and encryption are flow attributes, not&lt;br /&gt;
separate sockets or protocols.&lt;br /&gt;
&lt;br /&gt;
The table below summarises additional adopted techniques and their&lt;br /&gt;
references.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| FRCP mechanism         | Heritage         | Reference / note       |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Random new seqno on    | TCP ISN          | RFC 6528 (Gont &amp;amp;       |&lt;br /&gt;
| seqno_rotate           |                  | Bellovin, 2012).       |&lt;br /&gt;
|                        |                  | QUIC PN-space reset    |&lt;br /&gt;
|                        |                  | (RFC 9000 sec. 12.3)   |&lt;br /&gt;
|                        |                  | is a structural        |&lt;br /&gt;
|                        |                  | analogue.              |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Cumulative ACK,        | TCP              | RFC 793 / RFC 9293     |&lt;br /&gt;
| left-window-edge       |                  |                        |&lt;br /&gt;
| advance                |                  |                        |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Receive window with    | TCP              | RFC 793 sec. 3.7 /     |&lt;br /&gt;
| non-shrink rule        |                  | RFC 9293 sec. 3.8.6;   |&lt;br /&gt;
|                        |                  | RFC 1122 sec. 4.2.2.16 |&lt;br /&gt;
|                        |                  | for the explicit non-  |&lt;br /&gt;
|                        |                  | shrink prohibition     |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Modular seqno          | TCP              | RFC 793 sec. 3.3 /     |&lt;br /&gt;
| arithmetic             |                  | RFC 9293 sec. 3.4      |&lt;br /&gt;
| (before/after helpers) |                  |                        |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Selective ACK block    | TCP              | RFC 2018 (Mathis et    |&lt;br /&gt;
| list                   |                  | al., 1996).  Encoded   |&lt;br /&gt;
|                        |                  | as a typed FRCP packet |&lt;br /&gt;
|                        |                  | rather than a TCP      |&lt;br /&gt;
|                        |                  | option, so framing is  |&lt;br /&gt;
|                        |                  | closer to QUIC ACK     |&lt;br /&gt;
|                        |                  | frames.  D-SACK (RFC   |&lt;br /&gt;
|                        |                  | 2883) carried in-band  |&lt;br /&gt;
|                        |                  | as block[0]; see       |&lt;br /&gt;
|                        |                  | Section 1.3.           |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| NewReno-careful        | TCP              | RFC 6582 (Henderson    |&lt;br /&gt;
| recovery with          |                  | et al., 2012); QUIC    |&lt;br /&gt;
| recovery_high gate     |                  | builds on the same     |&lt;br /&gt;
|                        |                  | model in RFC 9002      |&lt;br /&gt;
|                        |                  | sec. 7.3.2.  Cwnd half |&lt;br /&gt;
|                        |                  | absent (CC in IPCP).   |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| RACK reordering        | TCP              | RFC 8985 (Cheng et     |&lt;br /&gt;
| window for fast        |                  | al., 2021).  FRCP      |&lt;br /&gt;
| retransmit             |                  | R = MIN(reo_wnd_mult * |&lt;br /&gt;
|                        |                  | min_RTT / 4, SRTT)     |&lt;br /&gt;
|                        |                  | with a MIN_REORDER_NS  |&lt;br /&gt;
|                        |                  | = 250 us floor against |&lt;br /&gt;
|                        |                  | srtt collapse; matches |&lt;br /&gt;
|                        |                  | RFC 8985 sec. 6.2 and  |&lt;br /&gt;
|                        |                  | Linux tcp_rack_reo_wnd.|&lt;br /&gt;
|                        |                  | DSACK-driven           |&lt;br /&gt;
|                        |                  | reo_wnd_mult (sec. 6.2 |&lt;br /&gt;
|                        |                  | step 4) is adopted;    |&lt;br /&gt;
|                        |                  | see Section 1.3 for    |&lt;br /&gt;
|                        |                  | the wire encoding.     |&lt;br /&gt;
|                        |                  | The hybrid RACK-or-    |&lt;br /&gt;
|                        |                  | DUP_THRESH trigger     |&lt;br /&gt;
|                        |                  | from RFC 8985 sec. 6.2 |&lt;br /&gt;
|                        |                  | step 4 is adopted      |&lt;br /&gt;
|                        |                  | (Section 8).  QUIC&#039;s   |&lt;br /&gt;
|                        |                  | analogue in RFC 9002   |&lt;br /&gt;
|                        |                  | sec. 6.1.2 uses        |&lt;br /&gt;
|                        |                  | max(srtt, latest_rtt)  |&lt;br /&gt;
|                        |                  | as the base.           |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Karn&#039;s algorithm:      | TCP              | Karn &amp;amp; Partridge,      |&lt;br /&gt;
| no RTT sample on       |                  | &amp;quot;Improving Round-Trip  |&lt;br /&gt;
| retransmits, RTO-      |                  | Time Estimates in      |&lt;br /&gt;
| collapse freeze        |                  | Reliable Transport     |&lt;br /&gt;
|                        |                  | Protocols&amp;quot;, SIGCOMM    |&lt;br /&gt;
|                        |                  | 1987; RFC 6298 sec. 3. |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| RTO formula            | TCP              | RFC 6298 (Paxson et    |&lt;br /&gt;
| RTO = max(RTO_MIN,     |                  | al., 2011).  RTO_MIN = |&lt;br /&gt;
| srtt + (mdev &amp;lt;&amp;lt;        |                  | 5 ms is below RFC 6298 |&lt;br /&gt;
| MDEV_MUL))             |                  | sec. 2.4&#039;s 1 s SHOULD- |&lt;br /&gt;
|                        |                  | floor - a recursive-   |&lt;br /&gt;
|                        |                  | layer choice.          |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Linux asymmetric mdev  | Linux kernel     | tcp_rtt_estimator() in |&lt;br /&gt;
| estimator (default)    |                  | net/ipv4/tcp_input.c;  |&lt;br /&gt;
|                        |                  | the if(delta&amp;lt;0) m&amp;gt;&amp;gt;=3  |&lt;br /&gt;
|                        |                  | dampening is a         |&lt;br /&gt;
|                        |                  | kernel divergence from |&lt;br /&gt;
|                        |                  | RFC 6298.  RFC 6298    |&lt;br /&gt;
|                        |                  | EWMA available behind  |&lt;br /&gt;
|                        |                  | a compile flag.        |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Delayed ACK with rate  | TCP              | RFC 813 (Clark, 1982); |&lt;br /&gt;
| suppression            |                  | RFC 1122 sec. 4.2.3.2; |&lt;br /&gt;
|                        |                  | RFC 5681 sec. 4.2.     |&lt;br /&gt;
|                        |                  | Single-deadline        |&lt;br /&gt;
|                        |                  | coalescing rather than |&lt;br /&gt;
|                        |                  | &amp;quot;ack-every-other-      |&lt;br /&gt;
|                        |                  | segment&amp;quot;.              |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Zero-window-probe /    | TCP              | RFC 1122 sec.          |&lt;br /&gt;
| persist-timer          |                  | 4.2.2.17 / RFC 9293    |&lt;br /&gt;
| analogue (RDVS)        |                  | sec. 3.8.6.1.  RDVS    |&lt;br /&gt;
|                        |                  | solicits an FC reply,  |&lt;br /&gt;
|                        |                  | distinct from QUIC     |&lt;br /&gt;
|                        |                  | DATA_BLOCKED (RFC 9000 |&lt;br /&gt;
|                        |                  | sec. 19.12), which is  |&lt;br /&gt;
|                        |                  | one-way notification.  |&lt;br /&gt;
|                        |                  | MAX_RDV give-up        |&lt;br /&gt;
|                        |                  | departs from TCP.      |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Multiplexed control    | SCTP / QUIC      | SCTP chunk bundling    |&lt;br /&gt;
| on a single PCI        |                  | (RFC 9260 sec. 6.10);  |&lt;br /&gt;
|                        |                  | QUIC frame             |&lt;br /&gt;
|                        |                  | multiplexing (RFC 9000 |&lt;br /&gt;
|                        |                  | sec. 12.4).  Cleaner   |&lt;br /&gt;
|                        |                  | fit than TCP&#039;s         |&lt;br /&gt;
|                        |                  | separate-flag-bits     |&lt;br /&gt;
|                        |                  | design.                |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| ACK ranges as          | QUIC             | QUIC ACK frame (RFC    |&lt;br /&gt;
| multiple discontiguous |                  | 9000 sec. 19.3).  FRCP |&lt;br /&gt;
| acked blocks           |                  | SACK is conceptually   |&lt;br /&gt;
|                        |                  | QUIC-frame-shaped      |&lt;br /&gt;
|                        |                  | even though encoded    |&lt;br /&gt;
|                        |                  | as absolute            |&lt;br /&gt;
|                        |                  | [start,end] pairs.     |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Nonce-authenticated    | QUIC             | PATH_CHALLENGE /       |&lt;br /&gt;
| active RTT / liveness  | PATH_CHALLENGE   | PATH_RESPONSE (RFC     |&lt;br /&gt;
| probing (RTTP)         |                  | 9000 sec. 8.2,         |&lt;br /&gt;
|                        |                  | sec. 19.17, sec.       |&lt;br /&gt;
|                        |                  | 19.18).  WebRTC ICE    |&lt;br /&gt;
|                        |                  | consent-freshness      |&lt;br /&gt;
|                        |                  | (RFC 7675) is the      |&lt;br /&gt;
|                        |                  | same pattern.  QUIC&#039;s  |&lt;br /&gt;
|                        |                  | nonce is 8 octets;     |&lt;br /&gt;
|                        |                  | FRCP chooses 16.       |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Probing distinct from  | QUIC             | KA timer answers       |&lt;br /&gt;
| keepalive              |                  | &amp;quot;peer alive?&amp;quot;, RTTP    |&lt;br /&gt;
|                        |                  | answers &amp;quot;path          |&lt;br /&gt;
|                        |                  | measurable?&amp;quot;, as in    |&lt;br /&gt;
|                        |                  | QUIC PING (RFC 9000    |&lt;br /&gt;
|                        |                  | sec. 19.2) vs          |&lt;br /&gt;
|                        |                  | PATH_CHALLENGE.        |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Bare KA + ACK          | QUIC / SCTP      | QUIC PING (RFC 9000    |&lt;br /&gt;
| keepalive packets      |                  | sec. 19.2); SCTP       |&lt;br /&gt;
|                        |                  | HEARTBEAT /            |&lt;br /&gt;
|                        |                  | HEARTBEAT-ACK (RFC     |&lt;br /&gt;
|                        |                  | 9260 sec. 8.3).  SCTP  |&lt;br /&gt;
|                        |                  | HEARTBEAT also carries |&lt;br /&gt;
|                        |                  | an opaque echoed blob, |&lt;br /&gt;
|                        |                  | structurally similar   |&lt;br /&gt;
|                        |                  | to FRCP RTTP.          |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| (FFGM, LFGM)           | SCTP             | RFC 9260 sec. 3.3.1    |&lt;br /&gt;
| fragment-role bits     |                  | DATA chunk B/E bits    |&lt;br /&gt;
| (Section 7.2)          |                  | encode the same four   |&lt;br /&gt;
|                        |                  | states (B+E=SOLE,      |&lt;br /&gt;
|                        |                  | B-only=FIRST, neither  |&lt;br /&gt;
|                        |                  | =MID, E-only=LAST).    |&lt;br /&gt;
|                        |                  | Each fragment carries  |&lt;br /&gt;
|                        |                  | its own seqno/TSN and  |&lt;br /&gt;
|                        |                  | is independently       |&lt;br /&gt;
|                        |                  | retransmitted.         |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Stream byte-offset     | QUIC             | QUIC STREAM frame      |&lt;br /&gt;
| reassembly             |                  | (RFC 9000 sec. 19.8)   |&lt;br /&gt;
| (Sections 1.5, 16)     |                  | uses Offset + Length   |&lt;br /&gt;
|                        |                  | varints; FRCP uses     |&lt;br /&gt;
|                        |                  | fixed 32-bit start /   |&lt;br /&gt;
|                        |                  | end.  One stream per   |&lt;br /&gt;
|                        |                  | flow vs QUIC&#039;s many    |&lt;br /&gt;
|                        |                  | streams multiplexed.   |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| FIN end-of-stream      | TCP / QUIC       | TCP FIN flag (RFC 9293 |&lt;br /&gt;
| marker                 |                  | sec. 3.1) closes one   |&lt;br /&gt;
| (Sections 1.2, 16)     |                  | half of the byte       |&lt;br /&gt;
|                        |                  | stream; QUIC STREAM    |&lt;br /&gt;
|                        |                  | frame FIN bit (RFC     |&lt;br /&gt;
|                        |                  | 9000 sec. 19.8) does   |&lt;br /&gt;
|                        |                  | the same per stream    |&lt;br /&gt;
|                        |                  | with an immutable      |&lt;br /&gt;
|                        |                  | final-size invariance  |&lt;br /&gt;
|                        |                  | (RFC 9000 sec. 4.5:    |&lt;br /&gt;
|                        |                  | the final size is      |&lt;br /&gt;
|                        |                  | fixed once observed).  |&lt;br /&gt;
|                        |                  | FRCP&#039;s FIN consumes    |&lt;br /&gt;
|                        |                  | one packet seqno (not  |&lt;br /&gt;
|                        |                  | one byte of stream     |&lt;br /&gt;
|                        |                  | space) and is          |&lt;br /&gt;
|                        |                  | idempotent on the      |&lt;br /&gt;
|                        |                  | sender side.           |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Stream byte-credit     | QUIC             | MAX_STREAM_DATA (RFC   |&lt;br /&gt;
| flow control           |                  | 9000 sec. 4.1, sec.    |&lt;br /&gt;
| (Section 16)           |                  | 19.10).  FRCP projects |&lt;br /&gt;
|                        |                  | a per-flow byte budget |&lt;br /&gt;
|                        |                  | onto the seqno-space   |&lt;br /&gt;
|                        |                  | rwe.  Single stream    |&lt;br /&gt;
|                        |                  | per flow collapses     |&lt;br /&gt;
|                        |                  | QUIC&#039;s MAX_DATA /      |&lt;br /&gt;
|                        |                  | MAX_STREAM_            |&lt;br /&gt;
|                        |                  | DATA distinction.      |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Header protection      | QUIC             | QUIC RFC 9001 sec. 5.4 |&lt;br /&gt;
| (encrypted seqnos)     |                  | applies header         |&lt;br /&gt;
|                        |                  | protection on top of   |&lt;br /&gt;
|                        |                  | AEAD to mask the       |&lt;br /&gt;
|                        |                  | packet number.  FRCP&#039;s |&lt;br /&gt;
|                        |                  | per-flow AEAD wrap     |&lt;br /&gt;
|                        |                  | (Section 16) is wider: |&lt;br /&gt;
|                        |                  | it encrypts the entire |&lt;br /&gt;
|                        |                  | PCI including seqno    |&lt;br /&gt;
|                        |                  | because the IPCP       |&lt;br /&gt;
|                        |                  | below already routes,  |&lt;br /&gt;
|                        |                  | so no destination      |&lt;br /&gt;
|                        |                  | connection-ID needs to |&lt;br /&gt;
|                        |                  | stay in clear (cf.     |&lt;br /&gt;
|                        |                  | RFC 9000 sec. 5.2).    |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Two-bit fragment role  | SCTP             | The (FFGM, LFGM) pair  |&lt;br /&gt;
| polarity               |                  | follows SCTP B/E       |&lt;br /&gt;
|                        |                  | (begin = 1 / end = 1)  |&lt;br /&gt;
|                        |                  | rather than IPv4 MF    |&lt;br /&gt;
|                        |                  | (RFC 791 sec. 3.2),    |&lt;br /&gt;
|                        |                  | which has the inverse  |&lt;br /&gt;
|                        |                  | polarity (MF = 1 means |&lt;br /&gt;
|                        |                  | NOT last).             |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Orthogonal reliability | SCTP             | PR-SCTP (RFC 3758,     |&lt;br /&gt;
| / ordering axes        |                  | per-message partial    |&lt;br /&gt;
| (Section 2.2)          |                  | reliability) and SCTP  |&lt;br /&gt;
|                        |                  | DATA U-bit (RFC 9260   |&lt;br /&gt;
|                        |                  | sec. 3.3.1, per-       |&lt;br /&gt;
|                        |                  | message unordered)     |&lt;br /&gt;
|                        |                  | are the closest        |&lt;br /&gt;
|                        |                  | precedents for         |&lt;br /&gt;
|                        |                  | decoupling reliability |&lt;br /&gt;
|                        |                  | from ordering; FRCP    |&lt;br /&gt;
|                        |                  | sets them per-flow     |&lt;br /&gt;
|                        |                  | rather than per-       |&lt;br /&gt;
|                        |                  | message.               |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Orthogonal CRC         | UDP-Lite         | RFC 3828 (Larzon et    |&lt;br /&gt;
| (qs.ber == 0)          |                  | al., 2004) lets the    |&lt;br /&gt;
|                        |                  | sender pick a per-     |&lt;br /&gt;
|                        |                  | packet Checksum        |&lt;br /&gt;
|                        |                  | Coverage and the       |&lt;br /&gt;
|                        |                  | receiver enforce a     |&lt;br /&gt;
|                        |                  | locally configured     |&lt;br /&gt;
|                        |                  | minimum (no in-band    |&lt;br /&gt;
|                        |                  | negotiation; sec. 3.1, |&lt;br /&gt;
|                        |                  | sec. 3.3).  FRCP       |&lt;br /&gt;
|                        |                  | gates a full CRC       |&lt;br /&gt;
|                        |                  | trailer on qs.ber == 0 |&lt;br /&gt;
|                        |                  | at flow setup.         |&lt;br /&gt;
|                        |                  | Contrast TCP / SCTP    |&lt;br /&gt;
|                        |                  | (mandatory checksum)   |&lt;br /&gt;
|                        |                  | and QUIC (AEAD         |&lt;br /&gt;
|                        |                  | subsumes CRC).         |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Setup-time service     | DCCP / SCTP /    | DCCP Service Codes     |&lt;br /&gt;
| negotiation            | QUIC             | (RFC 4340 sec. 8.1.2,  |&lt;br /&gt;
|                        |                  | RFC 5595); SCTP INIT   |&lt;br /&gt;
|                        |                  | parameters (RFC 9260   |&lt;br /&gt;
|                        |                  | sec. 3.3.2); QUIC      |&lt;br /&gt;
|                        |                  | transport parameters   |&lt;br /&gt;
|                        |                  | (RFC 9000 sec. 7.4).   |&lt;br /&gt;
|                        |                  | All negotiate service  |&lt;br /&gt;
|                        |                  | properties at          |&lt;br /&gt;
|                        |                  | connection setup; only |&lt;br /&gt;
|                        |                  | RINA&#039;s QoS cube        |&lt;br /&gt;
|                        |                  | exposes them as an     |&lt;br /&gt;
|                        |                  | orthogonal vector.     |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.1. Original to FRCP (no clean prior art) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - Pre-DRF NACK (Section 9): receiver-driven nudge exploiting&lt;br /&gt;
    snd_cr.inact &amp;gt; rcv_cr.inact.  Closest analogues are SCTP Gap Ack&lt;br /&gt;
    Blocks (RFC 9260 sec. 3.3.4) and DCCP Ack Vector (RFC 4340&lt;br /&gt;
    sec. 11.4) - both let the receiver describe gaps to the sender,&lt;br /&gt;
    but neither targets the cross-epoch / pre-DRF case.&lt;br /&gt;
  - MAX_RDV window-probe give-up: neither TCP (persist-timer&lt;br /&gt;
    probes until application or R2 abort, RFC 9293 sec. 3.8.6.1)&lt;br /&gt;
    nor QUIC has an explicit FC-give-up counter.  A recursive-&lt;br /&gt;
    network choice: outer layers can drop the flow.&lt;br /&gt;
  - Skip-past-gap reassembly (Section 7.2): SCTP fragments and&lt;br /&gt;
    reassembles every flow regardless of reliability/ordering,&lt;br /&gt;
    using its own per-stream reassembly queue; QUIC fragments via&lt;br /&gt;
    STREAM offsets.  FRCP fragments best-effort flows too, but&lt;br /&gt;
    the receiver drops the broken prefix the moment a later run-&lt;br /&gt;
    start (FIRST or SOLE role) is visible inside the RQ_SIZE-wide&lt;br /&gt;
    reorder ring - no IP-frag-style timeout, no SCTP-style&lt;br /&gt;
    explicit abort.  If no later run-start arrives within the&lt;br /&gt;
    ring, frag_run_inspect returns NOT_READY and the partial run&lt;br /&gt;
    keeps its slots; the next inspect retries.  The trade-off: a&lt;br /&gt;
    permanently-lost MID in a long isolated run holds slots until&lt;br /&gt;
    either a later FIRST/SOLE appears in the ring or the writer&lt;br /&gt;
    stops, at which point the slots are reclaimed on flow&lt;br /&gt;
    teardown.&lt;br /&gt;
  - Reassembly deferred to consume time (Section 7.2), message&lt;br /&gt;
    mode only (qos.service == SVC_MESSAGE): SCTP (RFC 9260&lt;br /&gt;
    sec. 6.9), QUIC (RFC 9000 sec. 2.2), and TCP (RFC 9293) all&lt;br /&gt;
    hold reassembly state at the receive boundary.  FRCP message-&lt;br /&gt;
    mode leaves fragments in the shared-memory ring until&lt;br /&gt;
    flow_read pulls and lands the SDU directly in the caller&#039;s&lt;br /&gt;
    buffer.  Stream mode (Section 16) uses the standard QUIC-&lt;br /&gt;
    style direct ring placement on receive and does not defer.&lt;br /&gt;
    The optimisation is enabled by the Shared-Memory Subsystem&lt;br /&gt;
    (SSM) packet-buffer ring (see struct ssm_pk_buff at&lt;br /&gt;
    Section 1.1); the analogue is OS-level scatter-gather I/O&lt;br /&gt;
    (recvmsg+iovec), not a transport-layer prior art.&lt;br /&gt;
  - TLP-equivalent tail-loss recovery (RFC 8985 sec. 7;&lt;br /&gt;
    RFC 9002 sec. 6.2): FRCP does not emit an explicit Tail Loss&lt;br /&gt;
    Probe packet, but the same goal is met implicitly by RACK&lt;br /&gt;
    loss detection (Section 8) firing on a non-advancing&lt;br /&gt;
    cumulative ACK once the head-of-line slot ages past the RACK&lt;br /&gt;
    reorder window R = MIN(reo_wnd_mult * min_RTT / 4, SRTT) -&lt;br /&gt;
    well below RTO = max(2 * SRTT, SRTT + (mdev &amp;lt;&amp;lt; MDEV_MUL)).&lt;br /&gt;
    A receiver-driven nudge is also available via the pre-DRF&lt;br /&gt;
    NACK (Section 9).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.2. Not adopted ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - Slow start, congestion window (cwnd), Additive Increase /&lt;br /&gt;
    Multiplicative Decrease (AIMD), NewReno cwnd inflation.&lt;br /&gt;
    Congestion control lives in the IPCP CA policies and is&lt;br /&gt;
    driven by Explicit Congestion Notification (ECN, RFC 3168).&lt;br /&gt;
  - Nagle / silly-window-syndrome (SWS) avoidance (RFC 896, RFC&lt;br /&gt;
    1122 sec. 4.2.3.4).  (Deferred work, not adopted in the&lt;br /&gt;
    current spec.)&lt;br /&gt;
  - TCP Timestamps (RFC 7323) / Protection Against Wrapped&lt;br /&gt;
    Sequences (PAWS) - RTT measurement uses RTTP,&lt;br /&gt;
    not per-segment timestamps.  A peer-supplied timestamp echoed&lt;br /&gt;
    on every ACK lets a malicious peer drive the srtt estimate&lt;br /&gt;
    arbitrarily low, collapsing the RTO and triggering a self-&lt;br /&gt;
    inflicted retransmit storm.  RTTP confines RTT measurement to&lt;br /&gt;
    nonce-authenticated probe round-trips, where a forged echo is&lt;br /&gt;
    rejected before it can reach the estimator.&lt;br /&gt;
  - ECN (Explicit Congestion Notification) response inside FRCP&lt;br /&gt;
    (consumed by IPCP Congestion Avoidance / CA).&lt;br /&gt;
  - IP-style fragment-offset reassembly (RFC 791 sec. 3.2; RFC 8200&lt;br /&gt;
    sec. 4.5).  Message-mode FRCP relies on the FRCT rq[] reorder&lt;br /&gt;
    ring keyed by seqno (shared by FRTX and best-effort flows) to&lt;br /&gt;
    put fragments back in order; no separate offset field is&lt;br /&gt;
    needed and no IP-style hole-list reassembly buffer is kept.&lt;br /&gt;
    Stream-mode FRCP does carry [start, end) byte offsets&lt;br /&gt;
    (Section 1.5) for direct ring placement on receive.&lt;br /&gt;
  - QUIC STREAM offset+length framing on *every* flow (RFC 9000&lt;br /&gt;
    sec. 19.8).  Message-mode FRCP uses the SCTP-style B/E flag-&lt;br /&gt;
    bit encoding (FFGM/LFGM) and skips the offsets; stream-mode&lt;br /&gt;
    FRCP adopts the QUIC offset model (heritage table above).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 16. Stream-mode flows ==&lt;br /&gt;
&lt;br /&gt;
When a flow is allocated with qos.service == SVC_STREAM both peers&lt;br /&gt;
switch to byte-stream semantics, layered on top of the FRTX reorder&lt;br /&gt;
machinery already described in Sections 6-8.&lt;br /&gt;
&lt;br /&gt;
=== 16.1. Send ===&lt;br /&gt;
&lt;br /&gt;
The sender splits the caller&#039;s octets into chunks of at most&lt;br /&gt;
(frag_mtu - base PCI - stream PCI extension) octets (Sections 1.1&lt;br /&gt;
and 1.5).  Each chunk is one DATA packet with its own seqno and a&lt;br /&gt;
[start, end) byte range copied from a monotonic stream counter.&lt;br /&gt;
In stream mode FFGM and LFGM are unused and MUST be transmitted as&lt;br /&gt;
zero; the per-byte position is carried by the [start, end)&lt;br /&gt;
extension instead.&lt;br /&gt;
&lt;br /&gt;
End-of-stream is signalled with a 0-byte DATA packet that has FIN&lt;br /&gt;
(bit 12) set, emitted on the FIN triggers listed in Section 1.2&lt;br /&gt;
(WR-half close, flow_dealloc, and any other path that yields the&lt;br /&gt;
final byte).  The sender MUST emit at most one FIN per flow; its&lt;br /&gt;
[start, end) MUST equal [final-byte, final-byte) (i.e., empty&lt;br /&gt;
interval at the final byte position; final-size invariance,&lt;br /&gt;
analogous to QUIC RFC 9000 sec. 4.5).  Idempotency is enforced by&lt;br /&gt;
an snd_fin_sent guard.&lt;br /&gt;
&lt;br /&gt;
=== 16.2. Receive ===&lt;br /&gt;
&lt;br /&gt;
On arrival the receiver places the payload directly into a per-flow&lt;br /&gt;
byte-indexed receive ring of width ring_sz (octets) at the position&lt;br /&gt;
indicated by start, with a two-segment memcpy across the ring&lt;br /&gt;
boundary if needed.  Receipt is recorded in the FRTX reorder&lt;br /&gt;
machinery (Section 6.2) augmented with the packet&#039;s start, end, and&lt;br /&gt;
FIN bit per slot.  When a packet&#039;s [start, end) front-overlaps&lt;br /&gt;
bytes already at or below the byte high-water mark, the overlap is&lt;br /&gt;
trimmed before placement so the same byte is never written twice.&lt;br /&gt;
After stashing, the receiver advances lwe and the byte high-water&lt;br /&gt;
mark across any newly-contiguous prefix.  Each slot advanced MUST&lt;br /&gt;
satisfy `start == the last-delivered slot&#039;s end`; a slot whose&lt;br /&gt;
start does not equal that end is silently dropped at delivery time&lt;br /&gt;
(the seqno is consumed, no stream bytes contributed) and the high-&lt;br /&gt;
water mark does not advance past it.  The stream byte-stream&lt;br /&gt;
stalls at that point - there is no flow-tear-down on mismatch.&lt;br /&gt;
This filters spliced or off-path-injected slots that fall in&lt;br /&gt;
window without strong cryptographic authentication.&lt;br /&gt;
&lt;br /&gt;
A FIN slot marks end-of-stream at advance time only if its byte&lt;br /&gt;
position equals the last-delivered slot&#039;s end; otherwise the FIN&lt;br /&gt;
is ignored and the corresponding seqno occupies a slot but&lt;br /&gt;
contributes no stream bytes.  No packet buffer is held after the&lt;br /&gt;
ring copy.&lt;br /&gt;
&lt;br /&gt;
=== 16.3. Read ===&lt;br /&gt;
&lt;br /&gt;
flow_read returns up to count octets from the contiguous prefix&lt;br /&gt;
[next, high-water), where next is the byte the application has&lt;br /&gt;
already consumed up to and high-water is the rightmost contiguous&lt;br /&gt;
byte received.  When the stream is fully drained AND end-of-stream&lt;br /&gt;
(EOS) was observed (next == EOS byte position), flow_read returns&lt;br /&gt;
0 (EOF) - the same shape POSIX read(2) uses on TCP after a peer&lt;br /&gt;
FIN.&lt;br /&gt;
&lt;br /&gt;
=== 16.4. Flow control ===&lt;br /&gt;
&lt;br /&gt;
ACK / SACK / RACK / RTO machinery is unchanged; the FRTX reorder&lt;br /&gt;
ring is reused as a per-seqno received-bitmap.  Let per_pkt =&lt;br /&gt;
(frag_mtu - base PCI - stream PCI extension), the maximum stream-&lt;br /&gt;
byte payload one DATA packet can carry (Section 16.1).  The&lt;br /&gt;
receive window advertised in FC is clamped so the byte window&lt;br /&gt;
(ring_sz) cannot be overrun: the seqno-space rwe is at most&lt;br /&gt;
`rcv_cr.lwe + ring_sz / per_pkt`.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC byte-credit flow-control model&lt;br /&gt;
(MAX_STREAM_DATA, RFC 9000 sec. 4.1 and sec. 19.10) projected onto&lt;br /&gt;
seqno space.  With one stream per flow there is no MAX_DATA /&lt;br /&gt;
MAX_STREAM_DATA distinction.  Receiver-side silly-window-syndrome&lt;br /&gt;
(SWS) avoidance (RFC 9293 sec. 3.8.6.2.2) is achieved by combining&lt;br /&gt;
the consume-time rwe bump with the global non-shrink rule from&lt;br /&gt;
Section 11.&lt;br /&gt;
&lt;br /&gt;
=== 16.5. Security considerations ===&lt;br /&gt;
&lt;br /&gt;
Threat model.  An attacker that can observe (on-path passive) or&lt;br /&gt;
predict (off-path blind) the flow&#039;s seqnos and byte offsets on an&lt;br /&gt;
unencrypted stream flow can inject DATA or FIN at any in-window&lt;br /&gt;
position.  The in-line consistency checks above (start == prior&lt;br /&gt;
end on advance; FIN MUST be 0-byte; FIN MUST sit at the final&lt;br /&gt;
byte position) realise the spirit of RFC 5961&#039;s &amp;quot;sequence-window&lt;br /&gt;
plus exact-position match for control bits&amp;quot; without an explicit&lt;br /&gt;
challenge-ACK probe; they make a few specific blind attack shapes&lt;br /&gt;
harder but are not cryptographic authentication. This is&lt;br /&gt;
comparable to TCP without the TCP Authentication Option (TCP-AO,&lt;br /&gt;
RFC 5925), tighter than a&lt;br /&gt;
pre-RFC-5961 TCP stack, and roughly equivalent to a modern&lt;br /&gt;
RFC 5961 stack against blind off-path injection - none of these&lt;br /&gt;
help once the attacker can sniff. TLS over TCP (RFC 8446)&lt;br /&gt;
encrypts only the TCP payload and leaves TCP seqnos, ACKs, FIN,&lt;br /&gt;
and RST in the clear, so TLS does NOT defend against TCP-header-&lt;br /&gt;
level injection; QUIC (RFC 9000) hides packet numbers under&lt;br /&gt;
header protection (RFC 9001 sec. 5.4), so this specific weakness&lt;br /&gt;
does not apply to QUIC.&lt;br /&gt;
&lt;br /&gt;
Mitigation: AEAD.  When the flow has encryption enabled the&lt;br /&gt;
recommended AEAD ciphers (AES-GCM, RFC 5288; or ChaCha20-Poly1305,&lt;br /&gt;
RFC 8439) wrap the entire FRCP packet on the wire - PCI, stream&lt;br /&gt;
extension, body, and the CRC trailer when ber == 0 - under a&lt;br /&gt;
per-flow symmetric key derived from the flow&#039;s own key exchange&lt;br /&gt;
(Section 1.1).  The AEAD tag (~2^-128 forgery probability)&lt;br /&gt;
dominates the CRC (~2^-32) for integrity in this mode but the CRC&lt;br /&gt;
trailer is currently retained inside the wrap (see Section 1.1).&lt;br /&gt;
Implementations MUST NOT rely on the security properties below&lt;br /&gt;
when a non-AEAD cipher (e.g. AES-CTR alone) is negotiated; non-&lt;br /&gt;
AEAD modes provide confidentiality only and the threat-model&lt;br /&gt;
claims do not hold.&lt;br /&gt;
&lt;br /&gt;
With an AEAD cipher in use, seqnos, byte offsets, and the FIN bit&lt;br /&gt;
are both authenticated and confidential. Against an off-path or&lt;br /&gt;
on-path-passive attacker this is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - Stronger than TCP+TLS (TCP header in the clear).&lt;br /&gt;
  - Stronger than TCP+TCP-AO (header authenticated but visible).&lt;br /&gt;
  - Comparable to IPsec ESP transport mode (RFC 4303), which&lt;br /&gt;
    similarly authenticates and encrypts the upper-layer header&lt;br /&gt;
    plus payload, and to QUIC packet protection (RFC 9001 sec. 5),&lt;br /&gt;
    with the difference that QUIC must leave the destination&lt;br /&gt;
    connection ID in the clear for routing whereas FRCP relies on&lt;br /&gt;
    the IPCP below for delivery and can therefore encrypt its&lt;br /&gt;
    entire PCI.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Keying granularity.  FRCP runs key exchange (kex) per flow, so&lt;br /&gt;
each flow_alloc yields independent symmetric keys.  This is&lt;br /&gt;
finer-grained than QUIC (per-connection, RFC 9001, where one&lt;br /&gt;
handshake covers all multiplexed streams) and finer-grained than&lt;br /&gt;
typical IPsec deployment (per-host-pair Security Associations,&lt;br /&gt;
SAs).  Forward secrecy follows from the kex when an ephemeral&lt;br /&gt;
Diffie-Hellman exchange (DHE), or a hybrid mode (classical DH +&lt;br /&gt;
post-quantum Key Encapsulation Mechanism / KEM), is selected.&lt;br /&gt;
&lt;br /&gt;
Replay protection.  The AEAD layer itself does NOT carry an&lt;br /&gt;
explicit anti-replay window (unlike IPsec ESP, RFC 4303 sec.&lt;br /&gt;
3.4.3, or DTLS, RFC 9147 sec. 4.5.1).  For FRCP-engaged flows the&lt;br /&gt;
seqno-space duplicate-suppression in Section 6.2 rejects replayed&lt;br /&gt;
DATA after the AEAD strips the wrap, because the AEAD authenticates&lt;br /&gt;
the seqno and a replay re-presents an old seqno that is then&lt;br /&gt;
discarded either as a duplicate (still inside the receive window)&lt;br /&gt;
or as outside the receive window, depending on how far lwe has&lt;br /&gt;
advanced since the original packet was delivered.  RAW&lt;br /&gt;
(qos.service == SVC_RAW) flows have no FRCP layer and therefore&lt;br /&gt;
no replay protection at the AEAD layer either; deployments that&lt;br /&gt;
need replay rejection on RAW flows MUST provide it at a higher&lt;br /&gt;
layer.&lt;br /&gt;
&lt;br /&gt;
Layering.  The AEAD wrap sits below FRCP on the data path, so&lt;br /&gt;
RAW best-effort flows (qos.service == SVC_RAW, the UDP-equivalent&lt;br /&gt;
service of Section 2.2) inherit the same per-flow integrity +&lt;br /&gt;
confidentiality scope as FRCP-engaged flows - whatever the IPCP&lt;br /&gt;
and FRCP (if any) put on the wire is what the AEAD authenticates.&lt;br /&gt;
No DTLS-equivalent layering is required for confidentiality and&lt;br /&gt;
integrity; replay protection above AEAD is a separate concern as&lt;br /&gt;
noted above.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 17. References ==&lt;br /&gt;
&lt;br /&gt;
This section lists the IETF documents, published works, and&lt;br /&gt;
source-code references cited inline elsewhere in this document.&lt;br /&gt;
IETF documents are cited inline as &amp;quot;RFC NNNN sec. X.Y&amp;quot;; books,&lt;br /&gt;
journal papers, and source-code references are cited inline by&lt;br /&gt;
author and year (or by file and function name) and are listed&lt;br /&gt;
here for convenience.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.1. IETF documents ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 791]   J. Postel, &amp;quot;Internet Protocol&amp;quot;, STD 5, RFC 791,&lt;br /&gt;
              September 1981.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 793]   J. Postel, &amp;quot;Transmission Control Protocol&amp;quot;, STD 7,&lt;br /&gt;
              RFC 793, September 1981.  Obsoleted by RFC 9293.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 813]   D. D. Clark, &amp;quot;Window and Acknowledgement Strategy&lt;br /&gt;
              in TCP&amp;quot;, RFC 813, July 1982.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 896]   J. Nagle, &amp;quot;Congestion Control in IP/TCP&lt;br /&gt;
              Internetworks&amp;quot;, RFC 896, January 1984.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 1122]  R. Braden (ed.), &amp;quot;Requirements for Internet Hosts&lt;br /&gt;
              -- Communication Layers&amp;quot;, STD 3, RFC 1122,&lt;br /&gt;
              October 1989.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 2018]  M. Mathis, J. Mahdavi, S. Floyd, A. Romanow,&lt;br /&gt;
              &amp;quot;TCP Selective Acknowledgment Options&amp;quot;, RFC 2018,&lt;br /&gt;
              October 1996.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 2119]  S. Bradner, &amp;quot;Key words for use in RFCs to Indicate&lt;br /&gt;
              Requirement Levels&amp;quot;, BCP 14, RFC 2119, March 1997.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 2883]  S. Floyd, J. Mahdavi, M. Mathis, M. Podolsky,&lt;br /&gt;
              &amp;quot;An Extension to the Selective Acknowledgement&lt;br /&gt;
              (SACK) Option for TCP&amp;quot;, RFC 2883, July 2000.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 3758]  R. Stewart, M. Ramalho, Q. Xie, M. Tuexen,&lt;br /&gt;
              P. Conrad, &amp;quot;Stream Control Transmission Protocol&lt;br /&gt;
              (SCTP) Partial Reliability Extension&amp;quot;, RFC 3758,&lt;br /&gt;
              May 2004.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 3828]  L-A. Larzon, M. Degermark, S. Pink, L-E. Jonsson&lt;br /&gt;
              (ed.), G. Fairhurst (ed.), &amp;quot;The Lightweight User&lt;br /&gt;
              Datagram Protocol (UDP-Lite)&amp;quot;, RFC 3828,&lt;br /&gt;
              July 2004.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 4303]  S. Kent, &amp;quot;IP Encapsulating Security Payload&lt;br /&gt;
              (ESP)&amp;quot;, RFC 4303, December 2005.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 4340]  E. Kohler, M. Handley, S. Floyd, &amp;quot;Datagram&lt;br /&gt;
              Congestion Control Protocol (DCCP)&amp;quot;, RFC 4340,&lt;br /&gt;
              March 2006.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 5288]  J. Salowey, A. Choudhury, D. McGrew, &amp;quot;AES Galois&lt;br /&gt;
              Counter Mode (GCM) Cipher Suites for TLS&amp;quot;,&lt;br /&gt;
              RFC 5288, August 2008.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 5595]  G. Fairhurst, &amp;quot;The Datagram Congestion Control&lt;br /&gt;
              Protocol (DCCP) Service Codes&amp;quot;, RFC 5595,&lt;br /&gt;
              September 2009.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 5681]  M. Allman, V. Paxson, E. Blanton, &amp;quot;TCP Congestion&lt;br /&gt;
              Control&amp;quot;, RFC 5681, September 2009.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 5925]  J. Touch, A. Mankin, R. Bonica, &amp;quot;The TCP&lt;br /&gt;
              Authentication Option&amp;quot;, RFC 5925, June 2010.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 5961]  A. Ramaiah, R. Stewart, M. Dalal, &amp;quot;Improving&lt;br /&gt;
              TCP&#039;s Robustness to Blind In-Window Attacks&amp;quot;,&lt;br /&gt;
              RFC 5961, August 2010.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 6298]  V. Paxson, M. Allman, J. Chu, M. Sargent,&lt;br /&gt;
              &amp;quot;Computing TCP&#039;s Retransmission Timer&amp;quot;, RFC 6298,&lt;br /&gt;
              June 2011.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 6528]  F. Gont, S. Bellovin, &amp;quot;Defending against Sequence&lt;br /&gt;
              Number Attacks&amp;quot;, RFC 6528, February 2012.&lt;br /&gt;
              Obsoletes RFC 1948.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 6582]  T. Henderson, S. Floyd, A. Gurtov, Y. Nishida,&lt;br /&gt;
              &amp;quot;The NewReno Modification to TCP&#039;s Fast Recovery&lt;br /&gt;
              Algorithm&amp;quot;, RFC 6582, April 2012.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 7323]  D. Borman, B. Braden, V. Jacobson,&lt;br /&gt;
              R. Scheffenegger (ed.), &amp;quot;TCP Extensions for High&lt;br /&gt;
              Performance&amp;quot;, RFC 7323, September 2014.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 7675]  M. Perumal, D. Wing, R. Ravindranath, T. Reddy,&lt;br /&gt;
              M. Thomson, &amp;quot;Session Traversal Utilities for NAT&lt;br /&gt;
              (STUN) Usage for Consent Freshness&amp;quot;, RFC 7675,&lt;br /&gt;
              October 2015.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 8174]  B. Leiba, &amp;quot;Ambiguity of Uppercase vs Lowercase in&lt;br /&gt;
              RFC 2119 Key Words&amp;quot;, BCP 14, RFC 8174, May 2017.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 8200]  S. Deering, R. Hinden, &amp;quot;Internet Protocol,&lt;br /&gt;
              Version 6 (IPv6) Specification&amp;quot;, STD 86, RFC 8200,&lt;br /&gt;
              July 2017.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 8439]  Y. Nir, A. Langley, &amp;quot;ChaCha20 and Poly1305 for IETF&lt;br /&gt;
              Protocols&amp;quot;, RFC 8439, June 2018.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 8446]  E. Rescorla, &amp;quot;The Transport Layer Security (TLS)&lt;br /&gt;
              Protocol Version 1.3&amp;quot;, RFC 8446, August 2018.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 8985]  Y. Cheng, N. Cardwell, N. Dukkipati, P. Jha,&lt;br /&gt;
              &amp;quot;The RACK-TLP Loss Detection Algorithm for TCP&amp;quot;,&lt;br /&gt;
              RFC 8985, February 2021.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 9000]  J. Iyengar (ed.), M. Thomson (ed.), &amp;quot;QUIC: A&lt;br /&gt;
              UDP-Based Multiplexed and Secure Transport&amp;quot;,&lt;br /&gt;
              RFC 9000, May 2021.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 9001]  M. Thomson (ed.), S. Turner (ed.), &amp;quot;Using TLS to&lt;br /&gt;
              Secure QUIC&amp;quot;, RFC 9001, May 2021.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 9002]  J. Iyengar (ed.), I. Swett (ed.), &amp;quot;QUIC Loss&lt;br /&gt;
              Detection and Congestion Control&amp;quot;, RFC 9002,&lt;br /&gt;
              May 2021.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 9147]  E. Rescorla, H. Tschofenig, N. Modadugu,&lt;br /&gt;
              &amp;quot;The Datagram Transport Layer Security (DTLS)&lt;br /&gt;
              Protocol Version 1.3&amp;quot;, RFC 9147, April 2022.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 9260]  R. Stewart, M. Tuexen, K. Nielsen, &amp;quot;Stream Control&lt;br /&gt;
              Transmission Protocol&amp;quot;, RFC 9260, June 2022.&lt;br /&gt;
              Obsoletes RFC 4960.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 9293]  W. Eddy (ed.), &amp;quot;Transmission Control Protocol&lt;br /&gt;
              (TCP)&amp;quot;, STD 7, RFC 9293, August 2022.  Obsoletes&lt;br /&gt;
              RFC 793 and several follow-ons; updates RFC 1122&lt;br /&gt;
              and others.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.2. Books and journal papers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - J. Day, &amp;quot;Patterns in Network Architecture: A Return to&lt;br /&gt;
    Fundamentals&amp;quot;, Prentice Hall, 2008.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - E. Grasa et al., &amp;quot;IRATI: investigating RINA as an alternative&lt;br /&gt;
    to TCP/IP&amp;quot;, Computer Networks, Vol. 92, December 2015.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - P. Karn, C. Partridge, &amp;quot;Improving Round-Trip Time Estimates&lt;br /&gt;
    in Reliable Transport Protocols&amp;quot;, ACM SIGCOMM, August 1987.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - R. W. Watson, &amp;quot;Timer-Based Mechanisms in Reliable Transport&lt;br /&gt;
    Protocol Connection Management&amp;quot;, Computer Networks, Vol. 5,&lt;br /&gt;
    1981.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.3. Source-code references ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - tcp_rtt_estimator() in net/ipv4/tcp_input.c of the Linux&lt;br /&gt;
    kernel, defining the asymmetric mdev variance update used as&lt;br /&gt;
    FRCP&#039;s default RTT estimator (Section 12).  Line-stable&lt;br /&gt;
    browseable copy at&lt;br /&gt;
    https://elixir.bootlin.com/linux/latest/source/net/ipv4/tcp_input.c.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1912</id>
		<title>Flow and Retransmission Control Protocol</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1912"/>
		<updated>2026-05-17T13:51:01Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* Table of Contents */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= FRCP - Flow and Retransmission Control Protocol =&lt;br /&gt;
&lt;br /&gt;
FRCP runs end-to-end between two peers over a flow.  It delivers&lt;br /&gt;
reliability, in-order delivery, flow control, and liveness.&lt;br /&gt;
Congestion Control (CC) is not in FRCP - that lives in the IPC&lt;br /&gt;
Process (IPCP) Congestion Avoidance (CA) policies, orthogonal to&lt;br /&gt;
FRCP.  Flow allocation, naming, and IPCP lifecycle are handled by&lt;br /&gt;
the IPC Resource Manager daemon (IRMd).&lt;br /&gt;
&lt;br /&gt;
FRCT (Flow and Retransmission Control Task) is the libouroboros&lt;br /&gt;
implementation of FRCP; the task lives in src/lib/frct.c.  The&lt;br /&gt;
remainder of this document describes the FRCP wire protocol and the&lt;br /&gt;
behaviour FRCT realises.  Code symbols retain the FRCT_ prefix&lt;br /&gt;
(FRCT_DATA, FRCT_RXM, ...) because they belong to the implementing&lt;br /&gt;
task; this document references them verbatim.&lt;br /&gt;
&lt;br /&gt;
The keywords &amp;quot;MUST&amp;quot;, &amp;quot;MUST NOT&amp;quot;, &amp;quot;REQUIRED&amp;quot;, &amp;quot;SHALL&amp;quot;, &amp;quot;SHALL NOT&amp;quot;,&lt;br /&gt;
&amp;quot;SHOULD&amp;quot;, &amp;quot;SHOULD NOT&amp;quot;, &amp;quot;RECOMMENDED&amp;quot;, &amp;quot;MAY&amp;quot;, and &amp;quot;OPTIONAL&amp;quot; in&lt;br /&gt;
this document are to be interpreted as described in BCP 14 (Best&lt;br /&gt;
Current Practice; RFC 2119, RFC 8174) when, and only when, they&lt;br /&gt;
appear in all capitals.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  u32, u8       Unsigned 32-bit / 8-bit integers (kernel-C style).&lt;br /&gt;
  ns            Nanoseconds.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Modular sequence-number comparators (32-bit, modulo 2^32):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    before(a, b)  ==  (int32_t)(a - b) &amp;lt; 0&lt;br /&gt;
    after(a, b)   ==  before(b, a)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Used throughout for ackno / seqno ordering checks.&lt;br /&gt;
&lt;br /&gt;
Round-Trip Time (RTT) abbreviations used throughout:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    SRTT          Smoothed RTT estimate (RFC 6298).&lt;br /&gt;
    mdev          Mean deviation of RTT (Linux variance estimator).&lt;br /&gt;
    EWMA          Exponentially Weighted Moving Average.&lt;br /&gt;
    RTO           Retransmission Timeout, max(RTO_MIN,&lt;br /&gt;
                  srtt + (mdev &amp;lt;&amp;lt; MDEV_MUL)).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Timer-bound symbols t_a (a-timer, ACK delay) and t_r (r-timer,&lt;br /&gt;
retransmission window) are defined in Section 8; t_mpl (Maximum&lt;br /&gt;
Packet Lifetime) is introduced in Section 2.1 (the inact field)&lt;br /&gt;
with heritage in Section 15.&lt;br /&gt;
&lt;br /&gt;
Wire-format diagrams follow the IETF convention: bit 0 is the&lt;br /&gt;
leftmost (most significant) bit and fields are in network byte&lt;br /&gt;
order unless stated otherwise.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1. Wire format ==&lt;br /&gt;
&lt;br /&gt;
=== 1.1. PCI header ===&lt;br /&gt;
&lt;br /&gt;
Fixed 16-octet base Protocol-Control Information (PCI) header&lt;br /&gt;
prefixed to every FRCP packet (RFC convention: bit 0 leftmost,&lt;br /&gt;
most-significant bit first).  All multi-byte fields except hcs&lt;br /&gt;
are in network byte order; hcs is an opaque 16-bit value that&lt;br /&gt;
the receiver recomputes from the wire bytes and compares to the&lt;br /&gt;
in-place pci-&amp;gt;hcs read, so its on-wire byte order need only&lt;br /&gt;
match between peers running compatible builds.  DATA packets on&lt;br /&gt;
stream-mode flows carry an additional 8-octet extension (see&lt;br /&gt;
Section 1.5); SACK and RTTP carry their own payloads after the&lt;br /&gt;
base PCI.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |             flags             |              hcs              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            window                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            seqno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            ackno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                     payload (variable) ...&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  flags   - feature/type bitmap (see 1.2).&lt;br /&gt;
  hcs     - CRC-16-CCITT-FALSE Header Check Sequence (HCS) over&lt;br /&gt;
            flags + window + seqno + ackno (+ stream extension when&lt;br /&gt;
            present); the two octets of the hcs field itself are&lt;br /&gt;
            omitted from the CRC input.  Verified on receive before&lt;br /&gt;
            any flag-driven dispatch.&lt;br /&gt;
  window  - receiver-advertised right window edge (valid iff FC).&lt;br /&gt;
  seqno   - per-flow sequence number.&lt;br /&gt;
  ackno   - cumulative Acknowledgement (ACK) (valid iff ACK).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A single packet can simultaneously carry DATA + ACK + FC (Flow&lt;br /&gt;
Control) + RXM (Retransmission) by ORing flag bits; the PCI&lt;br /&gt;
multiplexes control on the same wire frame in the spirit of SCTP&lt;br /&gt;
chunk bundling (RFC 9260 sec. 6.10) and QUIC frame multiplexing&lt;br /&gt;
(RFC 9000 sec. 12.4).  DATA-bearing packets carry the caller&#039;s&lt;br /&gt;
payload after the PCI; SACK (Selective Acknowledgement) and RTTP&lt;br /&gt;
(Round-Trip Time Probe) carry their own typed payloads after the&lt;br /&gt;
PCI.&lt;br /&gt;
&lt;br /&gt;
Optional framing (per-flow, see Section 2.2).  On the wire, the&lt;br /&gt;
order from inside out is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    [   PCI + body          ]    -- the FRCP packet&lt;br /&gt;
    [   PCI + body + CRC-32 ]    -- CRC-32 covers the body only (PCI&lt;br /&gt;
                                    is in HCS); appended iff qs.ber&lt;br /&gt;
                                    == 0 on DATA, or on every SACK&lt;br /&gt;
                                    packet&lt;br /&gt;
    [ AEAD-wrap of above    ]    -- iff Authenticated Encryption&lt;br /&gt;
                                    with Associated Data (AEAD) is&lt;br /&gt;
                                    enabled&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - HCS in the PCI covers the header fields on every packet and is&lt;br /&gt;
    verified before any flag-driven dispatch.&lt;br /&gt;
  - The CRC-32 trailer (IEEE 802.3 / zlib reflected polynomial&lt;br /&gt;
    0xEDB88320, init 0xFFFFFFFF, xor-out 0xFFFFFFFF) covers the&lt;br /&gt;
    body on DATA when qs.ber == 0 and on every SACK packet; the&lt;br /&gt;
    trailer is written as a raw uint32_t (the same convention as&lt;br /&gt;
    hcs: opaque on the wire as long as both peers run compatible&lt;br /&gt;
    builds).  The PCI is not under the CRC (Cyclic Redundancy&lt;br /&gt;
    Check) because the HCS already protects it.  It is&lt;br /&gt;
    appended before AEAD encryption and therefore rides inside the&lt;br /&gt;
    AEAD wrap when both are active; the AEAD tag (~2^-128 forgery&lt;br /&gt;
    probability) dominates the CRC (~2^-32) for integrity in that&lt;br /&gt;
    mode but the CRC trailer is currently retained.&lt;br /&gt;
  - When encryption is enabled, the entire (possibly-CRC&#039;d) FRCP&lt;br /&gt;
    packet is wrapped with AEAD inside the shared-memory packet&lt;br /&gt;
    buffer (spb, struct ssm_pk_buff); the packet grows by the AEAD&lt;br /&gt;
    overhead, namely a leading nonce / Initialization Vector (IV)&lt;br /&gt;
    of headsz bytes (crypt_get_ivsz) and a trailing authentication&lt;br /&gt;
    tag of tailsz bytes (crypt_get_tagsz).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Both CRC and AEAD are layered around the FRCP wire format and&lt;br /&gt;
are not visible to the FRCP machinery itself.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.2. Flag bits ===&lt;br /&gt;
&lt;br /&gt;
Flag bits are numbered most-significant-bit first to match the wire&lt;br /&gt;
diagram (bit numbering per Section 1.1; bit 0 is the MSB of the&lt;br /&gt;
16-bit flags field and lands at wire-position 0 in network byte&lt;br /&gt;
order).  Bits 13..15 are reserved and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    +------+--------+--------+----------------------------------------+&lt;br /&gt;
    | Bit  | Mask   | Name   | Meaning                                |&lt;br /&gt;
    +------+--------+--------+----------------------------------------+&lt;br /&gt;
    |   0  | 0x8000 | DATA   | Carries caller payload                 |&lt;br /&gt;
    |   1  | 0x4000 | DRF    | Data Run Flag: start of a fresh run    |&lt;br /&gt;
    |   2  | 0x2000 | ACK    | Acknowledgement: ackno field valid     |&lt;br /&gt;
    |   3  | 0x1000 | NACK   | Negative ACK; seqno = arrival_seqno-1  |&lt;br /&gt;
    |   4  | 0x0800 | FC     | Flow Control: window field valid (rwe) |&lt;br /&gt;
    |   5  | 0x0400 | RDVS   | Rendezvous probe (window-closed)       |&lt;br /&gt;
    |   6  | 0x0200 | FFGM   | First Fragment (role bit 0; see below) |&lt;br /&gt;
    |   7  | 0x0100 | LFGM   | Last Fragment (role bit 1; see below)  |&lt;br /&gt;
    |   8  | 0x0080 | RXM    | Retransmission                         |&lt;br /&gt;
    |   9  | 0x0040 | SACK   | Selective ACK block list in payload    |&lt;br /&gt;
    |  10  | 0x0020 | RTTP   | RTT Probe / echo (payload follows)     |&lt;br /&gt;
    |  11  | 0x0010 | KA     | Keepalive                              |&lt;br /&gt;
    |  12  | 0x0008 | FIN    | End-of-stream marker (stream mode)     |&lt;br /&gt;
    | 13-15|   --   |  --    | Reserved (MUST be zero)                |&lt;br /&gt;
    +------+--------+--------+----------------------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The (FFGM, LFGM) pair encodes the fragment role of a DATA-bearing&lt;br /&gt;
Service Data Unit (SDU), SCTP-style begin/end flags (RFC 9260&lt;br /&gt;
sec. 3.3.1):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    +-----------+-------------------------------------------------+&lt;br /&gt;
    | FFGM LFGM | Role                                            |&lt;br /&gt;
    +-----------+-------------------------------------------------+&lt;br /&gt;
    |   1   1   | Sole / un-fragmented SDU (begin AND end)        |&lt;br /&gt;
    |   1   0   | First fragment of a multi-fragment SDU          |&lt;br /&gt;
    |   0   0   | Middle fragment                                 |&lt;br /&gt;
    |   0   1   | Last fragment                                   |&lt;br /&gt;
    +-----------+-------------------------------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each fragment is carried in its own FRCP packet with its own seqno;&lt;br /&gt;
FRTX (the FRCT Retransmission service mode, see Section 2.2)&lt;br /&gt;
recovers individual fragments via the normal Retransmission Timeout&lt;br /&gt;
(RTO) / SACK / Recent Acknowledgement (RACK, RFC 8985) path.  The&lt;br /&gt;
receiver reassembles the SDU at consume time once the contiguous&lt;br /&gt;
[FIRST .. LAST] run has fully arrived.  On non-DATA packets the role&lt;br /&gt;
bits are unused and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
In stream mode (qos.service == SVC_STREAM, see Section 16) there are&lt;br /&gt;
no SDU boundaries to encode, so FFGM and LFGM are unused and MUST&lt;br /&gt;
be transmitted as zero.  End-of-stream uses a dedicated bit (FIN,&lt;br /&gt;
bit 12) carried on a 0-byte DATA packet, emitted at write-half close&lt;br /&gt;
(fccntl to FLOWFRDONLY), during linger drain, and at flow_dealloc;&lt;br /&gt;
emission is idempotent (first call wins).  After contiguous delivery&lt;br /&gt;
of the FIN-bearing slot, the receiver latches byte_fin at the FIN&#039;s&lt;br /&gt;
start offset; flow_read returns 0 (end-of-file, EOF) once buffered&lt;br /&gt;
bytes have been drained up to byte_fin.  Per-byte position is&lt;br /&gt;
carried by the [start, end) extension (Section 1.5).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.3. SACK payload ===&lt;br /&gt;
&lt;br /&gt;
A SACK packet has the FRCT_ACK | FRCT_FC | FRCT_SACK flag bits set&lt;br /&gt;
(bit numbering per Section 1.1).  Following the 16-octet PCI, the&lt;br /&gt;
payload is a 2-octet block count (network byte order), 2 octets of&lt;br /&gt;
padding to 4-byte align the block list, then n_blocks pairs of&lt;br /&gt;
32-bit start/end seqnos describing *present* (received) ranges&lt;br /&gt;
above the cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |           n_blocks            |        padding (2 octets)     |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[0]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            end[0]                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[1]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
                       ... n_blocks pairs total ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
n_blocks &amp;lt;= SACK_MAX_BLOCKS (2048).  The per-flow effective cap is&lt;br /&gt;
further bounded by (frag_mtu - PCI - 4) / 8 blocks per packet; SACK&lt;br /&gt;
packets carry no stream extension, so PCI here is the 16-octet base&lt;br /&gt;
header even on stream-mode flows.&lt;br /&gt;
&lt;br /&gt;
Wire invariant: every block produced by the receiver, except an&lt;br /&gt;
optional leading Duplicate SACK (D-SACK) block as described below,&lt;br /&gt;
describes a range strictly above the cumulative ACK carried in the&lt;br /&gt;
PCI ackno field (after(start[i], ackno)).  This makes the D-SACK&lt;br /&gt;
convention below unambiguous; the receiver-side builder MUST&lt;br /&gt;
preserve it.&lt;br /&gt;
&lt;br /&gt;
Duplicate SACK (D-SACK, RFC 2883) is signalled in-band: no flag&lt;br /&gt;
bit, no extra framing.  Modular seqno arithmetic uses the&lt;br /&gt;
before() / after() comparators defined in the Notation block.&lt;br /&gt;
Block[0] carries a D-SACK report when either:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  case 1 (RFC 2883 sec. 4.1.1, full duplicate):&lt;br /&gt;
      before(blocks[0].start, ackno) and ackno - blocks[0].start is&lt;br /&gt;
      within MAX_DSACK_LAG (== RQ_SIZE).  A single duplicate seqno&lt;br /&gt;
      observed below the cumulative ACK.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  case 2 (RFC 2883 sec. 4.1.2, partial duplicate):&lt;br /&gt;
      blocks[0] is a sub-range of some blocks[i&amp;gt;0] (not exactly&lt;br /&gt;
      equal).  Reports a duplicate of an in-window seqno that the&lt;br /&gt;
      same packet&#039;s remaining SACK blocks already describe as&lt;br /&gt;
      received.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Senders that do not implement D-SACK process block[0] through the&lt;br /&gt;
normal SACK-mark loop and the existing clamp-and-skip path makes&lt;br /&gt;
case-1 a no-op (start &amp;lt; snd_cr.lwe clamps to snd_cr.lwe, the inner&lt;br /&gt;
loop then skips k == snd_cr.lwe) and case-2 idempotent (same slots&lt;br /&gt;
NULL&#039;d twice).  D-SACK-aware senders feed the report into the RACK&lt;br /&gt;
reo_wnd_mult scaler (RFC 8985 sec. 6.2 step 4): bump on receipt&lt;br /&gt;
(cap 20), halve once per 16 cumulatively-ACK&#039;d seqnos since the&lt;br /&gt;
most recent D-SACK arrival or halve event, reset to 1 on an RTO&lt;br /&gt;
timer fire at the head-of-line.  D-SACK alone never enters&lt;br /&gt;
NewReno-careful recovery (see Section 8); only non-D-SACK blocks&lt;br /&gt;
count as gaps.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.4. RTTP payload ===&lt;br /&gt;
&lt;br /&gt;
An RTTP (Round-Trip Time Probe) packet has only the FRCT_RTTP flag&lt;br /&gt;
set (bit numbering per Section 1.1).  Following the 16-octet PCI,&lt;br /&gt;
the payload is 24 octets (packed):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          probe_id                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          echo_id                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +                  nonce (16 octets, echoed verbatim)           +&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  probe_id - sender counter, 0 on reply, 0 reserved.&lt;br /&gt;
  echo_id  - peer&#039;s probe_id, 0 on outbound probe.&lt;br /&gt;
  nonce    - random, echoed unmodified, memcmp&#039;d to defeat spoof.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.5. Stream PCI extension ===&lt;br /&gt;
&lt;br /&gt;
A stream-mode flow (qos.service == SVC_STREAM) carries an extra&lt;br /&gt;
8-octet extension after the 16-octet base PCI on every DATA packet&lt;br /&gt;
(bit numbering per Section 1.1):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            start                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                             end                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  start - octet offset of the first payload byte in the stream.&lt;br /&gt;
  end   - octet offset one past the last payload byte;&lt;br /&gt;
          end - start equals the on-wire payload length.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total stream-mode PCI for DATA packets is 24 octets (16 base + 8&lt;br /&gt;
extension); control packets (SACK, RTTP, bare ACK, KA, etc.) retain&lt;br /&gt;
the 16-octet base PCI.  Stream mode MUST be negotiated at flow&lt;br /&gt;
allocation; the extension is present iff stream mode is in use,&lt;br /&gt;
never on a per-packet basis.  Both peers MUST treat start/end as&lt;br /&gt;
monotonic 32-bit byte offsets; when a slot reaches the head of the&lt;br /&gt;
contiguous run with start not equal to the prior packet&#039;s end the&lt;br /&gt;
slot is silently dropped at delivery time (Section 16) rather&lt;br /&gt;
than rejected at stash.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC STREAM-frame reassembly model (RFC 9000 sec. 19.8):&lt;br /&gt;
each packet carries its packet seqno (this PCI&#039;s seqno field) and a&lt;br /&gt;
separate stream byte position (start/end).  Separating the two&lt;br /&gt;
avoids TCP&#039;s conflation of packet identity with byte position which&lt;br /&gt;
forces Karn&#039;s algorithm for Round-Trip Time (RTT) sampling (no RTT&lt;br /&gt;
sample on retransmits, RFC 6298 sec. 3); FRCP applies the&lt;br /&gt;
Karn-equivalent gate via a combination of per-packet FRCT_RXM,&lt;br /&gt;
per-slot SND_RTX flags, and a sample-fence rtt_lwe (see Section 2.1&lt;br /&gt;
and Section 12).  FRCP&#039;s fixed-32-bit start/end wrap at 4 GiB of&lt;br /&gt;
wire bytes, narrower than QUIC&#039;s 62-bit varint offset (cf. RFC 9000&lt;br /&gt;
sec. 16); the on-wire wrap is handled by the same modular before()&lt;br /&gt;
/ after() comparators (Section 1.3) FRCP uses for seqnos, which&lt;br /&gt;
remain unambiguous as long as the in-flight byte window stays&lt;br /&gt;
strictly under 2 GiB (the half-range of the signed-int32 difference&lt;br /&gt;
in before()).  The default per-flow ring is 1 MiB; the&lt;br /&gt;
implementation caps ring_sz at 128 MiB (FRCT_STREAM_RING_SZ_MAX),&lt;br /&gt;
well below the 2 GiB half-range bound.  The runtime byte counters&lt;br /&gt;
exposed via FUSE (Filesystem in Userspace) in the Ouroboros&lt;br /&gt;
Resource Information Base (RIB, a virtual-filesystem introspection&lt;br /&gt;
bridge) are platform size_t and do not wrap on 64-bit hosts.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2. Per-flow state and service modes ==&lt;br /&gt;
&lt;br /&gt;
=== 2.1. Per-flow state ===&lt;br /&gt;
&lt;br /&gt;
Each flow keeps a sender control record and a receiver control&lt;br /&gt;
record:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    lwe    : u32  snd: oldest unacked seqno (cumulative ACK&lt;br /&gt;
                  boundary as seen by sender);&lt;br /&gt;
                  rcv: next in-order seqno expected&lt;br /&gt;
    rwe    : u32  snd: peer-advertised right window edge;&lt;br /&gt;
                  rcv: locally-advertised right window edge&lt;br /&gt;
    cflags : u8   per-direction feature flags: retransmission&lt;br /&gt;
                  (FRCTFRTX), receiver flow control&lt;br /&gt;
                  (FRCTFRESCNTL), linger-on-close (FRCTFLINGER);&lt;br /&gt;
                  see &amp;lt;ouroboros/fccntl.h&amp;gt;&lt;br /&gt;
    seqno  : u32  snd: next seqno to send;&lt;br /&gt;
                  rcv: force-ACK trigger - set on a stale or dup&lt;br /&gt;
                  DATA so the next ack_snd emits a fresh&lt;br /&gt;
                  cumulative ACK&lt;br /&gt;
    ackno  : u32  snd: outbound ACK-packet seqno counter,&lt;br /&gt;
                  incremented for every ACK-bearing packet (bare&lt;br /&gt;
                  ACK, delayed ACK, SACK); used by wire-dup ACK&lt;br /&gt;
                  detection;&lt;br /&gt;
                  rcv: incoming-ACK dedup tracker&lt;br /&gt;
    act    : ns   last activity (used by inactivity / DRF)&lt;br /&gt;
    inact  : ns   inactivity threshold; sender = 3*mpl + a + r + 1s,&lt;br /&gt;
                  receiver = 2*mpl + a + r + 1s.  mpl is the&lt;br /&gt;
                  Maximum Packet Lifetime (delta-t terminology;&lt;br /&gt;
                  see Section 15); a and r are the FRCT a-timer&lt;br /&gt;
                  and r-timer bounds (see Section 8).  The&lt;br /&gt;
                  asymmetry is load-bearing for pre-DRF NACK&lt;br /&gt;
                  (Section 9).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sender holds a per-slot ring snd_slots[RQ_SIZE] keyed by&lt;br /&gt;
(seqno mod RQ_SIZE).  Each slot tracks its retransmit entry (rxm),&lt;br /&gt;
last-send timestamp, and retransmit flag bits: SND_RTX (a&lt;br /&gt;
retransmit is pending or has fired, gates the next RTT sample&lt;br /&gt;
under Karn) and SND_FAST_RXM (one-shot fast-retransmit staged for&lt;br /&gt;
this loss event).&lt;br /&gt;
&lt;br /&gt;
The receiver holds a parallel reorder ring rcv_slots[RQ_SIZE]&lt;br /&gt;
(referred to as rq[] in prose) holding stashed out-of-order&lt;br /&gt;
packet-buffer indexes; both FRTX and best-effort flows share this&lt;br /&gt;
path.  The invariant rwe - lwe &amp;lt;= RQ_SIZE holds: on each consume&lt;br /&gt;
the receiver advances rwe by the consumed count, capping the&lt;br /&gt;
receive window at RQ_SIZE seqno slots.&lt;br /&gt;
&lt;br /&gt;
A separate fence variable rtt_lwe is bumped on every retransmit&lt;br /&gt;
(timer-fire, SACK-driven, fast-rxm, NACK-driven) and on every&lt;br /&gt;
seqno_rotate (Section 4) to mark the seqno range whose RTT samples&lt;br /&gt;
MUST be discarded.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 2.2. Service modes (orthogonal axes) ===&lt;br /&gt;
&lt;br /&gt;
FRCP exposes its wire features as a vector of independent QoS&lt;br /&gt;
axes selected at flow allocation time.  All flows go through the&lt;br /&gt;
same flow_alloc(name, qos, ...) primitive; the qosspec_t passed&lt;br /&gt;
in determines which protocol machinery engages on the wire.  This&lt;br /&gt;
contrasts with the POSIX BSD socket model where TCP and UDP&lt;br /&gt;
require different socket types (SOCK_STREAM / SOCK_DGRAM).&lt;br /&gt;
&lt;br /&gt;
The axes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  service   0 = unordered (no FRCP engagement: raw datagrams,&lt;br /&gt;
              no PCI on the wire, UDP-equivalent at this layer)&lt;br /&gt;
            1 = message-ordered (FRCP engaged; SDU boundaries&lt;br /&gt;
              preserved across fragmentation)&lt;br /&gt;
            2 = stream (byte-oriented, no SDU boundaries; FRTX&lt;br /&gt;
              required)&lt;br /&gt;
  loss      0 = lossless service requested: FRTX retransmit&lt;br /&gt;
              machinery engages (Section 8); MUST be 0 for&lt;br /&gt;
              service=2.  Non-zero = best-effort, FRTX off.&lt;br /&gt;
  ber       Bit Error Rate tolerance.&lt;br /&gt;
            0 = error-free service requested: a CRC trailer is&lt;br /&gt;
              appended after the body of DATA packets and verified&lt;br /&gt;
              on receive (added / checked outside the FRCP PCI;&lt;br /&gt;
              see Section 1.1).  Non-zero = peer accepts errors;&lt;br /&gt;
              trailer omitted.  SACK control packets carry a&lt;br /&gt;
              CRC32 trailer regardless of ber; the ber gate&lt;br /&gt;
              applies to DATA only.&lt;br /&gt;
  timeout   Peer-timeout (ms); 0 disables the keepalive timer.&lt;br /&gt;
              Independent of FRCP engagement.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Encryption is a separate per-flow attribute set at flow setup;&lt;br /&gt;
when enabled it wraps the FRCP packet (PCI + body, plus the CRC&lt;br /&gt;
trailer if any) under AEAD, expanding the spb by headsz + tailsz&lt;br /&gt;
octets (nonce / tag).  The CRC trailer is currently kept inside&lt;br /&gt;
the AEAD wrap (see Section 1.1).&lt;br /&gt;
&lt;br /&gt;
Reachable combinations exported by include/ouroboros/qos.h:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  +-----------------+---------+------+-----+-----------------------+&lt;br /&gt;
  | Cube            | service | loss | ber | Engaged               |&lt;br /&gt;
  +-----------------+---------+------+-----+-----------------------+&lt;br /&gt;
  | qos_raw         |    0    |   1  |   1 | Raw passthrough       |&lt;br /&gt;
  | qos_raw_safe    |    0    |   1  |   0 | Raw + CRC trailer     |&lt;br /&gt;
  | qos_rt          |    1    |   1  |   1 | FRCP, no FRTX, no CRC |&lt;br /&gt;
  | qos_rt_safe     |    1    |   1  |   0 | FRCP, no FRTX, CRC    |&lt;br /&gt;
  | qos_msg         |    1    |   0  |   0 | FRCP + FRTX           |&lt;br /&gt;
  | qos_stream      |    2    |   0  |   0 | FRCP + FRTX, stream   |&lt;br /&gt;
  +-----------------+---------+------+-----+-----------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Forced couplings actually enforced by the public API:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - service == SVC_STREAM (2) requires loss == 0; flow_alloc /&lt;br /&gt;
    flow_accept reject the pair otherwise with -EINVAL.&lt;br /&gt;
  - FRTX requires FRCP engagement (service != SVC_RAW); requesting&lt;br /&gt;
    loss = 0 with service = SVC_RAW is structurally a no-op&lt;br /&gt;
    because no frcti is created.&lt;br /&gt;
  - The QOS_DISABLE_CRC build flag globally forces ber = 1.&lt;br /&gt;
    Note: this flag defaults to ON, so default builds ship with&lt;br /&gt;
    CRC disabled until QOS_DISABLE_CRC is set to OFF.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Caveat: the API does NOT force ber = 0 when service != SVC_RAW.&lt;br /&gt;
qos_rt has service = SVC_MESSAGE with ber = 1, which means the PCI&lt;br /&gt;
itself is not CRC-protected on that cube; the HCS (Section 1.1)&lt;br /&gt;
remains the only integrity check on the header.&lt;br /&gt;
&lt;br /&gt;
The FRCP-no-FRTX regime (service = SVC_MESSAGE, loss &amp;gt; 0) is meaningful&lt;br /&gt;
and live: sequence numbering, in-order delivery, flow-control&lt;br /&gt;
advertisement, KA, DRF rotation, and SDU fragmentation /&lt;br /&gt;
reassembly (Section 7.2) all run.  Lost packets are dropped&lt;br /&gt;
rather than retransmitted; a permanently-lost mid-fragment is&lt;br /&gt;
dropped via skip-past-gap once a later SDU is visible in the&lt;br /&gt;
reorder ring.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. Protocol parameters ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    +--------------------+------------------------+-------------------+&lt;br /&gt;
    | Parameter          | Value                  | Role              |&lt;br /&gt;
    +--------------------+------------------------+-------------------+&lt;br /&gt;
    | RQ_SIZE            | compile-time, power of | Slot ring / rcv   |&lt;br /&gt;
    |                    |  2 (default 128)       | window width      |&lt;br /&gt;
    | START_WINDOW       | compile-time, power of | Initial rwe-lwe   |&lt;br /&gt;
    |                    |  2 (default 128)       | after rotate      |&lt;br /&gt;
    | RTO_MIN            | MAX(250 us build-tun-  | RTO floor; also   |&lt;br /&gt;
    |                    |  able, 1&amp;lt;&amp;lt;RXMQ_RES);   | floored at the    |&lt;br /&gt;
    |                    |  per-flow via fccntl   | retransmit-wheel  |&lt;br /&gt;
    |                    |  (FRCTSRTOMIN).        | resolution        |&lt;br /&gt;
    |                    |  Default ~1 ms with    | (~1 ms by         |&lt;br /&gt;
    |                    |  RXMQ_RES=20.          | default).         |&lt;br /&gt;
    | MAX_RTO_MUL        | 20                     | Backoff shift cap |&lt;br /&gt;
    | RACK window R      | MIN(reo_wnd_mult       | Reorder window;   |&lt;br /&gt;
    |                    |  * min_RTT/4, SRTT)    | per RFC 8985      |&lt;br /&gt;
    |                    |  with MIN_REORDER_NS   | sec. 6.2;         |&lt;br /&gt;
    |                    |  = 250 us floor;       | reo_wnd_mult per  |&lt;br /&gt;
    |                    |  reo_wnd_mult scales   | sec. 6.2 step 4   |&lt;br /&gt;
    |                    |  on D-SACK, cap 20     |                   |&lt;br /&gt;
    | MIN_RTT_WIN_NS     | 300 s (5 min, Linux    | min_RTT windowed  |&lt;br /&gt;
    |                    |  tcp_min_rtt_wlen)     | re-anchor         |&lt;br /&gt;
    | REO_WND_MULT_MAX   | 20 (RFC 8985 sec.      | reo_wnd_mult cap  |&lt;br /&gt;
    |                    |  6.2 step 4)           |                   |&lt;br /&gt;
    | REO_DECAY_PKTS     | 16 (RFC 8985 sec.      | Fresh-ACK&#039;d seq   |&lt;br /&gt;
    |                    |  6.2 step 4 /          | count per halving |&lt;br /&gt;
    |                    |  RACK.reo_wnd_persist) |                   |&lt;br /&gt;
    | MAX_DSACK_LAG      | RQ_SIZE                | D-SACK sanity cap |&lt;br /&gt;
    | RTT_QUARANTINE     | 32 (seqno steps)       | NewReno gate pad  |&lt;br /&gt;
    | SACK rate-limit    | SACK_MIN_GAP_NS        | Min SACK gap      |&lt;br /&gt;
    |                    |  (250 us, fixed)       |                   |&lt;br /&gt;
    | SACK_MAX_BLOCKS    | 2048 (wire cap; per-   | Per-SACK block    |&lt;br /&gt;
    |                    |  flow capped at        | cap               |&lt;br /&gt;
    |                    |  (frag_mtu-PCI-4)/8)   |                   |&lt;br /&gt;
    | SACK_RXM_MAX       | 32                     | Per-pass staged   |&lt;br /&gt;
    |                    |                        | retransmit cap    |&lt;br /&gt;
    | DUP_THRESH         | 3 (RFC 8985 default)   | Hybrid fast-rxm   |&lt;br /&gt;
    |                    |                        | trigger (Sec. 8)  |&lt;br /&gt;
    | MDEV_MUL           | 2 (build-tunable via   | mdev shift in     |&lt;br /&gt;
    |                    |  FRCT_RTO_MDEV_-       | RTO = srtt +      |&lt;br /&gt;
    |                    |  MULTIPLIER)           | (mdev &amp;lt;&amp;lt; MDEV_MUL)|&lt;br /&gt;
    | RTTP nonce         | 16 octets              | Echoed verbatim   |&lt;br /&gt;
    | RTTP_RING          | 8                      | In-flight probes  |&lt;br /&gt;
    | RTT clamp          | 16 * srtt              | Probe-sample      |&lt;br /&gt;
    |                    |                        | upper bound       |&lt;br /&gt;
    |                    |                        | (ACK-derived RTT  |&lt;br /&gt;
    |                    |                        | samples gated by  |&lt;br /&gt;
    |                    |                        | Karn / recovery   |&lt;br /&gt;
    |                    |                        | only)             |&lt;br /&gt;
    | Cold-probe cadence | 100 ms (rx-driven;     | Pre-srtt RTTP     |&lt;br /&gt;
    |                    |  see Section 12)       | rate              |&lt;br /&gt;
    | DELT_RDV           | 100 ms                 | RDVS emit cadence |&lt;br /&gt;
    | MAX_RDV            | 1 s                    | RDVS give-up      |&lt;br /&gt;
    | Delayed-ACK fire   | 2 * TICTIME (TICTIME   | Fired after the   |&lt;br /&gt;
    |                    |  = FRCT tick gran-     | first in-order    |&lt;br /&gt;
    |                    |  ularity, default      | DATA arrival;     |&lt;br /&gt;
    |                    |  5 ms; 2*TICTIME       | tick is build-    |&lt;br /&gt;
    |                    |  = 10 ms by default)   | tunable           |&lt;br /&gt;
    | NACK send cooldown | srtt when an srtt      | Pre-DRF NACK      |&lt;br /&gt;
    |                    |  sample exists, else   | rate-limit        |&lt;br /&gt;
    |                    |  100 ms                |                   |&lt;br /&gt;
    | MAX_SDU            | 1 MiB                  | Max reassembled   |&lt;br /&gt;
    |                    |                        | SDU; configurable |&lt;br /&gt;
    |                    |                        | per flow          |&lt;br /&gt;
    +--------------------+------------------------+-------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The per-flow fragment Maximum Transmission Unit (MTU) is computed&lt;br /&gt;
at flow setup from the lower IPCP&#039;s mtu minus encryption&lt;br /&gt;
headsz / tailsz and CRC trailer; there is no FRCT-level default or&lt;br /&gt;
environment-variable override.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 4. Sequence-number rotation (DRF) ==&lt;br /&gt;
&lt;br /&gt;
The DRF (Data Run Flag) bit on an outbound packet means &amp;quot;this is&lt;br /&gt;
the start of a fresh data run&amp;quot; and is set whenever the sender has&lt;br /&gt;
nothing in flight (snd_cr.seqno == snd_cr.lwe).&lt;br /&gt;
&lt;br /&gt;
Independently of that, if the sender has been idle longer than&lt;br /&gt;
snd_cr.inact AND the pipe is empty (snd_cr.seqno == snd_cr.lwe),&lt;br /&gt;
seqno_rotate() rolls a random new seqno before the send and&lt;br /&gt;
resets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    snd_cr.seqno  = random()&lt;br /&gt;
    snd_cr.lwe    = snd_cr.seqno&lt;br /&gt;
    snd_cr.rwe    = snd_cr.seqno + START_WINDOW&lt;br /&gt;
    rtt_lwe       = snd_cr.seqno&lt;br /&gt;
    in_recovery   = false   (recovery state, see Section 8)&lt;br /&gt;
    recovery_high = snd_cr.seqno&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The receiver, on observing rcv-side inactivity&lt;br /&gt;
(now - rcv_cr.act &amp;gt; rcv_cr.inact), requires a DRF on the next&lt;br /&gt;
DATA packet; otherwise it replies with a rate-limited NACK (see&lt;br /&gt;
below).  Non-DATA control packets pass through without the DRF&lt;br /&gt;
requirement.  On DRF the receiver releases the rq[] slots and&lt;br /&gt;
rebases&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    rcv_cr.lwe   = seqno&lt;br /&gt;
    rcv_cr.rwe   = seqno + RQ_SIZE&lt;br /&gt;
    rcv_cr.seqno = seqno&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the inactive packet has DATA but no DRF, a rate-limited NACK is&lt;br /&gt;
fired back to the sender (cooldown per Section 3); non-DATA stale&lt;br /&gt;
arrivals fall through to normal processing (no NACK, no drop).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 5. Send path ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    1. If the SDU exceeds (frag_mtu - data_hdr_len), the caller&lt;br /&gt;
       (dev.c) fans it out into ceil(count / (frag_mtu -&lt;br /&gt;
       data_hdr_len)) fragments, each emitted via frcti_snd as its&lt;br /&gt;
       own DATA packet with a per-fragment role (Section 7.2);&lt;br /&gt;
       both FRTX and best-effort flows fragment.  Raw flows (no&lt;br /&gt;
       FRCP engagement, qos.service == SVC_RAW) carry no PCI and&lt;br /&gt;
       return -EMSGSIZE for any SDU larger than one packet at the&lt;br /&gt;
       layer below.  An SDU that fits in a single packet is sent&lt;br /&gt;
       as SOLE.  frcti_snd reserves PCI head room; sets DATA, plus&lt;br /&gt;
       DRF when the pipe is empty (snd_cr.seqno == snd_cr.lwe).&lt;br /&gt;
    2. seqno_rotate() if past sender inactivity and the pipe is&lt;br /&gt;
       empty (Section 4).&lt;br /&gt;
    3. Advertise FC (pci.window = frcti_advert_rwe(frcti), i.e.&lt;br /&gt;
       rcv_cr.rwe clamped to rcv_cr.lwe + ring_seq_cap in stream&lt;br /&gt;
       mode) when the receiver side is recent: now - rcv_cr.act&lt;br /&gt;
       &amp;lt; rcv_cr.inact.&lt;br /&gt;
    4. Reliable mode (FRTX): leave snd_cr.lwe where it is; reset&lt;br /&gt;
       the slot at RQ_SLOT(seqno) (snd_slots[p].time = now,&lt;br /&gt;
       snd_slots[p].flags = 0); queue an rxm_entry (saves a packet&lt;br /&gt;
       copy, arms a wheel timer at now + (rto &amp;lt;&amp;lt; rto_mul)).&lt;br /&gt;
       Piggyback ACK (pci.ackno = rcv_cr.lwe) while the a-timer&lt;br /&gt;
       for the most recent received DATA packet has not yet&lt;br /&gt;
       expired (now - rcv_cr.act &amp;lt;= t_a); on piggyback, set&lt;br /&gt;
       rcv_cr.seqno = rcv_cr.lwe so the next delayed-ACK fire is&lt;br /&gt;
       suppressed.  See Section 8 for t_a / t_r semantics.&lt;br /&gt;
    5. Best-effort mode (no FRTX): advance snd_cr.lwe immediately&lt;br /&gt;
       (snd_cr.lwe = snd_cr.lwe + 1, snd_cr.rwe = snd_cr.lwe +&lt;br /&gt;
       RQ_SIZE); no retransmit state.  No send-side RTT probe is&lt;br /&gt;
       armed in this mode (rtt_probe_arm requires an in-flight&lt;br /&gt;
       seqno, which best-effort never has); the rx-driven cold&lt;br /&gt;
       seeder in frcti_rcv is the only probe path.&lt;br /&gt;
    6. In reliable mode, optionally arm an RTT probe (Section 12).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 6. Receive path ==&lt;br /&gt;
&lt;br /&gt;
=== 6.1. Early-exit dispatch ===&lt;br /&gt;
&lt;br /&gt;
Keepalive (KA), RTT probe (RTTP), pre-DRF NACK, and rendezvous&lt;br /&gt;
(RDVS) packets short-circuit out of frcti_rcv before the locked&lt;br /&gt;
main path; each handler takes its own lock internally.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      incoming packet&lt;br /&gt;
            |&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | KA?     |---yes--&amp;gt; ka_rcv  ; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RTTP?   |---yes--&amp;gt; rttp_rcv; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | NACK?   |---yes--&amp;gt; nack_rcv; return  (see Section 9)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RDVS?   |---yes--&amp;gt; rdv_rcv ; return  (reply bare FC, ackno=0)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       acquire wrlock; enter locked main path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - KA   : refresh t_ka_rcv, honour piggybacked ACK.&lt;br /&gt;
  - RTTP : probe (echo back nonce) or echo (verify nonce, sample&lt;br /&gt;
           RTT).&lt;br /&gt;
  - NACK : pre-DRF, sender-side handler.  See Section 9.&lt;br /&gt;
  - RDVS : reply with a bare FC packet (ackno = 0); rdlock only.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 6.2. Locked main path ===&lt;br /&gt;
&lt;br /&gt;
Steps below run with the per-flow frcti.lock held for writing&lt;br /&gt;
(pthread_rwlock_wrlock) unless noted.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  rcv_inact_check&lt;br /&gt;
      Only meaningful when the receive side is stale.  On DRF&lt;br /&gt;
      (Data Run Flag): release rq[] slots, rebase rcv_cr, continue.&lt;br /&gt;
      On stale DATA without DRF: fire a pre-DRF NACK if cooldown&lt;br /&gt;
      allows (Section 9), then discard the packet; on cooldown,&lt;br /&gt;
      drop without sending a NACK (a pending cumulative ACK from&lt;br /&gt;
      drop_packet may still go out).  Non-DATA, non-DRF arrivals&lt;br /&gt;
      bypass rcv_inact_check entirely; pure-DRF stale arrivals fall&lt;br /&gt;
      through after the DRF rebase branch.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  DATA-only act refresh&lt;br /&gt;
      Refresh rcv_cr.act only when FRCT_DATA is set, so that non-DATA&lt;br /&gt;
      packets never block the next DRF rebase.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  Wire-dup gate&lt;br /&gt;
      Before flag-driven dispatch, drop wire-duplicate ACKs and&lt;br /&gt;
      wire-duplicate DATA (is_dup_ack / is_dup_data).  The DATA&lt;br /&gt;
      check is bypassed for FRCT_RXM-bearing arrivals so the&lt;br /&gt;
      piggybacked ACK / SACK / FC carried on a retransmitted DATA&lt;br /&gt;
      at an already-ACK&#039;d seqno is still applied; the stale-in-&lt;br /&gt;
      window branch below then drops the packet.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  ACK&lt;br /&gt;
      Drop ACKs whose ackno falls outside (snd_cr.lwe, snd_cr.seqno].&lt;br /&gt;
      If ackno == snd_cr.lwe (non-advancing cumulative ACK), drive&lt;br /&gt;
      RACK fast-retransmit consideration (Section 8).  Otherwise&lt;br /&gt;
      advance snd_cr.lwe = ackno, collapse rto_mul to 0 (Karn-gated&lt;br /&gt;
      by SND_RTX on the just-acknowledged slot, the old head-of-&lt;br /&gt;
      line), reset dup_thresh to 0, update t_latest_ack to the&lt;br /&gt;
      send-time of the slot at ackno-1 (consumed by RACK and SACK&lt;br /&gt;
      below), decay reo_wnd_mult per RFC 8985 sec. 6.2 step 4,&lt;br /&gt;
      exit NewReno-careful recovery (see Section 8) on&lt;br /&gt;
      ackno &amp;gt;= recovery_high or ackno == snd_cr.seqno, and feed an&lt;br /&gt;
      RTT sample if eligible (Section 12).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  SACK&lt;br /&gt;
      Walk the block list.  For each block (a present range above&lt;br /&gt;
      lwe) NULL out snd_slots[k].rxm, clear the slot&#039;s per-send&lt;br /&gt;
      flags, and advance t_latest_ack to the latest send-time&lt;br /&gt;
      covered (the Forward Acknowledgement / fack equivalent,&lt;br /&gt;
      Mathis &amp;amp; Mahdavi 1996); the first block whose start&lt;br /&gt;
      clamps to snd_cr.lwe skips this fack update so that a head-&lt;br /&gt;
      of-line clamp does not falsely advance fack.  For un-SACKed&lt;br /&gt;
      gaps below hi_sacked, stage a retransmit per slot that is&lt;br /&gt;
      (1) still owned (rxm != NULL), (2) not already SND_FAST_RXM,&lt;br /&gt;
      (3) not aged out past t_r, and (4) either outside the RACK&lt;br /&gt;
      reorder window R OR with dup_thresh &amp;gt;= DUP_THRESH (the RFC&lt;br /&gt;
      8985 sec. 6.2 hybrid trigger).  Mark the slot SND_FAST_RXM&lt;br /&gt;
      and NULL the rxm at stage time.  Capped at SACK_RXM_MAX&lt;br /&gt;
      staged retransmits per receive pass; what&#039;s left rides the&lt;br /&gt;
      next SACK.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  FC&lt;br /&gt;
      Bump snd_cr.rwe (clamped to lwe + RQ_SIZE, never shrinks)&lt;br /&gt;
      and mark window open.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  DATA&lt;br /&gt;
      Bounds-check seqno against window.  On stale-dup&lt;br /&gt;
      (seqno &amp;lt; rcv_cr.lwe), set rcv_cr.seqno = seqno to force a&lt;br /&gt;
      fresh ACK on the next ack_snd, then drop.  On accept: both&lt;br /&gt;
      FRTX and best-effort stash the packet-buffer index into&lt;br /&gt;
      rq[seqno mod RQ_SIZE].  Fragments stash unchanged - the role&lt;br /&gt;
      bits are inspected only at consume time (Section 7.2).  On&lt;br /&gt;
      out-of-order arrival, build a SACK reply if not rate-limited&lt;br /&gt;
      (per Section 3) and not deduplicated against the previous&lt;br /&gt;
      (rcv_cr.lwe, n_blocks) pair; D-SACK reports always bypass the&lt;br /&gt;
      dedup.  If both rate-limit and dedup suppress the reply,&lt;br /&gt;
      neither SACK nor delayed-ACK fires (the sender picks up the&lt;br /&gt;
      gap on its next ACK).  On in-order arrival, arm the delayed-&lt;br /&gt;
      ACK timer.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  drop_packet exit&lt;br /&gt;
      Releases the per-packet shared-memory buffer (spb), then&lt;br /&gt;
      calls ack_snd synchronously after the spb release to surface&lt;br /&gt;
      any pending cumulative ACK.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 7. Read path and reassembly ==&lt;br /&gt;
&lt;br /&gt;
=== 7.1. Read path ===&lt;br /&gt;
&lt;br /&gt;
flow_read returns a full reassembled SDU (Service Data Unit) via&lt;br /&gt;
frcti_consume on every FRCP SDU-mode flow (FRTX or best-effort);&lt;br /&gt;
stream-mode is covered in Section 16.  An incomplete head-of-line&lt;br /&gt;
(HoL) run yields -EAGAIN; an oversized run yields -EMSGSIZE (the&lt;br /&gt;
run is dropped so the flow does not stall).  On best-effort flows,&lt;br /&gt;
a permanently-lost mid-fragment is dropped as soon as a later&lt;br /&gt;
complete SDU becomes visible in the ring (Section 7.2 skip-past-&lt;br /&gt;
gap).&lt;br /&gt;
&lt;br /&gt;
Raw flows carry no frcti, so flow_read returns the next pending&lt;br /&gt;
packet-buffer index directly, with no role-bit inspection.  (Raw&lt;br /&gt;
service is selected via qos.service == SVC_RAW at flow allocation,&lt;br /&gt;
which suppresses frcti creation.)&lt;br /&gt;
&lt;br /&gt;
frcti_pdu_ready is the no-advance peek used by fevent (the&lt;br /&gt;
Ouroboros flow-event multiplexer, the poll(2)-equivalent on&lt;br /&gt;
flows).  It returns ready only when the head-of-line run is&lt;br /&gt;
complete and the lead packet (a Protocol Data Unit, here one FRCP&lt;br /&gt;
packet) is present at rcv_cr.rwe - RQ_SIZE; any other state&lt;br /&gt;
(including the best-effort skip-past-gap case) returns not ready,&lt;br /&gt;
and frcti_consume is left to drop the broken prefix and re-&lt;br /&gt;
inspect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 7.2. Fragmentation and reassembly ===&lt;br /&gt;
&lt;br /&gt;
Send side (flow_write_frag).  An SDU larger than&lt;br /&gt;
(frag_mtu - PCI) is split into ceil(count / (frag_mtu - PCI))&lt;br /&gt;
fragments; each fragment is its own FRCP packet with its own&lt;br /&gt;
seqno and a per-fragment role flag pair (Section 1.2).  Roles are&lt;br /&gt;
assigned at emit time:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    +------+--------+&lt;br /&gt;
    | i    | Role   |&lt;br /&gt;
    +------+--------+&lt;br /&gt;
    | n=1  | SOLE   |&lt;br /&gt;
    | i=0  | FIRST  |&lt;br /&gt;
    | i=n-1| LAST   |&lt;br /&gt;
    | else | MID    |&lt;br /&gt;
    +------+--------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A mid-loop allocation or transmit failure may yield a partial&lt;br /&gt;
write: the call returns the bytes already enqueued (off &amp;gt; 0) or&lt;br /&gt;
the underlying error (off == 0).  Best-effort flows fragment&lt;br /&gt;
identically; on the receiver, a partial run with a permanently-&lt;br /&gt;
lost fragment is dropped when a later complete SDU is visible in&lt;br /&gt;
the ring (see skip-past-gap below).  Raw flows carry no PCI and&lt;br /&gt;
refuse anything larger than the layer&#039;s user MTU (-EMSGSIZE).&lt;br /&gt;
&lt;br /&gt;
Wire-level recovery is fragment-agnostic on FRTX flows: each&lt;br /&gt;
fragment&#039;s seqno flows through SACK / RACK / RTO / NACK exactly&lt;br /&gt;
as for a SOLE DATA packet, and reassembly does not re-enter the&lt;br /&gt;
loss-detection path.  Best-effort flows run the same seqno&lt;br /&gt;
machinery (DRF, FC, ACK piggyback, pre-DRF NACK emit) but queue&lt;br /&gt;
no rxm state at the sender, so a lost MID is unrecoverable;&lt;br /&gt;
skip-past-gap handles it (below).&lt;br /&gt;
&lt;br /&gt;
Receive side.  Fragments stash into rq[seqno] unchanged; role bits&lt;br /&gt;
are read only at consume time.  frag_run_inspect, called from&lt;br /&gt;
frcti_consume, walks the ring starting at the oldest still-&lt;br /&gt;
undelivered seqno base = rcv_cr.rwe - RQ_SIZE (equal to rcv_cr.lwe&lt;br /&gt;
only when no partial run is in progress; during a partial run lwe&lt;br /&gt;
has already advanced past base).  It produces one of three&lt;br /&gt;
outcomes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    +---------------+---------------------------------------------+&lt;br /&gt;
    | Outcome       | Cause                                       |&lt;br /&gt;
    +---------------+---------------------------------------------+&lt;br /&gt;
    | DELIVER (n)   | rq[base]=SOLE (n=1), or rq[base]=FIRST and  |&lt;br /&gt;
    |               | a LAST follows in slots [base+1..base+n-1]  |&lt;br /&gt;
    |               | with all intermediate roles in {MID,FIRST,  |&lt;br /&gt;
    |               | LAST} contiguous.                           |&lt;br /&gt;
    | DROP (n)      | rq[base] is MID or LAST without a preceding |&lt;br /&gt;
    |               | FIRST (n=1); a FIRST..[non-LAST]..new-FIRST |&lt;br /&gt;
    |               | or new-SOLE mid-run (drop the broken prefix |&lt;br /&gt;
    |               | with n = run length minus 1, so the new     |&lt;br /&gt;
    |               | FIRST/SOLE stays); or, on best-effort       |&lt;br /&gt;
    |               | flows, a gap at base with a FIRST/SOLE      |&lt;br /&gt;
    |               | later in the ring (drop up to the new run   |&lt;br /&gt;
    |               | start).                                     |&lt;br /&gt;
    | NOT_READY     | rq[base] absent or FIRST..[non-LAST] with   |&lt;br /&gt;
    |               | no later FIRST/SOLE in the ring (FRTX waits |&lt;br /&gt;
    |               | for retx; best-effort waits for arrival).   |&lt;br /&gt;
    +---------------+---------------------------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DELIVER triggers frag_gather: a scatter-gather memcpy of the n&lt;br /&gt;
consecutive fragments at rq[base..base+n-1] directly into the&lt;br /&gt;
caller&#039;s buffer; each per-packet shared-memory buffer (spb) is&lt;br /&gt;
released and rwe advances by n.  lwe was already advanced&lt;br /&gt;
incrementally as each contiguous fragment arrived; frag_gather&lt;br /&gt;
only restores the fixed-width invariant rwe == lwe + RQ_SIZE.&lt;br /&gt;
No intermediate reassembly buffer is allocated.&lt;br /&gt;
&lt;br /&gt;
DROP advances rwe past the broken prefix (releasing the spbs)&lt;br /&gt;
and pulls lwe up to the new trailing edge if needed; the next&lt;br /&gt;
consume retries from the new base.  Oversize or arithmetically&lt;br /&gt;
overflowing delivery (sum of fragment lengths &amp;gt; max_rcv_sdu, sum&lt;br /&gt;
&amp;gt; caller&#039;s buffer, or running-sum overflow) also drops the run&lt;br /&gt;
with -EMSGSIZE.&lt;br /&gt;
&lt;br /&gt;
Skip-past-gap (best-effort only).  On FRTX, a gap in the run means&lt;br /&gt;
&amp;quot;waiting for retransmit&amp;quot; and frag_run_inspect returns NOT_READY.&lt;br /&gt;
On best-effort flows the gap is permanent, so frag_run_inspect&lt;br /&gt;
scans forward in the ring for the next FIRST or SOLE; if one is&lt;br /&gt;
visible within RQ_SIZE, it returns DROP for the broken prefix and&lt;br /&gt;
the consume loop retries at the new lwe.  Memory hold is bounded&lt;br /&gt;
by RQ_SIZE; the partial releases on the next consume call once a&lt;br /&gt;
later complete run exists.  Voice-like flows (one SOLE per SDU)&lt;br /&gt;
see no extra wait: any later SOLE makes the prior gap droppable&lt;br /&gt;
immediately.&lt;br /&gt;
&lt;br /&gt;
The choice to defer reassembly to consume time keeps the receive&lt;br /&gt;
path zero-copy: fragments stay in the shared-memory ring until&lt;br /&gt;
the application pulls, and the SDU lands directly in the caller&#039;s&lt;br /&gt;
buffer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 8. Retransmission ==&lt;br /&gt;
&lt;br /&gt;
FRCP is bounded by two delta-t-derived timers (Watson 1981, see&lt;br /&gt;
Section 15):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - t_a (a-timer): upper bound on ACK delay.  An ACK for a received&lt;br /&gt;
    DATA packet MUST be emitted within t_a of receipt; an attempt&lt;br /&gt;
    to send an ACK after the a-timer has expired is suppressed&lt;br /&gt;
    (the sender&#039;s RTO is already in motion).&lt;br /&gt;
  - t_r (r-timer): upper bound on retransmission.  A given DATA&lt;br /&gt;
    packet MUST NOT be retransmitted after t_r has elapsed since&lt;br /&gt;
    its first send (t0); when the bound is hit, the flow is&lt;br /&gt;
    declared down (raising the Ouroboros asynchronous flow&lt;br /&gt;
    condition ACL_FLOWDOWN, which marks the flow dead to both&lt;br /&gt;
    endpoints) rather than retransmitted again.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each in-flight FRTX seqno owns one rxm_entry, armed in a hashed&lt;br /&gt;
timing wheel; the wheel deadline is the slot&#039;s next eligible&lt;br /&gt;
retransmit time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  RTO timer&lt;br /&gt;
      On fire (rxm_due), re-emit with FRCT_RXM, mark SND_RTX&lt;br /&gt;
      (Karn-suppress next ACK&#039;s RTT sample), and (for the head-of-&lt;br /&gt;
      line (HoL) slot only) bump rto_mul up to MAX_RTO_MUL.  Wheel&lt;br /&gt;
      deadline is t_send + (rto &amp;lt;&amp;lt; rto_mul).  Re-armed unless&lt;br /&gt;
      consumed.  The RTO timer also clears SND_FAST_RXM (re-arming&lt;br /&gt;
      fast-retransmit eligibility), resets reo_wnd_mult to 1 on a&lt;br /&gt;
      HoL fire (RFC 8985 sec. 6.2 step 4 reset clause), and marks&lt;br /&gt;
      the flow ACL_FLOWDOWN if its frct_tx call fails.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  r-timer guard&lt;br /&gt;
      Before any retransmit attempt, check (now - t0) against t_r.&lt;br /&gt;
      If exceeded, the slot is no longer eligible for retransmit.&lt;br /&gt;
      Only the RTO timer (rxm_due) treats r-timer expiry as&lt;br /&gt;
      terminal: it marks the flow ACL_FLOWDOWN (peer unreachable).&lt;br /&gt;
      Fast-retransmit, SACK-driven retransmit, and NACK-driven&lt;br /&gt;
      head-of-line re-emit silently skip aged-out slots and defer&lt;br /&gt;
      the flow-down decision to the next RTO fire.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  Fast retransmit (hybrid trigger, RFC 8985 sec. 6.2)&lt;br /&gt;
      On a non-advancing cumulative ACK with the scoreboard&lt;br /&gt;
      advanced, fire one fast retransmit when EITHER (a) the head-&lt;br /&gt;
      of-line slot&#039;s latest send is older than the RACK reorder&lt;br /&gt;
      window R (Section 3) and not yet aged out, OR (b) the SACK&lt;br /&gt;
      dup-thresh count above snd_cr.lwe reaches DUP_THRESH (= 3,&lt;br /&gt;
      RFC 8985 sec. 6.2 step 4).  Fires at most once per non-&lt;br /&gt;
      advancing cumulative-ACK value, gated by rack_fired_lwe (the&lt;br /&gt;
      snd_cr.lwe at which fast-retransmit last fired).  Set&lt;br /&gt;
      SND_FAST_RXM on the slot (one-shot per-slot gate) and enter&lt;br /&gt;
      NewReno-style careful recovery (see NewReno below in this&lt;br /&gt;
      section).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      The RACK reorder window R uses the RFC 8985 sec. 6.2 form&lt;br /&gt;
      R = MIN(reo_wnd_mult * min_RTT / 4, SRTT) with a&lt;br /&gt;
      MIN_REORDER_NS = 250 us floor.  Before the first RTT sample&lt;br /&gt;
      seeds min_rtt, R falls back to MIN(reo_wnd_mult * SRTT / 4,&lt;br /&gt;
      SRTT), still floored at MIN_REORDER_NS (consistent with the&lt;br /&gt;
      windowed-minimum fallback described in Section 12).  min_rtt&lt;br /&gt;
      is a windowed minimum over the last MIN_RTT_WIN_NS = 5 min of&lt;br /&gt;
      RTT samples (matches the Linux tcp_min_rtt_wlen default) so a&lt;br /&gt;
      route change to a longer path eventually re-anchors the&lt;br /&gt;
      reorder window without relying on reo_wnd_mult growth alone.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  SACK-driven retransmit&lt;br /&gt;
      For each gap below hi_sacked whose slot is (1) still owned,&lt;br /&gt;
      (2) not already SND_FAST_RXM, (3) not aged out past t_r, and&lt;br /&gt;
      (4) either outside the RACK window R OR with dup_thresh &amp;gt;=&lt;br /&gt;
      DUP_THRESH (same hybrid as fast-retransmit, see Section 6.2),&lt;br /&gt;
      re-emit.  Each SACK-driven retransmit re-arms a fresh rxm so&lt;br /&gt;
      a lost retransmit can still be recovered by its own RTO&lt;br /&gt;
      timer.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  NewReno&lt;br /&gt;
      On entry, recovery_high = snd_cr.seqno + RTT_QUARANTINE.&lt;br /&gt;
      Exit when ackno &amp;gt;= recovery_high or ackno == snd_cr.seqno&lt;br /&gt;
      (the latter means everything sent has been acknowledged).&lt;br /&gt;
      seqno_rotate also clears recovery.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 9. Pre-DRF NACK ==&lt;br /&gt;
&lt;br /&gt;
The two sides have different inactivity thresholds&lt;br /&gt;
(snd_cr.inact &amp;gt; rcv_cr.inact), so a receiver can detect &amp;quot;stale data&lt;br /&gt;
run&amp;quot; before the sender&#039;s own DRF logic kicks in.  NACK is the&lt;br /&gt;
receiver-driven nudge that asks the sender to re-transmit the head&lt;br /&gt;
of the run.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  Send (frcti_nack_snd, called by frcti_rcv when rcv_inact_check&lt;br /&gt;
        returns FRCT_INACT_NEED_NACK)&lt;br /&gt;
      When an incoming DATA packet has no DRF and rcv-side activity&lt;br /&gt;
      is older than rcv_cr.inact, the receiver emits a bare packet&lt;br /&gt;
      with flags = FRCT_NACK and seqno = arrival_seqno - 1&lt;br /&gt;
      (informational only, not consulted by the receive handler).&lt;br /&gt;
      The cooldown in Section 3 rate-limits the burst.  Non-DATA&lt;br /&gt;
      non-DRF arrivals bypass rcv_inact_check entirely; non-DATA&lt;br /&gt;
      DRF still rebases via the DRF branch.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  Receive (frcti_nack_rcv)&lt;br /&gt;
      Dispatched in the early-exit branch (Section 6.1), before&lt;br /&gt;
      rcv_inact_check.  The sender copies the head-of-line (HoL)&lt;br /&gt;
      rxm packet, marks the slot SND_RTX | SND_FAST_RXM (Karn-&lt;br /&gt;
      suppress next ACK, one-shot fast-rxm gate), sets rtt_lwe =&lt;br /&gt;
      snd_cr.lwe + 1, and re-emits via fast_rxm_send with FRCT_RXM&lt;br /&gt;
      and a refreshed ackno.  The original rxm_entry and its RTO&lt;br /&gt;
      timer are left armed - the NACK emit is additive to the&lt;br /&gt;
      normal retransmit machinery, not a replacement.  No-op if&lt;br /&gt;
      nothing is in flight, the HoL slot has aged past t_r, or&lt;br /&gt;
      the HoL rxm pointer has been cleared by SACK or RACK.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NACK serves two roles:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  1. Lost first-of-run (DRF) packet recovery.  Required.  Until&lt;br /&gt;
     the DRF packet arrives, the receiver cannot rebase its&lt;br /&gt;
     window, so any subsequent in-flight packets look stale to&lt;br /&gt;
     the receiver.  The NACK fires the moment the second&lt;br /&gt;
     packet arrives at a stale receiver, telling the sender to&lt;br /&gt;
     re-emit the HoL (DRF) packet at NACK-cooldown latency rather&lt;br /&gt;
     than waiting for the initial RTO (which is the configured&lt;br /&gt;
     default until srtt is seeded by the first probe round-trip).&lt;br /&gt;
  2. General loss-recovery accelerator.  When loss is detected&lt;br /&gt;
     receiver-first, the NACK skips one RTO of latency relative to&lt;br /&gt;
     waiting for the sender&#039;s RTO to fire.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In both cases the existing rxm_entry and its RTO timer are left&lt;br /&gt;
armed, so the RTO path remains the eventual fallback.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 10. Cumulative + selective ACK ==&lt;br /&gt;
&lt;br /&gt;
Cumulative ACK is ackno = rcv_cr.lwe.  On out-of-order arrival the&lt;br /&gt;
receiver also emits a SACK packet (Section 1.3) whose payload lists&lt;br /&gt;
*present* blocks above lwe (analogous to TCP SACK / QUIC ACK&lt;br /&gt;
ranges).  SACKs are rate-limited per Section 3 and suppressed when&lt;br /&gt;
neither lwe nor block count has changed since the last SACK.&lt;br /&gt;
&lt;br /&gt;
D-SACK reports (RFC 2883) are emitted in-band as block[0] of an&lt;br /&gt;
otherwise normal SACK frame (see Section 1.3 for the encoding).&lt;br /&gt;
Two receiver triggers arm a pending D-SACK report (single-slot,&lt;br /&gt;
latest-wins):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - DATA arrival with seqno &amp;lt; rcv_cr.lwe, both wire-dup (no RXM,&lt;br /&gt;
    is_dup_data path) and retransmit (RXM, post-FC branch)&lt;br /&gt;
    (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
  - rq_accept conflict, slot already occupied in [lwe, rwe)&lt;br /&gt;
    (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a D-SACK is pending and the standard scoreboard SACK would be&lt;br /&gt;
suppressed by dedup or rate-limit, the report is emitted as a&lt;br /&gt;
stand-alone SACK frame through the normal ack_snd path; when a&lt;br /&gt;
D-SACK report is pending the path bypasses dedup and the TICTIME&lt;br /&gt;
rate-limit, but the a-timer suppression on rcv inactivity still&lt;br /&gt;
applies.&lt;br /&gt;
&lt;br /&gt;
Bare ACKs are deferred via a per-flow delayed-ACK timer (one in&lt;br /&gt;
flight at a time, atomic test-and-set dedup; fires per Section 3&lt;br /&gt;
after the first in-order arrival).  Suppressed if (1) no new&lt;br /&gt;
seqno, (2) rcv side is inactive (older than t_a), or (3) the&lt;br /&gt;
sender just sent within TICTIME.  A pending D-SACK ride-through&lt;br /&gt;
bypasses (1) and (3); the a-timer gate (2) is unconditional.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 11. Flow control ==&lt;br /&gt;
&lt;br /&gt;
The receiver advertises rwe in every FC field.  The sender treats&lt;br /&gt;
its snd_cr.rwe as the absolute right edge: when&lt;br /&gt;
snd_cr.seqno &amp;gt;= snd_cr.rwe the window is closed and flow_write&lt;br /&gt;
yields.  While closed, the sender periodically emits RDVS&lt;br /&gt;
(rendezvous) packets (cadence DELT_RDV); the receiver replies with&lt;br /&gt;
a bare FC packet (ackno = 0) that reopens the window.  Once the&lt;br /&gt;
window has been closed for longer than MAX_RDV the sender stops&lt;br /&gt;
emitting RDVS but does not tear the flow down - the writer keeps&lt;br /&gt;
blocking until either a peer-driven FC arrives or the KA&lt;br /&gt;
(keepalive) / r-timer marks the flow.&lt;br /&gt;
&lt;br /&gt;
rwe is clamped to lwe + RQ_SIZE on receipt and MUST NOT shrink:&lt;br /&gt;
a backward rwe is silently clamped to the current snd_cr.rwe;&lt;br /&gt;
the FC packet still reopens the window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 12. RTT estimation ==&lt;br /&gt;
&lt;br /&gt;
Active RTTP probes (Section 1.4) carry a 32-bit probe_id (0&lt;br /&gt;
reserved) and a 16-byte random nonce echoed verbatim - defends&lt;br /&gt;
against spoofed replies.  A ring of RTTP_RING in-flight probes is&lt;br /&gt;
kept; an echo whose (id, nonce) doesn&#039;t match the ring slot is&lt;br /&gt;
dropped.  A single RTTP sample is clamped to RTT_CLAMP_MUL * srtt&lt;br /&gt;
(compile-time RTT_CLAMP_MUL = 16) once srtt is seeded; the first&lt;br /&gt;
cold-probe sample feeds rtt_update raw.&lt;br /&gt;
&lt;br /&gt;
Probe arming gates:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - Cold (no srtt yet): the receive path arms at most one probe&lt;br /&gt;
    per 100 ms via frcti_rcv_probe (PROBE_DUE_COLD); arming&lt;br /&gt;
    requires an incoming packet.  Active send-path arming bails&lt;br /&gt;
    while srtt == 0.&lt;br /&gt;
  - Warm (rtt_probe_arm, called from frcti_snd): outstanding&lt;br /&gt;
    data (snd_cr.seqno &amp;gt; snd_cr.lwe), AND at least 2 * srtt&lt;br /&gt;
    since t_rcv_rtt (last RTT receive of any kind), AND at&lt;br /&gt;
    least srtt since t_snd_probe (last probe emit).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sample feeds either Linux&#039;s asymmetric mdev estimator&lt;br /&gt;
(FRCT_LINUX_RTT_ESTIMATOR, default ON) or RFC 6298 symmetric EWMA&lt;br /&gt;
(compile option).  srtt is floored at 10 ms when seeded from a&lt;br /&gt;
hint, at 1 us after every update (including the first seeding&lt;br /&gt;
sample); mdev floored at 100 ns.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    RTO = max(rto_min, 2 * srtt, srtt + (mdev &amp;lt;&amp;lt; MDEV_MUL))&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(the 2 * srtt floor is an FRCT addition not in RFC 6298).&lt;br /&gt;
Effective wheel deadline capped per Section 3.&lt;br /&gt;
&lt;br /&gt;
ACK-derived samples (frcti_ack_rcv -&amp;gt; rtt_sample_eligible), beyond&lt;br /&gt;
the cum-ACK advance gate in frcti_ack_rcv (ackno &amp;gt; lwe and&lt;br /&gt;
ackno &amp;lt;= seqno), require all of: not in recovery; ACK packet does&lt;br /&gt;
not carry FRCT_RXM; HoL slot&#039;s SND_RTX bit clear; slot&#039;s rxm&lt;br /&gt;
pointer non-NULL (not SACK-consumed); lwe not below the rtt_lwe&lt;br /&gt;
fence; srtt already seeded by an RTTP probe.  There is no ACK-only&lt;br /&gt;
seeding.&lt;br /&gt;
&lt;br /&gt;
Every eligible sample also feeds RACK.min_RTT (RFC 8985 sec. 6.2)&lt;br /&gt;
via a windowed minimum: replace whenever the sample is strictly&lt;br /&gt;
smaller OR more than MIN_RTT_WIN_NS (5 min, matches Linux&lt;br /&gt;
tcp_min_rtt_wlen) has elapsed since the current min was set.  The&lt;br /&gt;
downward branch is immediate (faster path picked up at once); the&lt;br /&gt;
upward branch is gated on the window (a transient queue burst does&lt;br /&gt;
not poison the estimate, but a sustained route change to a longer&lt;br /&gt;
path re-anchors min_RTT after at most one window).  Seeded from&lt;br /&gt;
rtt_hint at rtt_init; 0 acts as the unset sentinel and the base&lt;br /&gt;
in rack_reorder_window falls back from min_RTT to SRTT (so&lt;br /&gt;
R = mult * SRTT/4, capped at SRTT, floored at MIN_REORDER_NS)&lt;br /&gt;
until the first sample.  See Section 6.2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 13. Liveness (keepalive) ==&lt;br /&gt;
&lt;br /&gt;
When qs.timeout &amp;gt; 0 a per-flow KA (keepalive) timer is armed.&lt;br /&gt;
Arming uses rcv_cr.act for the deadline computation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    deadline = min(snd_act + qs.timeout/4,&lt;br /&gt;
                   rcv_act + qs.timeout)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(clamped to now + qs.timeout/4 if already past).  The timer fires&lt;br /&gt;
either on sender idleness (to send a KA) or on receiver idleness&lt;br /&gt;
(to declare the peer dead).  On fire (ka_snd) the peer-dead test&lt;br /&gt;
uses max(rcv_cr.act, t_ka_rcv) so a recent KA reply counts even&lt;br /&gt;
when no DATA has arrived:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - If now - max(rcv_cr.act, t_ka_rcv) &amp;gt; qs.timeout, mark the flow&lt;br /&gt;
    ACL_FLOWPEER and notify the per-process flow-event set&lt;br /&gt;
    (proc.fqset) with FLOW_PEER.&lt;br /&gt;
  - Else if snd_idle &amp;gt; qs.timeout/4, emit a bare KA | ACK&lt;br /&gt;
    (ackno = rcv_cr.lwe) and re-arm.&lt;br /&gt;
  - Else just re-arm.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: rx_rb and tx_rb are the receive and transmit shared-memory&lt;br /&gt;
ring buffers.  The r-timer raises ACL_FLOWDOWN on both (route is&lt;br /&gt;
broken); keepalive raises ACL_FLOWPEER on rx_rb only and notifies&lt;br /&gt;
the flow-event set (peer is silent, writer keeps tx_rb usable) -&lt;br /&gt;
distinct ACLs.  qs.timeout == 0 disables keepalive entirely; a&lt;br /&gt;
silent peer crash is then undetected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 14. Linger / teardown ==&lt;br /&gt;
&lt;br /&gt;
On flow_dealloc, frcti_dealloc computes a grace timeout&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    max(rcv_cr.act + rcv_cr.inact, snd_cr.act + snd_cr.inact) - now&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(floored at 0 and converted to seconds) and returns it; flow_dealloc&lt;br /&gt;
forwards this to the IRMd as the dealloc grace.  The IRMd, not FRCT,&lt;br /&gt;
performs the wait.  Before computing the timeout, FRCT may emit a&lt;br /&gt;
final ACK when rcv_cr.lwe != rcv_cr.seqno (the peer has not been&lt;br /&gt;
told the most recent cumulative ACK) AND the rcv side has been&lt;br /&gt;
active within t_a (a-timer not aged out).&lt;br /&gt;
&lt;br /&gt;
FRCTFLINGER is honoured only when snd_cr.lwe &amp;lt; edge, where edge =&lt;br /&gt;
snd_fin_seqno after FIN has been sent in stream mode and&lt;br /&gt;
snd_cr.seqno otherwise (data or FIN still in flight).  The drain&lt;br /&gt;
itself runs in flow_dealloc&#039;s while (FRCTI_LINGERING) loop, not in&lt;br /&gt;
frcti_dealloc.&lt;br /&gt;
&lt;br /&gt;
The fd is single-reader / single-writer (documented in the&lt;br /&gt;
manpages).  flow_write pumps rx_rb on every call (via&lt;br /&gt;
flow_wait_window -&amp;gt; flow_drain_rx_nb) and additionally blocks on&lt;br /&gt;
rx_rb when the send window is closed.  A pure-writer thread thus&lt;br /&gt;
consumes ACKs without a dedicated reader.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 15. Heritage and adopted techniques ==&lt;br /&gt;
&lt;br /&gt;
Delta-t (Watson, 1981) is the primary heritage; FRCP descends from&lt;br /&gt;
the delta-t protocol family via the Recursive InterNetwork&lt;br /&gt;
Architecture (RINA; Day, &amp;quot;Patterns in Network Architecture&amp;quot;, 2008,&lt;br /&gt;
ch. 9).  Timer-based connection management&lt;br /&gt;
(no SYN/FIN handshake, per-flow state born on first DATA and&lt;br /&gt;
reclaimed after t_mpl + a + r of silence), the DRF marker, and the&lt;br /&gt;
t_mpl / t_a / t_r timers all come from delta-t.  See Watson,&lt;br /&gt;
&amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection&lt;br /&gt;
Management&amp;quot;, Computer Networks 5 (1981).&lt;br /&gt;
&lt;br /&gt;
The unified `flow_alloc(name, qos, ...)` primitive and its&lt;br /&gt;
multi-axis QoS-cube argument (Section 2.2) also come from RINA&lt;br /&gt;
(Day 2008, ch. 6; Grasa et al., &amp;quot;IRATI: investigating RINA as an&lt;br /&gt;
alternative to TCP/IP&amp;quot;, Computer Networks 92 (2015)) - reliability,&lt;br /&gt;
ordering, CRC presence, and encryption are flow attributes, not&lt;br /&gt;
separate sockets or protocols.&lt;br /&gt;
&lt;br /&gt;
The table below summarises additional adopted techniques and their&lt;br /&gt;
references.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| FRCP mechanism         | Heritage         | Reference / note       |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Random new seqno on    | TCP ISN          | RFC 6528 (Gont &amp;amp;       |&lt;br /&gt;
| seqno_rotate           |                  | Bellovin, 2012).       |&lt;br /&gt;
|                        |                  | QUIC PN-space reset    |&lt;br /&gt;
|                        |                  | (RFC 9000 sec. 12.3)   |&lt;br /&gt;
|                        |                  | is a structural        |&lt;br /&gt;
|                        |                  | analogue.              |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Cumulative ACK,        | TCP              | RFC 793 / RFC 9293     |&lt;br /&gt;
| left-window-edge       |                  |                        |&lt;br /&gt;
| advance                |                  |                        |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Receive window with    | TCP              | RFC 793 sec. 3.7 /     |&lt;br /&gt;
| non-shrink rule        |                  | RFC 9293 sec. 3.8.6;   |&lt;br /&gt;
|                        |                  | RFC 1122 sec. 4.2.2.16 |&lt;br /&gt;
|                        |                  | for the explicit non-  |&lt;br /&gt;
|                        |                  | shrink prohibition     |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Modular seqno          | TCP              | RFC 793 sec. 3.3 /     |&lt;br /&gt;
| arithmetic             |                  | RFC 9293 sec. 3.4      |&lt;br /&gt;
| (before/after helpers) |                  |                        |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Selective ACK block    | TCP              | RFC 2018 (Mathis et    |&lt;br /&gt;
| list                   |                  | al., 1996).  Encoded   |&lt;br /&gt;
|                        |                  | as a typed FRCP packet |&lt;br /&gt;
|                        |                  | rather than a TCP      |&lt;br /&gt;
|                        |                  | option, so framing is  |&lt;br /&gt;
|                        |                  | closer to QUIC ACK     |&lt;br /&gt;
|                        |                  | frames.  D-SACK (RFC   |&lt;br /&gt;
|                        |                  | 2883) carried in-band  |&lt;br /&gt;
|                        |                  | as block[0]; see       |&lt;br /&gt;
|                        |                  | Section 1.3.           |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| NewReno-careful        | TCP              | RFC 6582 (Henderson    |&lt;br /&gt;
| recovery with          |                  | et al., 2012); QUIC    |&lt;br /&gt;
| recovery_high gate     |                  | builds on the same     |&lt;br /&gt;
|                        |                  | model in RFC 9002      |&lt;br /&gt;
|                        |                  | sec. 7.3.2.  Cwnd half |&lt;br /&gt;
|                        |                  | absent (CC in IPCP).   |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| RACK reordering        | TCP              | RFC 8985 (Cheng et     |&lt;br /&gt;
| window for fast        |                  | al., 2021).  FRCP      |&lt;br /&gt;
| retransmit             |                  | R = MIN(reo_wnd_mult * |&lt;br /&gt;
|                        |                  | min_RTT / 4, SRTT)     |&lt;br /&gt;
|                        |                  | with a MIN_REORDER_NS  |&lt;br /&gt;
|                        |                  | = 250 us floor against |&lt;br /&gt;
|                        |                  | srtt collapse; matches |&lt;br /&gt;
|                        |                  | RFC 8985 sec. 6.2 and  |&lt;br /&gt;
|                        |                  | Linux tcp_rack_reo_wnd.|&lt;br /&gt;
|                        |                  | DSACK-driven           |&lt;br /&gt;
|                        |                  | reo_wnd_mult (sec. 6.2 |&lt;br /&gt;
|                        |                  | step 4) is adopted;    |&lt;br /&gt;
|                        |                  | see Section 1.3 for    |&lt;br /&gt;
|                        |                  | the wire encoding.     |&lt;br /&gt;
|                        |                  | The hybrid RACK-or-    |&lt;br /&gt;
|                        |                  | DUP_THRESH trigger     |&lt;br /&gt;
|                        |                  | from RFC 8985 sec. 6.2 |&lt;br /&gt;
|                        |                  | step 4 is adopted      |&lt;br /&gt;
|                        |                  | (Section 8).  QUIC&#039;s   |&lt;br /&gt;
|                        |                  | analogue in RFC 9002   |&lt;br /&gt;
|                        |                  | sec. 6.1.2 uses        |&lt;br /&gt;
|                        |                  | max(srtt, latest_rtt)  |&lt;br /&gt;
|                        |                  | as the base.           |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Karn&#039;s algorithm:      | TCP              | Karn &amp;amp; Partridge,      |&lt;br /&gt;
| no RTT sample on       |                  | &amp;quot;Improving Round-Trip  |&lt;br /&gt;
| retransmits, RTO-      |                  | Time Estimates in      |&lt;br /&gt;
| collapse freeze        |                  | Reliable Transport     |&lt;br /&gt;
|                        |                  | Protocols&amp;quot;, SIGCOMM    |&lt;br /&gt;
|                        |                  | 1987; RFC 6298 sec. 3. |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| RTO formula            | TCP              | RFC 6298 (Paxson et    |&lt;br /&gt;
| RTO = max(RTO_MIN,     |                  | al., 2011).  RTO_MIN = |&lt;br /&gt;
| srtt + (mdev &amp;lt;&amp;lt;        |                  | 5 ms is below RFC 6298 |&lt;br /&gt;
| MDEV_MUL))             |                  | sec. 2.4&#039;s 1 s SHOULD- |&lt;br /&gt;
|                        |                  | floor - a recursive-   |&lt;br /&gt;
|                        |                  | layer choice.          |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Linux asymmetric mdev  | Linux kernel     | tcp_rtt_estimator() in |&lt;br /&gt;
| estimator (default)    |                  | net/ipv4/tcp_input.c;  |&lt;br /&gt;
|                        |                  | the if(delta&amp;lt;0) m&amp;gt;&amp;gt;=3  |&lt;br /&gt;
|                        |                  | dampening is a         |&lt;br /&gt;
|                        |                  | kernel divergence from |&lt;br /&gt;
|                        |                  | RFC 6298.  RFC 6298    |&lt;br /&gt;
|                        |                  | EWMA available behind  |&lt;br /&gt;
|                        |                  | a compile flag.        |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Delayed ACK with rate  | TCP              | RFC 813 (Clark, 1982); |&lt;br /&gt;
| suppression            |                  | RFC 1122 sec. 4.2.3.2; |&lt;br /&gt;
|                        |                  | RFC 5681 sec. 4.2.     |&lt;br /&gt;
|                        |                  | Single-deadline        |&lt;br /&gt;
|                        |                  | coalescing rather than |&lt;br /&gt;
|                        |                  | &amp;quot;ack-every-other-      |&lt;br /&gt;
|                        |                  | segment&amp;quot;.              |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Zero-window-probe /    | TCP              | RFC 1122 sec.          |&lt;br /&gt;
| persist-timer          |                  | 4.2.2.17 / RFC 9293    |&lt;br /&gt;
| analogue (RDVS)        |                  | sec. 3.8.6.1.  RDVS    |&lt;br /&gt;
|                        |                  | solicits an FC reply,  |&lt;br /&gt;
|                        |                  | distinct from QUIC     |&lt;br /&gt;
|                        |                  | DATA_BLOCKED (RFC 9000 |&lt;br /&gt;
|                        |                  | sec. 19.12), which is  |&lt;br /&gt;
|                        |                  | one-way notification.  |&lt;br /&gt;
|                        |                  | MAX_RDV give-up        |&lt;br /&gt;
|                        |                  | departs from TCP.      |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Multiplexed control    | SCTP / QUIC      | SCTP chunk bundling    |&lt;br /&gt;
| on a single PCI        |                  | (RFC 9260 sec. 6.10);  |&lt;br /&gt;
|                        |                  | QUIC frame             |&lt;br /&gt;
|                        |                  | multiplexing (RFC 9000 |&lt;br /&gt;
|                        |                  | sec. 12.4).  Cleaner   |&lt;br /&gt;
|                        |                  | fit than TCP&#039;s         |&lt;br /&gt;
|                        |                  | separate-flag-bits     |&lt;br /&gt;
|                        |                  | design.                |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| ACK ranges as          | QUIC             | QUIC ACK frame (RFC    |&lt;br /&gt;
| multiple discontiguous |                  | 9000 sec. 19.3).  FRCP |&lt;br /&gt;
| acked blocks           |                  | SACK is conceptually   |&lt;br /&gt;
|                        |                  | QUIC-frame-shaped      |&lt;br /&gt;
|                        |                  | even though encoded    |&lt;br /&gt;
|                        |                  | as absolute            |&lt;br /&gt;
|                        |                  | [start,end] pairs.     |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Nonce-authenticated    | QUIC             | PATH_CHALLENGE /       |&lt;br /&gt;
| active RTT / liveness  | PATH_CHALLENGE   | PATH_RESPONSE (RFC     |&lt;br /&gt;
| probing (RTTP)         |                  | 9000 sec. 8.2,         |&lt;br /&gt;
|                        |                  | sec. 19.17, sec.       |&lt;br /&gt;
|                        |                  | 19.18).  WebRTC ICE    |&lt;br /&gt;
|                        |                  | consent-freshness      |&lt;br /&gt;
|                        |                  | (RFC 7675) is the      |&lt;br /&gt;
|                        |                  | same pattern.  QUIC&#039;s  |&lt;br /&gt;
|                        |                  | nonce is 8 octets;     |&lt;br /&gt;
|                        |                  | FRCP chooses 16.       |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Probing distinct from  | QUIC             | KA timer answers       |&lt;br /&gt;
| keepalive              |                  | &amp;quot;peer alive?&amp;quot;, RTTP    |&lt;br /&gt;
|                        |                  | answers &amp;quot;path          |&lt;br /&gt;
|                        |                  | measurable?&amp;quot;, as in    |&lt;br /&gt;
|                        |                  | QUIC PING (RFC 9000    |&lt;br /&gt;
|                        |                  | sec. 19.2) vs          |&lt;br /&gt;
|                        |                  | PATH_CHALLENGE.        |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Bare KA + ACK          | QUIC / SCTP      | QUIC PING (RFC 9000    |&lt;br /&gt;
| keepalive packets      |                  | sec. 19.2); SCTP       |&lt;br /&gt;
|                        |                  | HEARTBEAT /            |&lt;br /&gt;
|                        |                  | HEARTBEAT-ACK (RFC     |&lt;br /&gt;
|                        |                  | 9260 sec. 8.3).  SCTP  |&lt;br /&gt;
|                        |                  | HEARTBEAT also carries |&lt;br /&gt;
|                        |                  | an opaque echoed blob, |&lt;br /&gt;
|                        |                  | structurally similar   |&lt;br /&gt;
|                        |                  | to FRCP RTTP.          |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| (FFGM, LFGM)           | SCTP             | RFC 9260 sec. 3.3.1    |&lt;br /&gt;
| fragment-role bits     |                  | DATA chunk B/E bits    |&lt;br /&gt;
| (Section 7.2)          |                  | encode the same four   |&lt;br /&gt;
|                        |                  | states (B+E=SOLE,      |&lt;br /&gt;
|                        |                  | B-only=FIRST, neither  |&lt;br /&gt;
|                        |                  | =MID, E-only=LAST).    |&lt;br /&gt;
|                        |                  | Each fragment carries  |&lt;br /&gt;
|                        |                  | its own seqno/TSN and  |&lt;br /&gt;
|                        |                  | is independently       |&lt;br /&gt;
|                        |                  | retransmitted.         |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Stream byte-offset     | QUIC             | QUIC STREAM frame      |&lt;br /&gt;
| reassembly             |                  | (RFC 9000 sec. 19.8)   |&lt;br /&gt;
| (Sections 1.5, 16)     |                  | uses Offset + Length   |&lt;br /&gt;
|                        |                  | varints; FRCP uses     |&lt;br /&gt;
|                        |                  | fixed 32-bit start /   |&lt;br /&gt;
|                        |                  | end.  One stream per   |&lt;br /&gt;
|                        |                  | flow vs QUIC&#039;s many    |&lt;br /&gt;
|                        |                  | streams multiplexed.   |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| FIN end-of-stream      | TCP / QUIC       | TCP FIN flag (RFC 9293 |&lt;br /&gt;
| marker                 |                  | sec. 3.1) closes one   |&lt;br /&gt;
| (Sections 1.2, 16)     |                  | half of the byte       |&lt;br /&gt;
|                        |                  | stream; QUIC STREAM    |&lt;br /&gt;
|                        |                  | frame FIN bit (RFC     |&lt;br /&gt;
|                        |                  | 9000 sec. 19.8) does   |&lt;br /&gt;
|                        |                  | the same per stream    |&lt;br /&gt;
|                        |                  | with an immutable      |&lt;br /&gt;
|                        |                  | final-size invariance  |&lt;br /&gt;
|                        |                  | (RFC 9000 sec. 4.5:    |&lt;br /&gt;
|                        |                  | the final size is      |&lt;br /&gt;
|                        |                  | fixed once observed).  |&lt;br /&gt;
|                        |                  | FRCP&#039;s FIN consumes    |&lt;br /&gt;
|                        |                  | one packet seqno (not  |&lt;br /&gt;
|                        |                  | one byte of stream     |&lt;br /&gt;
|                        |                  | space) and is          |&lt;br /&gt;
|                        |                  | idempotent on the      |&lt;br /&gt;
|                        |                  | sender side.           |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Stream byte-credit     | QUIC             | MAX_STREAM_DATA (RFC   |&lt;br /&gt;
| flow control           |                  | 9000 sec. 4.1, sec.    |&lt;br /&gt;
| (Section 16)           |                  | 19.10).  FRCP projects |&lt;br /&gt;
|                        |                  | a per-flow byte budget |&lt;br /&gt;
|                        |                  | onto the seqno-space   |&lt;br /&gt;
|                        |                  | rwe.  Single stream    |&lt;br /&gt;
|                        |                  | per flow collapses     |&lt;br /&gt;
|                        |                  | QUIC&#039;s MAX_DATA /      |&lt;br /&gt;
|                        |                  | MAX_STREAM_            |&lt;br /&gt;
|                        |                  | DATA distinction.      |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Header protection      | QUIC             | QUIC RFC 9001 sec. 5.4 |&lt;br /&gt;
| (encrypted seqnos)     |                  | applies header         |&lt;br /&gt;
|                        |                  | protection on top of   |&lt;br /&gt;
|                        |                  | AEAD to mask the       |&lt;br /&gt;
|                        |                  | packet number.  FRCP&#039;s |&lt;br /&gt;
|                        |                  | per-flow AEAD wrap     |&lt;br /&gt;
|                        |                  | (Section 16) is wider: |&lt;br /&gt;
|                        |                  | it encrypts the entire |&lt;br /&gt;
|                        |                  | PCI including seqno    |&lt;br /&gt;
|                        |                  | because the IPCP       |&lt;br /&gt;
|                        |                  | below already routes,  |&lt;br /&gt;
|                        |                  | so no destination      |&lt;br /&gt;
|                        |                  | connection-ID needs to |&lt;br /&gt;
|                        |                  | stay in clear (cf.     |&lt;br /&gt;
|                        |                  | RFC 9000 sec. 5.2).    |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Two-bit fragment role  | SCTP             | The (FFGM, LFGM) pair  |&lt;br /&gt;
| polarity               |                  | follows SCTP B/E       |&lt;br /&gt;
|                        |                  | (begin = 1 / end = 1)  |&lt;br /&gt;
|                        |                  | rather than IPv4 MF    |&lt;br /&gt;
|                        |                  | (RFC 791 sec. 3.2),    |&lt;br /&gt;
|                        |                  | which has the inverse  |&lt;br /&gt;
|                        |                  | polarity (MF = 1 means |&lt;br /&gt;
|                        |                  | NOT last).             |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Orthogonal reliability | SCTP             | PR-SCTP (RFC 3758,     |&lt;br /&gt;
| / ordering axes        |                  | per-message partial    |&lt;br /&gt;
| (Section 2.2)          |                  | reliability) and SCTP  |&lt;br /&gt;
|                        |                  | DATA U-bit (RFC 9260   |&lt;br /&gt;
|                        |                  | sec. 3.3.1, per-       |&lt;br /&gt;
|                        |                  | message unordered)     |&lt;br /&gt;
|                        |                  | are the closest        |&lt;br /&gt;
|                        |                  | precedents for         |&lt;br /&gt;
|                        |                  | decoupling reliability |&lt;br /&gt;
|                        |                  | from ordering; FRCP    |&lt;br /&gt;
|                        |                  | sets them per-flow     |&lt;br /&gt;
|                        |                  | rather than per-       |&lt;br /&gt;
|                        |                  | message.               |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Orthogonal CRC         | UDP-Lite         | RFC 3828 (Larzon et    |&lt;br /&gt;
| (qs.ber == 0)          |                  | al., 2004) lets the    |&lt;br /&gt;
|                        |                  | sender pick a per-     |&lt;br /&gt;
|                        |                  | packet Checksum        |&lt;br /&gt;
|                        |                  | Coverage and the       |&lt;br /&gt;
|                        |                  | receiver enforce a     |&lt;br /&gt;
|                        |                  | locally configured     |&lt;br /&gt;
|                        |                  | minimum (no in-band    |&lt;br /&gt;
|                        |                  | negotiation; sec. 3.1, |&lt;br /&gt;
|                        |                  | sec. 3.3).  FRCP       |&lt;br /&gt;
|                        |                  | gates a full CRC       |&lt;br /&gt;
|                        |                  | trailer on qs.ber == 0 |&lt;br /&gt;
|                        |                  | at flow setup.         |&lt;br /&gt;
|                        |                  | Contrast TCP / SCTP    |&lt;br /&gt;
|                        |                  | (mandatory checksum)   |&lt;br /&gt;
|                        |                  | and QUIC (AEAD         |&lt;br /&gt;
|                        |                  | subsumes CRC).         |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Setup-time service     | DCCP / SCTP /    | DCCP Service Codes     |&lt;br /&gt;
| negotiation            | QUIC             | (RFC 4340 sec. 8.1.2,  |&lt;br /&gt;
|                        |                  | RFC 5595); SCTP INIT   |&lt;br /&gt;
|                        |                  | parameters (RFC 9260   |&lt;br /&gt;
|                        |                  | sec. 3.3.2); QUIC      |&lt;br /&gt;
|                        |                  | transport parameters   |&lt;br /&gt;
|                        |                  | (RFC 9000 sec. 7.4).   |&lt;br /&gt;
|                        |                  | All negotiate service  |&lt;br /&gt;
|                        |                  | properties at          |&lt;br /&gt;
|                        |                  | connection setup; only |&lt;br /&gt;
|                        |                  | RINA&#039;s QoS cube        |&lt;br /&gt;
|                        |                  | exposes them as an     |&lt;br /&gt;
|                        |                  | orthogonal vector.     |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.1. Original to FRCP (no clean prior art) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - Pre-DRF NACK (Section 9): receiver-driven nudge exploiting&lt;br /&gt;
    snd_cr.inact &amp;gt; rcv_cr.inact.  Closest analogues are SCTP Gap Ack&lt;br /&gt;
    Blocks (RFC 9260 sec. 3.3.4) and DCCP Ack Vector (RFC 4340&lt;br /&gt;
    sec. 11.4) - both let the receiver describe gaps to the sender,&lt;br /&gt;
    but neither targets the cross-epoch / pre-DRF case.&lt;br /&gt;
  - MAX_RDV window-probe give-up: neither TCP (persist-timer&lt;br /&gt;
    probes until application or R2 abort, RFC 9293 sec. 3.8.6.1)&lt;br /&gt;
    nor QUIC has an explicit FC-give-up counter.  A recursive-&lt;br /&gt;
    network choice: outer layers can drop the flow.&lt;br /&gt;
  - Skip-past-gap reassembly (Section 7.2): SCTP fragments and&lt;br /&gt;
    reassembles every flow regardless of reliability/ordering,&lt;br /&gt;
    using its own per-stream reassembly queue; QUIC fragments via&lt;br /&gt;
    STREAM offsets.  FRCP fragments best-effort flows too, but&lt;br /&gt;
    the receiver drops the broken prefix the moment a later run-&lt;br /&gt;
    start (FIRST or SOLE role) is visible inside the RQ_SIZE-wide&lt;br /&gt;
    reorder ring - no IP-frag-style timeout, no SCTP-style&lt;br /&gt;
    explicit abort.  If no later run-start arrives within the&lt;br /&gt;
    ring, frag_run_inspect returns NOT_READY and the partial run&lt;br /&gt;
    keeps its slots; the next inspect retries.  The trade-off: a&lt;br /&gt;
    permanently-lost MID in a long isolated run holds slots until&lt;br /&gt;
    either a later FIRST/SOLE appears in the ring or the writer&lt;br /&gt;
    stops, at which point the slots are reclaimed on flow&lt;br /&gt;
    teardown.&lt;br /&gt;
  - Reassembly deferred to consume time (Section 7.2), message&lt;br /&gt;
    mode only (qos.service == SVC_MESSAGE): SCTP (RFC 9260&lt;br /&gt;
    sec. 6.9), QUIC (RFC 9000 sec. 2.2), and TCP (RFC 9293) all&lt;br /&gt;
    hold reassembly state at the receive boundary.  FRCP message-&lt;br /&gt;
    mode leaves fragments in the shared-memory ring until&lt;br /&gt;
    flow_read pulls and lands the SDU directly in the caller&#039;s&lt;br /&gt;
    buffer.  Stream mode (Section 16) uses the standard QUIC-&lt;br /&gt;
    style direct ring placement on receive and does not defer.&lt;br /&gt;
    The optimisation is enabled by the Shared-Memory Subsystem&lt;br /&gt;
    (SSM) packet-buffer ring (see struct ssm_pk_buff at&lt;br /&gt;
    Section 1.1); the analogue is OS-level scatter-gather I/O&lt;br /&gt;
    (recvmsg+iovec), not a transport-layer prior art.&lt;br /&gt;
  - TLP-equivalent tail-loss recovery (RFC 8985 sec. 7;&lt;br /&gt;
    RFC 9002 sec. 6.2): FRCP does not emit an explicit Tail Loss&lt;br /&gt;
    Probe packet, but the same goal is met implicitly by RACK&lt;br /&gt;
    loss detection (Section 8) firing on a non-advancing&lt;br /&gt;
    cumulative ACK once the head-of-line slot ages past the RACK&lt;br /&gt;
    reorder window R = MIN(reo_wnd_mult * min_RTT / 4, SRTT) -&lt;br /&gt;
    well below RTO = max(2 * SRTT, SRTT + (mdev &amp;lt;&amp;lt; MDEV_MUL)).&lt;br /&gt;
    A receiver-driven nudge is also available via the pre-DRF&lt;br /&gt;
    NACK (Section 9).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.2. Not adopted ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - Slow start, congestion window (cwnd), Additive Increase /&lt;br /&gt;
    Multiplicative Decrease (AIMD), NewReno cwnd inflation.&lt;br /&gt;
    Congestion control lives in the IPCP CA policies and is&lt;br /&gt;
    driven by Explicit Congestion Notification (ECN, RFC 3168).&lt;br /&gt;
  - Nagle / silly-window-syndrome (SWS) avoidance (RFC 896, RFC&lt;br /&gt;
    1122 sec. 4.2.3.4).  (Deferred work, not adopted in the&lt;br /&gt;
    current spec.)&lt;br /&gt;
  - TCP Timestamps (RFC 7323) / Protection Against Wrapped&lt;br /&gt;
    Sequences (PAWS) - RTT measurement uses RTTP,&lt;br /&gt;
    not per-segment timestamps.  A peer-supplied timestamp echoed&lt;br /&gt;
    on every ACK lets a malicious peer drive the srtt estimate&lt;br /&gt;
    arbitrarily low, collapsing the RTO and triggering a self-&lt;br /&gt;
    inflicted retransmit storm.  RTTP confines RTT measurement to&lt;br /&gt;
    nonce-authenticated probe round-trips, where a forged echo is&lt;br /&gt;
    rejected before it can reach the estimator.&lt;br /&gt;
  - ECN (Explicit Congestion Notification) response inside FRCP&lt;br /&gt;
    (consumed by IPCP Congestion Avoidance / CA).&lt;br /&gt;
  - IP-style fragment-offset reassembly (RFC 791 sec. 3.2; RFC 8200&lt;br /&gt;
    sec. 4.5).  Message-mode FRCP relies on the FRCT rq[] reorder&lt;br /&gt;
    ring keyed by seqno (shared by FRTX and best-effort flows) to&lt;br /&gt;
    put fragments back in order; no separate offset field is&lt;br /&gt;
    needed and no IP-style hole-list reassembly buffer is kept.&lt;br /&gt;
    Stream-mode FRCP does carry [start, end) byte offsets&lt;br /&gt;
    (Section 1.5) for direct ring placement on receive.&lt;br /&gt;
  - QUIC STREAM offset+length framing on *every* flow (RFC 9000&lt;br /&gt;
    sec. 19.8).  Message-mode FRCP uses the SCTP-style B/E flag-&lt;br /&gt;
    bit encoding (FFGM/LFGM) and skips the offsets; stream-mode&lt;br /&gt;
    FRCP adopts the QUIC offset model (heritage table above).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 16. Stream-mode flows ==&lt;br /&gt;
&lt;br /&gt;
When a flow is allocated with qos.service == SVC_STREAM both peers&lt;br /&gt;
switch to byte-stream semantics, layered on top of the FRTX reorder&lt;br /&gt;
machinery already described in Sections 6-8.&lt;br /&gt;
&lt;br /&gt;
=== 16.1. Send ===&lt;br /&gt;
&lt;br /&gt;
The sender splits the caller&#039;s octets into chunks of at most&lt;br /&gt;
(frag_mtu - base PCI - stream PCI extension) octets (Sections 1.1&lt;br /&gt;
and 1.5).  Each chunk is one DATA packet with its own seqno and a&lt;br /&gt;
[start, end) byte range copied from a monotonic stream counter.&lt;br /&gt;
In stream mode FFGM and LFGM are unused and MUST be transmitted as&lt;br /&gt;
zero; the per-byte position is carried by the [start, end)&lt;br /&gt;
extension instead.&lt;br /&gt;
&lt;br /&gt;
End-of-stream is signalled with a 0-byte DATA packet that has FIN&lt;br /&gt;
(bit 12) set, emitted on the FIN triggers listed in Section 1.2&lt;br /&gt;
(WR-half close, flow_dealloc, and any other path that yields the&lt;br /&gt;
final byte).  The sender MUST emit at most one FIN per flow; its&lt;br /&gt;
[start, end) MUST equal [final-byte, final-byte) (i.e., empty&lt;br /&gt;
interval at the final byte position; final-size invariance,&lt;br /&gt;
analogous to QUIC RFC 9000 sec. 4.5).  Idempotency is enforced by&lt;br /&gt;
an snd_fin_sent guard.&lt;br /&gt;
&lt;br /&gt;
=== 16.2. Receive ===&lt;br /&gt;
&lt;br /&gt;
On arrival the receiver places the payload directly into a per-flow&lt;br /&gt;
byte-indexed receive ring of width ring_sz (octets) at the position&lt;br /&gt;
indicated by start, with a two-segment memcpy across the ring&lt;br /&gt;
boundary if needed.  Receipt is recorded in the FRTX reorder&lt;br /&gt;
machinery (Section 6.2) augmented with the packet&#039;s start, end, and&lt;br /&gt;
FIN bit per slot.  When a packet&#039;s [start, end) front-overlaps&lt;br /&gt;
bytes already at or below the byte high-water mark, the overlap is&lt;br /&gt;
trimmed before placement so the same byte is never written twice.&lt;br /&gt;
After stashing, the receiver advances lwe and the byte high-water&lt;br /&gt;
mark across any newly-contiguous prefix.  Each slot advanced MUST&lt;br /&gt;
satisfy `start == the last-delivered slot&#039;s end`; a slot whose&lt;br /&gt;
start does not equal that end is silently dropped at delivery time&lt;br /&gt;
(the seqno is consumed, no stream bytes contributed) and the high-&lt;br /&gt;
water mark does not advance past it.  The stream byte-stream&lt;br /&gt;
stalls at that point - there is no flow-tear-down on mismatch.&lt;br /&gt;
This filters spliced or off-path-injected slots that fall in&lt;br /&gt;
window without strong cryptographic authentication.&lt;br /&gt;
&lt;br /&gt;
A FIN slot marks end-of-stream at advance time only if its byte&lt;br /&gt;
position equals the last-delivered slot&#039;s end; otherwise the FIN&lt;br /&gt;
is ignored and the corresponding seqno occupies a slot but&lt;br /&gt;
contributes no stream bytes.  No packet buffer is held after the&lt;br /&gt;
ring copy.&lt;br /&gt;
&lt;br /&gt;
=== 16.3. Read ===&lt;br /&gt;
&lt;br /&gt;
flow_read returns up to count octets from the contiguous prefix&lt;br /&gt;
[next, high-water), where next is the byte the application has&lt;br /&gt;
already consumed up to and high-water is the rightmost contiguous&lt;br /&gt;
byte received.  When the stream is fully drained AND end-of-stream&lt;br /&gt;
(EOS) was observed (next == EOS byte position), flow_read returns&lt;br /&gt;
0 (EOF) - the same shape POSIX read(2) uses on TCP after a peer&lt;br /&gt;
FIN.&lt;br /&gt;
&lt;br /&gt;
=== 16.4. Flow control ===&lt;br /&gt;
&lt;br /&gt;
ACK / SACK / RACK / RTO machinery is unchanged; the FRTX reorder&lt;br /&gt;
ring is reused as a per-seqno received-bitmap.  Let per_pkt =&lt;br /&gt;
(frag_mtu - base PCI - stream PCI extension), the maximum stream-&lt;br /&gt;
byte payload one DATA packet can carry (Section 16.1).  The&lt;br /&gt;
receive window advertised in FC is clamped so the byte window&lt;br /&gt;
(ring_sz) cannot be overrun: the seqno-space rwe is at most&lt;br /&gt;
`rcv_cr.lwe + ring_sz / per_pkt`.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC byte-credit flow-control model&lt;br /&gt;
(MAX_STREAM_DATA, RFC 9000 sec. 4.1 and sec. 19.10) projected onto&lt;br /&gt;
seqno space.  With one stream per flow there is no MAX_DATA /&lt;br /&gt;
MAX_STREAM_DATA distinction.  Receiver-side silly-window-syndrome&lt;br /&gt;
(SWS) avoidance (RFC 9293 sec. 3.8.6.2.2) is achieved by combining&lt;br /&gt;
the consume-time rwe bump with the global non-shrink rule from&lt;br /&gt;
Section 11.&lt;br /&gt;
&lt;br /&gt;
=== 16.5. Security considerations ===&lt;br /&gt;
&lt;br /&gt;
Threat model.  An attacker that can observe (on-path passive) or&lt;br /&gt;
predict (off-path blind) the flow&#039;s seqnos and byte offsets on an&lt;br /&gt;
unencrypted stream flow can inject DATA or FIN at any in-window&lt;br /&gt;
position.  The in-line consistency checks above (start == prior&lt;br /&gt;
end on advance; FIN MUST be 0-byte; FIN MUST sit at the final&lt;br /&gt;
byte position) realise the spirit of RFC 5961&#039;s &amp;quot;sequence-window&lt;br /&gt;
plus exact-position match for control bits&amp;quot; without an explicit&lt;br /&gt;
challenge-ACK probe; they make a few specific blind attack shapes&lt;br /&gt;
harder but are not cryptographic authentication. This is&lt;br /&gt;
comparable to TCP without the TCP Authentication Option (TCP-AO,&lt;br /&gt;
RFC 5925), tighter than a&lt;br /&gt;
pre-RFC-5961 TCP stack, and roughly equivalent to a modern&lt;br /&gt;
RFC 5961 stack against blind off-path injection - none of these&lt;br /&gt;
help once the attacker can sniff. TLS over TCP (RFC 8446)&lt;br /&gt;
encrypts only the TCP payload and leaves TCP seqnos, ACKs, FIN,&lt;br /&gt;
and RST in the clear, so TLS does NOT defend against TCP-header-&lt;br /&gt;
level injection; QUIC (RFC 9000) hides packet numbers under&lt;br /&gt;
header protection (RFC 9001 sec. 5.4), so this specific weakness&lt;br /&gt;
does not apply to QUIC.&lt;br /&gt;
&lt;br /&gt;
Mitigation: AEAD.  When the flow has encryption enabled the&lt;br /&gt;
recommended AEAD ciphers (AES-GCM, RFC 5288; or ChaCha20-Poly1305,&lt;br /&gt;
RFC 8439) wrap the entire FRCP packet on the wire - PCI, stream&lt;br /&gt;
extension, body, and the CRC trailer when ber == 0 - under a&lt;br /&gt;
per-flow symmetric key derived from the flow&#039;s own key exchange&lt;br /&gt;
(Section 1.1).  The AEAD tag (~2^-128 forgery probability)&lt;br /&gt;
dominates the CRC (~2^-32) for integrity in this mode but the CRC&lt;br /&gt;
trailer is currently retained inside the wrap (see Section 1.1).&lt;br /&gt;
Implementations MUST NOT rely on the security properties below&lt;br /&gt;
when a non-AEAD cipher (e.g. AES-CTR alone) is negotiated; non-&lt;br /&gt;
AEAD modes provide confidentiality only and the threat-model&lt;br /&gt;
claims do not hold.&lt;br /&gt;
&lt;br /&gt;
With an AEAD cipher in use, seqnos, byte offsets, and the FIN bit&lt;br /&gt;
are both authenticated and confidential. Against an off-path or&lt;br /&gt;
on-path-passive attacker this is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - Stronger than TCP+TLS (TCP header in the clear).&lt;br /&gt;
  - Stronger than TCP+TCP-AO (header authenticated but visible).&lt;br /&gt;
  - Comparable to IPsec ESP transport mode (RFC 4303), which&lt;br /&gt;
    similarly authenticates and encrypts the upper-layer header&lt;br /&gt;
    plus payload, and to QUIC packet protection (RFC 9001 sec. 5),&lt;br /&gt;
    with the difference that QUIC must leave the destination&lt;br /&gt;
    connection ID in the clear for routing whereas FRCP relies on&lt;br /&gt;
    the IPCP below for delivery and can therefore encrypt its&lt;br /&gt;
    entire PCI.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Keying granularity.  FRCP runs key exchange (kex) per flow, so&lt;br /&gt;
each flow_alloc yields independent symmetric keys.  This is&lt;br /&gt;
finer-grained than QUIC (per-connection, RFC 9001, where one&lt;br /&gt;
handshake covers all multiplexed streams) and finer-grained than&lt;br /&gt;
typical IPsec deployment (per-host-pair Security Associations,&lt;br /&gt;
SAs).  Forward secrecy follows from the kex when an ephemeral&lt;br /&gt;
Diffie-Hellman exchange (DHE), or a hybrid mode (classical DH +&lt;br /&gt;
post-quantum Key Encapsulation Mechanism / KEM), is selected.&lt;br /&gt;
&lt;br /&gt;
Replay protection.  The AEAD layer itself does NOT carry an&lt;br /&gt;
explicit anti-replay window (unlike IPsec ESP, RFC 4303 sec.&lt;br /&gt;
3.4.3, or DTLS, RFC 9147 sec. 4.5.1).  For FRCP-engaged flows the&lt;br /&gt;
seqno-space duplicate-suppression in Section 6.2 rejects replayed&lt;br /&gt;
DATA after the AEAD strips the wrap, because the AEAD authenticates&lt;br /&gt;
the seqno and a replay re-presents an old seqno that is then&lt;br /&gt;
discarded either as a duplicate (still inside the receive window)&lt;br /&gt;
or as outside the receive window, depending on how far lwe has&lt;br /&gt;
advanced since the original packet was delivered.  RAW&lt;br /&gt;
(qos.service == SVC_RAW) flows have no FRCP layer and therefore&lt;br /&gt;
no replay protection at the AEAD layer either; deployments that&lt;br /&gt;
need replay rejection on RAW flows MUST provide it at a higher&lt;br /&gt;
layer.&lt;br /&gt;
&lt;br /&gt;
Layering.  The AEAD wrap sits below FRCP on the data path, so&lt;br /&gt;
RAW best-effort flows (qos.service == SVC_RAW, the UDP-equivalent&lt;br /&gt;
service of Section 2.2) inherit the same per-flow integrity +&lt;br /&gt;
confidentiality scope as FRCP-engaged flows - whatever the IPCP&lt;br /&gt;
and FRCP (if any) put on the wire is what the AEAD authenticates.&lt;br /&gt;
No DTLS-equivalent layering is required for confidentiality and&lt;br /&gt;
integrity; replay protection above AEAD is a separate concern as&lt;br /&gt;
noted above.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 17. References ==&lt;br /&gt;
&lt;br /&gt;
This section lists the IETF documents, published works, and&lt;br /&gt;
source-code references cited inline elsewhere in this document.&lt;br /&gt;
IETF documents are cited inline as &amp;quot;RFC NNNN sec. X.Y&amp;quot;; books,&lt;br /&gt;
journal papers, and source-code references are cited inline by&lt;br /&gt;
author and year (or by file and function name) and are listed&lt;br /&gt;
here for convenience.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.1. IETF documents ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 791]   J. Postel, &amp;quot;Internet Protocol&amp;quot;, STD 5, RFC 791,&lt;br /&gt;
              September 1981.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 793]   J. Postel, &amp;quot;Transmission Control Protocol&amp;quot;, STD 7,&lt;br /&gt;
              RFC 793, September 1981.  Obsoleted by RFC 9293.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 813]   D. D. Clark, &amp;quot;Window and Acknowledgement Strategy&lt;br /&gt;
              in TCP&amp;quot;, RFC 813, July 1982.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 896]   J. Nagle, &amp;quot;Congestion Control in IP/TCP&lt;br /&gt;
              Internetworks&amp;quot;, RFC 896, January 1984.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 1122]  R. Braden (ed.), &amp;quot;Requirements for Internet Hosts&lt;br /&gt;
              -- Communication Layers&amp;quot;, STD 3, RFC 1122,&lt;br /&gt;
              October 1989.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 2018]  M. Mathis, J. Mahdavi, S. Floyd, A. Romanow,&lt;br /&gt;
              &amp;quot;TCP Selective Acknowledgment Options&amp;quot;, RFC 2018,&lt;br /&gt;
              October 1996.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 2119]  S. Bradner, &amp;quot;Key words for use in RFCs to Indicate&lt;br /&gt;
              Requirement Levels&amp;quot;, BCP 14, RFC 2119, March 1997.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 2883]  S. Floyd, J. Mahdavi, M. Mathis, M. Podolsky,&lt;br /&gt;
              &amp;quot;An Extension to the Selective Acknowledgement&lt;br /&gt;
              (SACK) Option for TCP&amp;quot;, RFC 2883, July 2000.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 3758]  R. Stewart, M. Ramalho, Q. Xie, M. Tuexen,&lt;br /&gt;
              P. Conrad, &amp;quot;Stream Control Transmission Protocol&lt;br /&gt;
              (SCTP) Partial Reliability Extension&amp;quot;, RFC 3758,&lt;br /&gt;
              May 2004.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 3828]  L-A. Larzon, M. Degermark, S. Pink, L-E. Jonsson&lt;br /&gt;
              (ed.), G. Fairhurst (ed.), &amp;quot;The Lightweight User&lt;br /&gt;
              Datagram Protocol (UDP-Lite)&amp;quot;, RFC 3828,&lt;br /&gt;
              July 2004.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 4303]  S. Kent, &amp;quot;IP Encapsulating Security Payload&lt;br /&gt;
              (ESP)&amp;quot;, RFC 4303, December 2005.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 4340]  E. Kohler, M. Handley, S. Floyd, &amp;quot;Datagram&lt;br /&gt;
              Congestion Control Protocol (DCCP)&amp;quot;, RFC 4340,&lt;br /&gt;
              March 2006.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 5288]  J. Salowey, A. Choudhury, D. McGrew, &amp;quot;AES Galois&lt;br /&gt;
              Counter Mode (GCM) Cipher Suites for TLS&amp;quot;,&lt;br /&gt;
              RFC 5288, August 2008.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 5595]  G. Fairhurst, &amp;quot;The Datagram Congestion Control&lt;br /&gt;
              Protocol (DCCP) Service Codes&amp;quot;, RFC 5595,&lt;br /&gt;
              September 2009.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 5681]  M. Allman, V. Paxson, E. Blanton, &amp;quot;TCP Congestion&lt;br /&gt;
              Control&amp;quot;, RFC 5681, September 2009.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 5925]  J. Touch, A. Mankin, R. Bonica, &amp;quot;The TCP&lt;br /&gt;
              Authentication Option&amp;quot;, RFC 5925, June 2010.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 5961]  A. Ramaiah, R. Stewart, M. Dalal, &amp;quot;Improving&lt;br /&gt;
              TCP&#039;s Robustness to Blind In-Window Attacks&amp;quot;,&lt;br /&gt;
              RFC 5961, August 2010.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 6298]  V. Paxson, M. Allman, J. Chu, M. Sargent,&lt;br /&gt;
              &amp;quot;Computing TCP&#039;s Retransmission Timer&amp;quot;, RFC 6298,&lt;br /&gt;
              June 2011.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 6528]  F. Gont, S. Bellovin, &amp;quot;Defending against Sequence&lt;br /&gt;
              Number Attacks&amp;quot;, RFC 6528, February 2012.&lt;br /&gt;
              Obsoletes RFC 1948.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 6582]  T. Henderson, S. Floyd, A. Gurtov, Y. Nishida,&lt;br /&gt;
              &amp;quot;The NewReno Modification to TCP&#039;s Fast Recovery&lt;br /&gt;
              Algorithm&amp;quot;, RFC 6582, April 2012.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 7323]  D. Borman, B. Braden, V. Jacobson,&lt;br /&gt;
              R. Scheffenegger (ed.), &amp;quot;TCP Extensions for High&lt;br /&gt;
              Performance&amp;quot;, RFC 7323, September 2014.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 7675]  M. Perumal, D. Wing, R. Ravindranath, T. Reddy,&lt;br /&gt;
              M. Thomson, &amp;quot;Session Traversal Utilities for NAT&lt;br /&gt;
              (STUN) Usage for Consent Freshness&amp;quot;, RFC 7675,&lt;br /&gt;
              October 2015.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 8174]  B. Leiba, &amp;quot;Ambiguity of Uppercase vs Lowercase in&lt;br /&gt;
              RFC 2119 Key Words&amp;quot;, BCP 14, RFC 8174, May 2017.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 8200]  S. Deering, R. Hinden, &amp;quot;Internet Protocol,&lt;br /&gt;
              Version 6 (IPv6) Specification&amp;quot;, STD 86, RFC 8200,&lt;br /&gt;
              July 2017.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 8439]  Y. Nir, A. Langley, &amp;quot;ChaCha20 and Poly1305 for IETF&lt;br /&gt;
              Protocols&amp;quot;, RFC 8439, June 2018.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 8446]  E. Rescorla, &amp;quot;The Transport Layer Security (TLS)&lt;br /&gt;
              Protocol Version 1.3&amp;quot;, RFC 8446, August 2018.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 8985]  Y. Cheng, N. Cardwell, N. Dukkipati, P. Jha,&lt;br /&gt;
              &amp;quot;The RACK-TLP Loss Detection Algorithm for TCP&amp;quot;,&lt;br /&gt;
              RFC 8985, February 2021.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 9000]  J. Iyengar (ed.), M. Thomson (ed.), &amp;quot;QUIC: A&lt;br /&gt;
              UDP-Based Multiplexed and Secure Transport&amp;quot;,&lt;br /&gt;
              RFC 9000, May 2021.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 9001]  M. Thomson (ed.), S. Turner (ed.), &amp;quot;Using TLS to&lt;br /&gt;
              Secure QUIC&amp;quot;, RFC 9001, May 2021.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 9002]  J. Iyengar (ed.), I. Swett (ed.), &amp;quot;QUIC Loss&lt;br /&gt;
              Detection and Congestion Control&amp;quot;, RFC 9002,&lt;br /&gt;
              May 2021.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 9147]  E. Rescorla, H. Tschofenig, N. Modadugu,&lt;br /&gt;
              &amp;quot;The Datagram Transport Layer Security (DTLS)&lt;br /&gt;
              Protocol Version 1.3&amp;quot;, RFC 9147, April 2022.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 9260]  R. Stewart, M. Tuexen, K. Nielsen, &amp;quot;Stream Control&lt;br /&gt;
              Transmission Protocol&amp;quot;, RFC 9260, June 2022.&lt;br /&gt;
              Obsoletes RFC 4960.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 9293]  W. Eddy (ed.), &amp;quot;Transmission Control Protocol&lt;br /&gt;
              (TCP)&amp;quot;, STD 7, RFC 9293, August 2022.  Obsoletes&lt;br /&gt;
              RFC 793 and several follow-ons; updates RFC 1122&lt;br /&gt;
              and others.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.2. Books and journal papers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - J. Day, &amp;quot;Patterns in Network Architecture: A Return to&lt;br /&gt;
    Fundamentals&amp;quot;, Prentice Hall, 2008.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - E. Grasa et al., &amp;quot;IRATI: investigating RINA as an alternative&lt;br /&gt;
    to TCP/IP&amp;quot;, Computer Networks, Vol. 92, December 2015.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - P. Karn, C. Partridge, &amp;quot;Improving Round-Trip Time Estimates&lt;br /&gt;
    in Reliable Transport Protocols&amp;quot;, ACM SIGCOMM, August 1987.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - R. W. Watson, &amp;quot;Timer-Based Mechanisms in Reliable Transport&lt;br /&gt;
    Protocol Connection Management&amp;quot;, Computer Networks, Vol. 5,&lt;br /&gt;
    1981.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.3. Source-code references ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - tcp_rtt_estimator() in net/ipv4/tcp_input.c of the Linux&lt;br /&gt;
    kernel, defining the asymmetric mdev variance update used as&lt;br /&gt;
    FRCP&#039;s default RTT estimator (Section 12).  Line-stable&lt;br /&gt;
    browseable copy at&lt;br /&gt;
    https://elixir.bootlin.com/linux/latest/source/net/ipv4/tcp_input.c.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1911</id>
		<title>Flow and Retransmission Control Protocol</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Flow_and_Retransmission_Control_Protocol&amp;diff=1911"/>
		<updated>2026-05-17T13:50:31Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= FRCP - Flow and Retransmission Control Protocol =&lt;br /&gt;
&lt;br /&gt;
FRCP runs end-to-end between two peers over a flow.  It delivers&lt;br /&gt;
reliability, in-order delivery, flow control, and liveness.&lt;br /&gt;
Congestion Control (CC) is not in FRCP - that lives in the IPC&lt;br /&gt;
Process (IPCP) Congestion Avoidance (CA) policies, orthogonal to&lt;br /&gt;
FRCP.  Flow allocation, naming, and IPCP lifecycle are handled by&lt;br /&gt;
the IPC Resource Manager daemon (IRMd).&lt;br /&gt;
&lt;br /&gt;
FRCT (Flow and Retransmission Control Task) is the libouroboros&lt;br /&gt;
implementation of FRCP; the task lives in src/lib/frct.c.  The&lt;br /&gt;
remainder of this document describes the FRCP wire protocol and the&lt;br /&gt;
behaviour FRCT realises.  Code symbols retain the FRCT_ prefix&lt;br /&gt;
(FRCT_DATA, FRCT_RXM, ...) because they belong to the implementing&lt;br /&gt;
task; this document references them verbatim.&lt;br /&gt;
&lt;br /&gt;
The keywords &amp;quot;MUST&amp;quot;, &amp;quot;MUST NOT&amp;quot;, &amp;quot;REQUIRED&amp;quot;, &amp;quot;SHALL&amp;quot;, &amp;quot;SHALL NOT&amp;quot;,&lt;br /&gt;
&amp;quot;SHOULD&amp;quot;, &amp;quot;SHOULD NOT&amp;quot;, &amp;quot;RECOMMENDED&amp;quot;, &amp;quot;MAY&amp;quot;, and &amp;quot;OPTIONAL&amp;quot; in&lt;br /&gt;
this document are to be interpreted as described in BCP 14 (Best&lt;br /&gt;
Current Practice; RFC 2119, RFC 8174) when, and only when, they&lt;br /&gt;
appear in all capitals.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Notation ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  u32, u8       Unsigned 32-bit / 8-bit integers (kernel-C style).&lt;br /&gt;
  ns            Nanoseconds.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Modular sequence-number comparators (32-bit, modulo 2^32):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    before(a, b)  ==  (int32_t)(a - b) &amp;lt; 0&lt;br /&gt;
    after(a, b)   ==  before(b, a)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Used throughout for ackno / seqno ordering checks.&lt;br /&gt;
&lt;br /&gt;
Round-Trip Time (RTT) abbreviations used throughout:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    SRTT          Smoothed RTT estimate (RFC 6298).&lt;br /&gt;
    mdev          Mean deviation of RTT (Linux variance estimator).&lt;br /&gt;
    EWMA          Exponentially Weighted Moving Average.&lt;br /&gt;
    RTO           Retransmission Timeout, max(RTO_MIN,&lt;br /&gt;
                  srtt + (mdev &amp;lt;&amp;lt; MDEV_MUL)).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Timer-bound symbols t_a (a-timer, ACK delay) and t_r (r-timer,&lt;br /&gt;
retransmission window) are defined in Section 8; t_mpl (Maximum&lt;br /&gt;
Packet Lifetime) is introduced in Section 2.1 (the inact field)&lt;br /&gt;
with heritage in Section 15.&lt;br /&gt;
&lt;br /&gt;
Wire-format diagrams follow the IETF convention: bit 0 is the&lt;br /&gt;
leftmost (most significant) bit and fields are in network byte&lt;br /&gt;
order unless stated otherwise.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Table of Contents ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  1. Wire format&lt;br /&gt;
     1.1. PCI header&lt;br /&gt;
     1.2. Flag bits&lt;br /&gt;
     1.3. SACK payload&lt;br /&gt;
     1.4. RTTP payload&lt;br /&gt;
     1.5. Stream PCI extension&lt;br /&gt;
  2. Per-flow state and service modes&lt;br /&gt;
     2.1. Per-flow state&lt;br /&gt;
     2.2. Service modes (orthogonal axes)&lt;br /&gt;
  3. Protocol parameters&lt;br /&gt;
  4. Sequence-number rotation (DRF)&lt;br /&gt;
  5. Send path&lt;br /&gt;
  6. Receive path&lt;br /&gt;
     6.1. Early-exit dispatch&lt;br /&gt;
     6.2. Locked main path&lt;br /&gt;
  7. Read path and reassembly&lt;br /&gt;
     7.1. Read path&lt;br /&gt;
     7.2. Fragmentation and reassembly&lt;br /&gt;
  8. Retransmission&lt;br /&gt;
  9. Pre-DRF NACK&lt;br /&gt;
 10. Cumulative + selective ACK&lt;br /&gt;
 11. Flow control&lt;br /&gt;
 12. RTT estimation&lt;br /&gt;
 13. Liveness (keepalive)&lt;br /&gt;
 14. Linger / teardown&lt;br /&gt;
 15. Heritage and adopted techniques&lt;br /&gt;
     15.1. Original to FRCP (no clean prior art)&lt;br /&gt;
     15.2. Not adopted&lt;br /&gt;
 16. Stream-mode flows&lt;br /&gt;
     16.1. Send&lt;br /&gt;
     16.2. Receive&lt;br /&gt;
     16.3. Read&lt;br /&gt;
     16.4. Flow control&lt;br /&gt;
     16.5. Security considerations&lt;br /&gt;
 17. References&lt;br /&gt;
     17.1. IETF documents&lt;br /&gt;
     17.2. Books and journal papers&lt;br /&gt;
     17.3. Source-code references&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 1. Wire format ==&lt;br /&gt;
&lt;br /&gt;
=== 1.1. PCI header ===&lt;br /&gt;
&lt;br /&gt;
Fixed 16-octet base Protocol-Control Information (PCI) header&lt;br /&gt;
prefixed to every FRCP packet (RFC convention: bit 0 leftmost,&lt;br /&gt;
most-significant bit first).  All multi-byte fields except hcs&lt;br /&gt;
are in network byte order; hcs is an opaque 16-bit value that&lt;br /&gt;
the receiver recomputes from the wire bytes and compares to the&lt;br /&gt;
in-place pci-&amp;gt;hcs read, so its on-wire byte order need only&lt;br /&gt;
match between peers running compatible builds.  DATA packets on&lt;br /&gt;
stream-mode flows carry an additional 8-octet extension (see&lt;br /&gt;
Section 1.5); SACK and RTTP carry their own payloads after the&lt;br /&gt;
base PCI.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |             flags             |              hcs              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            window                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            seqno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            ackno                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                     payload (variable) ...&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  flags   - feature/type bitmap (see 1.2).&lt;br /&gt;
  hcs     - CRC-16-CCITT-FALSE Header Check Sequence (HCS) over&lt;br /&gt;
            flags + window + seqno + ackno (+ stream extension when&lt;br /&gt;
            present); the two octets of the hcs field itself are&lt;br /&gt;
            omitted from the CRC input.  Verified on receive before&lt;br /&gt;
            any flag-driven dispatch.&lt;br /&gt;
  window  - receiver-advertised right window edge (valid iff FC).&lt;br /&gt;
  seqno   - per-flow sequence number.&lt;br /&gt;
  ackno   - cumulative Acknowledgement (ACK) (valid iff ACK).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A single packet can simultaneously carry DATA + ACK + FC (Flow&lt;br /&gt;
Control) + RXM (Retransmission) by ORing flag bits; the PCI&lt;br /&gt;
multiplexes control on the same wire frame in the spirit of SCTP&lt;br /&gt;
chunk bundling (RFC 9260 sec. 6.10) and QUIC frame multiplexing&lt;br /&gt;
(RFC 9000 sec. 12.4).  DATA-bearing packets carry the caller&#039;s&lt;br /&gt;
payload after the PCI; SACK (Selective Acknowledgement) and RTTP&lt;br /&gt;
(Round-Trip Time Probe) carry their own typed payloads after the&lt;br /&gt;
PCI.&lt;br /&gt;
&lt;br /&gt;
Optional framing (per-flow, see Section 2.2).  On the wire, the&lt;br /&gt;
order from inside out is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    [   PCI + body          ]    -- the FRCP packet&lt;br /&gt;
    [   PCI + body + CRC-32 ]    -- CRC-32 covers the body only (PCI&lt;br /&gt;
                                    is in HCS); appended iff qs.ber&lt;br /&gt;
                                    == 0 on DATA, or on every SACK&lt;br /&gt;
                                    packet&lt;br /&gt;
    [ AEAD-wrap of above    ]    -- iff Authenticated Encryption&lt;br /&gt;
                                    with Associated Data (AEAD) is&lt;br /&gt;
                                    enabled&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - HCS in the PCI covers the header fields on every packet and is&lt;br /&gt;
    verified before any flag-driven dispatch.&lt;br /&gt;
  - The CRC-32 trailer (IEEE 802.3 / zlib reflected polynomial&lt;br /&gt;
    0xEDB88320, init 0xFFFFFFFF, xor-out 0xFFFFFFFF) covers the&lt;br /&gt;
    body on DATA when qs.ber == 0 and on every SACK packet; the&lt;br /&gt;
    trailer is written as a raw uint32_t (the same convention as&lt;br /&gt;
    hcs: opaque on the wire as long as both peers run compatible&lt;br /&gt;
    builds).  The PCI is not under the CRC (Cyclic Redundancy&lt;br /&gt;
    Check) because the HCS already protects it.  It is&lt;br /&gt;
    appended before AEAD encryption and therefore rides inside the&lt;br /&gt;
    AEAD wrap when both are active; the AEAD tag (~2^-128 forgery&lt;br /&gt;
    probability) dominates the CRC (~2^-32) for integrity in that&lt;br /&gt;
    mode but the CRC trailer is currently retained.&lt;br /&gt;
  - When encryption is enabled, the entire (possibly-CRC&#039;d) FRCP&lt;br /&gt;
    packet is wrapped with AEAD inside the shared-memory packet&lt;br /&gt;
    buffer (spb, struct ssm_pk_buff); the packet grows by the AEAD&lt;br /&gt;
    overhead, namely a leading nonce / Initialization Vector (IV)&lt;br /&gt;
    of headsz bytes (crypt_get_ivsz) and a trailing authentication&lt;br /&gt;
    tag of tailsz bytes (crypt_get_tagsz).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Both CRC and AEAD are layered around the FRCP wire format and&lt;br /&gt;
are not visible to the FRCP machinery itself.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.2. Flag bits ===&lt;br /&gt;
&lt;br /&gt;
Flag bits are numbered most-significant-bit first to match the wire&lt;br /&gt;
diagram (bit numbering per Section 1.1; bit 0 is the MSB of the&lt;br /&gt;
16-bit flags field and lands at wire-position 0 in network byte&lt;br /&gt;
order).  Bits 13..15 are reserved and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    +------+--------+--------+----------------------------------------+&lt;br /&gt;
    | Bit  | Mask   | Name   | Meaning                                |&lt;br /&gt;
    +------+--------+--------+----------------------------------------+&lt;br /&gt;
    |   0  | 0x8000 | DATA   | Carries caller payload                 |&lt;br /&gt;
    |   1  | 0x4000 | DRF    | Data Run Flag: start of a fresh run    |&lt;br /&gt;
    |   2  | 0x2000 | ACK    | Acknowledgement: ackno field valid     |&lt;br /&gt;
    |   3  | 0x1000 | NACK   | Negative ACK; seqno = arrival_seqno-1  |&lt;br /&gt;
    |   4  | 0x0800 | FC     | Flow Control: window field valid (rwe) |&lt;br /&gt;
    |   5  | 0x0400 | RDVS   | Rendezvous probe (window-closed)       |&lt;br /&gt;
    |   6  | 0x0200 | FFGM   | First Fragment (role bit 0; see below) |&lt;br /&gt;
    |   7  | 0x0100 | LFGM   | Last Fragment (role bit 1; see below)  |&lt;br /&gt;
    |   8  | 0x0080 | RXM    | Retransmission                         |&lt;br /&gt;
    |   9  | 0x0040 | SACK   | Selective ACK block list in payload    |&lt;br /&gt;
    |  10  | 0x0020 | RTTP   | RTT Probe / echo (payload follows)     |&lt;br /&gt;
    |  11  | 0x0010 | KA     | Keepalive                              |&lt;br /&gt;
    |  12  | 0x0008 | FIN    | End-of-stream marker (stream mode)     |&lt;br /&gt;
    | 13-15|   --   |  --    | Reserved (MUST be zero)                |&lt;br /&gt;
    +------+--------+--------+----------------------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The (FFGM, LFGM) pair encodes the fragment role of a DATA-bearing&lt;br /&gt;
Service Data Unit (SDU), SCTP-style begin/end flags (RFC 9260&lt;br /&gt;
sec. 3.3.1):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    +-----------+-------------------------------------------------+&lt;br /&gt;
    | FFGM LFGM | Role                                            |&lt;br /&gt;
    +-----------+-------------------------------------------------+&lt;br /&gt;
    |   1   1   | Sole / un-fragmented SDU (begin AND end)        |&lt;br /&gt;
    |   1   0   | First fragment of a multi-fragment SDU          |&lt;br /&gt;
    |   0   0   | Middle fragment                                 |&lt;br /&gt;
    |   0   1   | Last fragment                                   |&lt;br /&gt;
    +-----------+-------------------------------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each fragment is carried in its own FRCP packet with its own seqno;&lt;br /&gt;
FRTX (the FRCT Retransmission service mode, see Section 2.2)&lt;br /&gt;
recovers individual fragments via the normal Retransmission Timeout&lt;br /&gt;
(RTO) / SACK / Recent Acknowledgement (RACK, RFC 8985) path.  The&lt;br /&gt;
receiver reassembles the SDU at consume time once the contiguous&lt;br /&gt;
[FIRST .. LAST] run has fully arrived.  On non-DATA packets the role&lt;br /&gt;
bits are unused and MUST be transmitted as zero.&lt;br /&gt;
&lt;br /&gt;
In stream mode (qos.service == SVC_STREAM, see Section 16) there are&lt;br /&gt;
no SDU boundaries to encode, so FFGM and LFGM are unused and MUST&lt;br /&gt;
be transmitted as zero.  End-of-stream uses a dedicated bit (FIN,&lt;br /&gt;
bit 12) carried on a 0-byte DATA packet, emitted at write-half close&lt;br /&gt;
(fccntl to FLOWFRDONLY), during linger drain, and at flow_dealloc;&lt;br /&gt;
emission is idempotent (first call wins).  After contiguous delivery&lt;br /&gt;
of the FIN-bearing slot, the receiver latches byte_fin at the FIN&#039;s&lt;br /&gt;
start offset; flow_read returns 0 (end-of-file, EOF) once buffered&lt;br /&gt;
bytes have been drained up to byte_fin.  Per-byte position is&lt;br /&gt;
carried by the [start, end) extension (Section 1.5).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.3. SACK payload ===&lt;br /&gt;
&lt;br /&gt;
A SACK packet has the FRCT_ACK | FRCT_FC | FRCT_SACK flag bits set&lt;br /&gt;
(bit numbering per Section 1.1).  Following the 16-octet PCI, the&lt;br /&gt;
payload is a 2-octet block count (network byte order), 2 octets of&lt;br /&gt;
padding to 4-byte align the block list, then n_blocks pairs of&lt;br /&gt;
32-bit start/end seqnos describing *present* (received) ranges&lt;br /&gt;
above the cumulative ACK.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |           n_blocks            |        padding (2 octets)     |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[0]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            end[0]                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                           start[1]                            |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
                       ... n_blocks pairs total ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
n_blocks &amp;lt;= SACK_MAX_BLOCKS (2048).  The per-flow effective cap is&lt;br /&gt;
further bounded by (frag_mtu - PCI - 4) / 8 blocks per packet; SACK&lt;br /&gt;
packets carry no stream extension, so PCI here is the 16-octet base&lt;br /&gt;
header even on stream-mode flows.&lt;br /&gt;
&lt;br /&gt;
Wire invariant: every block produced by the receiver, except an&lt;br /&gt;
optional leading Duplicate SACK (D-SACK) block as described below,&lt;br /&gt;
describes a range strictly above the cumulative ACK carried in the&lt;br /&gt;
PCI ackno field (after(start[i], ackno)).  This makes the D-SACK&lt;br /&gt;
convention below unambiguous; the receiver-side builder MUST&lt;br /&gt;
preserve it.&lt;br /&gt;
&lt;br /&gt;
Duplicate SACK (D-SACK, RFC 2883) is signalled in-band: no flag&lt;br /&gt;
bit, no extra framing.  Modular seqno arithmetic uses the&lt;br /&gt;
before() / after() comparators defined in the Notation block.&lt;br /&gt;
Block[0] carries a D-SACK report when either:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  case 1 (RFC 2883 sec. 4.1.1, full duplicate):&lt;br /&gt;
      before(blocks[0].start, ackno) and ackno - blocks[0].start is&lt;br /&gt;
      within MAX_DSACK_LAG (== RQ_SIZE).  A single duplicate seqno&lt;br /&gt;
      observed below the cumulative ACK.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  case 2 (RFC 2883 sec. 4.1.2, partial duplicate):&lt;br /&gt;
      blocks[0] is a sub-range of some blocks[i&amp;gt;0] (not exactly&lt;br /&gt;
      equal).  Reports a duplicate of an in-window seqno that the&lt;br /&gt;
      same packet&#039;s remaining SACK blocks already describe as&lt;br /&gt;
      received.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Senders that do not implement D-SACK process block[0] through the&lt;br /&gt;
normal SACK-mark loop and the existing clamp-and-skip path makes&lt;br /&gt;
case-1 a no-op (start &amp;lt; snd_cr.lwe clamps to snd_cr.lwe, the inner&lt;br /&gt;
loop then skips k == snd_cr.lwe) and case-2 idempotent (same slots&lt;br /&gt;
NULL&#039;d twice).  D-SACK-aware senders feed the report into the RACK&lt;br /&gt;
reo_wnd_mult scaler (RFC 8985 sec. 6.2 step 4): bump on receipt&lt;br /&gt;
(cap 20), halve once per 16 cumulatively-ACK&#039;d seqnos since the&lt;br /&gt;
most recent D-SACK arrival or halve event, reset to 1 on an RTO&lt;br /&gt;
timer fire at the head-of-line.  D-SACK alone never enters&lt;br /&gt;
NewReno-careful recovery (see Section 8); only non-D-SACK blocks&lt;br /&gt;
count as gaps.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.4. RTTP payload ===&lt;br /&gt;
&lt;br /&gt;
An RTTP (Round-Trip Time Probe) packet has only the FRCT_RTTP flag&lt;br /&gt;
set (bit numbering per Section 1.1).  Following the 16-octet PCI,&lt;br /&gt;
the payload is 24 octets (packed):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          probe_id                             |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                          echo_id                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +                  nonce (16 octets, echoed verbatim)           +&lt;br /&gt;
    |                                                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  probe_id - sender counter, 0 on reply, 0 reserved.&lt;br /&gt;
  echo_id  - peer&#039;s probe_id, 0 on outbound probe.&lt;br /&gt;
  nonce    - random, echoed unmodified, memcmp&#039;d to defeat spoof.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 1.5. Stream PCI extension ===&lt;br /&gt;
&lt;br /&gt;
A stream-mode flow (qos.service == SVC_STREAM) carries an extra&lt;br /&gt;
8-octet extension after the 16-octet base PCI on every DATA packet&lt;br /&gt;
(bit numbering per Section 1.1):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     0                   1                   2                   3&lt;br /&gt;
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                            start                              |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
    |                             end                               |&lt;br /&gt;
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  start - octet offset of the first payload byte in the stream.&lt;br /&gt;
  end   - octet offset one past the last payload byte;&lt;br /&gt;
          end - start equals the on-wire payload length.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Total stream-mode PCI for DATA packets is 24 octets (16 base + 8&lt;br /&gt;
extension); control packets (SACK, RTTP, bare ACK, KA, etc.) retain&lt;br /&gt;
the 16-octet base PCI.  Stream mode MUST be negotiated at flow&lt;br /&gt;
allocation; the extension is present iff stream mode is in use,&lt;br /&gt;
never on a per-packet basis.  Both peers MUST treat start/end as&lt;br /&gt;
monotonic 32-bit byte offsets; when a slot reaches the head of the&lt;br /&gt;
contiguous run with start not equal to the prior packet&#039;s end the&lt;br /&gt;
slot is silently dropped at delivery time (Section 16) rather&lt;br /&gt;
than rejected at stash.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC STREAM-frame reassembly model (RFC 9000 sec. 19.8):&lt;br /&gt;
each packet carries its packet seqno (this PCI&#039;s seqno field) and a&lt;br /&gt;
separate stream byte position (start/end).  Separating the two&lt;br /&gt;
avoids TCP&#039;s conflation of packet identity with byte position which&lt;br /&gt;
forces Karn&#039;s algorithm for Round-Trip Time (RTT) sampling (no RTT&lt;br /&gt;
sample on retransmits, RFC 6298 sec. 3); FRCP applies the&lt;br /&gt;
Karn-equivalent gate via a combination of per-packet FRCT_RXM,&lt;br /&gt;
per-slot SND_RTX flags, and a sample-fence rtt_lwe (see Section 2.1&lt;br /&gt;
and Section 12).  FRCP&#039;s fixed-32-bit start/end wrap at 4 GiB of&lt;br /&gt;
wire bytes, narrower than QUIC&#039;s 62-bit varint offset (cf. RFC 9000&lt;br /&gt;
sec. 16); the on-wire wrap is handled by the same modular before()&lt;br /&gt;
/ after() comparators (Section 1.3) FRCP uses for seqnos, which&lt;br /&gt;
remain unambiguous as long as the in-flight byte window stays&lt;br /&gt;
strictly under 2 GiB (the half-range of the signed-int32 difference&lt;br /&gt;
in before()).  The default per-flow ring is 1 MiB; the&lt;br /&gt;
implementation caps ring_sz at 128 MiB (FRCT_STREAM_RING_SZ_MAX),&lt;br /&gt;
well below the 2 GiB half-range bound.  The runtime byte counters&lt;br /&gt;
exposed via FUSE (Filesystem in Userspace) in the Ouroboros&lt;br /&gt;
Resource Information Base (RIB, a virtual-filesystem introspection&lt;br /&gt;
bridge) are platform size_t and do not wrap on 64-bit hosts.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 2. Per-flow state and service modes ==&lt;br /&gt;
&lt;br /&gt;
=== 2.1. Per-flow state ===&lt;br /&gt;
&lt;br /&gt;
Each flow keeps a sender control record and a receiver control&lt;br /&gt;
record:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    lwe    : u32  snd: oldest unacked seqno (cumulative ACK&lt;br /&gt;
                  boundary as seen by sender);&lt;br /&gt;
                  rcv: next in-order seqno expected&lt;br /&gt;
    rwe    : u32  snd: peer-advertised right window edge;&lt;br /&gt;
                  rcv: locally-advertised right window edge&lt;br /&gt;
    cflags : u8   per-direction feature flags: retransmission&lt;br /&gt;
                  (FRCTFRTX), receiver flow control&lt;br /&gt;
                  (FRCTFRESCNTL), linger-on-close (FRCTFLINGER);&lt;br /&gt;
                  see &amp;lt;ouroboros/fccntl.h&amp;gt;&lt;br /&gt;
    seqno  : u32  snd: next seqno to send;&lt;br /&gt;
                  rcv: force-ACK trigger - set on a stale or dup&lt;br /&gt;
                  DATA so the next ack_snd emits a fresh&lt;br /&gt;
                  cumulative ACK&lt;br /&gt;
    ackno  : u32  snd: outbound ACK-packet seqno counter,&lt;br /&gt;
                  incremented for every ACK-bearing packet (bare&lt;br /&gt;
                  ACK, delayed ACK, SACK); used by wire-dup ACK&lt;br /&gt;
                  detection;&lt;br /&gt;
                  rcv: incoming-ACK dedup tracker&lt;br /&gt;
    act    : ns   last activity (used by inactivity / DRF)&lt;br /&gt;
    inact  : ns   inactivity threshold; sender = 3*mpl + a + r + 1s,&lt;br /&gt;
                  receiver = 2*mpl + a + r + 1s.  mpl is the&lt;br /&gt;
                  Maximum Packet Lifetime (delta-t terminology;&lt;br /&gt;
                  see Section 15); a and r are the FRCT a-timer&lt;br /&gt;
                  and r-timer bounds (see Section 8).  The&lt;br /&gt;
                  asymmetry is load-bearing for pre-DRF NACK&lt;br /&gt;
                  (Section 9).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The sender holds a per-slot ring snd_slots[RQ_SIZE] keyed by&lt;br /&gt;
(seqno mod RQ_SIZE).  Each slot tracks its retransmit entry (rxm),&lt;br /&gt;
last-send timestamp, and retransmit flag bits: SND_RTX (a&lt;br /&gt;
retransmit is pending or has fired, gates the next RTT sample&lt;br /&gt;
under Karn) and SND_FAST_RXM (one-shot fast-retransmit staged for&lt;br /&gt;
this loss event).&lt;br /&gt;
&lt;br /&gt;
The receiver holds a parallel reorder ring rcv_slots[RQ_SIZE]&lt;br /&gt;
(referred to as rq[] in prose) holding stashed out-of-order&lt;br /&gt;
packet-buffer indexes; both FRTX and best-effort flows share this&lt;br /&gt;
path.  The invariant rwe - lwe &amp;lt;= RQ_SIZE holds: on each consume&lt;br /&gt;
the receiver advances rwe by the consumed count, capping the&lt;br /&gt;
receive window at RQ_SIZE seqno slots.&lt;br /&gt;
&lt;br /&gt;
A separate fence variable rtt_lwe is bumped on every retransmit&lt;br /&gt;
(timer-fire, SACK-driven, fast-rxm, NACK-driven) and on every&lt;br /&gt;
seqno_rotate (Section 4) to mark the seqno range whose RTT samples&lt;br /&gt;
MUST be discarded.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 2.2. Service modes (orthogonal axes) ===&lt;br /&gt;
&lt;br /&gt;
FRCP exposes its wire features as a vector of independent QoS&lt;br /&gt;
axes selected at flow allocation time.  All flows go through the&lt;br /&gt;
same flow_alloc(name, qos, ...) primitive; the qosspec_t passed&lt;br /&gt;
in determines which protocol machinery engages on the wire.  This&lt;br /&gt;
contrasts with the POSIX BSD socket model where TCP and UDP&lt;br /&gt;
require different socket types (SOCK_STREAM / SOCK_DGRAM).&lt;br /&gt;
&lt;br /&gt;
The axes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  service   0 = unordered (no FRCP engagement: raw datagrams,&lt;br /&gt;
              no PCI on the wire, UDP-equivalent at this layer)&lt;br /&gt;
            1 = message-ordered (FRCP engaged; SDU boundaries&lt;br /&gt;
              preserved across fragmentation)&lt;br /&gt;
            2 = stream (byte-oriented, no SDU boundaries; FRTX&lt;br /&gt;
              required)&lt;br /&gt;
  loss      0 = lossless service requested: FRTX retransmit&lt;br /&gt;
              machinery engages (Section 8); MUST be 0 for&lt;br /&gt;
              service=2.  Non-zero = best-effort, FRTX off.&lt;br /&gt;
  ber       Bit Error Rate tolerance.&lt;br /&gt;
            0 = error-free service requested: a CRC trailer is&lt;br /&gt;
              appended after the body of DATA packets and verified&lt;br /&gt;
              on receive (added / checked outside the FRCP PCI;&lt;br /&gt;
              see Section 1.1).  Non-zero = peer accepts errors;&lt;br /&gt;
              trailer omitted.  SACK control packets carry a&lt;br /&gt;
              CRC32 trailer regardless of ber; the ber gate&lt;br /&gt;
              applies to DATA only.&lt;br /&gt;
  timeout   Peer-timeout (ms); 0 disables the keepalive timer.&lt;br /&gt;
              Independent of FRCP engagement.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Encryption is a separate per-flow attribute set at flow setup;&lt;br /&gt;
when enabled it wraps the FRCP packet (PCI + body, plus the CRC&lt;br /&gt;
trailer if any) under AEAD, expanding the spb by headsz + tailsz&lt;br /&gt;
octets (nonce / tag).  The CRC trailer is currently kept inside&lt;br /&gt;
the AEAD wrap (see Section 1.1).&lt;br /&gt;
&lt;br /&gt;
Reachable combinations exported by include/ouroboros/qos.h:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  +-----------------+---------+------+-----+-----------------------+&lt;br /&gt;
  | Cube            | service | loss | ber | Engaged               |&lt;br /&gt;
  +-----------------+---------+------+-----+-----------------------+&lt;br /&gt;
  | qos_raw         |    0    |   1  |   1 | Raw passthrough       |&lt;br /&gt;
  | qos_raw_safe    |    0    |   1  |   0 | Raw + CRC trailer     |&lt;br /&gt;
  | qos_rt          |    1    |   1  |   1 | FRCP, no FRTX, no CRC |&lt;br /&gt;
  | qos_rt_safe     |    1    |   1  |   0 | FRCP, no FRTX, CRC    |&lt;br /&gt;
  | qos_msg         |    1    |   0  |   0 | FRCP + FRTX           |&lt;br /&gt;
  | qos_stream      |    2    |   0  |   0 | FRCP + FRTX, stream   |&lt;br /&gt;
  +-----------------+---------+------+-----+-----------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Forced couplings actually enforced by the public API:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - service == SVC_STREAM (2) requires loss == 0; flow_alloc /&lt;br /&gt;
    flow_accept reject the pair otherwise with -EINVAL.&lt;br /&gt;
  - FRTX requires FRCP engagement (service != SVC_RAW); requesting&lt;br /&gt;
    loss = 0 with service = SVC_RAW is structurally a no-op&lt;br /&gt;
    because no frcti is created.&lt;br /&gt;
  - The QOS_DISABLE_CRC build flag globally forces ber = 1.&lt;br /&gt;
    Note: this flag defaults to ON, so default builds ship with&lt;br /&gt;
    CRC disabled until QOS_DISABLE_CRC is set to OFF.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Caveat: the API does NOT force ber = 0 when service != SVC_RAW.&lt;br /&gt;
qos_rt has service = SVC_MESSAGE with ber = 1, which means the PCI&lt;br /&gt;
itself is not CRC-protected on that cube; the HCS (Section 1.1)&lt;br /&gt;
remains the only integrity check on the header.&lt;br /&gt;
&lt;br /&gt;
The FRCP-no-FRTX regime (service = SVC_MESSAGE, loss &amp;gt; 0) is meaningful&lt;br /&gt;
and live: sequence numbering, in-order delivery, flow-control&lt;br /&gt;
advertisement, KA, DRF rotation, and SDU fragmentation /&lt;br /&gt;
reassembly (Section 7.2) all run.  Lost packets are dropped&lt;br /&gt;
rather than retransmitted; a permanently-lost mid-fragment is&lt;br /&gt;
dropped via skip-past-gap once a later SDU is visible in the&lt;br /&gt;
reorder ring.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 3. Protocol parameters ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    +--------------------+------------------------+-------------------+&lt;br /&gt;
    | Parameter          | Value                  | Role              |&lt;br /&gt;
    +--------------------+------------------------+-------------------+&lt;br /&gt;
    | RQ_SIZE            | compile-time, power of | Slot ring / rcv   |&lt;br /&gt;
    |                    |  2 (default 128)       | window width      |&lt;br /&gt;
    | START_WINDOW       | compile-time, power of | Initial rwe-lwe   |&lt;br /&gt;
    |                    |  2 (default 128)       | after rotate      |&lt;br /&gt;
    | RTO_MIN            | MAX(250 us build-tun-  | RTO floor; also   |&lt;br /&gt;
    |                    |  able, 1&amp;lt;&amp;lt;RXMQ_RES);   | floored at the    |&lt;br /&gt;
    |                    |  per-flow via fccntl   | retransmit-wheel  |&lt;br /&gt;
    |                    |  (FRCTSRTOMIN).        | resolution        |&lt;br /&gt;
    |                    |  Default ~1 ms with    | (~1 ms by         |&lt;br /&gt;
    |                    |  RXMQ_RES=20.          | default).         |&lt;br /&gt;
    | MAX_RTO_MUL        | 20                     | Backoff shift cap |&lt;br /&gt;
    | RACK window R      | MIN(reo_wnd_mult       | Reorder window;   |&lt;br /&gt;
    |                    |  * min_RTT/4, SRTT)    | per RFC 8985      |&lt;br /&gt;
    |                    |  with MIN_REORDER_NS   | sec. 6.2;         |&lt;br /&gt;
    |                    |  = 250 us floor;       | reo_wnd_mult per  |&lt;br /&gt;
    |                    |  reo_wnd_mult scales   | sec. 6.2 step 4   |&lt;br /&gt;
    |                    |  on D-SACK, cap 20     |                   |&lt;br /&gt;
    | MIN_RTT_WIN_NS     | 300 s (5 min, Linux    | min_RTT windowed  |&lt;br /&gt;
    |                    |  tcp_min_rtt_wlen)     | re-anchor         |&lt;br /&gt;
    | REO_WND_MULT_MAX   | 20 (RFC 8985 sec.      | reo_wnd_mult cap  |&lt;br /&gt;
    |                    |  6.2 step 4)           |                   |&lt;br /&gt;
    | REO_DECAY_PKTS     | 16 (RFC 8985 sec.      | Fresh-ACK&#039;d seq   |&lt;br /&gt;
    |                    |  6.2 step 4 /          | count per halving |&lt;br /&gt;
    |                    |  RACK.reo_wnd_persist) |                   |&lt;br /&gt;
    | MAX_DSACK_LAG      | RQ_SIZE                | D-SACK sanity cap |&lt;br /&gt;
    | RTT_QUARANTINE     | 32 (seqno steps)       | NewReno gate pad  |&lt;br /&gt;
    | SACK rate-limit    | SACK_MIN_GAP_NS        | Min SACK gap      |&lt;br /&gt;
    |                    |  (250 us, fixed)       |                   |&lt;br /&gt;
    | SACK_MAX_BLOCKS    | 2048 (wire cap; per-   | Per-SACK block    |&lt;br /&gt;
    |                    |  flow capped at        | cap               |&lt;br /&gt;
    |                    |  (frag_mtu-PCI-4)/8)   |                   |&lt;br /&gt;
    | SACK_RXM_MAX       | 32                     | Per-pass staged   |&lt;br /&gt;
    |                    |                        | retransmit cap    |&lt;br /&gt;
    | DUP_THRESH         | 3 (RFC 8985 default)   | Hybrid fast-rxm   |&lt;br /&gt;
    |                    |                        | trigger (Sec. 8)  |&lt;br /&gt;
    | MDEV_MUL           | 2 (build-tunable via   | mdev shift in     |&lt;br /&gt;
    |                    |  FRCT_RTO_MDEV_-       | RTO = srtt +      |&lt;br /&gt;
    |                    |  MULTIPLIER)           | (mdev &amp;lt;&amp;lt; MDEV_MUL)|&lt;br /&gt;
    | RTTP nonce         | 16 octets              | Echoed verbatim   |&lt;br /&gt;
    | RTTP_RING          | 8                      | In-flight probes  |&lt;br /&gt;
    | RTT clamp          | 16 * srtt              | Probe-sample      |&lt;br /&gt;
    |                    |                        | upper bound       |&lt;br /&gt;
    |                    |                        | (ACK-derived RTT  |&lt;br /&gt;
    |                    |                        | samples gated by  |&lt;br /&gt;
    |                    |                        | Karn / recovery   |&lt;br /&gt;
    |                    |                        | only)             |&lt;br /&gt;
    | Cold-probe cadence | 100 ms (rx-driven;     | Pre-srtt RTTP     |&lt;br /&gt;
    |                    |  see Section 12)       | rate              |&lt;br /&gt;
    | DELT_RDV           | 100 ms                 | RDVS emit cadence |&lt;br /&gt;
    | MAX_RDV            | 1 s                    | RDVS give-up      |&lt;br /&gt;
    | Delayed-ACK fire   | 2 * TICTIME (TICTIME   | Fired after the   |&lt;br /&gt;
    |                    |  = FRCT tick gran-     | first in-order    |&lt;br /&gt;
    |                    |  ularity, default      | DATA arrival;     |&lt;br /&gt;
    |                    |  5 ms; 2*TICTIME       | tick is build-    |&lt;br /&gt;
    |                    |  = 10 ms by default)   | tunable           |&lt;br /&gt;
    | NACK send cooldown | srtt when an srtt      | Pre-DRF NACK      |&lt;br /&gt;
    |                    |  sample exists, else   | rate-limit        |&lt;br /&gt;
    |                    |  100 ms                |                   |&lt;br /&gt;
    | MAX_SDU            | 1 MiB                  | Max reassembled   |&lt;br /&gt;
    |                    |                        | SDU; configurable |&lt;br /&gt;
    |                    |                        | per flow          |&lt;br /&gt;
    +--------------------+------------------------+-------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The per-flow fragment Maximum Transmission Unit (MTU) is computed&lt;br /&gt;
at flow setup from the lower IPCP&#039;s mtu minus encryption&lt;br /&gt;
headsz / tailsz and CRC trailer; there is no FRCT-level default or&lt;br /&gt;
environment-variable override.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 4. Sequence-number rotation (DRF) ==&lt;br /&gt;
&lt;br /&gt;
The DRF (Data Run Flag) bit on an outbound packet means &amp;quot;this is&lt;br /&gt;
the start of a fresh data run&amp;quot; and is set whenever the sender has&lt;br /&gt;
nothing in flight (snd_cr.seqno == snd_cr.lwe).&lt;br /&gt;
&lt;br /&gt;
Independently of that, if the sender has been idle longer than&lt;br /&gt;
snd_cr.inact AND the pipe is empty (snd_cr.seqno == snd_cr.lwe),&lt;br /&gt;
seqno_rotate() rolls a random new seqno before the send and&lt;br /&gt;
resets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    snd_cr.seqno  = random()&lt;br /&gt;
    snd_cr.lwe    = snd_cr.seqno&lt;br /&gt;
    snd_cr.rwe    = snd_cr.seqno + START_WINDOW&lt;br /&gt;
    rtt_lwe       = snd_cr.seqno&lt;br /&gt;
    in_recovery   = false   (recovery state, see Section 8)&lt;br /&gt;
    recovery_high = snd_cr.seqno&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The receiver, on observing rcv-side inactivity&lt;br /&gt;
(now - rcv_cr.act &amp;gt; rcv_cr.inact), requires a DRF on the next&lt;br /&gt;
DATA packet; otherwise it replies with a rate-limited NACK (see&lt;br /&gt;
below).  Non-DATA control packets pass through without the DRF&lt;br /&gt;
requirement.  On DRF the receiver releases the rq[] slots and&lt;br /&gt;
rebases&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    rcv_cr.lwe   = seqno&lt;br /&gt;
    rcv_cr.rwe   = seqno + RQ_SIZE&lt;br /&gt;
    rcv_cr.seqno = seqno&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the inactive packet has DATA but no DRF, a rate-limited NACK is&lt;br /&gt;
fired back to the sender (cooldown per Section 3); non-DATA stale&lt;br /&gt;
arrivals fall through to normal processing (no NACK, no drop).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 5. Send path ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    1. If the SDU exceeds (frag_mtu - data_hdr_len), the caller&lt;br /&gt;
       (dev.c) fans it out into ceil(count / (frag_mtu -&lt;br /&gt;
       data_hdr_len)) fragments, each emitted via frcti_snd as its&lt;br /&gt;
       own DATA packet with a per-fragment role (Section 7.2);&lt;br /&gt;
       both FRTX and best-effort flows fragment.  Raw flows (no&lt;br /&gt;
       FRCP engagement, qos.service == SVC_RAW) carry no PCI and&lt;br /&gt;
       return -EMSGSIZE for any SDU larger than one packet at the&lt;br /&gt;
       layer below.  An SDU that fits in a single packet is sent&lt;br /&gt;
       as SOLE.  frcti_snd reserves PCI head room; sets DATA, plus&lt;br /&gt;
       DRF when the pipe is empty (snd_cr.seqno == snd_cr.lwe).&lt;br /&gt;
    2. seqno_rotate() if past sender inactivity and the pipe is&lt;br /&gt;
       empty (Section 4).&lt;br /&gt;
    3. Advertise FC (pci.window = frcti_advert_rwe(frcti), i.e.&lt;br /&gt;
       rcv_cr.rwe clamped to rcv_cr.lwe + ring_seq_cap in stream&lt;br /&gt;
       mode) when the receiver side is recent: now - rcv_cr.act&lt;br /&gt;
       &amp;lt; rcv_cr.inact.&lt;br /&gt;
    4. Reliable mode (FRTX): leave snd_cr.lwe where it is; reset&lt;br /&gt;
       the slot at RQ_SLOT(seqno) (snd_slots[p].time = now,&lt;br /&gt;
       snd_slots[p].flags = 0); queue an rxm_entry (saves a packet&lt;br /&gt;
       copy, arms a wheel timer at now + (rto &amp;lt;&amp;lt; rto_mul)).&lt;br /&gt;
       Piggyback ACK (pci.ackno = rcv_cr.lwe) while the a-timer&lt;br /&gt;
       for the most recent received DATA packet has not yet&lt;br /&gt;
       expired (now - rcv_cr.act &amp;lt;= t_a); on piggyback, set&lt;br /&gt;
       rcv_cr.seqno = rcv_cr.lwe so the next delayed-ACK fire is&lt;br /&gt;
       suppressed.  See Section 8 for t_a / t_r semantics.&lt;br /&gt;
    5. Best-effort mode (no FRTX): advance snd_cr.lwe immediately&lt;br /&gt;
       (snd_cr.lwe = snd_cr.lwe + 1, snd_cr.rwe = snd_cr.lwe +&lt;br /&gt;
       RQ_SIZE); no retransmit state.  No send-side RTT probe is&lt;br /&gt;
       armed in this mode (rtt_probe_arm requires an in-flight&lt;br /&gt;
       seqno, which best-effort never has); the rx-driven cold&lt;br /&gt;
       seeder in frcti_rcv is the only probe path.&lt;br /&gt;
    6. In reliable mode, optionally arm an RTT probe (Section 12).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 6. Receive path ==&lt;br /&gt;
&lt;br /&gt;
=== 6.1. Early-exit dispatch ===&lt;br /&gt;
&lt;br /&gt;
Keepalive (KA), RTT probe (RTTP), pre-DRF NACK, and rendezvous&lt;br /&gt;
(RDVS) packets short-circuit out of frcti_rcv before the locked&lt;br /&gt;
main path; each handler takes its own lock internally.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      incoming packet&lt;br /&gt;
            |&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | KA?     |---yes--&amp;gt; ka_rcv  ; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RTTP?   |---yes--&amp;gt; rttp_rcv; return&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | NACK?   |---yes--&amp;gt; nack_rcv; return  (see Section 9)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       +---------+&lt;br /&gt;
       | RDVS?   |---yes--&amp;gt; rdv_rcv ; return  (reply bare FC, ackno=0)&lt;br /&gt;
       +---------+&lt;br /&gt;
            |no&lt;br /&gt;
            v&lt;br /&gt;
       acquire wrlock; enter locked main path&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - KA   : refresh t_ka_rcv, honour piggybacked ACK.&lt;br /&gt;
  - RTTP : probe (echo back nonce) or echo (verify nonce, sample&lt;br /&gt;
           RTT).&lt;br /&gt;
  - NACK : pre-DRF, sender-side handler.  See Section 9.&lt;br /&gt;
  - RDVS : reply with a bare FC packet (ackno = 0); rdlock only.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 6.2. Locked main path ===&lt;br /&gt;
&lt;br /&gt;
Steps below run with the per-flow frcti.lock held for writing&lt;br /&gt;
(pthread_rwlock_wrlock) unless noted.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  rcv_inact_check&lt;br /&gt;
      Only meaningful when the receive side is stale.  On DRF&lt;br /&gt;
      (Data Run Flag): release rq[] slots, rebase rcv_cr, continue.&lt;br /&gt;
      On stale DATA without DRF: fire a pre-DRF NACK if cooldown&lt;br /&gt;
      allows (Section 9), then discard the packet; on cooldown,&lt;br /&gt;
      drop without sending a NACK (a pending cumulative ACK from&lt;br /&gt;
      drop_packet may still go out).  Non-DATA, non-DRF arrivals&lt;br /&gt;
      bypass rcv_inact_check entirely; pure-DRF stale arrivals fall&lt;br /&gt;
      through after the DRF rebase branch.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  DATA-only act refresh&lt;br /&gt;
      Refresh rcv_cr.act only when FRCT_DATA is set, so that non-DATA&lt;br /&gt;
      packets never block the next DRF rebase.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  Wire-dup gate&lt;br /&gt;
      Before flag-driven dispatch, drop wire-duplicate ACKs and&lt;br /&gt;
      wire-duplicate DATA (is_dup_ack / is_dup_data).  The DATA&lt;br /&gt;
      check is bypassed for FRCT_RXM-bearing arrivals so the&lt;br /&gt;
      piggybacked ACK / SACK / FC carried on a retransmitted DATA&lt;br /&gt;
      at an already-ACK&#039;d seqno is still applied; the stale-in-&lt;br /&gt;
      window branch below then drops the packet.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  ACK&lt;br /&gt;
      Drop ACKs whose ackno falls outside (snd_cr.lwe, snd_cr.seqno].&lt;br /&gt;
      If ackno == snd_cr.lwe (non-advancing cumulative ACK), drive&lt;br /&gt;
      RACK fast-retransmit consideration (Section 8).  Otherwise&lt;br /&gt;
      advance snd_cr.lwe = ackno, collapse rto_mul to 0 (Karn-gated&lt;br /&gt;
      by SND_RTX on the just-acknowledged slot, the old head-of-&lt;br /&gt;
      line), reset dup_thresh to 0, update t_latest_ack to the&lt;br /&gt;
      send-time of the slot at ackno-1 (consumed by RACK and SACK&lt;br /&gt;
      below), decay reo_wnd_mult per RFC 8985 sec. 6.2 step 4,&lt;br /&gt;
      exit NewReno-careful recovery (see Section 8) on&lt;br /&gt;
      ackno &amp;gt;= recovery_high or ackno == snd_cr.seqno, and feed an&lt;br /&gt;
      RTT sample if eligible (Section 12).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  SACK&lt;br /&gt;
      Walk the block list.  For each block (a present range above&lt;br /&gt;
      lwe) NULL out snd_slots[k].rxm, clear the slot&#039;s per-send&lt;br /&gt;
      flags, and advance t_latest_ack to the latest send-time&lt;br /&gt;
      covered (the Forward Acknowledgement / fack equivalent,&lt;br /&gt;
      Mathis &amp;amp; Mahdavi 1996); the first block whose start&lt;br /&gt;
      clamps to snd_cr.lwe skips this fack update so that a head-&lt;br /&gt;
      of-line clamp does not falsely advance fack.  For un-SACKed&lt;br /&gt;
      gaps below hi_sacked, stage a retransmit per slot that is&lt;br /&gt;
      (1) still owned (rxm != NULL), (2) not already SND_FAST_RXM,&lt;br /&gt;
      (3) not aged out past t_r, and (4) either outside the RACK&lt;br /&gt;
      reorder window R OR with dup_thresh &amp;gt;= DUP_THRESH (the RFC&lt;br /&gt;
      8985 sec. 6.2 hybrid trigger).  Mark the slot SND_FAST_RXM&lt;br /&gt;
      and NULL the rxm at stage time.  Capped at SACK_RXM_MAX&lt;br /&gt;
      staged retransmits per receive pass; what&#039;s left rides the&lt;br /&gt;
      next SACK.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  FC&lt;br /&gt;
      Bump snd_cr.rwe (clamped to lwe + RQ_SIZE, never shrinks)&lt;br /&gt;
      and mark window open.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  DATA&lt;br /&gt;
      Bounds-check seqno against window.  On stale-dup&lt;br /&gt;
      (seqno &amp;lt; rcv_cr.lwe), set rcv_cr.seqno = seqno to force a&lt;br /&gt;
      fresh ACK on the next ack_snd, then drop.  On accept: both&lt;br /&gt;
      FRTX and best-effort stash the packet-buffer index into&lt;br /&gt;
      rq[seqno mod RQ_SIZE].  Fragments stash unchanged - the role&lt;br /&gt;
      bits are inspected only at consume time (Section 7.2).  On&lt;br /&gt;
      out-of-order arrival, build a SACK reply if not rate-limited&lt;br /&gt;
      (per Section 3) and not deduplicated against the previous&lt;br /&gt;
      (rcv_cr.lwe, n_blocks) pair; D-SACK reports always bypass the&lt;br /&gt;
      dedup.  If both rate-limit and dedup suppress the reply,&lt;br /&gt;
      neither SACK nor delayed-ACK fires (the sender picks up the&lt;br /&gt;
      gap on its next ACK).  On in-order arrival, arm the delayed-&lt;br /&gt;
      ACK timer.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  drop_packet exit&lt;br /&gt;
      Releases the per-packet shared-memory buffer (spb), then&lt;br /&gt;
      calls ack_snd synchronously after the spb release to surface&lt;br /&gt;
      any pending cumulative ACK.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 7. Read path and reassembly ==&lt;br /&gt;
&lt;br /&gt;
=== 7.1. Read path ===&lt;br /&gt;
&lt;br /&gt;
flow_read returns a full reassembled SDU (Service Data Unit) via&lt;br /&gt;
frcti_consume on every FRCP SDU-mode flow (FRTX or best-effort);&lt;br /&gt;
stream-mode is covered in Section 16.  An incomplete head-of-line&lt;br /&gt;
(HoL) run yields -EAGAIN; an oversized run yields -EMSGSIZE (the&lt;br /&gt;
run is dropped so the flow does not stall).  On best-effort flows,&lt;br /&gt;
a permanently-lost mid-fragment is dropped as soon as a later&lt;br /&gt;
complete SDU becomes visible in the ring (Section 7.2 skip-past-&lt;br /&gt;
gap).&lt;br /&gt;
&lt;br /&gt;
Raw flows carry no frcti, so flow_read returns the next pending&lt;br /&gt;
packet-buffer index directly, with no role-bit inspection.  (Raw&lt;br /&gt;
service is selected via qos.service == SVC_RAW at flow allocation,&lt;br /&gt;
which suppresses frcti creation.)&lt;br /&gt;
&lt;br /&gt;
frcti_pdu_ready is the no-advance peek used by fevent (the&lt;br /&gt;
Ouroboros flow-event multiplexer, the poll(2)-equivalent on&lt;br /&gt;
flows).  It returns ready only when the head-of-line run is&lt;br /&gt;
complete and the lead packet (a Protocol Data Unit, here one FRCP&lt;br /&gt;
packet) is present at rcv_cr.rwe - RQ_SIZE; any other state&lt;br /&gt;
(including the best-effort skip-past-gap case) returns not ready,&lt;br /&gt;
and frcti_consume is left to drop the broken prefix and re-&lt;br /&gt;
inspect.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 7.2. Fragmentation and reassembly ===&lt;br /&gt;
&lt;br /&gt;
Send side (flow_write_frag).  An SDU larger than&lt;br /&gt;
(frag_mtu - PCI) is split into ceil(count / (frag_mtu - PCI))&lt;br /&gt;
fragments; each fragment is its own FRCP packet with its own&lt;br /&gt;
seqno and a per-fragment role flag pair (Section 1.2).  Roles are&lt;br /&gt;
assigned at emit time:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    +------+--------+&lt;br /&gt;
    | i    | Role   |&lt;br /&gt;
    +------+--------+&lt;br /&gt;
    | n=1  | SOLE   |&lt;br /&gt;
    | i=0  | FIRST  |&lt;br /&gt;
    | i=n-1| LAST   |&lt;br /&gt;
    | else | MID    |&lt;br /&gt;
    +------+--------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A mid-loop allocation or transmit failure may yield a partial&lt;br /&gt;
write: the call returns the bytes already enqueued (off &amp;gt; 0) or&lt;br /&gt;
the underlying error (off == 0).  Best-effort flows fragment&lt;br /&gt;
identically; on the receiver, a partial run with a permanently-&lt;br /&gt;
lost fragment is dropped when a later complete SDU is visible in&lt;br /&gt;
the ring (see skip-past-gap below).  Raw flows carry no PCI and&lt;br /&gt;
refuse anything larger than the layer&#039;s user MTU (-EMSGSIZE).&lt;br /&gt;
&lt;br /&gt;
Wire-level recovery is fragment-agnostic on FRTX flows: each&lt;br /&gt;
fragment&#039;s seqno flows through SACK / RACK / RTO / NACK exactly&lt;br /&gt;
as for a SOLE DATA packet, and reassembly does not re-enter the&lt;br /&gt;
loss-detection path.  Best-effort flows run the same seqno&lt;br /&gt;
machinery (DRF, FC, ACK piggyback, pre-DRF NACK emit) but queue&lt;br /&gt;
no rxm state at the sender, so a lost MID is unrecoverable;&lt;br /&gt;
skip-past-gap handles it (below).&lt;br /&gt;
&lt;br /&gt;
Receive side.  Fragments stash into rq[seqno] unchanged; role bits&lt;br /&gt;
are read only at consume time.  frag_run_inspect, called from&lt;br /&gt;
frcti_consume, walks the ring starting at the oldest still-&lt;br /&gt;
undelivered seqno base = rcv_cr.rwe - RQ_SIZE (equal to rcv_cr.lwe&lt;br /&gt;
only when no partial run is in progress; during a partial run lwe&lt;br /&gt;
has already advanced past base).  It produces one of three&lt;br /&gt;
outcomes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    +---------------+---------------------------------------------+&lt;br /&gt;
    | Outcome       | Cause                                       |&lt;br /&gt;
    +---------------+---------------------------------------------+&lt;br /&gt;
    | DELIVER (n)   | rq[base]=SOLE (n=1), or rq[base]=FIRST and  |&lt;br /&gt;
    |               | a LAST follows in slots [base+1..base+n-1]  |&lt;br /&gt;
    |               | with all intermediate roles in {MID,FIRST,  |&lt;br /&gt;
    |               | LAST} contiguous.                           |&lt;br /&gt;
    | DROP (n)      | rq[base] is MID or LAST without a preceding |&lt;br /&gt;
    |               | FIRST (n=1); a FIRST..[non-LAST]..new-FIRST |&lt;br /&gt;
    |               | or new-SOLE mid-run (drop the broken prefix |&lt;br /&gt;
    |               | with n = run length minus 1, so the new     |&lt;br /&gt;
    |               | FIRST/SOLE stays); or, on best-effort       |&lt;br /&gt;
    |               | flows, a gap at base with a FIRST/SOLE      |&lt;br /&gt;
    |               | later in the ring (drop up to the new run   |&lt;br /&gt;
    |               | start).                                     |&lt;br /&gt;
    | NOT_READY     | rq[base] absent or FIRST..[non-LAST] with   |&lt;br /&gt;
    |               | no later FIRST/SOLE in the ring (FRTX waits |&lt;br /&gt;
    |               | for retx; best-effort waits for arrival).   |&lt;br /&gt;
    +---------------+---------------------------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DELIVER triggers frag_gather: a scatter-gather memcpy of the n&lt;br /&gt;
consecutive fragments at rq[base..base+n-1] directly into the&lt;br /&gt;
caller&#039;s buffer; each per-packet shared-memory buffer (spb) is&lt;br /&gt;
released and rwe advances by n.  lwe was already advanced&lt;br /&gt;
incrementally as each contiguous fragment arrived; frag_gather&lt;br /&gt;
only restores the fixed-width invariant rwe == lwe + RQ_SIZE.&lt;br /&gt;
No intermediate reassembly buffer is allocated.&lt;br /&gt;
&lt;br /&gt;
DROP advances rwe past the broken prefix (releasing the spbs)&lt;br /&gt;
and pulls lwe up to the new trailing edge if needed; the next&lt;br /&gt;
consume retries from the new base.  Oversize or arithmetically&lt;br /&gt;
overflowing delivery (sum of fragment lengths &amp;gt; max_rcv_sdu, sum&lt;br /&gt;
&amp;gt; caller&#039;s buffer, or running-sum overflow) also drops the run&lt;br /&gt;
with -EMSGSIZE.&lt;br /&gt;
&lt;br /&gt;
Skip-past-gap (best-effort only).  On FRTX, a gap in the run means&lt;br /&gt;
&amp;quot;waiting for retransmit&amp;quot; and frag_run_inspect returns NOT_READY.&lt;br /&gt;
On best-effort flows the gap is permanent, so frag_run_inspect&lt;br /&gt;
scans forward in the ring for the next FIRST or SOLE; if one is&lt;br /&gt;
visible within RQ_SIZE, it returns DROP for the broken prefix and&lt;br /&gt;
the consume loop retries at the new lwe.  Memory hold is bounded&lt;br /&gt;
by RQ_SIZE; the partial releases on the next consume call once a&lt;br /&gt;
later complete run exists.  Voice-like flows (one SOLE per SDU)&lt;br /&gt;
see no extra wait: any later SOLE makes the prior gap droppable&lt;br /&gt;
immediately.&lt;br /&gt;
&lt;br /&gt;
The choice to defer reassembly to consume time keeps the receive&lt;br /&gt;
path zero-copy: fragments stay in the shared-memory ring until&lt;br /&gt;
the application pulls, and the SDU lands directly in the caller&#039;s&lt;br /&gt;
buffer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 8. Retransmission ==&lt;br /&gt;
&lt;br /&gt;
FRCP is bounded by two delta-t-derived timers (Watson 1981, see&lt;br /&gt;
Section 15):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - t_a (a-timer): upper bound on ACK delay.  An ACK for a received&lt;br /&gt;
    DATA packet MUST be emitted within t_a of receipt; an attempt&lt;br /&gt;
    to send an ACK after the a-timer has expired is suppressed&lt;br /&gt;
    (the sender&#039;s RTO is already in motion).&lt;br /&gt;
  - t_r (r-timer): upper bound on retransmission.  A given DATA&lt;br /&gt;
    packet MUST NOT be retransmitted after t_r has elapsed since&lt;br /&gt;
    its first send (t0); when the bound is hit, the flow is&lt;br /&gt;
    declared down (raising the Ouroboros asynchronous flow&lt;br /&gt;
    condition ACL_FLOWDOWN, which marks the flow dead to both&lt;br /&gt;
    endpoints) rather than retransmitted again.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each in-flight FRTX seqno owns one rxm_entry, armed in a hashed&lt;br /&gt;
timing wheel; the wheel deadline is the slot&#039;s next eligible&lt;br /&gt;
retransmit time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  RTO timer&lt;br /&gt;
      On fire (rxm_due), re-emit with FRCT_RXM, mark SND_RTX&lt;br /&gt;
      (Karn-suppress next ACK&#039;s RTT sample), and (for the head-of-&lt;br /&gt;
      line (HoL) slot only) bump rto_mul up to MAX_RTO_MUL.  Wheel&lt;br /&gt;
      deadline is t_send + (rto &amp;lt;&amp;lt; rto_mul).  Re-armed unless&lt;br /&gt;
      consumed.  The RTO timer also clears SND_FAST_RXM (re-arming&lt;br /&gt;
      fast-retransmit eligibility), resets reo_wnd_mult to 1 on a&lt;br /&gt;
      HoL fire (RFC 8985 sec. 6.2 step 4 reset clause), and marks&lt;br /&gt;
      the flow ACL_FLOWDOWN if its frct_tx call fails.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  r-timer guard&lt;br /&gt;
      Before any retransmit attempt, check (now - t0) against t_r.&lt;br /&gt;
      If exceeded, the slot is no longer eligible for retransmit.&lt;br /&gt;
      Only the RTO timer (rxm_due) treats r-timer expiry as&lt;br /&gt;
      terminal: it marks the flow ACL_FLOWDOWN (peer unreachable).&lt;br /&gt;
      Fast-retransmit, SACK-driven retransmit, and NACK-driven&lt;br /&gt;
      head-of-line re-emit silently skip aged-out slots and defer&lt;br /&gt;
      the flow-down decision to the next RTO fire.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  Fast retransmit (hybrid trigger, RFC 8985 sec. 6.2)&lt;br /&gt;
      On a non-advancing cumulative ACK with the scoreboard&lt;br /&gt;
      advanced, fire one fast retransmit when EITHER (a) the head-&lt;br /&gt;
      of-line slot&#039;s latest send is older than the RACK reorder&lt;br /&gt;
      window R (Section 3) and not yet aged out, OR (b) the SACK&lt;br /&gt;
      dup-thresh count above snd_cr.lwe reaches DUP_THRESH (= 3,&lt;br /&gt;
      RFC 8985 sec. 6.2 step 4).  Fires at most once per non-&lt;br /&gt;
      advancing cumulative-ACK value, gated by rack_fired_lwe (the&lt;br /&gt;
      snd_cr.lwe at which fast-retransmit last fired).  Set&lt;br /&gt;
      SND_FAST_RXM on the slot (one-shot per-slot gate) and enter&lt;br /&gt;
      NewReno-style careful recovery (see NewReno below in this&lt;br /&gt;
      section).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      The RACK reorder window R uses the RFC 8985 sec. 6.2 form&lt;br /&gt;
      R = MIN(reo_wnd_mult * min_RTT / 4, SRTT) with a&lt;br /&gt;
      MIN_REORDER_NS = 250 us floor.  Before the first RTT sample&lt;br /&gt;
      seeds min_rtt, R falls back to MIN(reo_wnd_mult * SRTT / 4,&lt;br /&gt;
      SRTT), still floored at MIN_REORDER_NS (consistent with the&lt;br /&gt;
      windowed-minimum fallback described in Section 12).  min_rtt&lt;br /&gt;
      is a windowed minimum over the last MIN_RTT_WIN_NS = 5 min of&lt;br /&gt;
      RTT samples (matches the Linux tcp_min_rtt_wlen default) so a&lt;br /&gt;
      route change to a longer path eventually re-anchors the&lt;br /&gt;
      reorder window without relying on reo_wnd_mult growth alone.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  SACK-driven retransmit&lt;br /&gt;
      For each gap below hi_sacked whose slot is (1) still owned,&lt;br /&gt;
      (2) not already SND_FAST_RXM, (3) not aged out past t_r, and&lt;br /&gt;
      (4) either outside the RACK window R OR with dup_thresh &amp;gt;=&lt;br /&gt;
      DUP_THRESH (same hybrid as fast-retransmit, see Section 6.2),&lt;br /&gt;
      re-emit.  Each SACK-driven retransmit re-arms a fresh rxm so&lt;br /&gt;
      a lost retransmit can still be recovered by its own RTO&lt;br /&gt;
      timer.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  NewReno&lt;br /&gt;
      On entry, recovery_high = snd_cr.seqno + RTT_QUARANTINE.&lt;br /&gt;
      Exit when ackno &amp;gt;= recovery_high or ackno == snd_cr.seqno&lt;br /&gt;
      (the latter means everything sent has been acknowledged).&lt;br /&gt;
      seqno_rotate also clears recovery.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 9. Pre-DRF NACK ==&lt;br /&gt;
&lt;br /&gt;
The two sides have different inactivity thresholds&lt;br /&gt;
(snd_cr.inact &amp;gt; rcv_cr.inact), so a receiver can detect &amp;quot;stale data&lt;br /&gt;
run&amp;quot; before the sender&#039;s own DRF logic kicks in.  NACK is the&lt;br /&gt;
receiver-driven nudge that asks the sender to re-transmit the head&lt;br /&gt;
of the run.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  Send (frcti_nack_snd, called by frcti_rcv when rcv_inact_check&lt;br /&gt;
        returns FRCT_INACT_NEED_NACK)&lt;br /&gt;
      When an incoming DATA packet has no DRF and rcv-side activity&lt;br /&gt;
      is older than rcv_cr.inact, the receiver emits a bare packet&lt;br /&gt;
      with flags = FRCT_NACK and seqno = arrival_seqno - 1&lt;br /&gt;
      (informational only, not consulted by the receive handler).&lt;br /&gt;
      The cooldown in Section 3 rate-limits the burst.  Non-DATA&lt;br /&gt;
      non-DRF arrivals bypass rcv_inact_check entirely; non-DATA&lt;br /&gt;
      DRF still rebases via the DRF branch.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  Receive (frcti_nack_rcv)&lt;br /&gt;
      Dispatched in the early-exit branch (Section 6.1), before&lt;br /&gt;
      rcv_inact_check.  The sender copies the head-of-line (HoL)&lt;br /&gt;
      rxm packet, marks the slot SND_RTX | SND_FAST_RXM (Karn-&lt;br /&gt;
      suppress next ACK, one-shot fast-rxm gate), sets rtt_lwe =&lt;br /&gt;
      snd_cr.lwe + 1, and re-emits via fast_rxm_send with FRCT_RXM&lt;br /&gt;
      and a refreshed ackno.  The original rxm_entry and its RTO&lt;br /&gt;
      timer are left armed - the NACK emit is additive to the&lt;br /&gt;
      normal retransmit machinery, not a replacement.  No-op if&lt;br /&gt;
      nothing is in flight, the HoL slot has aged past t_r, or&lt;br /&gt;
      the HoL rxm pointer has been cleared by SACK or RACK.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NACK serves two roles:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  1. Lost first-of-run (DRF) packet recovery.  Required.  Until&lt;br /&gt;
     the DRF packet arrives, the receiver cannot rebase its&lt;br /&gt;
     window, so any subsequent in-flight packets look stale to&lt;br /&gt;
     the receiver.  The NACK fires the moment the second&lt;br /&gt;
     packet arrives at a stale receiver, telling the sender to&lt;br /&gt;
     re-emit the HoL (DRF) packet at NACK-cooldown latency rather&lt;br /&gt;
     than waiting for the initial RTO (which is the configured&lt;br /&gt;
     default until srtt is seeded by the first probe round-trip).&lt;br /&gt;
  2. General loss-recovery accelerator.  When loss is detected&lt;br /&gt;
     receiver-first, the NACK skips one RTO of latency relative to&lt;br /&gt;
     waiting for the sender&#039;s RTO to fire.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In both cases the existing rxm_entry and its RTO timer are left&lt;br /&gt;
armed, so the RTO path remains the eventual fallback.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 10. Cumulative + selective ACK ==&lt;br /&gt;
&lt;br /&gt;
Cumulative ACK is ackno = rcv_cr.lwe.  On out-of-order arrival the&lt;br /&gt;
receiver also emits a SACK packet (Section 1.3) whose payload lists&lt;br /&gt;
*present* blocks above lwe (analogous to TCP SACK / QUIC ACK&lt;br /&gt;
ranges).  SACKs are rate-limited per Section 3 and suppressed when&lt;br /&gt;
neither lwe nor block count has changed since the last SACK.&lt;br /&gt;
&lt;br /&gt;
D-SACK reports (RFC 2883) are emitted in-band as block[0] of an&lt;br /&gt;
otherwise normal SACK frame (see Section 1.3 for the encoding).&lt;br /&gt;
Two receiver triggers arm a pending D-SACK report (single-slot,&lt;br /&gt;
latest-wins):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - DATA arrival with seqno &amp;lt; rcv_cr.lwe, both wire-dup (no RXM,&lt;br /&gt;
    is_dup_data path) and retransmit (RXM, post-FC branch)&lt;br /&gt;
    (RFC 2883 sec. 4.1.1, full duplicate)&lt;br /&gt;
  - rq_accept conflict, slot already occupied in [lwe, rwe)&lt;br /&gt;
    (RFC 2883 sec. 4.1.2, partial duplicate)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When a D-SACK is pending and the standard scoreboard SACK would be&lt;br /&gt;
suppressed by dedup or rate-limit, the report is emitted as a&lt;br /&gt;
stand-alone SACK frame through the normal ack_snd path; when a&lt;br /&gt;
D-SACK report is pending the path bypasses dedup and the TICTIME&lt;br /&gt;
rate-limit, but the a-timer suppression on rcv inactivity still&lt;br /&gt;
applies.&lt;br /&gt;
&lt;br /&gt;
Bare ACKs are deferred via a per-flow delayed-ACK timer (one in&lt;br /&gt;
flight at a time, atomic test-and-set dedup; fires per Section 3&lt;br /&gt;
after the first in-order arrival).  Suppressed if (1) no new&lt;br /&gt;
seqno, (2) rcv side is inactive (older than t_a), or (3) the&lt;br /&gt;
sender just sent within TICTIME.  A pending D-SACK ride-through&lt;br /&gt;
bypasses (1) and (3); the a-timer gate (2) is unconditional.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 11. Flow control ==&lt;br /&gt;
&lt;br /&gt;
The receiver advertises rwe in every FC field.  The sender treats&lt;br /&gt;
its snd_cr.rwe as the absolute right edge: when&lt;br /&gt;
snd_cr.seqno &amp;gt;= snd_cr.rwe the window is closed and flow_write&lt;br /&gt;
yields.  While closed, the sender periodically emits RDVS&lt;br /&gt;
(rendezvous) packets (cadence DELT_RDV); the receiver replies with&lt;br /&gt;
a bare FC packet (ackno = 0) that reopens the window.  Once the&lt;br /&gt;
window has been closed for longer than MAX_RDV the sender stops&lt;br /&gt;
emitting RDVS but does not tear the flow down - the writer keeps&lt;br /&gt;
blocking until either a peer-driven FC arrives or the KA&lt;br /&gt;
(keepalive) / r-timer marks the flow.&lt;br /&gt;
&lt;br /&gt;
rwe is clamped to lwe + RQ_SIZE on receipt and MUST NOT shrink:&lt;br /&gt;
a backward rwe is silently clamped to the current snd_cr.rwe;&lt;br /&gt;
the FC packet still reopens the window.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 12. RTT estimation ==&lt;br /&gt;
&lt;br /&gt;
Active RTTP probes (Section 1.4) carry a 32-bit probe_id (0&lt;br /&gt;
reserved) and a 16-byte random nonce echoed verbatim - defends&lt;br /&gt;
against spoofed replies.  A ring of RTTP_RING in-flight probes is&lt;br /&gt;
kept; an echo whose (id, nonce) doesn&#039;t match the ring slot is&lt;br /&gt;
dropped.  A single RTTP sample is clamped to RTT_CLAMP_MUL * srtt&lt;br /&gt;
(compile-time RTT_CLAMP_MUL = 16) once srtt is seeded; the first&lt;br /&gt;
cold-probe sample feeds rtt_update raw.&lt;br /&gt;
&lt;br /&gt;
Probe arming gates:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - Cold (no srtt yet): the receive path arms at most one probe&lt;br /&gt;
    per 100 ms via frcti_rcv_probe (PROBE_DUE_COLD); arming&lt;br /&gt;
    requires an incoming packet.  Active send-path arming bails&lt;br /&gt;
    while srtt == 0.&lt;br /&gt;
  - Warm (rtt_probe_arm, called from frcti_snd): outstanding&lt;br /&gt;
    data (snd_cr.seqno &amp;gt; snd_cr.lwe), AND at least 2 * srtt&lt;br /&gt;
    since t_rcv_rtt (last RTT receive of any kind), AND at&lt;br /&gt;
    least srtt since t_snd_probe (last probe emit).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sample feeds either Linux&#039;s asymmetric mdev estimator&lt;br /&gt;
(FRCT_LINUX_RTT_ESTIMATOR, default ON) or RFC 6298 symmetric EWMA&lt;br /&gt;
(compile option).  srtt is floored at 10 ms when seeded from a&lt;br /&gt;
hint, at 1 us after every update (including the first seeding&lt;br /&gt;
sample); mdev floored at 100 ns.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    RTO = max(rto_min, 2 * srtt, srtt + (mdev &amp;lt;&amp;lt; MDEV_MUL))&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(the 2 * srtt floor is an FRCT addition not in RFC 6298).&lt;br /&gt;
Effective wheel deadline capped per Section 3.&lt;br /&gt;
&lt;br /&gt;
ACK-derived samples (frcti_ack_rcv -&amp;gt; rtt_sample_eligible), beyond&lt;br /&gt;
the cum-ACK advance gate in frcti_ack_rcv (ackno &amp;gt; lwe and&lt;br /&gt;
ackno &amp;lt;= seqno), require all of: not in recovery; ACK packet does&lt;br /&gt;
not carry FRCT_RXM; HoL slot&#039;s SND_RTX bit clear; slot&#039;s rxm&lt;br /&gt;
pointer non-NULL (not SACK-consumed); lwe not below the rtt_lwe&lt;br /&gt;
fence; srtt already seeded by an RTTP probe.  There is no ACK-only&lt;br /&gt;
seeding.&lt;br /&gt;
&lt;br /&gt;
Every eligible sample also feeds RACK.min_RTT (RFC 8985 sec. 6.2)&lt;br /&gt;
via a windowed minimum: replace whenever the sample is strictly&lt;br /&gt;
smaller OR more than MIN_RTT_WIN_NS (5 min, matches Linux&lt;br /&gt;
tcp_min_rtt_wlen) has elapsed since the current min was set.  The&lt;br /&gt;
downward branch is immediate (faster path picked up at once); the&lt;br /&gt;
upward branch is gated on the window (a transient queue burst does&lt;br /&gt;
not poison the estimate, but a sustained route change to a longer&lt;br /&gt;
path re-anchors min_RTT after at most one window).  Seeded from&lt;br /&gt;
rtt_hint at rtt_init; 0 acts as the unset sentinel and the base&lt;br /&gt;
in rack_reorder_window falls back from min_RTT to SRTT (so&lt;br /&gt;
R = mult * SRTT/4, capped at SRTT, floored at MIN_REORDER_NS)&lt;br /&gt;
until the first sample.  See Section 6.2.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 13. Liveness (keepalive) ==&lt;br /&gt;
&lt;br /&gt;
When qs.timeout &amp;gt; 0 a per-flow KA (keepalive) timer is armed.&lt;br /&gt;
Arming uses rcv_cr.act for the deadline computation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    deadline = min(snd_act + qs.timeout/4,&lt;br /&gt;
                   rcv_act + qs.timeout)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(clamped to now + qs.timeout/4 if already past).  The timer fires&lt;br /&gt;
either on sender idleness (to send a KA) or on receiver idleness&lt;br /&gt;
(to declare the peer dead).  On fire (ka_snd) the peer-dead test&lt;br /&gt;
uses max(rcv_cr.act, t_ka_rcv) so a recent KA reply counts even&lt;br /&gt;
when no DATA has arrived:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - If now - max(rcv_cr.act, t_ka_rcv) &amp;gt; qs.timeout, mark the flow&lt;br /&gt;
    ACL_FLOWPEER and notify the per-process flow-event set&lt;br /&gt;
    (proc.fqset) with FLOW_PEER.&lt;br /&gt;
  - Else if snd_idle &amp;gt; qs.timeout/4, emit a bare KA | ACK&lt;br /&gt;
    (ackno = rcv_cr.lwe) and re-arm.&lt;br /&gt;
  - Else just re-arm.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: rx_rb and tx_rb are the receive and transmit shared-memory&lt;br /&gt;
ring buffers.  The r-timer raises ACL_FLOWDOWN on both (route is&lt;br /&gt;
broken); keepalive raises ACL_FLOWPEER on rx_rb only and notifies&lt;br /&gt;
the flow-event set (peer is silent, writer keeps tx_rb usable) -&lt;br /&gt;
distinct ACLs.  qs.timeout == 0 disables keepalive entirely; a&lt;br /&gt;
silent peer crash is then undetected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 14. Linger / teardown ==&lt;br /&gt;
&lt;br /&gt;
On flow_dealloc, frcti_dealloc computes a grace timeout&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    max(rcv_cr.act + rcv_cr.inact, snd_cr.act + snd_cr.inact) - now&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(floored at 0 and converted to seconds) and returns it; flow_dealloc&lt;br /&gt;
forwards this to the IRMd as the dealloc grace.  The IRMd, not FRCT,&lt;br /&gt;
performs the wait.  Before computing the timeout, FRCT may emit a&lt;br /&gt;
final ACK when rcv_cr.lwe != rcv_cr.seqno (the peer has not been&lt;br /&gt;
told the most recent cumulative ACK) AND the rcv side has been&lt;br /&gt;
active within t_a (a-timer not aged out).&lt;br /&gt;
&lt;br /&gt;
FRCTFLINGER is honoured only when snd_cr.lwe &amp;lt; edge, where edge =&lt;br /&gt;
snd_fin_seqno after FIN has been sent in stream mode and&lt;br /&gt;
snd_cr.seqno otherwise (data or FIN still in flight).  The drain&lt;br /&gt;
itself runs in flow_dealloc&#039;s while (FRCTI_LINGERING) loop, not in&lt;br /&gt;
frcti_dealloc.&lt;br /&gt;
&lt;br /&gt;
The fd is single-reader / single-writer (documented in the&lt;br /&gt;
manpages).  flow_write pumps rx_rb on every call (via&lt;br /&gt;
flow_wait_window -&amp;gt; flow_drain_rx_nb) and additionally blocks on&lt;br /&gt;
rx_rb when the send window is closed.  A pure-writer thread thus&lt;br /&gt;
consumes ACKs without a dedicated reader.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 15. Heritage and adopted techniques ==&lt;br /&gt;
&lt;br /&gt;
Delta-t (Watson, 1981) is the primary heritage; FRCP descends from&lt;br /&gt;
the delta-t protocol family via the Recursive InterNetwork&lt;br /&gt;
Architecture (RINA; Day, &amp;quot;Patterns in Network Architecture&amp;quot;, 2008,&lt;br /&gt;
ch. 9).  Timer-based connection management&lt;br /&gt;
(no SYN/FIN handshake, per-flow state born on first DATA and&lt;br /&gt;
reclaimed after t_mpl + a + r of silence), the DRF marker, and the&lt;br /&gt;
t_mpl / t_a / t_r timers all come from delta-t.  See Watson,&lt;br /&gt;
&amp;quot;Timer-Based Mechanisms in Reliable Transport Protocol Connection&lt;br /&gt;
Management&amp;quot;, Computer Networks 5 (1981).&lt;br /&gt;
&lt;br /&gt;
The unified `flow_alloc(name, qos, ...)` primitive and its&lt;br /&gt;
multi-axis QoS-cube argument (Section 2.2) also come from RINA&lt;br /&gt;
(Day 2008, ch. 6; Grasa et al., &amp;quot;IRATI: investigating RINA as an&lt;br /&gt;
alternative to TCP/IP&amp;quot;, Computer Networks 92 (2015)) - reliability,&lt;br /&gt;
ordering, CRC presence, and encryption are flow attributes, not&lt;br /&gt;
separate sockets or protocols.&lt;br /&gt;
&lt;br /&gt;
The table below summarises additional adopted techniques and their&lt;br /&gt;
references.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| FRCP mechanism         | Heritage         | Reference / note       |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Random new seqno on    | TCP ISN          | RFC 6528 (Gont &amp;amp;       |&lt;br /&gt;
| seqno_rotate           |                  | Bellovin, 2012).       |&lt;br /&gt;
|                        |                  | QUIC PN-space reset    |&lt;br /&gt;
|                        |                  | (RFC 9000 sec. 12.3)   |&lt;br /&gt;
|                        |                  | is a structural        |&lt;br /&gt;
|                        |                  | analogue.              |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Cumulative ACK,        | TCP              | RFC 793 / RFC 9293     |&lt;br /&gt;
| left-window-edge       |                  |                        |&lt;br /&gt;
| advance                |                  |                        |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Receive window with    | TCP              | RFC 793 sec. 3.7 /     |&lt;br /&gt;
| non-shrink rule        |                  | RFC 9293 sec. 3.8.6;   |&lt;br /&gt;
|                        |                  | RFC 1122 sec. 4.2.2.16 |&lt;br /&gt;
|                        |                  | for the explicit non-  |&lt;br /&gt;
|                        |                  | shrink prohibition     |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Modular seqno          | TCP              | RFC 793 sec. 3.3 /     |&lt;br /&gt;
| arithmetic             |                  | RFC 9293 sec. 3.4      |&lt;br /&gt;
| (before/after helpers) |                  |                        |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Selective ACK block    | TCP              | RFC 2018 (Mathis et    |&lt;br /&gt;
| list                   |                  | al., 1996).  Encoded   |&lt;br /&gt;
|                        |                  | as a typed FRCP packet |&lt;br /&gt;
|                        |                  | rather than a TCP      |&lt;br /&gt;
|                        |                  | option, so framing is  |&lt;br /&gt;
|                        |                  | closer to QUIC ACK     |&lt;br /&gt;
|                        |                  | frames.  D-SACK (RFC   |&lt;br /&gt;
|                        |                  | 2883) carried in-band  |&lt;br /&gt;
|                        |                  | as block[0]; see       |&lt;br /&gt;
|                        |                  | Section 1.3.           |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| NewReno-careful        | TCP              | RFC 6582 (Henderson    |&lt;br /&gt;
| recovery with          |                  | et al., 2012); QUIC    |&lt;br /&gt;
| recovery_high gate     |                  | builds on the same     |&lt;br /&gt;
|                        |                  | model in RFC 9002      |&lt;br /&gt;
|                        |                  | sec. 7.3.2.  Cwnd half |&lt;br /&gt;
|                        |                  | absent (CC in IPCP).   |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| RACK reordering        | TCP              | RFC 8985 (Cheng et     |&lt;br /&gt;
| window for fast        |                  | al., 2021).  FRCP      |&lt;br /&gt;
| retransmit             |                  | R = MIN(reo_wnd_mult * |&lt;br /&gt;
|                        |                  | min_RTT / 4, SRTT)     |&lt;br /&gt;
|                        |                  | with a MIN_REORDER_NS  |&lt;br /&gt;
|                        |                  | = 250 us floor against |&lt;br /&gt;
|                        |                  | srtt collapse; matches |&lt;br /&gt;
|                        |                  | RFC 8985 sec. 6.2 and  |&lt;br /&gt;
|                        |                  | Linux tcp_rack_reo_wnd.|&lt;br /&gt;
|                        |                  | DSACK-driven           |&lt;br /&gt;
|                        |                  | reo_wnd_mult (sec. 6.2 |&lt;br /&gt;
|                        |                  | step 4) is adopted;    |&lt;br /&gt;
|                        |                  | see Section 1.3 for    |&lt;br /&gt;
|                        |                  | the wire encoding.     |&lt;br /&gt;
|                        |                  | The hybrid RACK-or-    |&lt;br /&gt;
|                        |                  | DUP_THRESH trigger     |&lt;br /&gt;
|                        |                  | from RFC 8985 sec. 6.2 |&lt;br /&gt;
|                        |                  | step 4 is adopted      |&lt;br /&gt;
|                        |                  | (Section 8).  QUIC&#039;s   |&lt;br /&gt;
|                        |                  | analogue in RFC 9002   |&lt;br /&gt;
|                        |                  | sec. 6.1.2 uses        |&lt;br /&gt;
|                        |                  | max(srtt, latest_rtt)  |&lt;br /&gt;
|                        |                  | as the base.           |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Karn&#039;s algorithm:      | TCP              | Karn &amp;amp; Partridge,      |&lt;br /&gt;
| no RTT sample on       |                  | &amp;quot;Improving Round-Trip  |&lt;br /&gt;
| retransmits, RTO-      |                  | Time Estimates in      |&lt;br /&gt;
| collapse freeze        |                  | Reliable Transport     |&lt;br /&gt;
|                        |                  | Protocols&amp;quot;, SIGCOMM    |&lt;br /&gt;
|                        |                  | 1987; RFC 6298 sec. 3. |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| RTO formula            | TCP              | RFC 6298 (Paxson et    |&lt;br /&gt;
| RTO = max(RTO_MIN,     |                  | al., 2011).  RTO_MIN = |&lt;br /&gt;
| srtt + (mdev &amp;lt;&amp;lt;        |                  | 5 ms is below RFC 6298 |&lt;br /&gt;
| MDEV_MUL))             |                  | sec. 2.4&#039;s 1 s SHOULD- |&lt;br /&gt;
|                        |                  | floor - a recursive-   |&lt;br /&gt;
|                        |                  | layer choice.          |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Linux asymmetric mdev  | Linux kernel     | tcp_rtt_estimator() in |&lt;br /&gt;
| estimator (default)    |                  | net/ipv4/tcp_input.c;  |&lt;br /&gt;
|                        |                  | the if(delta&amp;lt;0) m&amp;gt;&amp;gt;=3  |&lt;br /&gt;
|                        |                  | dampening is a         |&lt;br /&gt;
|                        |                  | kernel divergence from |&lt;br /&gt;
|                        |                  | RFC 6298.  RFC 6298    |&lt;br /&gt;
|                        |                  | EWMA available behind  |&lt;br /&gt;
|                        |                  | a compile flag.        |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Delayed ACK with rate  | TCP              | RFC 813 (Clark, 1982); |&lt;br /&gt;
| suppression            |                  | RFC 1122 sec. 4.2.3.2; |&lt;br /&gt;
|                        |                  | RFC 5681 sec. 4.2.     |&lt;br /&gt;
|                        |                  | Single-deadline        |&lt;br /&gt;
|                        |                  | coalescing rather than |&lt;br /&gt;
|                        |                  | &amp;quot;ack-every-other-      |&lt;br /&gt;
|                        |                  | segment&amp;quot;.              |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Zero-window-probe /    | TCP              | RFC 1122 sec.          |&lt;br /&gt;
| persist-timer          |                  | 4.2.2.17 / RFC 9293    |&lt;br /&gt;
| analogue (RDVS)        |                  | sec. 3.8.6.1.  RDVS    |&lt;br /&gt;
|                        |                  | solicits an FC reply,  |&lt;br /&gt;
|                        |                  | distinct from QUIC     |&lt;br /&gt;
|                        |                  | DATA_BLOCKED (RFC 9000 |&lt;br /&gt;
|                        |                  | sec. 19.12), which is  |&lt;br /&gt;
|                        |                  | one-way notification.  |&lt;br /&gt;
|                        |                  | MAX_RDV give-up        |&lt;br /&gt;
|                        |                  | departs from TCP.      |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Multiplexed control    | SCTP / QUIC      | SCTP chunk bundling    |&lt;br /&gt;
| on a single PCI        |                  | (RFC 9260 sec. 6.10);  |&lt;br /&gt;
|                        |                  | QUIC frame             |&lt;br /&gt;
|                        |                  | multiplexing (RFC 9000 |&lt;br /&gt;
|                        |                  | sec. 12.4).  Cleaner   |&lt;br /&gt;
|                        |                  | fit than TCP&#039;s         |&lt;br /&gt;
|                        |                  | separate-flag-bits     |&lt;br /&gt;
|                        |                  | design.                |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| ACK ranges as          | QUIC             | QUIC ACK frame (RFC    |&lt;br /&gt;
| multiple discontiguous |                  | 9000 sec. 19.3).  FRCP |&lt;br /&gt;
| acked blocks           |                  | SACK is conceptually   |&lt;br /&gt;
|                        |                  | QUIC-frame-shaped      |&lt;br /&gt;
|                        |                  | even though encoded    |&lt;br /&gt;
|                        |                  | as absolute            |&lt;br /&gt;
|                        |                  | [start,end] pairs.     |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Nonce-authenticated    | QUIC             | PATH_CHALLENGE /       |&lt;br /&gt;
| active RTT / liveness  | PATH_CHALLENGE   | PATH_RESPONSE (RFC     |&lt;br /&gt;
| probing (RTTP)         |                  | 9000 sec. 8.2,         |&lt;br /&gt;
|                        |                  | sec. 19.17, sec.       |&lt;br /&gt;
|                        |                  | 19.18).  WebRTC ICE    |&lt;br /&gt;
|                        |                  | consent-freshness      |&lt;br /&gt;
|                        |                  | (RFC 7675) is the      |&lt;br /&gt;
|                        |                  | same pattern.  QUIC&#039;s  |&lt;br /&gt;
|                        |                  | nonce is 8 octets;     |&lt;br /&gt;
|                        |                  | FRCP chooses 16.       |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Probing distinct from  | QUIC             | KA timer answers       |&lt;br /&gt;
| keepalive              |                  | &amp;quot;peer alive?&amp;quot;, RTTP    |&lt;br /&gt;
|                        |                  | answers &amp;quot;path          |&lt;br /&gt;
|                        |                  | measurable?&amp;quot;, as in    |&lt;br /&gt;
|                        |                  | QUIC PING (RFC 9000    |&lt;br /&gt;
|                        |                  | sec. 19.2) vs          |&lt;br /&gt;
|                        |                  | PATH_CHALLENGE.        |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Bare KA + ACK          | QUIC / SCTP      | QUIC PING (RFC 9000    |&lt;br /&gt;
| keepalive packets      |                  | sec. 19.2); SCTP       |&lt;br /&gt;
|                        |                  | HEARTBEAT /            |&lt;br /&gt;
|                        |                  | HEARTBEAT-ACK (RFC     |&lt;br /&gt;
|                        |                  | 9260 sec. 8.3).  SCTP  |&lt;br /&gt;
|                        |                  | HEARTBEAT also carries |&lt;br /&gt;
|                        |                  | an opaque echoed blob, |&lt;br /&gt;
|                        |                  | structurally similar   |&lt;br /&gt;
|                        |                  | to FRCP RTTP.          |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| (FFGM, LFGM)           | SCTP             | RFC 9260 sec. 3.3.1    |&lt;br /&gt;
| fragment-role bits     |                  | DATA chunk B/E bits    |&lt;br /&gt;
| (Section 7.2)          |                  | encode the same four   |&lt;br /&gt;
|                        |                  | states (B+E=SOLE,      |&lt;br /&gt;
|                        |                  | B-only=FIRST, neither  |&lt;br /&gt;
|                        |                  | =MID, E-only=LAST).    |&lt;br /&gt;
|                        |                  | Each fragment carries  |&lt;br /&gt;
|                        |                  | its own seqno/TSN and  |&lt;br /&gt;
|                        |                  | is independently       |&lt;br /&gt;
|                        |                  | retransmitted.         |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Stream byte-offset     | QUIC             | QUIC STREAM frame      |&lt;br /&gt;
| reassembly             |                  | (RFC 9000 sec. 19.8)   |&lt;br /&gt;
| (Sections 1.5, 16)     |                  | uses Offset + Length   |&lt;br /&gt;
|                        |                  | varints; FRCP uses     |&lt;br /&gt;
|                        |                  | fixed 32-bit start /   |&lt;br /&gt;
|                        |                  | end.  One stream per   |&lt;br /&gt;
|                        |                  | flow vs QUIC&#039;s many    |&lt;br /&gt;
|                        |                  | streams multiplexed.   |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| FIN end-of-stream      | TCP / QUIC       | TCP FIN flag (RFC 9293 |&lt;br /&gt;
| marker                 |                  | sec. 3.1) closes one   |&lt;br /&gt;
| (Sections 1.2, 16)     |                  | half of the byte       |&lt;br /&gt;
|                        |                  | stream; QUIC STREAM    |&lt;br /&gt;
|                        |                  | frame FIN bit (RFC     |&lt;br /&gt;
|                        |                  | 9000 sec. 19.8) does   |&lt;br /&gt;
|                        |                  | the same per stream    |&lt;br /&gt;
|                        |                  | with an immutable      |&lt;br /&gt;
|                        |                  | final-size invariance  |&lt;br /&gt;
|                        |                  | (RFC 9000 sec. 4.5:    |&lt;br /&gt;
|                        |                  | the final size is      |&lt;br /&gt;
|                        |                  | fixed once observed).  |&lt;br /&gt;
|                        |                  | FRCP&#039;s FIN consumes    |&lt;br /&gt;
|                        |                  | one packet seqno (not  |&lt;br /&gt;
|                        |                  | one byte of stream     |&lt;br /&gt;
|                        |                  | space) and is          |&lt;br /&gt;
|                        |                  | idempotent on the      |&lt;br /&gt;
|                        |                  | sender side.           |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Stream byte-credit     | QUIC             | MAX_STREAM_DATA (RFC   |&lt;br /&gt;
| flow control           |                  | 9000 sec. 4.1, sec.    |&lt;br /&gt;
| (Section 16)           |                  | 19.10).  FRCP projects |&lt;br /&gt;
|                        |                  | a per-flow byte budget |&lt;br /&gt;
|                        |                  | onto the seqno-space   |&lt;br /&gt;
|                        |                  | rwe.  Single stream    |&lt;br /&gt;
|                        |                  | per flow collapses     |&lt;br /&gt;
|                        |                  | QUIC&#039;s MAX_DATA /      |&lt;br /&gt;
|                        |                  | MAX_STREAM_            |&lt;br /&gt;
|                        |                  | DATA distinction.      |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Header protection      | QUIC             | QUIC RFC 9001 sec. 5.4 |&lt;br /&gt;
| (encrypted seqnos)     |                  | applies header         |&lt;br /&gt;
|                        |                  | protection on top of   |&lt;br /&gt;
|                        |                  | AEAD to mask the       |&lt;br /&gt;
|                        |                  | packet number.  FRCP&#039;s |&lt;br /&gt;
|                        |                  | per-flow AEAD wrap     |&lt;br /&gt;
|                        |                  | (Section 16) is wider: |&lt;br /&gt;
|                        |                  | it encrypts the entire |&lt;br /&gt;
|                        |                  | PCI including seqno    |&lt;br /&gt;
|                        |                  | because the IPCP       |&lt;br /&gt;
|                        |                  | below already routes,  |&lt;br /&gt;
|                        |                  | so no destination      |&lt;br /&gt;
|                        |                  | connection-ID needs to |&lt;br /&gt;
|                        |                  | stay in clear (cf.     |&lt;br /&gt;
|                        |                  | RFC 9000 sec. 5.2).    |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Two-bit fragment role  | SCTP             | The (FFGM, LFGM) pair  |&lt;br /&gt;
| polarity               |                  | follows SCTP B/E       |&lt;br /&gt;
|                        |                  | (begin = 1 / end = 1)  |&lt;br /&gt;
|                        |                  | rather than IPv4 MF    |&lt;br /&gt;
|                        |                  | (RFC 791 sec. 3.2),    |&lt;br /&gt;
|                        |                  | which has the inverse  |&lt;br /&gt;
|                        |                  | polarity (MF = 1 means |&lt;br /&gt;
|                        |                  | NOT last).             |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Orthogonal reliability | SCTP             | PR-SCTP (RFC 3758,     |&lt;br /&gt;
| / ordering axes        |                  | per-message partial    |&lt;br /&gt;
| (Section 2.2)          |                  | reliability) and SCTP  |&lt;br /&gt;
|                        |                  | DATA U-bit (RFC 9260   |&lt;br /&gt;
|                        |                  | sec. 3.3.1, per-       |&lt;br /&gt;
|                        |                  | message unordered)     |&lt;br /&gt;
|                        |                  | are the closest        |&lt;br /&gt;
|                        |                  | precedents for         |&lt;br /&gt;
|                        |                  | decoupling reliability |&lt;br /&gt;
|                        |                  | from ordering; FRCP    |&lt;br /&gt;
|                        |                  | sets them per-flow     |&lt;br /&gt;
|                        |                  | rather than per-       |&lt;br /&gt;
|                        |                  | message.               |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Orthogonal CRC         | UDP-Lite         | RFC 3828 (Larzon et    |&lt;br /&gt;
| (qs.ber == 0)          |                  | al., 2004) lets the    |&lt;br /&gt;
|                        |                  | sender pick a per-     |&lt;br /&gt;
|                        |                  | packet Checksum        |&lt;br /&gt;
|                        |                  | Coverage and the       |&lt;br /&gt;
|                        |                  | receiver enforce a     |&lt;br /&gt;
|                        |                  | locally configured     |&lt;br /&gt;
|                        |                  | minimum (no in-band    |&lt;br /&gt;
|                        |                  | negotiation; sec. 3.1, |&lt;br /&gt;
|                        |                  | sec. 3.3).  FRCP       |&lt;br /&gt;
|                        |                  | gates a full CRC       |&lt;br /&gt;
|                        |                  | trailer on qs.ber == 0 |&lt;br /&gt;
|                        |                  | at flow setup.         |&lt;br /&gt;
|                        |                  | Contrast TCP / SCTP    |&lt;br /&gt;
|                        |                  | (mandatory checksum)   |&lt;br /&gt;
|                        |                  | and QUIC (AEAD         |&lt;br /&gt;
|                        |                  | subsumes CRC).         |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
| Setup-time service     | DCCP / SCTP /    | DCCP Service Codes     |&lt;br /&gt;
| negotiation            | QUIC             | (RFC 4340 sec. 8.1.2,  |&lt;br /&gt;
|                        |                  | RFC 5595); SCTP INIT   |&lt;br /&gt;
|                        |                  | parameters (RFC 9260   |&lt;br /&gt;
|                        |                  | sec. 3.3.2); QUIC      |&lt;br /&gt;
|                        |                  | transport parameters   |&lt;br /&gt;
|                        |                  | (RFC 9000 sec. 7.4).   |&lt;br /&gt;
|                        |                  | All negotiate service  |&lt;br /&gt;
|                        |                  | properties at          |&lt;br /&gt;
|                        |                  | connection setup; only |&lt;br /&gt;
|                        |                  | RINA&#039;s QoS cube        |&lt;br /&gt;
|                        |                  | exposes them as an     |&lt;br /&gt;
|                        |                  | orthogonal vector.     |&lt;br /&gt;
+------------------------+------------------+------------------------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.1. Original to FRCP (no clean prior art) ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - Pre-DRF NACK (Section 9): receiver-driven nudge exploiting&lt;br /&gt;
    snd_cr.inact &amp;gt; rcv_cr.inact.  Closest analogues are SCTP Gap Ack&lt;br /&gt;
    Blocks (RFC 9260 sec. 3.3.4) and DCCP Ack Vector (RFC 4340&lt;br /&gt;
    sec. 11.4) - both let the receiver describe gaps to the sender,&lt;br /&gt;
    but neither targets the cross-epoch / pre-DRF case.&lt;br /&gt;
  - MAX_RDV window-probe give-up: neither TCP (persist-timer&lt;br /&gt;
    probes until application or R2 abort, RFC 9293 sec. 3.8.6.1)&lt;br /&gt;
    nor QUIC has an explicit FC-give-up counter.  A recursive-&lt;br /&gt;
    network choice: outer layers can drop the flow.&lt;br /&gt;
  - Skip-past-gap reassembly (Section 7.2): SCTP fragments and&lt;br /&gt;
    reassembles every flow regardless of reliability/ordering,&lt;br /&gt;
    using its own per-stream reassembly queue; QUIC fragments via&lt;br /&gt;
    STREAM offsets.  FRCP fragments best-effort flows too, but&lt;br /&gt;
    the receiver drops the broken prefix the moment a later run-&lt;br /&gt;
    start (FIRST or SOLE role) is visible inside the RQ_SIZE-wide&lt;br /&gt;
    reorder ring - no IP-frag-style timeout, no SCTP-style&lt;br /&gt;
    explicit abort.  If no later run-start arrives within the&lt;br /&gt;
    ring, frag_run_inspect returns NOT_READY and the partial run&lt;br /&gt;
    keeps its slots; the next inspect retries.  The trade-off: a&lt;br /&gt;
    permanently-lost MID in a long isolated run holds slots until&lt;br /&gt;
    either a later FIRST/SOLE appears in the ring or the writer&lt;br /&gt;
    stops, at which point the slots are reclaimed on flow&lt;br /&gt;
    teardown.&lt;br /&gt;
  - Reassembly deferred to consume time (Section 7.2), message&lt;br /&gt;
    mode only (qos.service == SVC_MESSAGE): SCTP (RFC 9260&lt;br /&gt;
    sec. 6.9), QUIC (RFC 9000 sec. 2.2), and TCP (RFC 9293) all&lt;br /&gt;
    hold reassembly state at the receive boundary.  FRCP message-&lt;br /&gt;
    mode leaves fragments in the shared-memory ring until&lt;br /&gt;
    flow_read pulls and lands the SDU directly in the caller&#039;s&lt;br /&gt;
    buffer.  Stream mode (Section 16) uses the standard QUIC-&lt;br /&gt;
    style direct ring placement on receive and does not defer.&lt;br /&gt;
    The optimisation is enabled by the Shared-Memory Subsystem&lt;br /&gt;
    (SSM) packet-buffer ring (see struct ssm_pk_buff at&lt;br /&gt;
    Section 1.1); the analogue is OS-level scatter-gather I/O&lt;br /&gt;
    (recvmsg+iovec), not a transport-layer prior art.&lt;br /&gt;
  - TLP-equivalent tail-loss recovery (RFC 8985 sec. 7;&lt;br /&gt;
    RFC 9002 sec. 6.2): FRCP does not emit an explicit Tail Loss&lt;br /&gt;
    Probe packet, but the same goal is met implicitly by RACK&lt;br /&gt;
    loss detection (Section 8) firing on a non-advancing&lt;br /&gt;
    cumulative ACK once the head-of-line slot ages past the RACK&lt;br /&gt;
    reorder window R = MIN(reo_wnd_mult * min_RTT / 4, SRTT) -&lt;br /&gt;
    well below RTO = max(2 * SRTT, SRTT + (mdev &amp;lt;&amp;lt; MDEV_MUL)).&lt;br /&gt;
    A receiver-driven nudge is also available via the pre-DRF&lt;br /&gt;
    NACK (Section 9).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 15.2. Not adopted ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - Slow start, congestion window (cwnd), Additive Increase /&lt;br /&gt;
    Multiplicative Decrease (AIMD), NewReno cwnd inflation.&lt;br /&gt;
    Congestion control lives in the IPCP CA policies and is&lt;br /&gt;
    driven by Explicit Congestion Notification (ECN, RFC 3168).&lt;br /&gt;
  - Nagle / silly-window-syndrome (SWS) avoidance (RFC 896, RFC&lt;br /&gt;
    1122 sec. 4.2.3.4).  (Deferred work, not adopted in the&lt;br /&gt;
    current spec.)&lt;br /&gt;
  - TCP Timestamps (RFC 7323) / Protection Against Wrapped&lt;br /&gt;
    Sequences (PAWS) - RTT measurement uses RTTP,&lt;br /&gt;
    not per-segment timestamps.  A peer-supplied timestamp echoed&lt;br /&gt;
    on every ACK lets a malicious peer drive the srtt estimate&lt;br /&gt;
    arbitrarily low, collapsing the RTO and triggering a self-&lt;br /&gt;
    inflicted retransmit storm.  RTTP confines RTT measurement to&lt;br /&gt;
    nonce-authenticated probe round-trips, where a forged echo is&lt;br /&gt;
    rejected before it can reach the estimator.&lt;br /&gt;
  - ECN (Explicit Congestion Notification) response inside FRCP&lt;br /&gt;
    (consumed by IPCP Congestion Avoidance / CA).&lt;br /&gt;
  - IP-style fragment-offset reassembly (RFC 791 sec. 3.2; RFC 8200&lt;br /&gt;
    sec. 4.5).  Message-mode FRCP relies on the FRCT rq[] reorder&lt;br /&gt;
    ring keyed by seqno (shared by FRTX and best-effort flows) to&lt;br /&gt;
    put fragments back in order; no separate offset field is&lt;br /&gt;
    needed and no IP-style hole-list reassembly buffer is kept.&lt;br /&gt;
    Stream-mode FRCP does carry [start, end) byte offsets&lt;br /&gt;
    (Section 1.5) for direct ring placement on receive.&lt;br /&gt;
  - QUIC STREAM offset+length framing on *every* flow (RFC 9000&lt;br /&gt;
    sec. 19.8).  Message-mode FRCP uses the SCTP-style B/E flag-&lt;br /&gt;
    bit encoding (FFGM/LFGM) and skips the offsets; stream-mode&lt;br /&gt;
    FRCP adopts the QUIC offset model (heritage table above).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 16. Stream-mode flows ==&lt;br /&gt;
&lt;br /&gt;
When a flow is allocated with qos.service == SVC_STREAM both peers&lt;br /&gt;
switch to byte-stream semantics, layered on top of the FRTX reorder&lt;br /&gt;
machinery already described in Sections 6-8.&lt;br /&gt;
&lt;br /&gt;
=== 16.1. Send ===&lt;br /&gt;
&lt;br /&gt;
The sender splits the caller&#039;s octets into chunks of at most&lt;br /&gt;
(frag_mtu - base PCI - stream PCI extension) octets (Sections 1.1&lt;br /&gt;
and 1.5).  Each chunk is one DATA packet with its own seqno and a&lt;br /&gt;
[start, end) byte range copied from a monotonic stream counter.&lt;br /&gt;
In stream mode FFGM and LFGM are unused and MUST be transmitted as&lt;br /&gt;
zero; the per-byte position is carried by the [start, end)&lt;br /&gt;
extension instead.&lt;br /&gt;
&lt;br /&gt;
End-of-stream is signalled with a 0-byte DATA packet that has FIN&lt;br /&gt;
(bit 12) set, emitted on the FIN triggers listed in Section 1.2&lt;br /&gt;
(WR-half close, flow_dealloc, and any other path that yields the&lt;br /&gt;
final byte).  The sender MUST emit at most one FIN per flow; its&lt;br /&gt;
[start, end) MUST equal [final-byte, final-byte) (i.e., empty&lt;br /&gt;
interval at the final byte position; final-size invariance,&lt;br /&gt;
analogous to QUIC RFC 9000 sec. 4.5).  Idempotency is enforced by&lt;br /&gt;
an snd_fin_sent guard.&lt;br /&gt;
&lt;br /&gt;
=== 16.2. Receive ===&lt;br /&gt;
&lt;br /&gt;
On arrival the receiver places the payload directly into a per-flow&lt;br /&gt;
byte-indexed receive ring of width ring_sz (octets) at the position&lt;br /&gt;
indicated by start, with a two-segment memcpy across the ring&lt;br /&gt;
boundary if needed.  Receipt is recorded in the FRTX reorder&lt;br /&gt;
machinery (Section 6.2) augmented with the packet&#039;s start, end, and&lt;br /&gt;
FIN bit per slot.  When a packet&#039;s [start, end) front-overlaps&lt;br /&gt;
bytes already at or below the byte high-water mark, the overlap is&lt;br /&gt;
trimmed before placement so the same byte is never written twice.&lt;br /&gt;
After stashing, the receiver advances lwe and the byte high-water&lt;br /&gt;
mark across any newly-contiguous prefix.  Each slot advanced MUST&lt;br /&gt;
satisfy `start == the last-delivered slot&#039;s end`; a slot whose&lt;br /&gt;
start does not equal that end is silently dropped at delivery time&lt;br /&gt;
(the seqno is consumed, no stream bytes contributed) and the high-&lt;br /&gt;
water mark does not advance past it.  The stream byte-stream&lt;br /&gt;
stalls at that point - there is no flow-tear-down on mismatch.&lt;br /&gt;
This filters spliced or off-path-injected slots that fall in&lt;br /&gt;
window without strong cryptographic authentication.&lt;br /&gt;
&lt;br /&gt;
A FIN slot marks end-of-stream at advance time only if its byte&lt;br /&gt;
position equals the last-delivered slot&#039;s end; otherwise the FIN&lt;br /&gt;
is ignored and the corresponding seqno occupies a slot but&lt;br /&gt;
contributes no stream bytes.  No packet buffer is held after the&lt;br /&gt;
ring copy.&lt;br /&gt;
&lt;br /&gt;
=== 16.3. Read ===&lt;br /&gt;
&lt;br /&gt;
flow_read returns up to count octets from the contiguous prefix&lt;br /&gt;
[next, high-water), where next is the byte the application has&lt;br /&gt;
already consumed up to and high-water is the rightmost contiguous&lt;br /&gt;
byte received.  When the stream is fully drained AND end-of-stream&lt;br /&gt;
(EOS) was observed (next == EOS byte position), flow_read returns&lt;br /&gt;
0 (EOF) - the same shape POSIX read(2) uses on TCP after a peer&lt;br /&gt;
FIN.&lt;br /&gt;
&lt;br /&gt;
=== 16.4. Flow control ===&lt;br /&gt;
&lt;br /&gt;
ACK / SACK / RACK / RTO machinery is unchanged; the FRTX reorder&lt;br /&gt;
ring is reused as a per-seqno received-bitmap.  Let per_pkt =&lt;br /&gt;
(frag_mtu - base PCI - stream PCI extension), the maximum stream-&lt;br /&gt;
byte payload one DATA packet can carry (Section 16.1).  The&lt;br /&gt;
receive window advertised in FC is clamped so the byte window&lt;br /&gt;
(ring_sz) cannot be overrun: the seqno-space rwe is at most&lt;br /&gt;
`rcv_cr.lwe + ring_sz / per_pkt`.&lt;br /&gt;
&lt;br /&gt;
This is the QUIC byte-credit flow-control model&lt;br /&gt;
(MAX_STREAM_DATA, RFC 9000 sec. 4.1 and sec. 19.10) projected onto&lt;br /&gt;
seqno space.  With one stream per flow there is no MAX_DATA /&lt;br /&gt;
MAX_STREAM_DATA distinction.  Receiver-side silly-window-syndrome&lt;br /&gt;
(SWS) avoidance (RFC 9293 sec. 3.8.6.2.2) is achieved by combining&lt;br /&gt;
the consume-time rwe bump with the global non-shrink rule from&lt;br /&gt;
Section 11.&lt;br /&gt;
&lt;br /&gt;
=== 16.5. Security considerations ===&lt;br /&gt;
&lt;br /&gt;
Threat model.  An attacker that can observe (on-path passive) or&lt;br /&gt;
predict (off-path blind) the flow&#039;s seqnos and byte offsets on an&lt;br /&gt;
unencrypted stream flow can inject DATA or FIN at any in-window&lt;br /&gt;
position.  The in-line consistency checks above (start == prior&lt;br /&gt;
end on advance; FIN MUST be 0-byte; FIN MUST sit at the final&lt;br /&gt;
byte position) realise the spirit of RFC 5961&#039;s &amp;quot;sequence-window&lt;br /&gt;
plus exact-position match for control bits&amp;quot; without an explicit&lt;br /&gt;
challenge-ACK probe; they make a few specific blind attack shapes&lt;br /&gt;
harder but are not cryptographic authentication. This is&lt;br /&gt;
comparable to TCP without the TCP Authentication Option (TCP-AO,&lt;br /&gt;
RFC 5925), tighter than a&lt;br /&gt;
pre-RFC-5961 TCP stack, and roughly equivalent to a modern&lt;br /&gt;
RFC 5961 stack against blind off-path injection - none of these&lt;br /&gt;
help once the attacker can sniff. TLS over TCP (RFC 8446)&lt;br /&gt;
encrypts only the TCP payload and leaves TCP seqnos, ACKs, FIN,&lt;br /&gt;
and RST in the clear, so TLS does NOT defend against TCP-header-&lt;br /&gt;
level injection; QUIC (RFC 9000) hides packet numbers under&lt;br /&gt;
header protection (RFC 9001 sec. 5.4), so this specific weakness&lt;br /&gt;
does not apply to QUIC.&lt;br /&gt;
&lt;br /&gt;
Mitigation: AEAD.  When the flow has encryption enabled the&lt;br /&gt;
recommended AEAD ciphers (AES-GCM, RFC 5288; or ChaCha20-Poly1305,&lt;br /&gt;
RFC 8439) wrap the entire FRCP packet on the wire - PCI, stream&lt;br /&gt;
extension, body, and the CRC trailer when ber == 0 - under a&lt;br /&gt;
per-flow symmetric key derived from the flow&#039;s own key exchange&lt;br /&gt;
(Section 1.1).  The AEAD tag (~2^-128 forgery probability)&lt;br /&gt;
dominates the CRC (~2^-32) for integrity in this mode but the CRC&lt;br /&gt;
trailer is currently retained inside the wrap (see Section 1.1).&lt;br /&gt;
Implementations MUST NOT rely on the security properties below&lt;br /&gt;
when a non-AEAD cipher (e.g. AES-CTR alone) is negotiated; non-&lt;br /&gt;
AEAD modes provide confidentiality only and the threat-model&lt;br /&gt;
claims do not hold.&lt;br /&gt;
&lt;br /&gt;
With an AEAD cipher in use, seqnos, byte offsets, and the FIN bit&lt;br /&gt;
are both authenticated and confidential. Against an off-path or&lt;br /&gt;
on-path-passive attacker this is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - Stronger than TCP+TLS (TCP header in the clear).&lt;br /&gt;
  - Stronger than TCP+TCP-AO (header authenticated but visible).&lt;br /&gt;
  - Comparable to IPsec ESP transport mode (RFC 4303), which&lt;br /&gt;
    similarly authenticates and encrypts the upper-layer header&lt;br /&gt;
    plus payload, and to QUIC packet protection (RFC 9001 sec. 5),&lt;br /&gt;
    with the difference that QUIC must leave the destination&lt;br /&gt;
    connection ID in the clear for routing whereas FRCP relies on&lt;br /&gt;
    the IPCP below for delivery and can therefore encrypt its&lt;br /&gt;
    entire PCI.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Keying granularity.  FRCP runs key exchange (kex) per flow, so&lt;br /&gt;
each flow_alloc yields independent symmetric keys.  This is&lt;br /&gt;
finer-grained than QUIC (per-connection, RFC 9001, where one&lt;br /&gt;
handshake covers all multiplexed streams) and finer-grained than&lt;br /&gt;
typical IPsec deployment (per-host-pair Security Associations,&lt;br /&gt;
SAs).  Forward secrecy follows from the kex when an ephemeral&lt;br /&gt;
Diffie-Hellman exchange (DHE), or a hybrid mode (classical DH +&lt;br /&gt;
post-quantum Key Encapsulation Mechanism / KEM), is selected.&lt;br /&gt;
&lt;br /&gt;
Replay protection.  The AEAD layer itself does NOT carry an&lt;br /&gt;
explicit anti-replay window (unlike IPsec ESP, RFC 4303 sec.&lt;br /&gt;
3.4.3, or DTLS, RFC 9147 sec. 4.5.1).  For FRCP-engaged flows the&lt;br /&gt;
seqno-space duplicate-suppression in Section 6.2 rejects replayed&lt;br /&gt;
DATA after the AEAD strips the wrap, because the AEAD authenticates&lt;br /&gt;
the seqno and a replay re-presents an old seqno that is then&lt;br /&gt;
discarded either as a duplicate (still inside the receive window)&lt;br /&gt;
or as outside the receive window, depending on how far lwe has&lt;br /&gt;
advanced since the original packet was delivered.  RAW&lt;br /&gt;
(qos.service == SVC_RAW) flows have no FRCP layer and therefore&lt;br /&gt;
no replay protection at the AEAD layer either; deployments that&lt;br /&gt;
need replay rejection on RAW flows MUST provide it at a higher&lt;br /&gt;
layer.&lt;br /&gt;
&lt;br /&gt;
Layering.  The AEAD wrap sits below FRCP on the data path, so&lt;br /&gt;
RAW best-effort flows (qos.service == SVC_RAW, the UDP-equivalent&lt;br /&gt;
service of Section 2.2) inherit the same per-flow integrity +&lt;br /&gt;
confidentiality scope as FRCP-engaged flows - whatever the IPCP&lt;br /&gt;
and FRCP (if any) put on the wire is what the AEAD authenticates.&lt;br /&gt;
No DTLS-equivalent layering is required for confidentiality and&lt;br /&gt;
integrity; replay protection above AEAD is a separate concern as&lt;br /&gt;
noted above.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== 17. References ==&lt;br /&gt;
&lt;br /&gt;
This section lists the IETF documents, published works, and&lt;br /&gt;
source-code references cited inline elsewhere in this document.&lt;br /&gt;
IETF documents are cited inline as &amp;quot;RFC NNNN sec. X.Y&amp;quot;; books,&lt;br /&gt;
journal papers, and source-code references are cited inline by&lt;br /&gt;
author and year (or by file and function name) and are listed&lt;br /&gt;
here for convenience.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.1. IETF documents ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 791]   J. Postel, &amp;quot;Internet Protocol&amp;quot;, STD 5, RFC 791,&lt;br /&gt;
              September 1981.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 793]   J. Postel, &amp;quot;Transmission Control Protocol&amp;quot;, STD 7,&lt;br /&gt;
              RFC 793, September 1981.  Obsoleted by RFC 9293.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 813]   D. D. Clark, &amp;quot;Window and Acknowledgement Strategy&lt;br /&gt;
              in TCP&amp;quot;, RFC 813, July 1982.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 896]   J. Nagle, &amp;quot;Congestion Control in IP/TCP&lt;br /&gt;
              Internetworks&amp;quot;, RFC 896, January 1984.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 1122]  R. Braden (ed.), &amp;quot;Requirements for Internet Hosts&lt;br /&gt;
              -- Communication Layers&amp;quot;, STD 3, RFC 1122,&lt;br /&gt;
              October 1989.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 2018]  M. Mathis, J. Mahdavi, S. Floyd, A. Romanow,&lt;br /&gt;
              &amp;quot;TCP Selective Acknowledgment Options&amp;quot;, RFC 2018,&lt;br /&gt;
              October 1996.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 2119]  S. Bradner, &amp;quot;Key words for use in RFCs to Indicate&lt;br /&gt;
              Requirement Levels&amp;quot;, BCP 14, RFC 2119, March 1997.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 2883]  S. Floyd, J. Mahdavi, M. Mathis, M. Podolsky,&lt;br /&gt;
              &amp;quot;An Extension to the Selective Acknowledgement&lt;br /&gt;
              (SACK) Option for TCP&amp;quot;, RFC 2883, July 2000.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 3758]  R. Stewart, M. Ramalho, Q. Xie, M. Tuexen,&lt;br /&gt;
              P. Conrad, &amp;quot;Stream Control Transmission Protocol&lt;br /&gt;
              (SCTP) Partial Reliability Extension&amp;quot;, RFC 3758,&lt;br /&gt;
              May 2004.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 3828]  L-A. Larzon, M. Degermark, S. Pink, L-E. Jonsson&lt;br /&gt;
              (ed.), G. Fairhurst (ed.), &amp;quot;The Lightweight User&lt;br /&gt;
              Datagram Protocol (UDP-Lite)&amp;quot;, RFC 3828,&lt;br /&gt;
              July 2004.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 4303]  S. Kent, &amp;quot;IP Encapsulating Security Payload&lt;br /&gt;
              (ESP)&amp;quot;, RFC 4303, December 2005.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 4340]  E. Kohler, M. Handley, S. Floyd, &amp;quot;Datagram&lt;br /&gt;
              Congestion Control Protocol (DCCP)&amp;quot;, RFC 4340,&lt;br /&gt;
              March 2006.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 5288]  J. Salowey, A. Choudhury, D. McGrew, &amp;quot;AES Galois&lt;br /&gt;
              Counter Mode (GCM) Cipher Suites for TLS&amp;quot;,&lt;br /&gt;
              RFC 5288, August 2008.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 5595]  G. Fairhurst, &amp;quot;The Datagram Congestion Control&lt;br /&gt;
              Protocol (DCCP) Service Codes&amp;quot;, RFC 5595,&lt;br /&gt;
              September 2009.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 5681]  M. Allman, V. Paxson, E. Blanton, &amp;quot;TCP Congestion&lt;br /&gt;
              Control&amp;quot;, RFC 5681, September 2009.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 5925]  J. Touch, A. Mankin, R. Bonica, &amp;quot;The TCP&lt;br /&gt;
              Authentication Option&amp;quot;, RFC 5925, June 2010.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 5961]  A. Ramaiah, R. Stewart, M. Dalal, &amp;quot;Improving&lt;br /&gt;
              TCP&#039;s Robustness to Blind In-Window Attacks&amp;quot;,&lt;br /&gt;
              RFC 5961, August 2010.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 6298]  V. Paxson, M. Allman, J. Chu, M. Sargent,&lt;br /&gt;
              &amp;quot;Computing TCP&#039;s Retransmission Timer&amp;quot;, RFC 6298,&lt;br /&gt;
              June 2011.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 6528]  F. Gont, S. Bellovin, &amp;quot;Defending against Sequence&lt;br /&gt;
              Number Attacks&amp;quot;, RFC 6528, February 2012.&lt;br /&gt;
              Obsoletes RFC 1948.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 6582]  T. Henderson, S. Floyd, A. Gurtov, Y. Nishida,&lt;br /&gt;
              &amp;quot;The NewReno Modification to TCP&#039;s Fast Recovery&lt;br /&gt;
              Algorithm&amp;quot;, RFC 6582, April 2012.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 7323]  D. Borman, B. Braden, V. Jacobson,&lt;br /&gt;
              R. Scheffenegger (ed.), &amp;quot;TCP Extensions for High&lt;br /&gt;
              Performance&amp;quot;, RFC 7323, September 2014.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 7675]  M. Perumal, D. Wing, R. Ravindranath, T. Reddy,&lt;br /&gt;
              M. Thomson, &amp;quot;Session Traversal Utilities for NAT&lt;br /&gt;
              (STUN) Usage for Consent Freshness&amp;quot;, RFC 7675,&lt;br /&gt;
              October 2015.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 8174]  B. Leiba, &amp;quot;Ambiguity of Uppercase vs Lowercase in&lt;br /&gt;
              RFC 2119 Key Words&amp;quot;, BCP 14, RFC 8174, May 2017.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 8200]  S. Deering, R. Hinden, &amp;quot;Internet Protocol,&lt;br /&gt;
              Version 6 (IPv6) Specification&amp;quot;, STD 86, RFC 8200,&lt;br /&gt;
              July 2017.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 8439]  Y. Nir, A. Langley, &amp;quot;ChaCha20 and Poly1305 for IETF&lt;br /&gt;
              Protocols&amp;quot;, RFC 8439, June 2018.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 8446]  E. Rescorla, &amp;quot;The Transport Layer Security (TLS)&lt;br /&gt;
              Protocol Version 1.3&amp;quot;, RFC 8446, August 2018.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 8985]  Y. Cheng, N. Cardwell, N. Dukkipati, P. Jha,&lt;br /&gt;
              &amp;quot;The RACK-TLP Loss Detection Algorithm for TCP&amp;quot;,&lt;br /&gt;
              RFC 8985, February 2021.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 9000]  J. Iyengar (ed.), M. Thomson (ed.), &amp;quot;QUIC: A&lt;br /&gt;
              UDP-Based Multiplexed and Secure Transport&amp;quot;,&lt;br /&gt;
              RFC 9000, May 2021.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 9001]  M. Thomson (ed.), S. Turner (ed.), &amp;quot;Using TLS to&lt;br /&gt;
              Secure QUIC&amp;quot;, RFC 9001, May 2021.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 9002]  J. Iyengar (ed.), I. Swett (ed.), &amp;quot;QUIC Loss&lt;br /&gt;
              Detection and Congestion Control&amp;quot;, RFC 9002,&lt;br /&gt;
              May 2021.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 9147]  E. Rescorla, H. Tschofenig, N. Modadugu,&lt;br /&gt;
              &amp;quot;The Datagram Transport Layer Security (DTLS)&lt;br /&gt;
              Protocol Version 1.3&amp;quot;, RFC 9147, April 2022.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 9260]  R. Stewart, M. Tuexen, K. Nielsen, &amp;quot;Stream Control&lt;br /&gt;
              Transmission Protocol&amp;quot;, RFC 9260, June 2022.&lt;br /&gt;
              Obsoletes RFC 4960.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  [RFC 9293]  W. Eddy (ed.), &amp;quot;Transmission Control Protocol&lt;br /&gt;
              (TCP)&amp;quot;, STD 7, RFC 9293, August 2022.  Obsoletes&lt;br /&gt;
              RFC 793 and several follow-ons; updates RFC 1122&lt;br /&gt;
              and others.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.2. Books and journal papers ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - J. Day, &amp;quot;Patterns in Network Architecture: A Return to&lt;br /&gt;
    Fundamentals&amp;quot;, Prentice Hall, 2008.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - E. Grasa et al., &amp;quot;IRATI: investigating RINA as an alternative&lt;br /&gt;
    to TCP/IP&amp;quot;, Computer Networks, Vol. 92, December 2015.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - P. Karn, C. Partridge, &amp;quot;Improving Round-Trip Time Estimates&lt;br /&gt;
    in Reliable Transport Protocols&amp;quot;, ACM SIGCOMM, August 1987.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - R. W. Watson, &amp;quot;Timer-Based Mechanisms in Reliable Transport&lt;br /&gt;
    Protocol Connection Management&amp;quot;, Computer Networks, Vol. 5,&lt;br /&gt;
    1981.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 17.3. Source-code references ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  - tcp_rtt_estimator() in net/ipv4/tcp_input.c of the Linux&lt;br /&gt;
    kernel, defining the asymmetric mdev variance update used as&lt;br /&gt;
    FRCP&#039;s default RTT estimator (Section 12).  Line-stable&lt;br /&gt;
    browseable copy at&lt;br /&gt;
    https://elixir.bootlin.com/linux/latest/source/net/ipv4/tcp_input.c.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1910</id>
		<title>Prototype Version History</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1910"/>
		<updated>2026-02-22T16:59:51Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 0.23 (Current version, February 22, 2026) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ouroboros is still in an early proof-of-concept prototype phase. Versions 0.x.y have no forward nor backward compatibility. We bump minor version with API or protocol changes. Within a minor version, different patch levels &#039;&#039;should&#039;&#039; be compatible (no guarantees).&lt;br /&gt;
&lt;br /&gt;
Once the functionality has reached a minimal critical mass, and a sufficient degree of software stability has been achieved, a version 1.0.0 will be released with the intention to maintain backwards compatibility from that point. This is still many (man)years off.&lt;br /&gt;
&lt;br /&gt;
This page summarizes the progress and the plans for the next prototype (minor) version. For a summary of the current implementation state, see [[Ouroboros Implementation Overview| our status page]].&lt;br /&gt;
&lt;br /&gt;
== 0.24 (Current development branch) ==&lt;br /&gt;
&lt;br /&gt;
== 0.23 (Current version, February 22, 2026) ==&lt;br /&gt;
&lt;br /&gt;
* Added PQC support to the library, including ML-KEM, Hybrid ECDH-KEM key exchange and ML-DSA and SLH-DSA based authentication.&lt;br /&gt;
* IRMd now negotiates encryption strength between endpoints based on lowest-floor exchange.&lt;br /&gt;
* Replaced the rdrbuff with per-user and priviliged shared slab allocator pools for shared memory packet buffers.&lt;br /&gt;
* Added automatic key rotation for encrypted flows.&lt;br /&gt;
* Refactored the ring buffer implementation and removed the old rbuff_ll variant.&lt;br /&gt;
* IRMd now allows direct ring buffer connections between local processes, reducing overhead for same-machine IPC.&lt;br /&gt;
* Major refactoring of CMake modules, plus new targets for coverage reports and automatic version parsing from git tags.&lt;br /&gt;
&lt;br /&gt;
== 0.22 ==&lt;br /&gt;
* Added flow authentication and the Flow Allocation Protocol header in the irmd&lt;br /&gt;
* Added UDP/IPv6 support&lt;br /&gt;
* Added protocol level debug logging&lt;br /&gt;
* Moved encryption control from qosspec to naming system&lt;br /&gt;
* Removed qosspec parameter from the broadcast API&lt;br /&gt;
* Refactored and simplified enrolment and connection manager&lt;br /&gt;
* Complete rewrite of the DHT for the unicast layer&lt;br /&gt;
* Refactored Ethernet IPCPs&lt;br /&gt;
* Deprecated appveyor in favor of codeberg/woodpecker CI&lt;br /&gt;
* Improved test coverage&lt;br /&gt;
* Improved systemd service and pkgconfig installation logic&lt;br /&gt;
* Improved ctest integration into build system&lt;br /&gt;
&lt;br /&gt;
== 0.21 ==&lt;br /&gt;
* Moved public key handling from library to IRMd&lt;br /&gt;
* Refactor of IRMd / registry implementation&lt;br /&gt;
&lt;br /&gt;
== 0.20 ==&lt;br /&gt;
* Improved configuration file handling&lt;br /&gt;
* Improved FUSE stability&lt;br /&gt;
* Log unique IDs to enrollment requests&lt;br /&gt;
* Print logo on startup when logging to std output&lt;br /&gt;
* Fix most common hangs on exit&lt;br /&gt;
&lt;br /&gt;
== 0.19 ==&lt;br /&gt;
* Pass the N-1 layer MPL at flow allocation&lt;br /&gt;
* Add flow liveness monitoring.&lt;br /&gt;
* Allow multiple directories in an IPCP &lt;br /&gt;
* Dropped support for raptor&lt;br /&gt;
* Use one UDP port for the IPCP over UDP/IPv4&lt;br /&gt;
&lt;br /&gt;
== 0.18 ==&lt;br /&gt;
* Use QoS cube for ECN marking&lt;br /&gt;
* Always use 64-bit endpoint IDs&lt;br /&gt;
* Add RIB statistics for flow allocator&lt;br /&gt;
* Add congestion avoidance policies&lt;br /&gt;
* Add flow control policies&lt;br /&gt;
* Add GCC 10 static analyzer build option&lt;br /&gt;
&lt;br /&gt;
== 0.17 ==&lt;br /&gt;
* Rename systemd service to ouroboros&lt;br /&gt;
* Add tests for LFA and ECMP routing&lt;br /&gt;
* Add Equal-Cost Multi-Path routing policy&lt;br /&gt;
&lt;br /&gt;
== 0.16 ==&lt;br /&gt;
* Removed support for SWIG bindings&lt;br /&gt;
* Add support for appveyor CI&lt;br /&gt;
* Add encryption support using OpenSSL&lt;br /&gt;
&lt;br /&gt;
== 0.15 ==&lt;br /&gt;
* Rename normal IPCP to unicast IPCP&lt;br /&gt;
* Add flow_join API for broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.14 ==&lt;br /&gt;
* Add Explicit Congestion Notification field to DT&lt;br /&gt;
* Add broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.13 ==&lt;br /&gt;
* Disable CRC32 by default&lt;br /&gt;
&lt;br /&gt;
== 0.12 ==&lt;br /&gt;
* Split error checking from FRCT&lt;br /&gt;
* Rename port_id to flow_id&lt;br /&gt;
* Rename SDU to packet&lt;br /&gt;
* Pass QoS spec at flow allocation&lt;br /&gt;
* Initial retransmission logic&lt;br /&gt;
* Support QoS specs for oping&lt;br /&gt;
* Use Endpoint ID&#039;s in fa protocol instead of fd&lt;br /&gt;
&lt;br /&gt;
== 0.11 ==&lt;br /&gt;
* Simplify reg/unreg API&lt;br /&gt;
* Support for partial packet reads&lt;br /&gt;
* Add IPCP over Ethernet DIX&lt;br /&gt;
&lt;br /&gt;
== 0.10 ==&lt;br /&gt;
* Revise lookup tracking in DHT&lt;br /&gt;
* Update man pages to CC-BY 4.0&lt;br /&gt;
&lt;br /&gt;
== 0.9 ==&lt;br /&gt;
* Add patchlevels for future versions&lt;br /&gt;
* Add support for Raptor FPGA Layer-1 PoC&lt;br /&gt;
&lt;br /&gt;
== 0.8 ==&lt;br /&gt;
* Use &amp;quot;program&amp;quot; instead of &amp;quot;application process&amp;quot;&lt;br /&gt;
* Use &amp;quot;process&amp;quot; instead of &amp;quot;application process instance&amp;quot;&lt;br /&gt;
* Send timestamp in oping&lt;br /&gt;
* Use 3-clause BSD license for tools&lt;br /&gt;
&lt;br /&gt;
== 0.7 ==&lt;br /&gt;
* Use ELF sections to init/fini ouroboros&lt;br /&gt;
* Simplify PFT for LFA routing&lt;br /&gt;
* Add RIB to expose metrics via FUSE&lt;br /&gt;
* Deprecate Common Distribution Application Protocol (CDAP)&lt;br /&gt;
* Simplify enroll API&lt;br /&gt;
* Deprecate Graph Adjancency Manager component&lt;br /&gt;
&lt;br /&gt;
== 0.6 ==&lt;br /&gt;
* Added a threadpool manager for improved concurrency&lt;br /&gt;
* Added Loop-Free Alternates (LFA) routing&lt;br /&gt;
* Add DHT as default directory policy&lt;br /&gt;
&lt;br /&gt;
== 0.5 ==&lt;br /&gt;
* Move FRCT logic from IPCP to application library&lt;br /&gt;
* Split connection establishment from flow allocation&lt;br /&gt;
* Split flow manager into flow allocator and data transfer&lt;br /&gt;
* Register hashes in a layer instead of cleartext name&lt;br /&gt;
* Add netmap support for LLC shim&lt;br /&gt;
* Initial lockless rbuff implementation&lt;br /&gt;
* Add generic thread pool management&lt;br /&gt;
* Revise flow allocation API&lt;br /&gt;
* Split authentication from CACEP&lt;br /&gt;
* Revise CACEP API&lt;br /&gt;
* Exchange application protocol information during CACEP&lt;br /&gt;
&lt;br /&gt;
== 0.4 ==&lt;br /&gt;
* Add support for syslog logging&lt;br /&gt;
* Revise RIB/CDAP as a btree structure&lt;br /&gt;
* Add B-tree implementation&lt;br /&gt;
* Add full-mesh policy for GAM&lt;br /&gt;
* Add Graph Adjacency Manager (GAM)&lt;br /&gt;
* Add flat address policy&lt;br /&gt;
* Add initial directory to normal IPCP&lt;br /&gt;
* Initial RIB/CDAP synchronization&lt;br /&gt;
* Add RIB/CDAP objects&lt;br /&gt;
* Add operf tool&lt;br /&gt;
&lt;br /&gt;
== 0.3 ==&lt;br /&gt;
* Move all shared memory (shm) mgmt to IRMd&lt;br /&gt;
* Use shared memory tx/rx ring buffer per flow&lt;br /&gt;
* Split off bind/unbind (map/unmap name to process) from reg/unreg&lt;br /&gt;
&lt;br /&gt;
== 0.2 ==&lt;br /&gt;
* Add shim DIF over LLC Ethernet&lt;br /&gt;
* Add CBR tool for performance testing&lt;br /&gt;
* Add IPCP over local memory&lt;br /&gt;
&lt;br /&gt;
== 0.1 ==&lt;br /&gt;
* Add full flow allocator to UDP shim&lt;br /&gt;
* Implementation of flow related ops&lt;br /&gt;
* Simple echo application&lt;br /&gt;
* Initial shim IPCP over UDP/IPv4&lt;br /&gt;
* Add RINA name helpers&lt;br /&gt;
* Initial library code&lt;br /&gt;
* Initial SDU/PDU buffer code&lt;br /&gt;
* Initial APIs for IRM/IPCP/CFAP/DIF allocator&lt;br /&gt;
* Initial CDAP header&lt;br /&gt;
* LICENSE and CONTRIBUTORS files&lt;br /&gt;
* Initial documenents and GPLv2 license&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1909</id>
		<title>Prototype Version History</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1909"/>
		<updated>2026-02-22T16:59:07Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 0.23 (Current version, February 22, 2026) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ouroboros is still in an early proof-of-concept prototype phase. Versions 0.x.y have no forward nor backward compatibility. We bump minor version with API or protocol changes. Within a minor version, different patch levels &#039;&#039;should&#039;&#039; be compatible (no guarantees).&lt;br /&gt;
&lt;br /&gt;
Once the functionality has reached a minimal critical mass, and a sufficient degree of software stability has been achieved, a version 1.0.0 will be released with the intention to maintain backwards compatibility from that point. This is still many (man)years off.&lt;br /&gt;
&lt;br /&gt;
This page summarizes the progress and the plans for the next prototype (minor) version. For a summary of the current implementation state, see [[Ouroboros Implementation Overview| our status page]].&lt;br /&gt;
&lt;br /&gt;
== 0.24 (Current development branch) ==&lt;br /&gt;
&lt;br /&gt;
== 0.23 (Current version, February 22, 2026) ==&lt;br /&gt;
&lt;br /&gt;
* Added PQC support to the library, including ML-KEM, Hybrid ECDH-KEM key exchange and ML-DSA and SLH-DSA based authentication.&lt;br /&gt;
* IRMd now negotiates encryption strength between endpoints based on lowest-floor exchange.&lt;br /&gt;
* Replaced the rdrbuff with per-user and priviliged shared slab allocator pools for shared memory packet buffers.&lt;br /&gt;
* Added automatic key rotation for encrypted flows.&lt;br /&gt;
* Refactored the ring buffer implementation and removed the old &amp;quot;lockless&amp;quot; rbuff_ll variant.&lt;br /&gt;
* IRMd now allows direct ring buffer connections between local processes, reducing overhead for same-machine IPC.&lt;br /&gt;
* Major refactoring of CMake modules, plus new targets for coverage reports and automatic version parsing from git tags.&lt;br /&gt;
&lt;br /&gt;
== 0.22 ==&lt;br /&gt;
* Added flow authentication and the Flow Allocation Protocol header in the irmd&lt;br /&gt;
* Added UDP/IPv6 support&lt;br /&gt;
* Added protocol level debug logging&lt;br /&gt;
* Moved encryption control from qosspec to naming system&lt;br /&gt;
* Removed qosspec parameter from the broadcast API&lt;br /&gt;
* Refactored and simplified enrolment and connection manager&lt;br /&gt;
* Complete rewrite of the DHT for the unicast layer&lt;br /&gt;
* Refactored Ethernet IPCPs&lt;br /&gt;
* Deprecated appveyor in favor of codeberg/woodpecker CI&lt;br /&gt;
* Improved test coverage&lt;br /&gt;
* Improved systemd service and pkgconfig installation logic&lt;br /&gt;
* Improved ctest integration into build system&lt;br /&gt;
&lt;br /&gt;
== 0.21 ==&lt;br /&gt;
* Moved public key handling from library to IRMd&lt;br /&gt;
* Refactor of IRMd / registry implementation&lt;br /&gt;
&lt;br /&gt;
== 0.20 ==&lt;br /&gt;
* Improved configuration file handling&lt;br /&gt;
* Improved FUSE stability&lt;br /&gt;
* Log unique IDs to enrollment requests&lt;br /&gt;
* Print logo on startup when logging to std output&lt;br /&gt;
* Fix most common hangs on exit&lt;br /&gt;
&lt;br /&gt;
== 0.19 ==&lt;br /&gt;
* Pass the N-1 layer MPL at flow allocation&lt;br /&gt;
* Add flow liveness monitoring.&lt;br /&gt;
* Allow multiple directories in an IPCP &lt;br /&gt;
* Dropped support for raptor&lt;br /&gt;
* Use one UDP port for the IPCP over UDP/IPv4&lt;br /&gt;
&lt;br /&gt;
== 0.18 ==&lt;br /&gt;
* Use QoS cube for ECN marking&lt;br /&gt;
* Always use 64-bit endpoint IDs&lt;br /&gt;
* Add RIB statistics for flow allocator&lt;br /&gt;
* Add congestion avoidance policies&lt;br /&gt;
* Add flow control policies&lt;br /&gt;
* Add GCC 10 static analyzer build option&lt;br /&gt;
&lt;br /&gt;
== 0.17 ==&lt;br /&gt;
* Rename systemd service to ouroboros&lt;br /&gt;
* Add tests for LFA and ECMP routing&lt;br /&gt;
* Add Equal-Cost Multi-Path routing policy&lt;br /&gt;
&lt;br /&gt;
== 0.16 ==&lt;br /&gt;
* Removed support for SWIG bindings&lt;br /&gt;
* Add support for appveyor CI&lt;br /&gt;
* Add encryption support using OpenSSL&lt;br /&gt;
&lt;br /&gt;
== 0.15 ==&lt;br /&gt;
* Rename normal IPCP to unicast IPCP&lt;br /&gt;
* Add flow_join API for broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.14 ==&lt;br /&gt;
* Add Explicit Congestion Notification field to DT&lt;br /&gt;
* Add broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.13 ==&lt;br /&gt;
* Disable CRC32 by default&lt;br /&gt;
&lt;br /&gt;
== 0.12 ==&lt;br /&gt;
* Split error checking from FRCT&lt;br /&gt;
* Rename port_id to flow_id&lt;br /&gt;
* Rename SDU to packet&lt;br /&gt;
* Pass QoS spec at flow allocation&lt;br /&gt;
* Initial retransmission logic&lt;br /&gt;
* Support QoS specs for oping&lt;br /&gt;
* Use Endpoint ID&#039;s in fa protocol instead of fd&lt;br /&gt;
&lt;br /&gt;
== 0.11 ==&lt;br /&gt;
* Simplify reg/unreg API&lt;br /&gt;
* Support for partial packet reads&lt;br /&gt;
* Add IPCP over Ethernet DIX&lt;br /&gt;
&lt;br /&gt;
== 0.10 ==&lt;br /&gt;
* Revise lookup tracking in DHT&lt;br /&gt;
* Update man pages to CC-BY 4.0&lt;br /&gt;
&lt;br /&gt;
== 0.9 ==&lt;br /&gt;
* Add patchlevels for future versions&lt;br /&gt;
* Add support for Raptor FPGA Layer-1 PoC&lt;br /&gt;
&lt;br /&gt;
== 0.8 ==&lt;br /&gt;
* Use &amp;quot;program&amp;quot; instead of &amp;quot;application process&amp;quot;&lt;br /&gt;
* Use &amp;quot;process&amp;quot; instead of &amp;quot;application process instance&amp;quot;&lt;br /&gt;
* Send timestamp in oping&lt;br /&gt;
* Use 3-clause BSD license for tools&lt;br /&gt;
&lt;br /&gt;
== 0.7 ==&lt;br /&gt;
* Use ELF sections to init/fini ouroboros&lt;br /&gt;
* Simplify PFT for LFA routing&lt;br /&gt;
* Add RIB to expose metrics via FUSE&lt;br /&gt;
* Deprecate Common Distribution Application Protocol (CDAP)&lt;br /&gt;
* Simplify enroll API&lt;br /&gt;
* Deprecate Graph Adjancency Manager component&lt;br /&gt;
&lt;br /&gt;
== 0.6 ==&lt;br /&gt;
* Added a threadpool manager for improved concurrency&lt;br /&gt;
* Added Loop-Free Alternates (LFA) routing&lt;br /&gt;
* Add DHT as default directory policy&lt;br /&gt;
&lt;br /&gt;
== 0.5 ==&lt;br /&gt;
* Move FRCT logic from IPCP to application library&lt;br /&gt;
* Split connection establishment from flow allocation&lt;br /&gt;
* Split flow manager into flow allocator and data transfer&lt;br /&gt;
* Register hashes in a layer instead of cleartext name&lt;br /&gt;
* Add netmap support for LLC shim&lt;br /&gt;
* Initial lockless rbuff implementation&lt;br /&gt;
* Add generic thread pool management&lt;br /&gt;
* Revise flow allocation API&lt;br /&gt;
* Split authentication from CACEP&lt;br /&gt;
* Revise CACEP API&lt;br /&gt;
* Exchange application protocol information during CACEP&lt;br /&gt;
&lt;br /&gt;
== 0.4 ==&lt;br /&gt;
* Add support for syslog logging&lt;br /&gt;
* Revise RIB/CDAP as a btree structure&lt;br /&gt;
* Add B-tree implementation&lt;br /&gt;
* Add full-mesh policy for GAM&lt;br /&gt;
* Add Graph Adjacency Manager (GAM)&lt;br /&gt;
* Add flat address policy&lt;br /&gt;
* Add initial directory to normal IPCP&lt;br /&gt;
* Initial RIB/CDAP synchronization&lt;br /&gt;
* Add RIB/CDAP objects&lt;br /&gt;
* Add operf tool&lt;br /&gt;
&lt;br /&gt;
== 0.3 ==&lt;br /&gt;
* Move all shared memory (shm) mgmt to IRMd&lt;br /&gt;
* Use shared memory tx/rx ring buffer per flow&lt;br /&gt;
* Split off bind/unbind (map/unmap name to process) from reg/unreg&lt;br /&gt;
&lt;br /&gt;
== 0.2 ==&lt;br /&gt;
* Add shim DIF over LLC Ethernet&lt;br /&gt;
* Add CBR tool for performance testing&lt;br /&gt;
* Add IPCP over local memory&lt;br /&gt;
&lt;br /&gt;
== 0.1 ==&lt;br /&gt;
* Add full flow allocator to UDP shim&lt;br /&gt;
* Implementation of flow related ops&lt;br /&gt;
* Simple echo application&lt;br /&gt;
* Initial shim IPCP over UDP/IPv4&lt;br /&gt;
* Add RINA name helpers&lt;br /&gt;
* Initial library code&lt;br /&gt;
* Initial SDU/PDU buffer code&lt;br /&gt;
* Initial APIs for IRM/IPCP/CFAP/DIF allocator&lt;br /&gt;
* Initial CDAP header&lt;br /&gt;
* LICENSE and CONTRIBUTORS files&lt;br /&gt;
* Initial documenents and GPLv2 license&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1908</id>
		<title>Prototype Version History</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1908"/>
		<updated>2026-02-22T16:58:10Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 0.23 (Current version, February 22, 2026) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ouroboros is still in an early proof-of-concept prototype phase. Versions 0.x.y have no forward nor backward compatibility. We bump minor version with API or protocol changes. Within a minor version, different patch levels &#039;&#039;should&#039;&#039; be compatible (no guarantees).&lt;br /&gt;
&lt;br /&gt;
Once the functionality has reached a minimal critical mass, and a sufficient degree of software stability has been achieved, a version 1.0.0 will be released with the intention to maintain backwards compatibility from that point. This is still many (man)years off.&lt;br /&gt;
&lt;br /&gt;
This page summarizes the progress and the plans for the next prototype (minor) version. For a summary of the current implementation state, see [[Ouroboros Implementation Overview| our status page]].&lt;br /&gt;
&lt;br /&gt;
== 0.24 (Current development branch) ==&lt;br /&gt;
&lt;br /&gt;
== 0.23 (Current version, February 22, 2026) ==&lt;br /&gt;
&lt;br /&gt;
* Added PQC support to the library, including ML-KEM, Hybrid ECDH-KEM key exchange and ML-DSA and SLH-DSA based authentication.&lt;br /&gt;
* IRMd now negotiates encryption strength between endpoints,.&lt;br /&gt;
* Replaced the rdrbuff with per-user and priviliged shared slab allocator pools for shared memory packet buffers.&lt;br /&gt;
* Added automatic key rotation for encrypted flows.&lt;br /&gt;
* Refactored the ring buffer implementation and removed the old &amp;quot;lockless&amp;quot; rbuff_ll variant.&lt;br /&gt;
* IRMd now allows direct ring buffer connections between local processes, reducing overhead for same-machine IPC.&lt;br /&gt;
* Major refactoring of CMake modules, plus new targets for coverage reports and automatic version parsing from git tags.&lt;br /&gt;
&lt;br /&gt;
== 0.22 ==&lt;br /&gt;
* Added flow authentication and the Flow Allocation Protocol header in the irmd&lt;br /&gt;
* Added UDP/IPv6 support&lt;br /&gt;
* Added protocol level debug logging&lt;br /&gt;
* Moved encryption control from qosspec to naming system&lt;br /&gt;
* Removed qosspec parameter from the broadcast API&lt;br /&gt;
* Refactored and simplified enrolment and connection manager&lt;br /&gt;
* Complete rewrite of the DHT for the unicast layer&lt;br /&gt;
* Refactored Ethernet IPCPs&lt;br /&gt;
* Deprecated appveyor in favor of codeberg/woodpecker CI&lt;br /&gt;
* Improved test coverage&lt;br /&gt;
* Improved systemd service and pkgconfig installation logic&lt;br /&gt;
* Improved ctest integration into build system&lt;br /&gt;
&lt;br /&gt;
== 0.21 ==&lt;br /&gt;
* Moved public key handling from library to IRMd&lt;br /&gt;
* Refactor of IRMd / registry implementation&lt;br /&gt;
&lt;br /&gt;
== 0.20 ==&lt;br /&gt;
* Improved configuration file handling&lt;br /&gt;
* Improved FUSE stability&lt;br /&gt;
* Log unique IDs to enrollment requests&lt;br /&gt;
* Print logo on startup when logging to std output&lt;br /&gt;
* Fix most common hangs on exit&lt;br /&gt;
&lt;br /&gt;
== 0.19 ==&lt;br /&gt;
* Pass the N-1 layer MPL at flow allocation&lt;br /&gt;
* Add flow liveness monitoring.&lt;br /&gt;
* Allow multiple directories in an IPCP &lt;br /&gt;
* Dropped support for raptor&lt;br /&gt;
* Use one UDP port for the IPCP over UDP/IPv4&lt;br /&gt;
&lt;br /&gt;
== 0.18 ==&lt;br /&gt;
* Use QoS cube for ECN marking&lt;br /&gt;
* Always use 64-bit endpoint IDs&lt;br /&gt;
* Add RIB statistics for flow allocator&lt;br /&gt;
* Add congestion avoidance policies&lt;br /&gt;
* Add flow control policies&lt;br /&gt;
* Add GCC 10 static analyzer build option&lt;br /&gt;
&lt;br /&gt;
== 0.17 ==&lt;br /&gt;
* Rename systemd service to ouroboros&lt;br /&gt;
* Add tests for LFA and ECMP routing&lt;br /&gt;
* Add Equal-Cost Multi-Path routing policy&lt;br /&gt;
&lt;br /&gt;
== 0.16 ==&lt;br /&gt;
* Removed support for SWIG bindings&lt;br /&gt;
* Add support for appveyor CI&lt;br /&gt;
* Add encryption support using OpenSSL&lt;br /&gt;
&lt;br /&gt;
== 0.15 ==&lt;br /&gt;
* Rename normal IPCP to unicast IPCP&lt;br /&gt;
* Add flow_join API for broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.14 ==&lt;br /&gt;
* Add Explicit Congestion Notification field to DT&lt;br /&gt;
* Add broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.13 ==&lt;br /&gt;
* Disable CRC32 by default&lt;br /&gt;
&lt;br /&gt;
== 0.12 ==&lt;br /&gt;
* Split error checking from FRCT&lt;br /&gt;
* Rename port_id to flow_id&lt;br /&gt;
* Rename SDU to packet&lt;br /&gt;
* Pass QoS spec at flow allocation&lt;br /&gt;
* Initial retransmission logic&lt;br /&gt;
* Support QoS specs for oping&lt;br /&gt;
* Use Endpoint ID&#039;s in fa protocol instead of fd&lt;br /&gt;
&lt;br /&gt;
== 0.11 ==&lt;br /&gt;
* Simplify reg/unreg API&lt;br /&gt;
* Support for partial packet reads&lt;br /&gt;
* Add IPCP over Ethernet DIX&lt;br /&gt;
&lt;br /&gt;
== 0.10 ==&lt;br /&gt;
* Revise lookup tracking in DHT&lt;br /&gt;
* Update man pages to CC-BY 4.0&lt;br /&gt;
&lt;br /&gt;
== 0.9 ==&lt;br /&gt;
* Add patchlevels for future versions&lt;br /&gt;
* Add support for Raptor FPGA Layer-1 PoC&lt;br /&gt;
&lt;br /&gt;
== 0.8 ==&lt;br /&gt;
* Use &amp;quot;program&amp;quot; instead of &amp;quot;application process&amp;quot;&lt;br /&gt;
* Use &amp;quot;process&amp;quot; instead of &amp;quot;application process instance&amp;quot;&lt;br /&gt;
* Send timestamp in oping&lt;br /&gt;
* Use 3-clause BSD license for tools&lt;br /&gt;
&lt;br /&gt;
== 0.7 ==&lt;br /&gt;
* Use ELF sections to init/fini ouroboros&lt;br /&gt;
* Simplify PFT for LFA routing&lt;br /&gt;
* Add RIB to expose metrics via FUSE&lt;br /&gt;
* Deprecate Common Distribution Application Protocol (CDAP)&lt;br /&gt;
* Simplify enroll API&lt;br /&gt;
* Deprecate Graph Adjancency Manager component&lt;br /&gt;
&lt;br /&gt;
== 0.6 ==&lt;br /&gt;
* Added a threadpool manager for improved concurrency&lt;br /&gt;
* Added Loop-Free Alternates (LFA) routing&lt;br /&gt;
* Add DHT as default directory policy&lt;br /&gt;
&lt;br /&gt;
== 0.5 ==&lt;br /&gt;
* Move FRCT logic from IPCP to application library&lt;br /&gt;
* Split connection establishment from flow allocation&lt;br /&gt;
* Split flow manager into flow allocator and data transfer&lt;br /&gt;
* Register hashes in a layer instead of cleartext name&lt;br /&gt;
* Add netmap support for LLC shim&lt;br /&gt;
* Initial lockless rbuff implementation&lt;br /&gt;
* Add generic thread pool management&lt;br /&gt;
* Revise flow allocation API&lt;br /&gt;
* Split authentication from CACEP&lt;br /&gt;
* Revise CACEP API&lt;br /&gt;
* Exchange application protocol information during CACEP&lt;br /&gt;
&lt;br /&gt;
== 0.4 ==&lt;br /&gt;
* Add support for syslog logging&lt;br /&gt;
* Revise RIB/CDAP as a btree structure&lt;br /&gt;
* Add B-tree implementation&lt;br /&gt;
* Add full-mesh policy for GAM&lt;br /&gt;
* Add Graph Adjacency Manager (GAM)&lt;br /&gt;
* Add flat address policy&lt;br /&gt;
* Add initial directory to normal IPCP&lt;br /&gt;
* Initial RIB/CDAP synchronization&lt;br /&gt;
* Add RIB/CDAP objects&lt;br /&gt;
* Add operf tool&lt;br /&gt;
&lt;br /&gt;
== 0.3 ==&lt;br /&gt;
* Move all shared memory (shm) mgmt to IRMd&lt;br /&gt;
* Use shared memory tx/rx ring buffer per flow&lt;br /&gt;
* Split off bind/unbind (map/unmap name to process) from reg/unreg&lt;br /&gt;
&lt;br /&gt;
== 0.2 ==&lt;br /&gt;
* Add shim DIF over LLC Ethernet&lt;br /&gt;
* Add CBR tool for performance testing&lt;br /&gt;
* Add IPCP over local memory&lt;br /&gt;
&lt;br /&gt;
== 0.1 ==&lt;br /&gt;
* Add full flow allocator to UDP shim&lt;br /&gt;
* Implementation of flow related ops&lt;br /&gt;
* Simple echo application&lt;br /&gt;
* Initial shim IPCP over UDP/IPv4&lt;br /&gt;
* Add RINA name helpers&lt;br /&gt;
* Initial library code&lt;br /&gt;
* Initial SDU/PDU buffer code&lt;br /&gt;
* Initial APIs for IRM/IPCP/CFAP/DIF allocator&lt;br /&gt;
* Initial CDAP header&lt;br /&gt;
* LICENSE and CONTRIBUTORS files&lt;br /&gt;
* Initial documenents and GPLv2 license&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1907</id>
		<title>Prototype Version History</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1907"/>
		<updated>2026-02-22T16:56:18Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 0.23 (Current version, February 22, 2026) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ouroboros is still in an early proof-of-concept prototype phase. Versions 0.x.y have no forward nor backward compatibility. We bump minor version with API or protocol changes. Within a minor version, different patch levels &#039;&#039;should&#039;&#039; be compatible (no guarantees).&lt;br /&gt;
&lt;br /&gt;
Once the functionality has reached a minimal critical mass, and a sufficient degree of software stability has been achieved, a version 1.0.0 will be released with the intention to maintain backwards compatibility from that point. This is still many (man)years off.&lt;br /&gt;
&lt;br /&gt;
This page summarizes the progress and the plans for the next prototype (minor) version. For a summary of the current implementation state, see [[Ouroboros Implementation Overview| our status page]].&lt;br /&gt;
&lt;br /&gt;
== 0.24 (Current development branch) ==&lt;br /&gt;
&lt;br /&gt;
== 0.23 (Current version, February 22, 2026) ==&lt;br /&gt;
&lt;br /&gt;
* Added PQC support to the library, including ML-KEM, Hybrid ECDH-KEM and ML-DSA and SLH-DSA authentication.&lt;br /&gt;
* IRMd now negotiates encryption strength between endpoints,.&lt;br /&gt;
* Replaced the rdrbuff with per-user and priviliged shared slab allocator pools for shared memory packet buffers.&lt;br /&gt;
* Added automatic key rotation for encrypted flows.&lt;br /&gt;
* Refactored the ring buffer implementation and removed the old &amp;quot;lockless&amp;quot; rbuff_ll variant.&lt;br /&gt;
* IRMd now allows direct ring buffer connections between local processes, reducing overhead for same-machine IPC.&lt;br /&gt;
* Major refactoring of CMake modules, plus new targets for coverage reports and automatic version parsing from git tags.&lt;br /&gt;
&lt;br /&gt;
== 0.22 ==&lt;br /&gt;
* Added flow authentication and the Flow Allocation Protocol header in the irmd&lt;br /&gt;
* Added UDP/IPv6 support&lt;br /&gt;
* Added protocol level debug logging&lt;br /&gt;
* Moved encryption control from qosspec to naming system&lt;br /&gt;
* Removed qosspec parameter from the broadcast API&lt;br /&gt;
* Refactored and simplified enrolment and connection manager&lt;br /&gt;
* Complete rewrite of the DHT for the unicast layer&lt;br /&gt;
* Refactored Ethernet IPCPs&lt;br /&gt;
* Deprecated appveyor in favor of codeberg/woodpecker CI&lt;br /&gt;
* Improved test coverage&lt;br /&gt;
* Improved systemd service and pkgconfig installation logic&lt;br /&gt;
* Improved ctest integration into build system&lt;br /&gt;
&lt;br /&gt;
== 0.21 ==&lt;br /&gt;
* Moved public key handling from library to IRMd&lt;br /&gt;
* Refactor of IRMd / registry implementation&lt;br /&gt;
&lt;br /&gt;
== 0.20 ==&lt;br /&gt;
* Improved configuration file handling&lt;br /&gt;
* Improved FUSE stability&lt;br /&gt;
* Log unique IDs to enrollment requests&lt;br /&gt;
* Print logo on startup when logging to std output&lt;br /&gt;
* Fix most common hangs on exit&lt;br /&gt;
&lt;br /&gt;
== 0.19 ==&lt;br /&gt;
* Pass the N-1 layer MPL at flow allocation&lt;br /&gt;
* Add flow liveness monitoring.&lt;br /&gt;
* Allow multiple directories in an IPCP &lt;br /&gt;
* Dropped support for raptor&lt;br /&gt;
* Use one UDP port for the IPCP over UDP/IPv4&lt;br /&gt;
&lt;br /&gt;
== 0.18 ==&lt;br /&gt;
* Use QoS cube for ECN marking&lt;br /&gt;
* Always use 64-bit endpoint IDs&lt;br /&gt;
* Add RIB statistics for flow allocator&lt;br /&gt;
* Add congestion avoidance policies&lt;br /&gt;
* Add flow control policies&lt;br /&gt;
* Add GCC 10 static analyzer build option&lt;br /&gt;
&lt;br /&gt;
== 0.17 ==&lt;br /&gt;
* Rename systemd service to ouroboros&lt;br /&gt;
* Add tests for LFA and ECMP routing&lt;br /&gt;
* Add Equal-Cost Multi-Path routing policy&lt;br /&gt;
&lt;br /&gt;
== 0.16 ==&lt;br /&gt;
* Removed support for SWIG bindings&lt;br /&gt;
* Add support for appveyor CI&lt;br /&gt;
* Add encryption support using OpenSSL&lt;br /&gt;
&lt;br /&gt;
== 0.15 ==&lt;br /&gt;
* Rename normal IPCP to unicast IPCP&lt;br /&gt;
* Add flow_join API for broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.14 ==&lt;br /&gt;
* Add Explicit Congestion Notification field to DT&lt;br /&gt;
* Add broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.13 ==&lt;br /&gt;
* Disable CRC32 by default&lt;br /&gt;
&lt;br /&gt;
== 0.12 ==&lt;br /&gt;
* Split error checking from FRCT&lt;br /&gt;
* Rename port_id to flow_id&lt;br /&gt;
* Rename SDU to packet&lt;br /&gt;
* Pass QoS spec at flow allocation&lt;br /&gt;
* Initial retransmission logic&lt;br /&gt;
* Support QoS specs for oping&lt;br /&gt;
* Use Endpoint ID&#039;s in fa protocol instead of fd&lt;br /&gt;
&lt;br /&gt;
== 0.11 ==&lt;br /&gt;
* Simplify reg/unreg API&lt;br /&gt;
* Support for partial packet reads&lt;br /&gt;
* Add IPCP over Ethernet DIX&lt;br /&gt;
&lt;br /&gt;
== 0.10 ==&lt;br /&gt;
* Revise lookup tracking in DHT&lt;br /&gt;
* Update man pages to CC-BY 4.0&lt;br /&gt;
&lt;br /&gt;
== 0.9 ==&lt;br /&gt;
* Add patchlevels for future versions&lt;br /&gt;
* Add support for Raptor FPGA Layer-1 PoC&lt;br /&gt;
&lt;br /&gt;
== 0.8 ==&lt;br /&gt;
* Use &amp;quot;program&amp;quot; instead of &amp;quot;application process&amp;quot;&lt;br /&gt;
* Use &amp;quot;process&amp;quot; instead of &amp;quot;application process instance&amp;quot;&lt;br /&gt;
* Send timestamp in oping&lt;br /&gt;
* Use 3-clause BSD license for tools&lt;br /&gt;
&lt;br /&gt;
== 0.7 ==&lt;br /&gt;
* Use ELF sections to init/fini ouroboros&lt;br /&gt;
* Simplify PFT for LFA routing&lt;br /&gt;
* Add RIB to expose metrics via FUSE&lt;br /&gt;
* Deprecate Common Distribution Application Protocol (CDAP)&lt;br /&gt;
* Simplify enroll API&lt;br /&gt;
* Deprecate Graph Adjancency Manager component&lt;br /&gt;
&lt;br /&gt;
== 0.6 ==&lt;br /&gt;
* Added a threadpool manager for improved concurrency&lt;br /&gt;
* Added Loop-Free Alternates (LFA) routing&lt;br /&gt;
* Add DHT as default directory policy&lt;br /&gt;
&lt;br /&gt;
== 0.5 ==&lt;br /&gt;
* Move FRCT logic from IPCP to application library&lt;br /&gt;
* Split connection establishment from flow allocation&lt;br /&gt;
* Split flow manager into flow allocator and data transfer&lt;br /&gt;
* Register hashes in a layer instead of cleartext name&lt;br /&gt;
* Add netmap support for LLC shim&lt;br /&gt;
* Initial lockless rbuff implementation&lt;br /&gt;
* Add generic thread pool management&lt;br /&gt;
* Revise flow allocation API&lt;br /&gt;
* Split authentication from CACEP&lt;br /&gt;
* Revise CACEP API&lt;br /&gt;
* Exchange application protocol information during CACEP&lt;br /&gt;
&lt;br /&gt;
== 0.4 ==&lt;br /&gt;
* Add support for syslog logging&lt;br /&gt;
* Revise RIB/CDAP as a btree structure&lt;br /&gt;
* Add B-tree implementation&lt;br /&gt;
* Add full-mesh policy for GAM&lt;br /&gt;
* Add Graph Adjacency Manager (GAM)&lt;br /&gt;
* Add flat address policy&lt;br /&gt;
* Add initial directory to normal IPCP&lt;br /&gt;
* Initial RIB/CDAP synchronization&lt;br /&gt;
* Add RIB/CDAP objects&lt;br /&gt;
* Add operf tool&lt;br /&gt;
&lt;br /&gt;
== 0.3 ==&lt;br /&gt;
* Move all shared memory (shm) mgmt to IRMd&lt;br /&gt;
* Use shared memory tx/rx ring buffer per flow&lt;br /&gt;
* Split off bind/unbind (map/unmap name to process) from reg/unreg&lt;br /&gt;
&lt;br /&gt;
== 0.2 ==&lt;br /&gt;
* Add shim DIF over LLC Ethernet&lt;br /&gt;
* Add CBR tool for performance testing&lt;br /&gt;
* Add IPCP over local memory&lt;br /&gt;
&lt;br /&gt;
== 0.1 ==&lt;br /&gt;
* Add full flow allocator to UDP shim&lt;br /&gt;
* Implementation of flow related ops&lt;br /&gt;
* Simple echo application&lt;br /&gt;
* Initial shim IPCP over UDP/IPv4&lt;br /&gt;
* Add RINA name helpers&lt;br /&gt;
* Initial library code&lt;br /&gt;
* Initial SDU/PDU buffer code&lt;br /&gt;
* Initial APIs for IRM/IPCP/CFAP/DIF allocator&lt;br /&gt;
* Initial CDAP header&lt;br /&gt;
* LICENSE and CONTRIBUTORS files&lt;br /&gt;
* Initial documenents and GPLv2 license&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1906</id>
		<title>Prototype Version History</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1906"/>
		<updated>2026-02-22T16:54:11Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 0.23 (Current version, February 22, 2026) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ouroboros is still in an early proof-of-concept prototype phase. Versions 0.x.y have no forward nor backward compatibility. We bump minor version with API or protocol changes. Within a minor version, different patch levels &#039;&#039;should&#039;&#039; be compatible (no guarantees).&lt;br /&gt;
&lt;br /&gt;
Once the functionality has reached a minimal critical mass, and a sufficient degree of software stability has been achieved, a version 1.0.0 will be released with the intention to maintain backwards compatibility from that point. This is still many (man)years off.&lt;br /&gt;
&lt;br /&gt;
This page summarizes the progress and the plans for the next prototype (minor) version. For a summary of the current implementation state, see [[Ouroboros Implementation Overview| our status page]].&lt;br /&gt;
&lt;br /&gt;
== 0.24 (Current development branch) ==&lt;br /&gt;
&lt;br /&gt;
== 0.23 (Current version, February 22, 2026) ==&lt;br /&gt;
&lt;br /&gt;
* Added PQC support to the library, including ML-KEM, Hybrid ECDH-KEM and ML-DSA and SLH-DSA authentication.&lt;br /&gt;
* IRMd now negotiates encryption strength between endpoints, with fixes for client-side encryption requests.&lt;br /&gt;
* Replaced the rdrbuff with a proper slab allocator for shared memory packet buffers, a significant data-plane redesign.&lt;br /&gt;
* Added automatic key rotation for encrypted flows, with related fixes for IV/tag allocation and speedup of key rotation tests.&lt;br /&gt;
* Introduced per-user shared memory packet pools, improving multi-user isolation. Also added mlock() on shared memory buffers.&lt;br /&gt;
* Refactored the ring buffer implementation and removed the old &amp;quot;lockless&amp;quot; rbuff_ll variant.&lt;br /&gt;
* IRMd now allows direct ring buffer connections between local processes, reducing overhead for same-machine IPC.&lt;br /&gt;
* Major refactoring of CMake modules back to in-tree CMakeLists, plus new targets for coverage reports, build_tests, and automatic version parsing from git tags.&lt;br /&gt;
&lt;br /&gt;
== 0.22 ==&lt;br /&gt;
* Added flow authentication and the Flow Allocation Protocol header in the irmd&lt;br /&gt;
* Added UDP/IPv6 support&lt;br /&gt;
* Added protocol level debug logging&lt;br /&gt;
* Moved encryption control from qosspec to naming system&lt;br /&gt;
* Removed qosspec parameter from the broadcast API&lt;br /&gt;
* Refactored and simplified enrolment and connection manager&lt;br /&gt;
* Complete rewrite of the DHT for the unicast layer&lt;br /&gt;
* Refactored Ethernet IPCPs&lt;br /&gt;
* Deprecated appveyor in favor of codeberg/woodpecker CI&lt;br /&gt;
* Improved test coverage&lt;br /&gt;
* Improved systemd service and pkgconfig installation logic&lt;br /&gt;
* Improved ctest integration into build system&lt;br /&gt;
&lt;br /&gt;
== 0.21 ==&lt;br /&gt;
* Moved public key handling from library to IRMd&lt;br /&gt;
* Refactor of IRMd / registry implementation&lt;br /&gt;
&lt;br /&gt;
== 0.20 ==&lt;br /&gt;
* Improved configuration file handling&lt;br /&gt;
* Improved FUSE stability&lt;br /&gt;
* Log unique IDs to enrollment requests&lt;br /&gt;
* Print logo on startup when logging to std output&lt;br /&gt;
* Fix most common hangs on exit&lt;br /&gt;
&lt;br /&gt;
== 0.19 ==&lt;br /&gt;
* Pass the N-1 layer MPL at flow allocation&lt;br /&gt;
* Add flow liveness monitoring.&lt;br /&gt;
* Allow multiple directories in an IPCP &lt;br /&gt;
* Dropped support for raptor&lt;br /&gt;
* Use one UDP port for the IPCP over UDP/IPv4&lt;br /&gt;
&lt;br /&gt;
== 0.18 ==&lt;br /&gt;
* Use QoS cube for ECN marking&lt;br /&gt;
* Always use 64-bit endpoint IDs&lt;br /&gt;
* Add RIB statistics for flow allocator&lt;br /&gt;
* Add congestion avoidance policies&lt;br /&gt;
* Add flow control policies&lt;br /&gt;
* Add GCC 10 static analyzer build option&lt;br /&gt;
&lt;br /&gt;
== 0.17 ==&lt;br /&gt;
* Rename systemd service to ouroboros&lt;br /&gt;
* Add tests for LFA and ECMP routing&lt;br /&gt;
* Add Equal-Cost Multi-Path routing policy&lt;br /&gt;
&lt;br /&gt;
== 0.16 ==&lt;br /&gt;
* Removed support for SWIG bindings&lt;br /&gt;
* Add support for appveyor CI&lt;br /&gt;
* Add encryption support using OpenSSL&lt;br /&gt;
&lt;br /&gt;
== 0.15 ==&lt;br /&gt;
* Rename normal IPCP to unicast IPCP&lt;br /&gt;
* Add flow_join API for broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.14 ==&lt;br /&gt;
* Add Explicit Congestion Notification field to DT&lt;br /&gt;
* Add broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.13 ==&lt;br /&gt;
* Disable CRC32 by default&lt;br /&gt;
&lt;br /&gt;
== 0.12 ==&lt;br /&gt;
* Split error checking from FRCT&lt;br /&gt;
* Rename port_id to flow_id&lt;br /&gt;
* Rename SDU to packet&lt;br /&gt;
* Pass QoS spec at flow allocation&lt;br /&gt;
* Initial retransmission logic&lt;br /&gt;
* Support QoS specs for oping&lt;br /&gt;
* Use Endpoint ID&#039;s in fa protocol instead of fd&lt;br /&gt;
&lt;br /&gt;
== 0.11 ==&lt;br /&gt;
* Simplify reg/unreg API&lt;br /&gt;
* Support for partial packet reads&lt;br /&gt;
* Add IPCP over Ethernet DIX&lt;br /&gt;
&lt;br /&gt;
== 0.10 ==&lt;br /&gt;
* Revise lookup tracking in DHT&lt;br /&gt;
* Update man pages to CC-BY 4.0&lt;br /&gt;
&lt;br /&gt;
== 0.9 ==&lt;br /&gt;
* Add patchlevels for future versions&lt;br /&gt;
* Add support for Raptor FPGA Layer-1 PoC&lt;br /&gt;
&lt;br /&gt;
== 0.8 ==&lt;br /&gt;
* Use &amp;quot;program&amp;quot; instead of &amp;quot;application process&amp;quot;&lt;br /&gt;
* Use &amp;quot;process&amp;quot; instead of &amp;quot;application process instance&amp;quot;&lt;br /&gt;
* Send timestamp in oping&lt;br /&gt;
* Use 3-clause BSD license for tools&lt;br /&gt;
&lt;br /&gt;
== 0.7 ==&lt;br /&gt;
* Use ELF sections to init/fini ouroboros&lt;br /&gt;
* Simplify PFT for LFA routing&lt;br /&gt;
* Add RIB to expose metrics via FUSE&lt;br /&gt;
* Deprecate Common Distribution Application Protocol (CDAP)&lt;br /&gt;
* Simplify enroll API&lt;br /&gt;
* Deprecate Graph Adjancency Manager component&lt;br /&gt;
&lt;br /&gt;
== 0.6 ==&lt;br /&gt;
* Added a threadpool manager for improved concurrency&lt;br /&gt;
* Added Loop-Free Alternates (LFA) routing&lt;br /&gt;
* Add DHT as default directory policy&lt;br /&gt;
&lt;br /&gt;
== 0.5 ==&lt;br /&gt;
* Move FRCT logic from IPCP to application library&lt;br /&gt;
* Split connection establishment from flow allocation&lt;br /&gt;
* Split flow manager into flow allocator and data transfer&lt;br /&gt;
* Register hashes in a layer instead of cleartext name&lt;br /&gt;
* Add netmap support for LLC shim&lt;br /&gt;
* Initial lockless rbuff implementation&lt;br /&gt;
* Add generic thread pool management&lt;br /&gt;
* Revise flow allocation API&lt;br /&gt;
* Split authentication from CACEP&lt;br /&gt;
* Revise CACEP API&lt;br /&gt;
* Exchange application protocol information during CACEP&lt;br /&gt;
&lt;br /&gt;
== 0.4 ==&lt;br /&gt;
* Add support for syslog logging&lt;br /&gt;
* Revise RIB/CDAP as a btree structure&lt;br /&gt;
* Add B-tree implementation&lt;br /&gt;
* Add full-mesh policy for GAM&lt;br /&gt;
* Add Graph Adjacency Manager (GAM)&lt;br /&gt;
* Add flat address policy&lt;br /&gt;
* Add initial directory to normal IPCP&lt;br /&gt;
* Initial RIB/CDAP synchronization&lt;br /&gt;
* Add RIB/CDAP objects&lt;br /&gt;
* Add operf tool&lt;br /&gt;
&lt;br /&gt;
== 0.3 ==&lt;br /&gt;
* Move all shared memory (shm) mgmt to IRMd&lt;br /&gt;
* Use shared memory tx/rx ring buffer per flow&lt;br /&gt;
* Split off bind/unbind (map/unmap name to process) from reg/unreg&lt;br /&gt;
&lt;br /&gt;
== 0.2 ==&lt;br /&gt;
* Add shim DIF over LLC Ethernet&lt;br /&gt;
* Add CBR tool for performance testing&lt;br /&gt;
* Add IPCP over local memory&lt;br /&gt;
&lt;br /&gt;
== 0.1 ==&lt;br /&gt;
* Add full flow allocator to UDP shim&lt;br /&gt;
* Implementation of flow related ops&lt;br /&gt;
* Simple echo application&lt;br /&gt;
* Initial shim IPCP over UDP/IPv4&lt;br /&gt;
* Add RINA name helpers&lt;br /&gt;
* Initial library code&lt;br /&gt;
* Initial SDU/PDU buffer code&lt;br /&gt;
* Initial APIs for IRM/IPCP/CFAP/DIF allocator&lt;br /&gt;
* Initial CDAP header&lt;br /&gt;
* LICENSE and CONTRIBUTORS files&lt;br /&gt;
* Initial documenents and GPLv2 license&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1905</id>
		<title>Prototype Version History</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1905"/>
		<updated>2026-02-22T16:53:49Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 0.23 (Current version, February 22, 2026) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ouroboros is still in an early proof-of-concept prototype phase. Versions 0.x.y have no forward nor backward compatibility. We bump minor version with API or protocol changes. Within a minor version, different patch levels &#039;&#039;should&#039;&#039; be compatible (no guarantees).&lt;br /&gt;
&lt;br /&gt;
Once the functionality has reached a minimal critical mass, and a sufficient degree of software stability has been achieved, a version 1.0.0 will be released with the intention to maintain backwards compatibility from that point. This is still many (man)years off.&lt;br /&gt;
&lt;br /&gt;
This page summarizes the progress and the plans for the next prototype (minor) version. For a summary of the current implementation state, see [[Ouroboros Implementation Overview| our status page]].&lt;br /&gt;
&lt;br /&gt;
== 0.24 (Current development branch) ==&lt;br /&gt;
&lt;br /&gt;
== 0.23 (Current version, February 22, 2026) ==&lt;br /&gt;
&lt;br /&gt;
* Added PQC support to the library, including ML-KEM, Hybrid ECDH-KEM and ML-DSA and SLH-DSA.&lt;br /&gt;
* IRMd now negotiates encryption strength between endpoints, with fixes for client-side encryption requests.&lt;br /&gt;
* Replaced the rdrbuff with a proper slab allocator for shared memory packet buffers, a significant data-plane redesign.&lt;br /&gt;
* Added automatic key rotation for encrypted flows, with related fixes for IV/tag allocation and speedup of key rotation tests.&lt;br /&gt;
* Introduced per-user shared memory packet pools, improving multi-user isolation. Also added mlock() on shared memory buffers.&lt;br /&gt;
* Refactored the ring buffer implementation and removed the old &amp;quot;lockless&amp;quot; rbuff_ll variant.&lt;br /&gt;
* IRMd now allows direct ring buffer connections between local processes, reducing overhead for same-machine IPC.&lt;br /&gt;
* Major refactoring of CMake modules back to in-tree CMakeLists, plus new targets for coverage reports, build_tests, and automatic version parsing from git tags.&lt;br /&gt;
&lt;br /&gt;
== 0.22 ==&lt;br /&gt;
* Added flow authentication and the Flow Allocation Protocol header in the irmd&lt;br /&gt;
* Added UDP/IPv6 support&lt;br /&gt;
* Added protocol level debug logging&lt;br /&gt;
* Moved encryption control from qosspec to naming system&lt;br /&gt;
* Removed qosspec parameter from the broadcast API&lt;br /&gt;
* Refactored and simplified enrolment and connection manager&lt;br /&gt;
* Complete rewrite of the DHT for the unicast layer&lt;br /&gt;
* Refactored Ethernet IPCPs&lt;br /&gt;
* Deprecated appveyor in favor of codeberg/woodpecker CI&lt;br /&gt;
* Improved test coverage&lt;br /&gt;
* Improved systemd service and pkgconfig installation logic&lt;br /&gt;
* Improved ctest integration into build system&lt;br /&gt;
&lt;br /&gt;
== 0.21 ==&lt;br /&gt;
* Moved public key handling from library to IRMd&lt;br /&gt;
* Refactor of IRMd / registry implementation&lt;br /&gt;
&lt;br /&gt;
== 0.20 ==&lt;br /&gt;
* Improved configuration file handling&lt;br /&gt;
* Improved FUSE stability&lt;br /&gt;
* Log unique IDs to enrollment requests&lt;br /&gt;
* Print logo on startup when logging to std output&lt;br /&gt;
* Fix most common hangs on exit&lt;br /&gt;
&lt;br /&gt;
== 0.19 ==&lt;br /&gt;
* Pass the N-1 layer MPL at flow allocation&lt;br /&gt;
* Add flow liveness monitoring.&lt;br /&gt;
* Allow multiple directories in an IPCP &lt;br /&gt;
* Dropped support for raptor&lt;br /&gt;
* Use one UDP port for the IPCP over UDP/IPv4&lt;br /&gt;
&lt;br /&gt;
== 0.18 ==&lt;br /&gt;
* Use QoS cube for ECN marking&lt;br /&gt;
* Always use 64-bit endpoint IDs&lt;br /&gt;
* Add RIB statistics for flow allocator&lt;br /&gt;
* Add congestion avoidance policies&lt;br /&gt;
* Add flow control policies&lt;br /&gt;
* Add GCC 10 static analyzer build option&lt;br /&gt;
&lt;br /&gt;
== 0.17 ==&lt;br /&gt;
* Rename systemd service to ouroboros&lt;br /&gt;
* Add tests for LFA and ECMP routing&lt;br /&gt;
* Add Equal-Cost Multi-Path routing policy&lt;br /&gt;
&lt;br /&gt;
== 0.16 ==&lt;br /&gt;
* Removed support for SWIG bindings&lt;br /&gt;
* Add support for appveyor CI&lt;br /&gt;
* Add encryption support using OpenSSL&lt;br /&gt;
&lt;br /&gt;
== 0.15 ==&lt;br /&gt;
* Rename normal IPCP to unicast IPCP&lt;br /&gt;
* Add flow_join API for broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.14 ==&lt;br /&gt;
* Add Explicit Congestion Notification field to DT&lt;br /&gt;
* Add broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.13 ==&lt;br /&gt;
* Disable CRC32 by default&lt;br /&gt;
&lt;br /&gt;
== 0.12 ==&lt;br /&gt;
* Split error checking from FRCT&lt;br /&gt;
* Rename port_id to flow_id&lt;br /&gt;
* Rename SDU to packet&lt;br /&gt;
* Pass QoS spec at flow allocation&lt;br /&gt;
* Initial retransmission logic&lt;br /&gt;
* Support QoS specs for oping&lt;br /&gt;
* Use Endpoint ID&#039;s in fa protocol instead of fd&lt;br /&gt;
&lt;br /&gt;
== 0.11 ==&lt;br /&gt;
* Simplify reg/unreg API&lt;br /&gt;
* Support for partial packet reads&lt;br /&gt;
* Add IPCP over Ethernet DIX&lt;br /&gt;
&lt;br /&gt;
== 0.10 ==&lt;br /&gt;
* Revise lookup tracking in DHT&lt;br /&gt;
* Update man pages to CC-BY 4.0&lt;br /&gt;
&lt;br /&gt;
== 0.9 ==&lt;br /&gt;
* Add patchlevels for future versions&lt;br /&gt;
* Add support for Raptor FPGA Layer-1 PoC&lt;br /&gt;
&lt;br /&gt;
== 0.8 ==&lt;br /&gt;
* Use &amp;quot;program&amp;quot; instead of &amp;quot;application process&amp;quot;&lt;br /&gt;
* Use &amp;quot;process&amp;quot; instead of &amp;quot;application process instance&amp;quot;&lt;br /&gt;
* Send timestamp in oping&lt;br /&gt;
* Use 3-clause BSD license for tools&lt;br /&gt;
&lt;br /&gt;
== 0.7 ==&lt;br /&gt;
* Use ELF sections to init/fini ouroboros&lt;br /&gt;
* Simplify PFT for LFA routing&lt;br /&gt;
* Add RIB to expose metrics via FUSE&lt;br /&gt;
* Deprecate Common Distribution Application Protocol (CDAP)&lt;br /&gt;
* Simplify enroll API&lt;br /&gt;
* Deprecate Graph Adjancency Manager component&lt;br /&gt;
&lt;br /&gt;
== 0.6 ==&lt;br /&gt;
* Added a threadpool manager for improved concurrency&lt;br /&gt;
* Added Loop-Free Alternates (LFA) routing&lt;br /&gt;
* Add DHT as default directory policy&lt;br /&gt;
&lt;br /&gt;
== 0.5 ==&lt;br /&gt;
* Move FRCT logic from IPCP to application library&lt;br /&gt;
* Split connection establishment from flow allocation&lt;br /&gt;
* Split flow manager into flow allocator and data transfer&lt;br /&gt;
* Register hashes in a layer instead of cleartext name&lt;br /&gt;
* Add netmap support for LLC shim&lt;br /&gt;
* Initial lockless rbuff implementation&lt;br /&gt;
* Add generic thread pool management&lt;br /&gt;
* Revise flow allocation API&lt;br /&gt;
* Split authentication from CACEP&lt;br /&gt;
* Revise CACEP API&lt;br /&gt;
* Exchange application protocol information during CACEP&lt;br /&gt;
&lt;br /&gt;
== 0.4 ==&lt;br /&gt;
* Add support for syslog logging&lt;br /&gt;
* Revise RIB/CDAP as a btree structure&lt;br /&gt;
* Add B-tree implementation&lt;br /&gt;
* Add full-mesh policy for GAM&lt;br /&gt;
* Add Graph Adjacency Manager (GAM)&lt;br /&gt;
* Add flat address policy&lt;br /&gt;
* Add initial directory to normal IPCP&lt;br /&gt;
* Initial RIB/CDAP synchronization&lt;br /&gt;
* Add RIB/CDAP objects&lt;br /&gt;
* Add operf tool&lt;br /&gt;
&lt;br /&gt;
== 0.3 ==&lt;br /&gt;
* Move all shared memory (shm) mgmt to IRMd&lt;br /&gt;
* Use shared memory tx/rx ring buffer per flow&lt;br /&gt;
* Split off bind/unbind (map/unmap name to process) from reg/unreg&lt;br /&gt;
&lt;br /&gt;
== 0.2 ==&lt;br /&gt;
* Add shim DIF over LLC Ethernet&lt;br /&gt;
* Add CBR tool for performance testing&lt;br /&gt;
* Add IPCP over local memory&lt;br /&gt;
&lt;br /&gt;
== 0.1 ==&lt;br /&gt;
* Add full flow allocator to UDP shim&lt;br /&gt;
* Implementation of flow related ops&lt;br /&gt;
* Simple echo application&lt;br /&gt;
* Initial shim IPCP over UDP/IPv4&lt;br /&gt;
* Add RINA name helpers&lt;br /&gt;
* Initial library code&lt;br /&gt;
* Initial SDU/PDU buffer code&lt;br /&gt;
* Initial APIs for IRM/IPCP/CFAP/DIF allocator&lt;br /&gt;
* Initial CDAP header&lt;br /&gt;
* LICENSE and CONTRIBUTORS files&lt;br /&gt;
* Initial documenents and GPLv2 license&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1904</id>
		<title>Prototype Version History</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1904"/>
		<updated>2026-02-22T16:52:57Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 0.23 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ouroboros is still in an early proof-of-concept prototype phase. Versions 0.x.y have no forward nor backward compatibility. We bump minor version with API or protocol changes. Within a minor version, different patch levels &#039;&#039;should&#039;&#039; be compatible (no guarantees).&lt;br /&gt;
&lt;br /&gt;
Once the functionality has reached a minimal critical mass, and a sufficient degree of software stability has been achieved, a version 1.0.0 will be released with the intention to maintain backwards compatibility from that point. This is still many (man)years off.&lt;br /&gt;
&lt;br /&gt;
This page summarizes the progress and the plans for the next prototype (minor) version. For a summary of the current implementation state, see [[Ouroboros Implementation Overview| our status page]].&lt;br /&gt;
&lt;br /&gt;
== 0.24 (Current development branch) ==&lt;br /&gt;
&lt;br /&gt;
== 0.23 (Current version, February 22, 2026) ==&lt;br /&gt;
&lt;br /&gt;
* Added PQC support to the library, including SLH-DSA tests and per-algorithm gating, and PQC-aware config loading in IRMd.&lt;br /&gt;
* IRMd now negotiates encryption strength between endpoints, with fixes for client-side encryption requests.&lt;br /&gt;
* Replaced the rdrbuff with a proper slab allocator for shared memory packet buffers, a significant data-plane redesign.&lt;br /&gt;
* Added automatic key rotation for encrypted flows, with related fixes for IV/tag allocation and speedup of key rotation tests.&lt;br /&gt;
* Introduced per-user shared memory packet pools, improving multi-user isolation. Also added mlock() on shared memory buffers.&lt;br /&gt;
* Refactored the ring buffer implementation and removed the old &amp;quot;lockless&amp;quot; rbuff_ll variant.&lt;br /&gt;
* IRMd now allows direct ring buffer connections between local processes, reducing overhead for same-machine IPC.&lt;br /&gt;
* Major refactoring of CMake modules back to in-tree CMakeLists, plus new targets for coverage reports, build_tests, and automatic version parsing from git tags.&lt;br /&gt;
&lt;br /&gt;
== 0.22 ==&lt;br /&gt;
* Added flow authentication and the Flow Allocation Protocol header in the irmd&lt;br /&gt;
* Added UDP/IPv6 support&lt;br /&gt;
* Added protocol level debug logging&lt;br /&gt;
* Moved encryption control from qosspec to naming system&lt;br /&gt;
* Removed qosspec parameter from the broadcast API&lt;br /&gt;
* Refactored and simplified enrolment and connection manager&lt;br /&gt;
* Complete rewrite of the DHT for the unicast layer&lt;br /&gt;
* Refactored Ethernet IPCPs&lt;br /&gt;
* Deprecated appveyor in favor of codeberg/woodpecker CI&lt;br /&gt;
* Improved test coverage&lt;br /&gt;
* Improved systemd service and pkgconfig installation logic&lt;br /&gt;
* Improved ctest integration into build system&lt;br /&gt;
&lt;br /&gt;
== 0.21 ==&lt;br /&gt;
* Moved public key handling from library to IRMd&lt;br /&gt;
* Refactor of IRMd / registry implementation&lt;br /&gt;
&lt;br /&gt;
== 0.20 ==&lt;br /&gt;
* Improved configuration file handling&lt;br /&gt;
* Improved FUSE stability&lt;br /&gt;
* Log unique IDs to enrollment requests&lt;br /&gt;
* Print logo on startup when logging to std output&lt;br /&gt;
* Fix most common hangs on exit&lt;br /&gt;
&lt;br /&gt;
== 0.19 ==&lt;br /&gt;
* Pass the N-1 layer MPL at flow allocation&lt;br /&gt;
* Add flow liveness monitoring.&lt;br /&gt;
* Allow multiple directories in an IPCP &lt;br /&gt;
* Dropped support for raptor&lt;br /&gt;
* Use one UDP port for the IPCP over UDP/IPv4&lt;br /&gt;
&lt;br /&gt;
== 0.18 ==&lt;br /&gt;
* Use QoS cube for ECN marking&lt;br /&gt;
* Always use 64-bit endpoint IDs&lt;br /&gt;
* Add RIB statistics for flow allocator&lt;br /&gt;
* Add congestion avoidance policies&lt;br /&gt;
* Add flow control policies&lt;br /&gt;
* Add GCC 10 static analyzer build option&lt;br /&gt;
&lt;br /&gt;
== 0.17 ==&lt;br /&gt;
* Rename systemd service to ouroboros&lt;br /&gt;
* Add tests for LFA and ECMP routing&lt;br /&gt;
* Add Equal-Cost Multi-Path routing policy&lt;br /&gt;
&lt;br /&gt;
== 0.16 ==&lt;br /&gt;
* Removed support for SWIG bindings&lt;br /&gt;
* Add support for appveyor CI&lt;br /&gt;
* Add encryption support using OpenSSL&lt;br /&gt;
&lt;br /&gt;
== 0.15 ==&lt;br /&gt;
* Rename normal IPCP to unicast IPCP&lt;br /&gt;
* Add flow_join API for broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.14 ==&lt;br /&gt;
* Add Explicit Congestion Notification field to DT&lt;br /&gt;
* Add broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.13 ==&lt;br /&gt;
* Disable CRC32 by default&lt;br /&gt;
&lt;br /&gt;
== 0.12 ==&lt;br /&gt;
* Split error checking from FRCT&lt;br /&gt;
* Rename port_id to flow_id&lt;br /&gt;
* Rename SDU to packet&lt;br /&gt;
* Pass QoS spec at flow allocation&lt;br /&gt;
* Initial retransmission logic&lt;br /&gt;
* Support QoS specs for oping&lt;br /&gt;
* Use Endpoint ID&#039;s in fa protocol instead of fd&lt;br /&gt;
&lt;br /&gt;
== 0.11 ==&lt;br /&gt;
* Simplify reg/unreg API&lt;br /&gt;
* Support for partial packet reads&lt;br /&gt;
* Add IPCP over Ethernet DIX&lt;br /&gt;
&lt;br /&gt;
== 0.10 ==&lt;br /&gt;
* Revise lookup tracking in DHT&lt;br /&gt;
* Update man pages to CC-BY 4.0&lt;br /&gt;
&lt;br /&gt;
== 0.9 ==&lt;br /&gt;
* Add patchlevels for future versions&lt;br /&gt;
* Add support for Raptor FPGA Layer-1 PoC&lt;br /&gt;
&lt;br /&gt;
== 0.8 ==&lt;br /&gt;
* Use &amp;quot;program&amp;quot; instead of &amp;quot;application process&amp;quot;&lt;br /&gt;
* Use &amp;quot;process&amp;quot; instead of &amp;quot;application process instance&amp;quot;&lt;br /&gt;
* Send timestamp in oping&lt;br /&gt;
* Use 3-clause BSD license for tools&lt;br /&gt;
&lt;br /&gt;
== 0.7 ==&lt;br /&gt;
* Use ELF sections to init/fini ouroboros&lt;br /&gt;
* Simplify PFT for LFA routing&lt;br /&gt;
* Add RIB to expose metrics via FUSE&lt;br /&gt;
* Deprecate Common Distribution Application Protocol (CDAP)&lt;br /&gt;
* Simplify enroll API&lt;br /&gt;
* Deprecate Graph Adjancency Manager component&lt;br /&gt;
&lt;br /&gt;
== 0.6 ==&lt;br /&gt;
* Added a threadpool manager for improved concurrency&lt;br /&gt;
* Added Loop-Free Alternates (LFA) routing&lt;br /&gt;
* Add DHT as default directory policy&lt;br /&gt;
&lt;br /&gt;
== 0.5 ==&lt;br /&gt;
* Move FRCT logic from IPCP to application library&lt;br /&gt;
* Split connection establishment from flow allocation&lt;br /&gt;
* Split flow manager into flow allocator and data transfer&lt;br /&gt;
* Register hashes in a layer instead of cleartext name&lt;br /&gt;
* Add netmap support for LLC shim&lt;br /&gt;
* Initial lockless rbuff implementation&lt;br /&gt;
* Add generic thread pool management&lt;br /&gt;
* Revise flow allocation API&lt;br /&gt;
* Split authentication from CACEP&lt;br /&gt;
* Revise CACEP API&lt;br /&gt;
* Exchange application protocol information during CACEP&lt;br /&gt;
&lt;br /&gt;
== 0.4 ==&lt;br /&gt;
* Add support for syslog logging&lt;br /&gt;
* Revise RIB/CDAP as a btree structure&lt;br /&gt;
* Add B-tree implementation&lt;br /&gt;
* Add full-mesh policy for GAM&lt;br /&gt;
* Add Graph Adjacency Manager (GAM)&lt;br /&gt;
* Add flat address policy&lt;br /&gt;
* Add initial directory to normal IPCP&lt;br /&gt;
* Initial RIB/CDAP synchronization&lt;br /&gt;
* Add RIB/CDAP objects&lt;br /&gt;
* Add operf tool&lt;br /&gt;
&lt;br /&gt;
== 0.3 ==&lt;br /&gt;
* Move all shared memory (shm) mgmt to IRMd&lt;br /&gt;
* Use shared memory tx/rx ring buffer per flow&lt;br /&gt;
* Split off bind/unbind (map/unmap name to process) from reg/unreg&lt;br /&gt;
&lt;br /&gt;
== 0.2 ==&lt;br /&gt;
* Add shim DIF over LLC Ethernet&lt;br /&gt;
* Add CBR tool for performance testing&lt;br /&gt;
* Add IPCP over local memory&lt;br /&gt;
&lt;br /&gt;
== 0.1 ==&lt;br /&gt;
* Add full flow allocator to UDP shim&lt;br /&gt;
* Implementation of flow related ops&lt;br /&gt;
* Simple echo application&lt;br /&gt;
* Initial shim IPCP over UDP/IPv4&lt;br /&gt;
* Add RINA name helpers&lt;br /&gt;
* Initial library code&lt;br /&gt;
* Initial SDU/PDU buffer code&lt;br /&gt;
* Initial APIs for IRM/IPCP/CFAP/DIF allocator&lt;br /&gt;
* Initial CDAP header&lt;br /&gt;
* LICENSE and CONTRIBUTORS files&lt;br /&gt;
* Initial documenents and GPLv2 license&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1903</id>
		<title>Prototype Version History</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1903"/>
		<updated>2026-02-22T16:52:31Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 0.22 (Current version, November 7th 2025) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ouroboros is still in an early proof-of-concept prototype phase. Versions 0.x.y have no forward nor backward compatibility. We bump minor version with API or protocol changes. Within a minor version, different patch levels &#039;&#039;should&#039;&#039; be compatible (no guarantees).&lt;br /&gt;
&lt;br /&gt;
Once the functionality has reached a minimal critical mass, and a sufficient degree of software stability has been achieved, a version 1.0.0 will be released with the intention to maintain backwards compatibility from that point. This is still many (man)years off.&lt;br /&gt;
&lt;br /&gt;
This page summarizes the progress and the plans for the next prototype (minor) version. For a summary of the current implementation state, see [[Ouroboros Implementation Overview| our status page]].&lt;br /&gt;
&lt;br /&gt;
== 0.24 (Current development branch) ==&lt;br /&gt;
&lt;br /&gt;
== 0.23 ==&lt;br /&gt;
&lt;br /&gt;
* Added PQC support to the library, including SLH-DSA tests and per-algorithm gating, and PQC-aware config loading in IRMd.&lt;br /&gt;
* IRMd now negotiates encryption strength between endpoints, with fixes for client-side encryption requests.&lt;br /&gt;
* Replaced the rdrbuff with a proper slab allocator for shared memory packet buffers, a significant data-plane redesign.&lt;br /&gt;
* Added automatic key rotation for encrypted flows, with related fixes for IV/tag allocation and speedup of key rotation tests.&lt;br /&gt;
* Introduced per-user shared memory packet pools, improving multi-user isolation. Also added mlock() on shared memory buffers.&lt;br /&gt;
* Refactored the ring buffer implementation and removed the old &amp;quot;lockless&amp;quot; rbuff_ll variant.&lt;br /&gt;
* IRMd now allows direct ring buffer connections between local processes, reducing overhead for same-machine IPC.&lt;br /&gt;
* Major refactoring of CMake modules back to in-tree CMakeLists, plus new targets for coverage reports, build_tests, and automatic version parsing from git tags.&lt;br /&gt;
&lt;br /&gt;
== 0.22 ==&lt;br /&gt;
* Added flow authentication and the Flow Allocation Protocol header in the irmd&lt;br /&gt;
* Added UDP/IPv6 support&lt;br /&gt;
* Added protocol level debug logging&lt;br /&gt;
* Moved encryption control from qosspec to naming system&lt;br /&gt;
* Removed qosspec parameter from the broadcast API&lt;br /&gt;
* Refactored and simplified enrolment and connection manager&lt;br /&gt;
* Complete rewrite of the DHT for the unicast layer&lt;br /&gt;
* Refactored Ethernet IPCPs&lt;br /&gt;
* Deprecated appveyor in favor of codeberg/woodpecker CI&lt;br /&gt;
* Improved test coverage&lt;br /&gt;
* Improved systemd service and pkgconfig installation logic&lt;br /&gt;
* Improved ctest integration into build system&lt;br /&gt;
&lt;br /&gt;
== 0.21 ==&lt;br /&gt;
* Moved public key handling from library to IRMd&lt;br /&gt;
* Refactor of IRMd / registry implementation&lt;br /&gt;
&lt;br /&gt;
== 0.20 ==&lt;br /&gt;
* Improved configuration file handling&lt;br /&gt;
* Improved FUSE stability&lt;br /&gt;
* Log unique IDs to enrollment requests&lt;br /&gt;
* Print logo on startup when logging to std output&lt;br /&gt;
* Fix most common hangs on exit&lt;br /&gt;
&lt;br /&gt;
== 0.19 ==&lt;br /&gt;
* Pass the N-1 layer MPL at flow allocation&lt;br /&gt;
* Add flow liveness monitoring.&lt;br /&gt;
* Allow multiple directories in an IPCP &lt;br /&gt;
* Dropped support for raptor&lt;br /&gt;
* Use one UDP port for the IPCP over UDP/IPv4&lt;br /&gt;
&lt;br /&gt;
== 0.18 ==&lt;br /&gt;
* Use QoS cube for ECN marking&lt;br /&gt;
* Always use 64-bit endpoint IDs&lt;br /&gt;
* Add RIB statistics for flow allocator&lt;br /&gt;
* Add congestion avoidance policies&lt;br /&gt;
* Add flow control policies&lt;br /&gt;
* Add GCC 10 static analyzer build option&lt;br /&gt;
&lt;br /&gt;
== 0.17 ==&lt;br /&gt;
* Rename systemd service to ouroboros&lt;br /&gt;
* Add tests for LFA and ECMP routing&lt;br /&gt;
* Add Equal-Cost Multi-Path routing policy&lt;br /&gt;
&lt;br /&gt;
== 0.16 ==&lt;br /&gt;
* Removed support for SWIG bindings&lt;br /&gt;
* Add support for appveyor CI&lt;br /&gt;
* Add encryption support using OpenSSL&lt;br /&gt;
&lt;br /&gt;
== 0.15 ==&lt;br /&gt;
* Rename normal IPCP to unicast IPCP&lt;br /&gt;
* Add flow_join API for broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.14 ==&lt;br /&gt;
* Add Explicit Congestion Notification field to DT&lt;br /&gt;
* Add broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.13 ==&lt;br /&gt;
* Disable CRC32 by default&lt;br /&gt;
&lt;br /&gt;
== 0.12 ==&lt;br /&gt;
* Split error checking from FRCT&lt;br /&gt;
* Rename port_id to flow_id&lt;br /&gt;
* Rename SDU to packet&lt;br /&gt;
* Pass QoS spec at flow allocation&lt;br /&gt;
* Initial retransmission logic&lt;br /&gt;
* Support QoS specs for oping&lt;br /&gt;
* Use Endpoint ID&#039;s in fa protocol instead of fd&lt;br /&gt;
&lt;br /&gt;
== 0.11 ==&lt;br /&gt;
* Simplify reg/unreg API&lt;br /&gt;
* Support for partial packet reads&lt;br /&gt;
* Add IPCP over Ethernet DIX&lt;br /&gt;
&lt;br /&gt;
== 0.10 ==&lt;br /&gt;
* Revise lookup tracking in DHT&lt;br /&gt;
* Update man pages to CC-BY 4.0&lt;br /&gt;
&lt;br /&gt;
== 0.9 ==&lt;br /&gt;
* Add patchlevels for future versions&lt;br /&gt;
* Add support for Raptor FPGA Layer-1 PoC&lt;br /&gt;
&lt;br /&gt;
== 0.8 ==&lt;br /&gt;
* Use &amp;quot;program&amp;quot; instead of &amp;quot;application process&amp;quot;&lt;br /&gt;
* Use &amp;quot;process&amp;quot; instead of &amp;quot;application process instance&amp;quot;&lt;br /&gt;
* Send timestamp in oping&lt;br /&gt;
* Use 3-clause BSD license for tools&lt;br /&gt;
&lt;br /&gt;
== 0.7 ==&lt;br /&gt;
* Use ELF sections to init/fini ouroboros&lt;br /&gt;
* Simplify PFT for LFA routing&lt;br /&gt;
* Add RIB to expose metrics via FUSE&lt;br /&gt;
* Deprecate Common Distribution Application Protocol (CDAP)&lt;br /&gt;
* Simplify enroll API&lt;br /&gt;
* Deprecate Graph Adjancency Manager component&lt;br /&gt;
&lt;br /&gt;
== 0.6 ==&lt;br /&gt;
* Added a threadpool manager for improved concurrency&lt;br /&gt;
* Added Loop-Free Alternates (LFA) routing&lt;br /&gt;
* Add DHT as default directory policy&lt;br /&gt;
&lt;br /&gt;
== 0.5 ==&lt;br /&gt;
* Move FRCT logic from IPCP to application library&lt;br /&gt;
* Split connection establishment from flow allocation&lt;br /&gt;
* Split flow manager into flow allocator and data transfer&lt;br /&gt;
* Register hashes in a layer instead of cleartext name&lt;br /&gt;
* Add netmap support for LLC shim&lt;br /&gt;
* Initial lockless rbuff implementation&lt;br /&gt;
* Add generic thread pool management&lt;br /&gt;
* Revise flow allocation API&lt;br /&gt;
* Split authentication from CACEP&lt;br /&gt;
* Revise CACEP API&lt;br /&gt;
* Exchange application protocol information during CACEP&lt;br /&gt;
&lt;br /&gt;
== 0.4 ==&lt;br /&gt;
* Add support for syslog logging&lt;br /&gt;
* Revise RIB/CDAP as a btree structure&lt;br /&gt;
* Add B-tree implementation&lt;br /&gt;
* Add full-mesh policy for GAM&lt;br /&gt;
* Add Graph Adjacency Manager (GAM)&lt;br /&gt;
* Add flat address policy&lt;br /&gt;
* Add initial directory to normal IPCP&lt;br /&gt;
* Initial RIB/CDAP synchronization&lt;br /&gt;
* Add RIB/CDAP objects&lt;br /&gt;
* Add operf tool&lt;br /&gt;
&lt;br /&gt;
== 0.3 ==&lt;br /&gt;
* Move all shared memory (shm) mgmt to IRMd&lt;br /&gt;
* Use shared memory tx/rx ring buffer per flow&lt;br /&gt;
* Split off bind/unbind (map/unmap name to process) from reg/unreg&lt;br /&gt;
&lt;br /&gt;
== 0.2 ==&lt;br /&gt;
* Add shim DIF over LLC Ethernet&lt;br /&gt;
* Add CBR tool for performance testing&lt;br /&gt;
* Add IPCP over local memory&lt;br /&gt;
&lt;br /&gt;
== 0.1 ==&lt;br /&gt;
* Add full flow allocator to UDP shim&lt;br /&gt;
* Implementation of flow related ops&lt;br /&gt;
* Simple echo application&lt;br /&gt;
* Initial shim IPCP over UDP/IPv4&lt;br /&gt;
* Add RINA name helpers&lt;br /&gt;
* Initial library code&lt;br /&gt;
* Initial SDU/PDU buffer code&lt;br /&gt;
* Initial APIs for IRM/IPCP/CFAP/DIF allocator&lt;br /&gt;
* Initial CDAP header&lt;br /&gt;
* LICENSE and CONTRIBUTORS files&lt;br /&gt;
* Initial documenents and GPLv2 license&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1902</id>
		<title>Prototype Version History</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1902"/>
		<updated>2026-02-22T16:52:13Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 0.23 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ouroboros is still in an early proof-of-concept prototype phase. Versions 0.x.y have no forward nor backward compatibility. We bump minor version with API or protocol changes. Within a minor version, different patch levels &#039;&#039;should&#039;&#039; be compatible (no guarantees).&lt;br /&gt;
&lt;br /&gt;
Once the functionality has reached a minimal critical mass, and a sufficient degree of software stability has been achieved, a version 1.0.0 will be released with the intention to maintain backwards compatibility from that point. This is still many (man)years off.&lt;br /&gt;
&lt;br /&gt;
This page summarizes the progress and the plans for the next prototype (minor) version. For a summary of the current implementation state, see [[Ouroboros Implementation Overview| our status page]].&lt;br /&gt;
&lt;br /&gt;
== 0.24 (Current development branch) ==&lt;br /&gt;
&lt;br /&gt;
== 0.23 ==&lt;br /&gt;
&lt;br /&gt;
* Added PQC support to the library, including SLH-DSA tests and per-algorithm gating, and PQC-aware config loading in IRMd.&lt;br /&gt;
* IRMd now negotiates encryption strength between endpoints, with fixes for client-side encryption requests.&lt;br /&gt;
* Replaced the rdrbuff with a proper slab allocator for shared memory packet buffers, a significant data-plane redesign.&lt;br /&gt;
* Added automatic key rotation for encrypted flows, with related fixes for IV/tag allocation and speedup of key rotation tests.&lt;br /&gt;
* Introduced per-user shared memory packet pools, improving multi-user isolation. Also added mlock() on shared memory buffers.&lt;br /&gt;
* Refactored the ring buffer implementation and removed the old &amp;quot;lockless&amp;quot; rbuff_ll variant.&lt;br /&gt;
* IRMd now allows direct ring buffer connections between local processes, reducing overhead for same-machine IPC.&lt;br /&gt;
* Major refactoring of CMake modules back to in-tree CMakeLists, plus new targets for coverage reports, build_tests, and automatic version parsing from git tags.&lt;br /&gt;
&lt;br /&gt;
== 0.22 (Current version, November 7th 2025) ==&lt;br /&gt;
* Added flow authentication and the Flow Allocation Protocol header in the irmd&lt;br /&gt;
* Added UDP/IPv6 support&lt;br /&gt;
* Added protocol level debug logging&lt;br /&gt;
* Moved encryption control from qosspec to naming system&lt;br /&gt;
* Removed qosspec parameter from the broadcast API&lt;br /&gt;
* Refactored and simplified enrolment and connection manager&lt;br /&gt;
* Complete rewrite of the DHT for the unicast layer&lt;br /&gt;
* Refactored Ethernet IPCPs&lt;br /&gt;
* Deprecated appveyor in favor of codeberg/woodpecker CI&lt;br /&gt;
* Improved test coverage&lt;br /&gt;
* Improved systemd service and pkgconfig installation logic&lt;br /&gt;
* Improved ctest integration into build system&lt;br /&gt;
&lt;br /&gt;
== 0.21 ==&lt;br /&gt;
* Moved public key handling from library to IRMd&lt;br /&gt;
* Refactor of IRMd / registry implementation&lt;br /&gt;
&lt;br /&gt;
== 0.20 ==&lt;br /&gt;
* Improved configuration file handling&lt;br /&gt;
* Improved FUSE stability&lt;br /&gt;
* Log unique IDs to enrollment requests&lt;br /&gt;
* Print logo on startup when logging to std output&lt;br /&gt;
* Fix most common hangs on exit&lt;br /&gt;
&lt;br /&gt;
== 0.19 ==&lt;br /&gt;
* Pass the N-1 layer MPL at flow allocation&lt;br /&gt;
* Add flow liveness monitoring.&lt;br /&gt;
* Allow multiple directories in an IPCP &lt;br /&gt;
* Dropped support for raptor&lt;br /&gt;
* Use one UDP port for the IPCP over UDP/IPv4&lt;br /&gt;
&lt;br /&gt;
== 0.18 ==&lt;br /&gt;
* Use QoS cube for ECN marking&lt;br /&gt;
* Always use 64-bit endpoint IDs&lt;br /&gt;
* Add RIB statistics for flow allocator&lt;br /&gt;
* Add congestion avoidance policies&lt;br /&gt;
* Add flow control policies&lt;br /&gt;
* Add GCC 10 static analyzer build option&lt;br /&gt;
&lt;br /&gt;
== 0.17 ==&lt;br /&gt;
* Rename systemd service to ouroboros&lt;br /&gt;
* Add tests for LFA and ECMP routing&lt;br /&gt;
* Add Equal-Cost Multi-Path routing policy&lt;br /&gt;
&lt;br /&gt;
== 0.16 ==&lt;br /&gt;
* Removed support for SWIG bindings&lt;br /&gt;
* Add support for appveyor CI&lt;br /&gt;
* Add encryption support using OpenSSL&lt;br /&gt;
&lt;br /&gt;
== 0.15 ==&lt;br /&gt;
* Rename normal IPCP to unicast IPCP&lt;br /&gt;
* Add flow_join API for broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.14 ==&lt;br /&gt;
* Add Explicit Congestion Notification field to DT&lt;br /&gt;
* Add broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.13 ==&lt;br /&gt;
* Disable CRC32 by default&lt;br /&gt;
&lt;br /&gt;
== 0.12 ==&lt;br /&gt;
* Split error checking from FRCT&lt;br /&gt;
* Rename port_id to flow_id&lt;br /&gt;
* Rename SDU to packet&lt;br /&gt;
* Pass QoS spec at flow allocation&lt;br /&gt;
* Initial retransmission logic&lt;br /&gt;
* Support QoS specs for oping&lt;br /&gt;
* Use Endpoint ID&#039;s in fa protocol instead of fd&lt;br /&gt;
&lt;br /&gt;
== 0.11 ==&lt;br /&gt;
* Simplify reg/unreg API&lt;br /&gt;
* Support for partial packet reads&lt;br /&gt;
* Add IPCP over Ethernet DIX&lt;br /&gt;
&lt;br /&gt;
== 0.10 ==&lt;br /&gt;
* Revise lookup tracking in DHT&lt;br /&gt;
* Update man pages to CC-BY 4.0&lt;br /&gt;
&lt;br /&gt;
== 0.9 ==&lt;br /&gt;
* Add patchlevels for future versions&lt;br /&gt;
* Add support for Raptor FPGA Layer-1 PoC&lt;br /&gt;
&lt;br /&gt;
== 0.8 ==&lt;br /&gt;
* Use &amp;quot;program&amp;quot; instead of &amp;quot;application process&amp;quot;&lt;br /&gt;
* Use &amp;quot;process&amp;quot; instead of &amp;quot;application process instance&amp;quot;&lt;br /&gt;
* Send timestamp in oping&lt;br /&gt;
* Use 3-clause BSD license for tools&lt;br /&gt;
&lt;br /&gt;
== 0.7 ==&lt;br /&gt;
* Use ELF sections to init/fini ouroboros&lt;br /&gt;
* Simplify PFT for LFA routing&lt;br /&gt;
* Add RIB to expose metrics via FUSE&lt;br /&gt;
* Deprecate Common Distribution Application Protocol (CDAP)&lt;br /&gt;
* Simplify enroll API&lt;br /&gt;
* Deprecate Graph Adjancency Manager component&lt;br /&gt;
&lt;br /&gt;
== 0.6 ==&lt;br /&gt;
* Added a threadpool manager for improved concurrency&lt;br /&gt;
* Added Loop-Free Alternates (LFA) routing&lt;br /&gt;
* Add DHT as default directory policy&lt;br /&gt;
&lt;br /&gt;
== 0.5 ==&lt;br /&gt;
* Move FRCT logic from IPCP to application library&lt;br /&gt;
* Split connection establishment from flow allocation&lt;br /&gt;
* Split flow manager into flow allocator and data transfer&lt;br /&gt;
* Register hashes in a layer instead of cleartext name&lt;br /&gt;
* Add netmap support for LLC shim&lt;br /&gt;
* Initial lockless rbuff implementation&lt;br /&gt;
* Add generic thread pool management&lt;br /&gt;
* Revise flow allocation API&lt;br /&gt;
* Split authentication from CACEP&lt;br /&gt;
* Revise CACEP API&lt;br /&gt;
* Exchange application protocol information during CACEP&lt;br /&gt;
&lt;br /&gt;
== 0.4 ==&lt;br /&gt;
* Add support for syslog logging&lt;br /&gt;
* Revise RIB/CDAP as a btree structure&lt;br /&gt;
* Add B-tree implementation&lt;br /&gt;
* Add full-mesh policy for GAM&lt;br /&gt;
* Add Graph Adjacency Manager (GAM)&lt;br /&gt;
* Add flat address policy&lt;br /&gt;
* Add initial directory to normal IPCP&lt;br /&gt;
* Initial RIB/CDAP synchronization&lt;br /&gt;
* Add RIB/CDAP objects&lt;br /&gt;
* Add operf tool&lt;br /&gt;
&lt;br /&gt;
== 0.3 ==&lt;br /&gt;
* Move all shared memory (shm) mgmt to IRMd&lt;br /&gt;
* Use shared memory tx/rx ring buffer per flow&lt;br /&gt;
* Split off bind/unbind (map/unmap name to process) from reg/unreg&lt;br /&gt;
&lt;br /&gt;
== 0.2 ==&lt;br /&gt;
* Add shim DIF over LLC Ethernet&lt;br /&gt;
* Add CBR tool for performance testing&lt;br /&gt;
* Add IPCP over local memory&lt;br /&gt;
&lt;br /&gt;
== 0.1 ==&lt;br /&gt;
* Add full flow allocator to UDP shim&lt;br /&gt;
* Implementation of flow related ops&lt;br /&gt;
* Simple echo application&lt;br /&gt;
* Initial shim IPCP over UDP/IPv4&lt;br /&gt;
* Add RINA name helpers&lt;br /&gt;
* Initial library code&lt;br /&gt;
* Initial SDU/PDU buffer code&lt;br /&gt;
* Initial APIs for IRM/IPCP/CFAP/DIF allocator&lt;br /&gt;
* Initial CDAP header&lt;br /&gt;
* LICENSE and CONTRIBUTORS files&lt;br /&gt;
* Initial documenents and GPLv2 license&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1901</id>
		<title>Prototype Version History</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1901"/>
		<updated>2026-02-22T16:51:29Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 0.23 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ouroboros is still in an early proof-of-concept prototype phase. Versions 0.x.y have no forward nor backward compatibility. We bump minor version with API or protocol changes. Within a minor version, different patch levels &#039;&#039;should&#039;&#039; be compatible (no guarantees).&lt;br /&gt;
&lt;br /&gt;
Once the functionality has reached a minimal critical mass, and a sufficient degree of software stability has been achieved, a version 1.0.0 will be released with the intention to maintain backwards compatibility from that point. This is still many (man)years off.&lt;br /&gt;
&lt;br /&gt;
This page summarizes the progress and the plans for the next prototype (minor) version. For a summary of the current implementation state, see [[Ouroboros Implementation Overview| our status page]].&lt;br /&gt;
&lt;br /&gt;
== 0.24 (Current development branch) ==&lt;br /&gt;
&lt;br /&gt;
== 0.23 ==&lt;br /&gt;
&lt;br /&gt;
* Post-quantum cryptography support — Added PQC support to the library, including SLH-DSA tests and per-algorithm gating, and PQC-aware config loading in IRMd.&lt;br /&gt;
* Strength-based crypto negotiation — IRMd now negotiates encryption strength between endpoints, with fixes for client-side encryption requests.&lt;br /&gt;
* Slab allocator replacing rdrbuff — Replaced the rdrbuff with a proper slab allocator for shared memory packet buffers, a significant data-plane redesign.&lt;br /&gt;
* Automatic key rotation for encryption — Added automatic key rotation for encrypted flows, with related fixes for IV/tag allocation and speedup of key rotation tests.&lt;br /&gt;
* Per-user packet pools — Introduced per-user shared memory packet pools, improving multi-user isolation. Also added mlock() on shared memory buffers.&lt;br /&gt;
* rbuff refactoring — Refactored the ring buffer implementation and removed the old &amp;quot;lockless&amp;quot; rbuff_ll variant.&lt;br /&gt;
* Direct rbuff between local processes — IRMd now allows direct ring buffer connections between local processes, reducing overhead for same-machine IPC.&lt;br /&gt;
* CMake build system overhaul — Major refactoring of CMake modules back to in-tree CMakeLists, plus new targets for coverage reports, build_tests, and automatic version parsing from git tags.&lt;br /&gt;
&lt;br /&gt;
== 0.22 (Current version, November 7th 2025) ==&lt;br /&gt;
* Added flow authentication and the Flow Allocation Protocol header in the irmd&lt;br /&gt;
* Added UDP/IPv6 support&lt;br /&gt;
* Added protocol level debug logging&lt;br /&gt;
* Moved encryption control from qosspec to naming system&lt;br /&gt;
* Removed qosspec parameter from the broadcast API&lt;br /&gt;
* Refactored and simplified enrolment and connection manager&lt;br /&gt;
* Complete rewrite of the DHT for the unicast layer&lt;br /&gt;
* Refactored Ethernet IPCPs&lt;br /&gt;
* Deprecated appveyor in favor of codeberg/woodpecker CI&lt;br /&gt;
* Improved test coverage&lt;br /&gt;
* Improved systemd service and pkgconfig installation logic&lt;br /&gt;
* Improved ctest integration into build system&lt;br /&gt;
&lt;br /&gt;
== 0.21 ==&lt;br /&gt;
* Moved public key handling from library to IRMd&lt;br /&gt;
* Refactor of IRMd / registry implementation&lt;br /&gt;
&lt;br /&gt;
== 0.20 ==&lt;br /&gt;
* Improved configuration file handling&lt;br /&gt;
* Improved FUSE stability&lt;br /&gt;
* Log unique IDs to enrollment requests&lt;br /&gt;
* Print logo on startup when logging to std output&lt;br /&gt;
* Fix most common hangs on exit&lt;br /&gt;
&lt;br /&gt;
== 0.19 ==&lt;br /&gt;
* Pass the N-1 layer MPL at flow allocation&lt;br /&gt;
* Add flow liveness monitoring.&lt;br /&gt;
* Allow multiple directories in an IPCP &lt;br /&gt;
* Dropped support for raptor&lt;br /&gt;
* Use one UDP port for the IPCP over UDP/IPv4&lt;br /&gt;
&lt;br /&gt;
== 0.18 ==&lt;br /&gt;
* Use QoS cube for ECN marking&lt;br /&gt;
* Always use 64-bit endpoint IDs&lt;br /&gt;
* Add RIB statistics for flow allocator&lt;br /&gt;
* Add congestion avoidance policies&lt;br /&gt;
* Add flow control policies&lt;br /&gt;
* Add GCC 10 static analyzer build option&lt;br /&gt;
&lt;br /&gt;
== 0.17 ==&lt;br /&gt;
* Rename systemd service to ouroboros&lt;br /&gt;
* Add tests for LFA and ECMP routing&lt;br /&gt;
* Add Equal-Cost Multi-Path routing policy&lt;br /&gt;
&lt;br /&gt;
== 0.16 ==&lt;br /&gt;
* Removed support for SWIG bindings&lt;br /&gt;
* Add support for appveyor CI&lt;br /&gt;
* Add encryption support using OpenSSL&lt;br /&gt;
&lt;br /&gt;
== 0.15 ==&lt;br /&gt;
* Rename normal IPCP to unicast IPCP&lt;br /&gt;
* Add flow_join API for broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.14 ==&lt;br /&gt;
* Add Explicit Congestion Notification field to DT&lt;br /&gt;
* Add broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.13 ==&lt;br /&gt;
* Disable CRC32 by default&lt;br /&gt;
&lt;br /&gt;
== 0.12 ==&lt;br /&gt;
* Split error checking from FRCT&lt;br /&gt;
* Rename port_id to flow_id&lt;br /&gt;
* Rename SDU to packet&lt;br /&gt;
* Pass QoS spec at flow allocation&lt;br /&gt;
* Initial retransmission logic&lt;br /&gt;
* Support QoS specs for oping&lt;br /&gt;
* Use Endpoint ID&#039;s in fa protocol instead of fd&lt;br /&gt;
&lt;br /&gt;
== 0.11 ==&lt;br /&gt;
* Simplify reg/unreg API&lt;br /&gt;
* Support for partial packet reads&lt;br /&gt;
* Add IPCP over Ethernet DIX&lt;br /&gt;
&lt;br /&gt;
== 0.10 ==&lt;br /&gt;
* Revise lookup tracking in DHT&lt;br /&gt;
* Update man pages to CC-BY 4.0&lt;br /&gt;
&lt;br /&gt;
== 0.9 ==&lt;br /&gt;
* Add patchlevels for future versions&lt;br /&gt;
* Add support for Raptor FPGA Layer-1 PoC&lt;br /&gt;
&lt;br /&gt;
== 0.8 ==&lt;br /&gt;
* Use &amp;quot;program&amp;quot; instead of &amp;quot;application process&amp;quot;&lt;br /&gt;
* Use &amp;quot;process&amp;quot; instead of &amp;quot;application process instance&amp;quot;&lt;br /&gt;
* Send timestamp in oping&lt;br /&gt;
* Use 3-clause BSD license for tools&lt;br /&gt;
&lt;br /&gt;
== 0.7 ==&lt;br /&gt;
* Use ELF sections to init/fini ouroboros&lt;br /&gt;
* Simplify PFT for LFA routing&lt;br /&gt;
* Add RIB to expose metrics via FUSE&lt;br /&gt;
* Deprecate Common Distribution Application Protocol (CDAP)&lt;br /&gt;
* Simplify enroll API&lt;br /&gt;
* Deprecate Graph Adjancency Manager component&lt;br /&gt;
&lt;br /&gt;
== 0.6 ==&lt;br /&gt;
* Added a threadpool manager for improved concurrency&lt;br /&gt;
* Added Loop-Free Alternates (LFA) routing&lt;br /&gt;
* Add DHT as default directory policy&lt;br /&gt;
&lt;br /&gt;
== 0.5 ==&lt;br /&gt;
* Move FRCT logic from IPCP to application library&lt;br /&gt;
* Split connection establishment from flow allocation&lt;br /&gt;
* Split flow manager into flow allocator and data transfer&lt;br /&gt;
* Register hashes in a layer instead of cleartext name&lt;br /&gt;
* Add netmap support for LLC shim&lt;br /&gt;
* Initial lockless rbuff implementation&lt;br /&gt;
* Add generic thread pool management&lt;br /&gt;
* Revise flow allocation API&lt;br /&gt;
* Split authentication from CACEP&lt;br /&gt;
* Revise CACEP API&lt;br /&gt;
* Exchange application protocol information during CACEP&lt;br /&gt;
&lt;br /&gt;
== 0.4 ==&lt;br /&gt;
* Add support for syslog logging&lt;br /&gt;
* Revise RIB/CDAP as a btree structure&lt;br /&gt;
* Add B-tree implementation&lt;br /&gt;
* Add full-mesh policy for GAM&lt;br /&gt;
* Add Graph Adjacency Manager (GAM)&lt;br /&gt;
* Add flat address policy&lt;br /&gt;
* Add initial directory to normal IPCP&lt;br /&gt;
* Initial RIB/CDAP synchronization&lt;br /&gt;
* Add RIB/CDAP objects&lt;br /&gt;
* Add operf tool&lt;br /&gt;
&lt;br /&gt;
== 0.3 ==&lt;br /&gt;
* Move all shared memory (shm) mgmt to IRMd&lt;br /&gt;
* Use shared memory tx/rx ring buffer per flow&lt;br /&gt;
* Split off bind/unbind (map/unmap name to process) from reg/unreg&lt;br /&gt;
&lt;br /&gt;
== 0.2 ==&lt;br /&gt;
* Add shim DIF over LLC Ethernet&lt;br /&gt;
* Add CBR tool for performance testing&lt;br /&gt;
* Add IPCP over local memory&lt;br /&gt;
&lt;br /&gt;
== 0.1 ==&lt;br /&gt;
* Add full flow allocator to UDP shim&lt;br /&gt;
* Implementation of flow related ops&lt;br /&gt;
* Simple echo application&lt;br /&gt;
* Initial shim IPCP over UDP/IPv4&lt;br /&gt;
* Add RINA name helpers&lt;br /&gt;
* Initial library code&lt;br /&gt;
* Initial SDU/PDU buffer code&lt;br /&gt;
* Initial APIs for IRM/IPCP/CFAP/DIF allocator&lt;br /&gt;
* Initial CDAP header&lt;br /&gt;
* LICENSE and CONTRIBUTORS files&lt;br /&gt;
* Initial documenents and GPLv2 license&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1900</id>
		<title>Prototype Version History</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Prototype_Version_History&amp;diff=1900"/>
		<updated>2026-02-22T16:49:17Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* 0.23 (Current development branch) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ouroboros is still in an early proof-of-concept prototype phase. Versions 0.x.y have no forward nor backward compatibility. We bump minor version with API or protocol changes. Within a minor version, different patch levels &#039;&#039;should&#039;&#039; be compatible (no guarantees).&lt;br /&gt;
&lt;br /&gt;
Once the functionality has reached a minimal critical mass, and a sufficient degree of software stability has been achieved, a version 1.0.0 will be released with the intention to maintain backwards compatibility from that point. This is still many (man)years off.&lt;br /&gt;
&lt;br /&gt;
This page summarizes the progress and the plans for the next prototype (minor) version. For a summary of the current implementation state, see [[Ouroboros Implementation Overview| our status page]].&lt;br /&gt;
&lt;br /&gt;
== 0.24 (Current development branch) ==&lt;br /&gt;
&lt;br /&gt;
== 0.23 ==&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Post-quantum cryptography support&#039;&#039;&#039; — Added PQC support to the library, including SLH-DSA tests and per-algorithm gating, and PQC-aware config loading in IRMd.&lt;br /&gt;
# &#039;&#039;&#039;Slab allocator replacing rdrbuff&#039;&#039;&#039; — Replaced the rdrbuff with a proper slab allocator for shared memory packet buffers, a significant data-plane redesign.&lt;br /&gt;
# &#039;&#039;&#039;Automatic key rotation for encryption&#039;&#039;&#039; — Added automatic key rotation for encrypted flows, with related fixes for IV/tag allocation and speedup of key rotation tests.&lt;br /&gt;
# &#039;&#039;&#039;Strength-based crypto negotiation&#039;&#039;&#039; — IRMd now negotiates encryption strength between endpoints, with fixes for client-side encryption requests.&lt;br /&gt;
# &#039;&#039;&#039;Per-user packet pools&#039;&#039;&#039; — Introduced per-user shared memory packet pools, improving multi-user isolation. Also added mlock() on shared memory buffers.&lt;br /&gt;
# &#039;&#039;&#039;Rbuff refactoring&#039;&#039;&#039; — Refactored the ring buffer implementation and removed the old &amp;quot;lockless&amp;quot; rbuff_ll variant.&lt;br /&gt;
# &#039;&#039;&#039;Direct rbuff between local processes&#039;&#039;&#039; — IRMd now allows direct ring buffer connections between local processes, reducing overhead for same-machine IPC.&lt;br /&gt;
# &#039;&#039;&#039;Linked list with length tracking&#039;&#039;&#039; — Added struct llist to the library, providing list operations that also track element count.&lt;br /&gt;
# &#039;&#039;&#039;CMake build system overhaul&#039;&#039;&#039; — Major refactoring of CMake modules back to in-tree CMakeLists, plus new targets for coverage reports, build_tests, and automatic version parsing from git tags.&lt;br /&gt;
# &#039;&#039;&#039;Container compatibility &amp;amp; OS X fixes&#039;&#039;&#039; — Added a container compatibility build option, and fixed SSM pool creation, OpenSSL includes, and explicit_bzero on OS X.&lt;br /&gt;
&lt;br /&gt;
== 0.22 (Current version, November 7th 2025) ==&lt;br /&gt;
* Added flow authentication and the Flow Allocation Protocol header in the irmd&lt;br /&gt;
* Added UDP/IPv6 support&lt;br /&gt;
* Added protocol level debug logging&lt;br /&gt;
* Moved encryption control from qosspec to naming system&lt;br /&gt;
* Removed qosspec parameter from the broadcast API&lt;br /&gt;
* Refactored and simplified enrolment and connection manager&lt;br /&gt;
* Complete rewrite of the DHT for the unicast layer&lt;br /&gt;
* Refactored Ethernet IPCPs&lt;br /&gt;
* Deprecated appveyor in favor of codeberg/woodpecker CI&lt;br /&gt;
* Improved test coverage&lt;br /&gt;
* Improved systemd service and pkgconfig installation logic&lt;br /&gt;
* Improved ctest integration into build system&lt;br /&gt;
&lt;br /&gt;
== 0.21 ==&lt;br /&gt;
* Moved public key handling from library to IRMd&lt;br /&gt;
* Refactor of IRMd / registry implementation&lt;br /&gt;
&lt;br /&gt;
== 0.20 ==&lt;br /&gt;
* Improved configuration file handling&lt;br /&gt;
* Improved FUSE stability&lt;br /&gt;
* Log unique IDs to enrollment requests&lt;br /&gt;
* Print logo on startup when logging to std output&lt;br /&gt;
* Fix most common hangs on exit&lt;br /&gt;
&lt;br /&gt;
== 0.19 ==&lt;br /&gt;
* Pass the N-1 layer MPL at flow allocation&lt;br /&gt;
* Add flow liveness monitoring.&lt;br /&gt;
* Allow multiple directories in an IPCP &lt;br /&gt;
* Dropped support for raptor&lt;br /&gt;
* Use one UDP port for the IPCP over UDP/IPv4&lt;br /&gt;
&lt;br /&gt;
== 0.18 ==&lt;br /&gt;
* Use QoS cube for ECN marking&lt;br /&gt;
* Always use 64-bit endpoint IDs&lt;br /&gt;
* Add RIB statistics for flow allocator&lt;br /&gt;
* Add congestion avoidance policies&lt;br /&gt;
* Add flow control policies&lt;br /&gt;
* Add GCC 10 static analyzer build option&lt;br /&gt;
&lt;br /&gt;
== 0.17 ==&lt;br /&gt;
* Rename systemd service to ouroboros&lt;br /&gt;
* Add tests for LFA and ECMP routing&lt;br /&gt;
* Add Equal-Cost Multi-Path routing policy&lt;br /&gt;
&lt;br /&gt;
== 0.16 ==&lt;br /&gt;
* Removed support for SWIG bindings&lt;br /&gt;
* Add support for appveyor CI&lt;br /&gt;
* Add encryption support using OpenSSL&lt;br /&gt;
&lt;br /&gt;
== 0.15 ==&lt;br /&gt;
* Rename normal IPCP to unicast IPCP&lt;br /&gt;
* Add flow_join API for broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.14 ==&lt;br /&gt;
* Add Explicit Congestion Notification field to DT&lt;br /&gt;
* Add broadcast IPCP&lt;br /&gt;
&lt;br /&gt;
== 0.13 ==&lt;br /&gt;
* Disable CRC32 by default&lt;br /&gt;
&lt;br /&gt;
== 0.12 ==&lt;br /&gt;
* Split error checking from FRCT&lt;br /&gt;
* Rename port_id to flow_id&lt;br /&gt;
* Rename SDU to packet&lt;br /&gt;
* Pass QoS spec at flow allocation&lt;br /&gt;
* Initial retransmission logic&lt;br /&gt;
* Support QoS specs for oping&lt;br /&gt;
* Use Endpoint ID&#039;s in fa protocol instead of fd&lt;br /&gt;
&lt;br /&gt;
== 0.11 ==&lt;br /&gt;
* Simplify reg/unreg API&lt;br /&gt;
* Support for partial packet reads&lt;br /&gt;
* Add IPCP over Ethernet DIX&lt;br /&gt;
&lt;br /&gt;
== 0.10 ==&lt;br /&gt;
* Revise lookup tracking in DHT&lt;br /&gt;
* Update man pages to CC-BY 4.0&lt;br /&gt;
&lt;br /&gt;
== 0.9 ==&lt;br /&gt;
* Add patchlevels for future versions&lt;br /&gt;
* Add support for Raptor FPGA Layer-1 PoC&lt;br /&gt;
&lt;br /&gt;
== 0.8 ==&lt;br /&gt;
* Use &amp;quot;program&amp;quot; instead of &amp;quot;application process&amp;quot;&lt;br /&gt;
* Use &amp;quot;process&amp;quot; instead of &amp;quot;application process instance&amp;quot;&lt;br /&gt;
* Send timestamp in oping&lt;br /&gt;
* Use 3-clause BSD license for tools&lt;br /&gt;
&lt;br /&gt;
== 0.7 ==&lt;br /&gt;
* Use ELF sections to init/fini ouroboros&lt;br /&gt;
* Simplify PFT for LFA routing&lt;br /&gt;
* Add RIB to expose metrics via FUSE&lt;br /&gt;
* Deprecate Common Distribution Application Protocol (CDAP)&lt;br /&gt;
* Simplify enroll API&lt;br /&gt;
* Deprecate Graph Adjancency Manager component&lt;br /&gt;
&lt;br /&gt;
== 0.6 ==&lt;br /&gt;
* Added a threadpool manager for improved concurrency&lt;br /&gt;
* Added Loop-Free Alternates (LFA) routing&lt;br /&gt;
* Add DHT as default directory policy&lt;br /&gt;
&lt;br /&gt;
== 0.5 ==&lt;br /&gt;
* Move FRCT logic from IPCP to application library&lt;br /&gt;
* Split connection establishment from flow allocation&lt;br /&gt;
* Split flow manager into flow allocator and data transfer&lt;br /&gt;
* Register hashes in a layer instead of cleartext name&lt;br /&gt;
* Add netmap support for LLC shim&lt;br /&gt;
* Initial lockless rbuff implementation&lt;br /&gt;
* Add generic thread pool management&lt;br /&gt;
* Revise flow allocation API&lt;br /&gt;
* Split authentication from CACEP&lt;br /&gt;
* Revise CACEP API&lt;br /&gt;
* Exchange application protocol information during CACEP&lt;br /&gt;
&lt;br /&gt;
== 0.4 ==&lt;br /&gt;
* Add support for syslog logging&lt;br /&gt;
* Revise RIB/CDAP as a btree structure&lt;br /&gt;
* Add B-tree implementation&lt;br /&gt;
* Add full-mesh policy for GAM&lt;br /&gt;
* Add Graph Adjacency Manager (GAM)&lt;br /&gt;
* Add flat address policy&lt;br /&gt;
* Add initial directory to normal IPCP&lt;br /&gt;
* Initial RIB/CDAP synchronization&lt;br /&gt;
* Add RIB/CDAP objects&lt;br /&gt;
* Add operf tool&lt;br /&gt;
&lt;br /&gt;
== 0.3 ==&lt;br /&gt;
* Move all shared memory (shm) mgmt to IRMd&lt;br /&gt;
* Use shared memory tx/rx ring buffer per flow&lt;br /&gt;
* Split off bind/unbind (map/unmap name to process) from reg/unreg&lt;br /&gt;
&lt;br /&gt;
== 0.2 ==&lt;br /&gt;
* Add shim DIF over LLC Ethernet&lt;br /&gt;
* Add CBR tool for performance testing&lt;br /&gt;
* Add IPCP over local memory&lt;br /&gt;
&lt;br /&gt;
== 0.1 ==&lt;br /&gt;
* Add full flow allocator to UDP shim&lt;br /&gt;
* Implementation of flow related ops&lt;br /&gt;
* Simple echo application&lt;br /&gt;
* Initial shim IPCP over UDP/IPv4&lt;br /&gt;
* Add RINA name helpers&lt;br /&gt;
* Initial library code&lt;br /&gt;
* Initial SDU/PDU buffer code&lt;br /&gt;
* Initial APIs for IRM/IPCP/CFAP/DIF allocator&lt;br /&gt;
* Initial CDAP header&lt;br /&gt;
* LICENSE and CONTRIBUTORS files&lt;br /&gt;
* Initial documenents and GPLv2 license&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Tutorial_06&amp;diff=1899</id>
		<title>Ouroboros Tutorial 06</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Tutorial_06&amp;diff=1899"/>
		<updated>2026-02-14T15:16:19Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* Test 1: No Authentication, No Encryption */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Ouroboros Tutorial 06 - Authenticated Flows =&lt;br /&gt;
&lt;br /&gt;
This tutorial demonstrates setting up and using authenticated flows in Ouroboros with certificate-based authentication.&lt;br /&gt;
&lt;br /&gt;
The overall flow of authenticated flow allocation is&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Client (IRMd)                              Server (IRMd)&lt;br /&gt;
     |                                           |&lt;br /&gt;
     | 1. Load client cert/key                   |&lt;br /&gt;
     | 2. Generate ephemeral keypair             |&lt;br /&gt;
     | 3. Build OAP_HDR (id, ts, crt, eph)       |&lt;br /&gt;
     | 4. Sign header with client key            |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |-------- FLOW_REQ (OAP_HDR) -------------&amp;gt; |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |                                           | 5. Load server cert/key&lt;br /&gt;
     |                                           | 6. Verify client cert against CA&lt;br /&gt;
     |                                           | 7. Verify client signature&lt;br /&gt;
     |                                           | 8. Generate ephemeral keypair&lt;br /&gt;
     |                                           | 9. Derive symmetric key (ECDHE)&lt;br /&gt;
     |                                           | 10. Build response OAP_HDR&lt;br /&gt;
     |                                           | 11. Sign with server key&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |&amp;lt;------- FLOW_REPLY (OAP_HDR) ------------ |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     | 12. Verify server cert against CA         |&lt;br /&gt;
     | 13. Verify server signature               |&lt;br /&gt;
     | 14. Derive symmetric key (ECDHE)          |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |===========================================|&lt;br /&gt;
     |         Encrypted data channel            |&lt;br /&gt;
     |===========================================|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Tutorial Directory:&#039;&#039;&#039; This tutorial will execute in &amp;lt;code&amp;gt;/tmp/o7s-tut06/&amp;lt;/code&amp;gt;. All configuration files, generated certificates, logs, and packet captures will be stored in this directory.&lt;br /&gt;
&lt;br /&gt;
We create a complete PKI (Public Key Infrastructure):&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Root CA&#039;&#039;&#039; (&amp;lt;code&amp;gt;ca.tut.o7s&amp;lt;/code&amp;gt;): Self-signed trust anchor&lt;br /&gt;
* &#039;&#039;&#039;Intermediate CA&#039;&#039;&#039; (&amp;lt;code&amp;gt;sign.tut.o7s&amp;lt;/code&amp;gt;): Signed by root with pathlen:0 constraint&lt;br /&gt;
* &#039;&#039;&#039;Server Certificate&#039;&#039;&#039; (&amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;): Signed by intermediate CA&lt;br /&gt;
&lt;br /&gt;
This tutorial uses ECDSA P-384 with SHA-384 hashing.&lt;br /&gt;
&lt;br /&gt;
== Setting Up the Tutorial ==&lt;br /&gt;
&lt;br /&gt;
To properly understand and debug the authenticated flows, this tutorial uses a debug build of Ouroboros with OAP protocol debugging enabled.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /path/to/ouroboros&lt;br /&gt;
mkdir build &amp;amp;&amp;amp; cd build&lt;br /&gt;
cmake -DCMAKE_BUILD_TYPE=Debug -DDEBUG_PROTO_OAP=ON ..&lt;br /&gt;
make -j$(nproc)&lt;br /&gt;
sudo make install&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When built with these options, the IRMd will output detailed OAP protocol information.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Files ===&lt;br /&gt;
&lt;br /&gt;
The following three files should be created in the tutorial directory (&amp;lt;code&amp;gt;/tmp/o7s-tut06/&amp;lt;/code&amp;gt;) before starting the tutorial:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;tut06.conf&#039;&#039;&#039; - IRMd configuration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
# Ouroboros Tutorial 06 - Authenticated Flows Configuration&lt;br /&gt;
# Uses system-installed certificates at /etc/ouroboros/security/&lt;br /&gt;
&lt;br /&gt;
[name.&amp;quot;sec.oping.tut.o7s&amp;quot;]&lt;br /&gt;
prog=[&amp;quot;/usr/bin/oping&amp;quot;]&lt;br /&gt;
args=[&amp;quot;--listen&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
[eth-dix.eth-dix-lo]&lt;br /&gt;
bootstrap=&amp;quot;eth-dix-network&amp;quot;&lt;br /&gt;
dev=&amp;quot;lo&amp;quot;&lt;br /&gt;
reg=[&amp;quot;sec.oping.tut.o7s&amp;quot;]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ca.tut.o7s.cnf&#039;&#039;&#039; - OpenSSL configuration for PKI generation&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
# Unified OpenSSL Configuration for Ouroboros Tutorial 06&lt;br /&gt;
# Named CA sections: CA_root (signs intermediate), CA_intermediate (signs server)&lt;br /&gt;
# Usage: openssl ca -name CA_root -config ca.tut.o7s.cnf ...&lt;br /&gt;
&lt;br /&gt;
[ req ]&lt;br /&gt;
default_bits       = 384&lt;br /&gt;
default_keyfile    = private/key.pem&lt;br /&gt;
distinguished_name = req_distinguished_name&lt;br /&gt;
string_mask        = utf8only&lt;br /&gt;
default_md         = sha384&lt;br /&gt;
x509_extensions    = v3_ca&lt;br /&gt;
&lt;br /&gt;
[ req_distinguished_name ]&lt;br /&gt;
countryName                 = Country Name (2 letter code)&lt;br /&gt;
stateOrProvinceName         = State or Province Name&lt;br /&gt;
localityName                = Locality Name&lt;br /&gt;
organizationName            = Organization Name&lt;br /&gt;
commonName                  = Common Name&lt;br /&gt;
&lt;br /&gt;
countryName_default         = BE&lt;br /&gt;
stateOrProvinceName_default = OVL&lt;br /&gt;
localityName_default        = Ghent&lt;br /&gt;
organizationName_default    = o7s&lt;br /&gt;
&lt;br /&gt;
[ ca ]&lt;br /&gt;
default_ca = CA_root&lt;br /&gt;
&lt;br /&gt;
[ CA_root ]&lt;br /&gt;
dir             = ./pki/root&lt;br /&gt;
database        = $dir/index.txt&lt;br /&gt;
serial          = $dir/serial&lt;br /&gt;
new_certs_dir   = $dir/certs&lt;br /&gt;
certificate     = $dir/certs/ca.tut.o7s.crt.pem&lt;br /&gt;
private_key     = $dir/private/ca.tut.o7s.key.pem&lt;br /&gt;
default_days    = 3650&lt;br /&gt;
default_md      = sha384&lt;br /&gt;
policy          = policy_loose&lt;br /&gt;
&lt;br /&gt;
[ CA_intermediate ]&lt;br /&gt;
dir             = ./pki/sign&lt;br /&gt;
database        = $dir/index.txt&lt;br /&gt;
serial          = $dir/serial&lt;br /&gt;
new_certs_dir   = $dir/certs&lt;br /&gt;
certificate     = $dir/certs/sign.tut.o7s.crt.pem&lt;br /&gt;
private_key     = $dir/private/sign.tut.o7s.key.pem&lt;br /&gt;
default_days    = 365&lt;br /&gt;
default_md      = sha384&lt;br /&gt;
policy          = policy_loose&lt;br /&gt;
&lt;br /&gt;
[ policy_loose ]&lt;br /&gt;
commonName = supplied&lt;br /&gt;
&lt;br /&gt;
[ v3_ca ]&lt;br /&gt;
subjectKeyIdentifier = hash&lt;br /&gt;
authorityKeyIdentifier = keyid:always,issuer&lt;br /&gt;
basicConstraints = critical, CA:true&lt;br /&gt;
keyUsage = critical, digitalSignature, cRLSign, keyCertSign&lt;br /&gt;
&lt;br /&gt;
[ v3_intermediate_ca ]&lt;br /&gt;
subjectKeyIdentifier = hash&lt;br /&gt;
authorityKeyIdentifier = keyid:always,issuer&lt;br /&gt;
basicConstraints = critical, CA:true, pathlen:0&lt;br /&gt;
keyUsage = critical, digitalSignature, cRLSign, keyCertSign&lt;br /&gt;
&lt;br /&gt;
[ server_cert ]&lt;br /&gt;
basicConstraints = CA:FALSE&lt;br /&gt;
subjectKeyIdentifier = hash&lt;br /&gt;
authorityKeyIdentifier = keyid,issuer:always&lt;br /&gt;
keyUsage = critical, digitalSignature, keyEncipherment&lt;br /&gt;
extendedKeyUsage = serverAuth&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;gen-pki.sh&#039;&#039;&#039; - PKI generation script&lt;br /&gt;
&lt;br /&gt;
This script will:&lt;br /&gt;
1. Create the directory structure&lt;br /&gt;
2. Generate the root CA key and certificate&lt;br /&gt;
3. Generate the intermediate CA key and CSR&lt;br /&gt;
4. Sign the intermediate CA certificate&lt;br /&gt;
5. Generate the server certificate key and CSR&lt;br /&gt;
6. Sign the server certificate&lt;br /&gt;
7. Verify the complete certificate chain&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
# Ouroboros Tutorial 06 - PKI Generation Script (Simplified)&lt;br /&gt;
# Generates: Root CA, Intermediate CA, and Server Certificate&lt;br /&gt;
&lt;br /&gt;
set -e&lt;br /&gt;
&lt;br /&gt;
if [ ! -f ca.tut.o7s.cnf ]; then&lt;br /&gt;
    echo &amp;quot;ERROR: ca.tut.o7s.cnf not found&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
mkdir -p pki/{root,sign,server}/{certs,private,csr}&lt;br /&gt;
&lt;br /&gt;
# Root CA&lt;br /&gt;
openssl ecparam -genkey -name secp384r1 -out pki/root/private/ca.tut.o7s.key.pem 2&amp;gt;/dev/null&lt;br /&gt;
openssl req -new -x509 -sha384 -days 7300 \&lt;br /&gt;
    -config ca.tut.o7s.cnf \&lt;br /&gt;
    -key pki/root/private/ca.tut.o7s.key.pem \&lt;br /&gt;
    -out pki/root/certs/ca.tut.o7s.crt.pem \&lt;br /&gt;
    -subj &amp;quot;/C=BE/ST=OVL/L=Ghent/O=o7s/CN=ca.tut.o7s&amp;quot; 2&amp;gt;/dev/null&lt;br /&gt;
&lt;br /&gt;
# Intermediate CA&lt;br /&gt;
openssl ecparam -genkey -name secp384r1 -out pki/sign/private/sign.tut.o7s.key.pem 2&amp;gt;/dev/null&lt;br /&gt;
openssl req -new -sha384 \&lt;br /&gt;
    -config ca.tut.o7s.cnf \&lt;br /&gt;
    -key pki/sign/private/sign.tut.o7s.key.pem \&lt;br /&gt;
    -out pki/sign/csr/sign.tut.o7s.csr \&lt;br /&gt;
    -subj &amp;quot;/C=BE/ST=OVL/L=Ghent/O=o7s/CN=sign.tut.o7s&amp;quot; 2&amp;gt;/dev/null&lt;br /&gt;
&lt;br /&gt;
touch pki/root/index.txt&lt;br /&gt;
echo 1000 &amp;gt; pki/root/serial&lt;br /&gt;
&lt;br /&gt;
openssl ca -name CA_root -config ca.tut.o7s.cnf \&lt;br /&gt;
    -extensions v3_intermediate_ca -days 3650 -md sha384 -batch \&lt;br /&gt;
    -in pki/sign/csr/sign.tut.o7s.csr \&lt;br /&gt;
    -out pki/sign/certs/sign.tut.o7s.crt.pem 2&amp;gt;&amp;amp;1 | grep -E &amp;quot;Signature ok|Database updated&amp;quot; || true&lt;br /&gt;
&lt;br /&gt;
# Server Certificate&lt;br /&gt;
openssl ecparam -genkey -name secp384r1 -out pki/server/private/sec.oping.tut.o7s.key.pem 2&amp;gt;/dev/null&lt;br /&gt;
openssl req -new -sha384 \&lt;br /&gt;
    -config ca.tut.o7s.cnf \&lt;br /&gt;
    -key pki/server/private/sec.oping.tut.o7s.key.pem \&lt;br /&gt;
    -out pki/server/csr/sec.oping.tut.o7s.csr \&lt;br /&gt;
    -subj &amp;quot;/C=BE/ST=OVL/L=Ghent/O=o7s/CN=sec.oping.tut.o7s&amp;quot; 2&amp;gt;/dev/null&lt;br /&gt;
&lt;br /&gt;
touch pki/sign/index.txt&lt;br /&gt;
echo 1000 &amp;gt; pki/sign/serial&lt;br /&gt;
&lt;br /&gt;
openssl ca -name CA_intermediate -config ca.tut.o7s.cnf \&lt;br /&gt;
    -extensions server_cert -days 365 -md sha384 -batch \&lt;br /&gt;
    -in pki/server/csr/sec.oping.tut.o7s.csr \&lt;br /&gt;
    -out pki/server/certs/sec.oping.tut.o7s.crt.pem 2&amp;gt;&amp;amp;1 | grep -E &amp;quot;Signature ok|Database updated&amp;quot; || true&lt;br /&gt;
&lt;br /&gt;
# Verify chain&lt;br /&gt;
openssl verify -CAfile pki/root/certs/ca.tut.o7s.crt.pem \&lt;br /&gt;
    -untrusted pki/sign/certs/sign.tut.o7s.crt.pem \&lt;br /&gt;
    pki/server/certs/sec.oping.tut.o7s.crt.pem &amp;gt; /dev/null 2&amp;gt;&amp;amp;1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;PKI generation complete.&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Part 1: Running the Tutorial - Single Session with 4 Tests ==&lt;br /&gt;
&lt;br /&gt;
This section demonstrates a single continuous session with one IRMd and tcpdump instance. The configuration file (&amp;lt;code&amp;gt;tut06.conf&amp;lt;/code&amp;gt;) includes autostart for oping, so the server is ready immediately when IRMd starts.&lt;br /&gt;
&lt;br /&gt;
First install the &#039;&#039;&#039;CA and Intermediate CA only&#039;&#039;&#039; to the system security directories. The server certificate will be installed later during Test 3 (authentication test):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo mkdir -p /etc/ouroboros/security/{cacert,untrusted,server/sec.oping.tut.o7s,client/sec.oping.tut.o7s}&lt;br /&gt;
&lt;br /&gt;
# Run the PKI generation script&lt;br /&gt;
cd /tmp/o7s-tut06&lt;br /&gt;
sudo chmod +x gen-pki.sh&lt;br /&gt;
sudo ./gen-pki.sh&lt;br /&gt;
&lt;br /&gt;
# Install Root CA (trust anchor)&lt;br /&gt;
sudo cp pki/root/certs/ca.tut.o7s.crt.pem /etc/ouroboros/security/cacert/&lt;br /&gt;
&lt;br /&gt;
# Install Intermediate CA (for certificate chain validation)&lt;br /&gt;
sudo cp pki/sign/certs/sign.tut.o7s.crt.pem /etc/ouroboros/security/untrusted/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Running the Tutorial (3 Terminals) ===&lt;br /&gt;
&lt;br /&gt;
In this tutorial, we run a single IRMd session with a concurrent tcpdump instance to capture it. We then run four oping client tests while the IRMd/tcpdump sessions are going, modifying the security configuration between tests. After the tests are complete, we can will down the IRMd and tcpdump sessions with Ctrl-C.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Terminal 1: Start tcpdump to capture all packets (runs continuously)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo tcpdump -i lo -n -A -v -U -w /tmp/o7s-tut06/tut06.pcap &amp;quot;ether proto 0xa000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Terminal 2: Start IRMd with debug output (runs continuously)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /tmp/o7s-tut06&lt;br /&gt;
sudo irmd --config tut06.conf --stdout 2&amp;gt;&amp;amp;1 | tee /tmp/o7s-tut06/irmd.log&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Terminal 3: Run the tests&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Test 1: No Authentication, No Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Verify directories are empty&lt;br /&gt;
sudo ls -la /etc/ouroboros/security/client/sec.oping.tut.o7s/*&lt;br /&gt;
sudo ls -la /etc/ouroboros/security/server/sec.oping.tut.o7s/*&lt;br /&gt;
&lt;br /&gt;
# Run first ping test&lt;br /&gt;
echo &amp;quot;=== Test 1: No Authentication, No Encryption ===&amp;quot;&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 1: Client initiates plaintext flow allocation&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [60e824383b3fbd6a @ 2026-02-14 14:08:56 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Cipher: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   KDF: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 1: Server accepts and completes handshake&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [60e824383b3fbd6a @ 2026-02-14 14:08:57 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Cipher: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   KDF: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [48 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; All OAP fields are &amp;lt;code&amp;gt;&amp;lt;none&amp;gt;&amp;lt;/code&amp;gt; because no security is configured (except for the request hash in the response). Flow succeeds with plaintext communication.&lt;br /&gt;
&lt;br /&gt;
==== Test 2: No Authentication, With Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Enable encryption for client only&lt;br /&gt;
sudo touch /etc/ouroboros/security/client/sec.oping.tut.o7s/enc.conf&lt;br /&gt;
&lt;br /&gt;
# Run second ping test&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 2: Client initiates flow with encryption enabled&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(II): Encryption enabled for sec.oping.tut.o7s.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Generated ephemeral prime256v1 keys (91 bytes).&lt;br /&gt;
irmd/oap(PP): OAP_HDR [80fd6f9509a996b0 @ 2026-02-14 14:09:38 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: sha256&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 2: Server receives and responds with ephemeral key&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] No crt provided.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Selected client cipher aes-256-gcm.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Selected client KDF sha256.&lt;br /&gt;
irmd/oap(II): [80fd6f9509a996b0] No key exchange.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Generated prime256v1 ephemeral keys.&lt;br /&gt;
irmd/oap(PP): OAP_HDR [80fd6f9509a996b0 @ 2026-02-14 14:09:38 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes] [Server encaps]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [32 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; Both client and server generate ephemeral keys (91 bytes each) for encryption. No certificates because authentication is not required. Encryption and authentication are independent.&lt;br /&gt;
&lt;br /&gt;
==== Test 3: With Authentication, With Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Install server certificates and keys&lt;br /&gt;
sudo cp /tmp/o7s-tut06/pki/server/certs/sec.oping.tut.o7s.crt.pem \&lt;br /&gt;
        /etc/ouroboros/security/server/sec.oping.tut.o7s/crt.pem&lt;br /&gt;
sudo cp /tmp/o7s-tut06/pki/server/private/sec.oping.tut.o7s.key.pem \&lt;br /&gt;
        /etc/ouroboros/security/server/sec.oping.tut.o7s/key.pem&lt;br /&gt;
&lt;br /&gt;
# enc.conf is still in place from Test 2&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 3: Client initiates flow with encryption and server has certificate&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [c904b18b563dc1b0 @ 2026-02-14 14:09:47 (UTC) ] &amp;lt;--&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes] [Server encaps]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: sha256&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 3: Server responds with certificate + ephemeral key + signature&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [c904b18b563dc1b0 @ 2026-02-14 14:09:47 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: [560 bytes]&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes] [Server encaps]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [32 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: [103 bytes]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 3: Client verifies certificate and authenticates&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [c904b18b563dc1b0] Loaded peer crt.&lt;br /&gt;
irmd/oap(DB): [c904b18b563dc1b0] Got public key from crt.&lt;br /&gt;
irmd/oap(DB): [c904b18b563dc1b0] Successfully verified peer crt.&lt;br /&gt;
irmd/oap(DB): [c904b18b563dc1b0] Successfully authenticated peer.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; Full OAP handshake with certificate (560 bytes) + ephemeral keys (91 bytes) + signature (103 bytes). Client verifies server&#039;s certificate against CA store and confirms authentication success.&lt;br /&gt;
&lt;br /&gt;
==== Test 4: With Authentication, No Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Remove encryption config but keep certificates&lt;br /&gt;
sudo rm -f /etc/ouroboros/security/client/sec.oping.tut.o7s/enc.conf&lt;br /&gt;
&lt;br /&gt;
# Run fourth ping test&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 4: Client initiates plaintext flow (no encryption file)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [03e47baa966a5823 @ 2026-02-14 14:09:57 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Cipher: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   KDF: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 4: Server responds with certificate + signature (no ephemeral key)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [03e47baa966a5823 @ 2026-02-14 14:09:57 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: [560 bytes]&lt;br /&gt;
irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Cipher: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   KDF: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [48 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: [103 bytes]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 4: Client verifies certificate and authenticates&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [03e47baa966a5823] Loaded peer crt.&lt;br /&gt;
irmd/oap(DB): [03e47baa966a5823] Got public key from crt.&lt;br /&gt;
irmd/oap(DB): [03e47baa966a5823] Successfully verified peer crt.&lt;br /&gt;
irmd/oap(DB): [03e47baa966a5823] Successfully authenticated peer.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; Server sends certificate + signature for authentication, but NO ephemeral keys (plaintext data). Data exchanged without encryption even though authenticated. Demonstrates that authentication and encryption are independent mechanisms.&lt;br /&gt;
&lt;br /&gt;
=== Stop the IRMd and tcpdump, clean up the tutorial files ===&lt;br /&gt;
&lt;br /&gt;
Once all tests complete:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Stop IRMd in Terminal 2 (Ctrl+C)&lt;br /&gt;
# Stop tcpdump in Terminal 1 (Ctrl+C)&lt;br /&gt;
&lt;br /&gt;
# Clean up tutorial security files from system&lt;br /&gt;
sudo rm -f /etc/ouroboros/security/cacert/ca.tut.o7s.crt.pem&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Part 2: PCAP Trace Analysis ==&lt;br /&gt;
&lt;br /&gt;
After the tutorial, we now explain the trace in the tcpdump pcap file.&lt;br /&gt;
&lt;br /&gt;
=== Protocol Overview ===&lt;br /&gt;
&lt;br /&gt;
This section summarizes the four protocols that work together in the captured packet flow.&lt;br /&gt;
&lt;br /&gt;
==== Ethernet DIX Frame with EID Header ====&lt;br /&gt;
&lt;br /&gt;
Ouroboros extends the DIX frame with a flow identifier (EID - Endpoint Identifier) and length field.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Octets !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Destination MAC || 0-5 || 6 bytes || Hardware address of destination&lt;br /&gt;
|-&lt;br /&gt;
| Source MAC || 6-11 || 6 bytes || Hardware address of source&lt;br /&gt;
|-&lt;br /&gt;
| EtherType || 12-13 || 2 bytes || Protocol identifier (0xA000 for Ouroboros)&lt;br /&gt;
|-&lt;br /&gt;
| EID || 14-15 || 2 bytes || Destination Endpoint Identifier&lt;br /&gt;
|-&lt;br /&gt;
| Length || 16-17 || 2 bytes || Payload length (needed because of runt frame padding)&lt;br /&gt;
|-&lt;br /&gt;
| Payload || 18+ || Variable || Frame data (up to MTU size)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Ethernet Flow Allocator - Management Protocol ====&lt;br /&gt;
&lt;br /&gt;
The Ethernet DIX management protocol handles flow allocation, setup, and teardown. All management frames use destination EID &amp;lt;code&amp;gt;0x0000&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Management Frame Types:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Code !! Type !! Direction !! Service Hash !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x00&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FLOW_REQ&amp;lt;/code&amp;gt; || Client → Server || ✓ Included || Request new flow allocation&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x01&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FLOW_REPLY&amp;lt;/code&amp;gt; || Server → Client || – Not included || Respond to flow request (success/failure)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x02&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NAME_QUERY_REQ&amp;lt;/code&amp;gt; || Client → Server || ✓ Included || Query if a remote name is reachable&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x03&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NAME_QUERY_REPLY&amp;lt;/code&amp;gt; || Server → Client || ✓ Included || Response to name query&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The 32-byte service hash (SHA3-256) is appended after the management protocol header for NAME_QUERY_* and FLOW_REQ messages to identify which service is being queried or allocated. FLOW_REPLY does not include the service hash; the endpoints are already identified by the allocated EIDs (SEID/DEID) and the flow allocation ID in the OAP header (see below).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frame Layout by Field:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| SEID || 0-1 || 2 bytes || Source Endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| DEID || 2-3 || 2 bytes || Destination Endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| Loss || 4-7 || 4 bytes || Acceptable packet loss (ppm)&lt;br /&gt;
|-&lt;br /&gt;
| Bandwidth || 8-15 || 8 bytes || Required bandwidth (bps)&lt;br /&gt;
|-&lt;br /&gt;
| BER || 16-19 || 4 bytes || Bit error rate (ppm)&lt;br /&gt;
|-&lt;br /&gt;
| Max Gap || 20-23 || 4 bytes || Maximum consecutive lost packets&lt;br /&gt;
|-&lt;br /&gt;
| Delay || 24-27 || 4 bytes || Maximum latency (ms)&lt;br /&gt;
|-&lt;br /&gt;
| Timeout || 28-31 || 4 bytes || Flow idle timeout (seconds)&lt;br /&gt;
|-&lt;br /&gt;
| Response || 32-35 || 4 bytes || Response code (0=success, negative=error)&lt;br /&gt;
|-&lt;br /&gt;
| In-Order || 36 || 1 byte || In-order delivery requirement (boolean)&lt;br /&gt;
|-&lt;br /&gt;
| Code || 37 || 1 byte || Message type (FLOW_REQ, FLOW_REPLY, etc.)&lt;br /&gt;
|-&lt;br /&gt;
| Availability || 38 || 1 byte || Availability status&lt;br /&gt;
|-&lt;br /&gt;
| Service hash || 39-61 || 32 bytes || SHA3-256 hash (optional, see above)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Ouroboros Flow Allocation Protocol (OAP) ====&lt;br /&gt;
&lt;br /&gt;
The Ouroboros Application Protocol (OAP) is the flow allocation and authentication protocol. It carries flow negotiation requests, responses, and authentication credentials. OAP frames are encapsulated as data payload over the management protocol.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frame Layout by Field:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| ID || 0-15 || 16 bytes || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| Timestamp || 16-23 || 8 bytes || Creation timestamp (UTC, seconds and microseconds)&lt;br /&gt;
|-&lt;br /&gt;
| Crt Length || 24-25 || 2 bytes || Certificate length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Certificate || 26+ || Variable || X.509 certificate (DER encoded)&lt;br /&gt;
|-&lt;br /&gt;
| Eph Length || Variable || 2 bytes || Ephemeral public key length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Ephemeral Key || Variable || Variable || ECDHE public key (DER/raw encoded)&lt;br /&gt;
|-&lt;br /&gt;
| Data Length || Variable || 2 bytes || Application data length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Data || Variable || Variable || Piggybacked application-layer data&lt;br /&gt;
|-&lt;br /&gt;
| Sig Length || Variable || 2 bytes || Signature length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Signature || Variable || Variable || Digital signature (ECDSA, DER encoded)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Oping Application Protocol ====&lt;br /&gt;
&lt;br /&gt;
The Ouroboros Ping (oping) application is a simple echo/reply protocol used to measure round-trip time and validate connectivity between applications. It implements a request/reply pattern similar to ICMP ping.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frame Layout by Field:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Type || 0-3 || 4 bytes || Message type (ECHO_REQUEST=0 or ECHO_REPLY=1)&lt;br /&gt;
|-&lt;br /&gt;
| ID || 4-7 || 4 bytes || Sequence number / message identifier&lt;br /&gt;
|-&lt;br /&gt;
| Timestamp (seconds) || 8-15 || 8 bytes || Seconds when message was sent (CLOCK_REALTIME)&lt;br /&gt;
|-&lt;br /&gt;
| Timestamp (nanoseconds) || 16-23 || 8 bytes || Nanoseconds component of timestamp&lt;br /&gt;
|-&lt;br /&gt;
| Payload || 24+ || Variable || Application data (configurable size, default 64 bytes)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Field Definitions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Type&#039;&#039;&#039; (4 bytes): Message type selector&lt;br /&gt;
** &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt; (ECHO_REQUEST): Client-to-server ping request&lt;br /&gt;
** &amp;lt;code&amp;gt;0x00000001&amp;lt;/code&amp;gt; (ECHO_REPLY): Server-to-client response&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;ID&#039;&#039;&#039; (4 bytes): Sequence number for matching requests with replies. Incremented for each ping sent.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; (8 bytes): Network-byte-order 64-bit seconds component from when the ping was sent (CLOCK_REALTIME).&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; (8 bytes): Network-byte-order 64-bit nanoseconds component (0-999999999) for high-resolution timing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Payload&#039;&#039;&#039; (Variable): Application data echoed back by the server. Size is configurable (default 64 bytes, maximum 1500 bytes).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Usage:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Client sends ECHO_REQUEST with current timestamp&lt;br /&gt;
* Server receives request and echoes back as ECHO_REPLY with the same ID and timestamps&lt;br /&gt;
* Client calculates RTT by comparing reception time with original timestamps&lt;br /&gt;
* Out-of-order detection by tracking sequence numbers (ID field)&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 1 - No authentication/encryption ===&lt;br /&gt;
&lt;br /&gt;
==== Packet 1: NAME_QUERY_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends a NAME_QUERY_REQ message to discover if the service &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt; is available. This is a broadcast discovery query sent because the service is not yet known for the flow allocation process.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:48.165639 00:00:00:00:00:00 &amp;gt; ff:ff:ff:ff:ff:ff, ethertype Unknown (0xa000), length 89:&lt;br /&gt;
        0x0000:  0000 0047 0000 0000 0000 0000 0000 0000  ...G............&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0002 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a              ..f.i.._...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0047&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 71 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0000 || Source Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || Response code (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x03 || Message Type: NAME_QUERY_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Packet 2: NAME_QUERY_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds to the NAME_QUERY_REQ by sending a NAME_QUERY_REPLY for the service hash.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:48.166073 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 89:&lt;br /&gt;
        0x0000:  0000 0047 0000 0000 0000 0000 0000 0000  ...G............&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0003 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a              ..f.i.._...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0047&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 71 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0000 || Source Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || Response code&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x03 || Message Type: NAME_QUERY_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt; (echoed back)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Packet 3: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates a flow allocation request (FLOW_REQ) with minimal OAP headers since no authentication or encryption is being used.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:48.167222 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 121:&lt;br /&gt;
        0x0000:  0000 0067 0040 0000 0000 0001 0000 0000  ...g.@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a51 8a56 ff6f  ..f.i.._...Q.V.o&lt;br /&gt;
        0x0050:  5ba6 9d03 7da1 cfc3 0f30 7718 86a8 e103  [...}....0w.....&lt;br /&gt;
        0x0060:  3e52 3300 0000 0000 0000 00              &amp;gt;R3........&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0067&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 103 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID (allocated flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || -- || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0001 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x00 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The OAP payload starts at offset 0x4b (after management protocol + service hash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;51 8a56 ff6f 5ba6 9d03 7da1 cfc3 0f30 77&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e103 3e52 33&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp (seconds + microseconds)&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || No ephemeral key&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0x68 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x69-0x6a || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0040 (first allocated flow ID for this session)&lt;br /&gt;
* Service hash is carried in management protocol payload (32 bytes)&lt;br /&gt;
* OAP header is minimal: only ID and timestamp, no optional fields&lt;br /&gt;
* No certificate, ephemeral key, data, or signature in this initial request&lt;br /&gt;
* Client sends minimal OAP headers with no authentication or encryption setup at allocation time&lt;br /&gt;
&lt;br /&gt;
==== Packet 4: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds to FLOW_REQ by sending FLOW_REPLY with a new DEID (destination endpoint ID 0x0041) to establish the allocated flow for data transfer.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:49.178732 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 89:&lt;br /&gt;
        0x0000:  0000 0047 0041 0040 0000 0000 0000 0000  ...G.A.@........&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 0051 8a56 ff6f  ...........Q.V.o&lt;br /&gt;
        0x0030:  5ba6 9d03 7da1 cfc3 0f30 7718 86a8 e13f  [...}....0w....?&lt;br /&gt;
        0x0040:  a347 3800 0000 0000 0000 00              .G8........&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0047&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 71 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0041 || Source Endpoint ID (allocated server-side flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client&#039;s flow ID from FLOW_REQ)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0|| Response code (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The OAP payload starts at offset 0x2b (no service hash in FLOW_REPLY):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;51 8a56 ff6f 5ba6 9d03 7da1 cfc3 0f30 77&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Echo of client&#039;s flow ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;18 86a8 e13f a347 38&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Echoed timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x46 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || No ephemeral key&lt;br /&gt;
|-&lt;br /&gt;
| 0x47-0x48 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x49-0x4a || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID 0x0041 is the newly allocated server-side flow endpoint&lt;br /&gt;
* DEID 0x0040 reflects the client&#039;s flow ID, creating a bidirectional mapping&lt;br /&gt;
* No service hash included (FLOW_REPLY only needs the EIDs to identify the flow)&lt;br /&gt;
* OAP echoes the client&#039;s ID and timestamp, confirming the flow allocation&lt;br /&gt;
* Response code 0x00000000 indicates success&lt;br /&gt;
* Both client and server now have their respective flow IDs (0x0040 and 0x0041) for data transfer&lt;br /&gt;
* Response code 0x00000000 indicates success&lt;br /&gt;
&lt;br /&gt;
==== Packet 5: ECHO_REQUEST - Plaintext Data ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends an oping ECHO_REQUEST packet to the server using the allocated flow.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:50.180824 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0041 0040 0000 0000 0000 0000 7377 0000  .A.@........sw..&lt;br /&gt;
        0x0010:  0000 0000 b03e e007 0000 0000 0000 0000  .....&amp;gt;..........&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0041 || Source Endpoint ID (server → client)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Oping payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REQUEST Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The oping payload starts at offset 0x04:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000000 || Message type: ECHO_REQUEST&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Sequence number (first ping)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;7377 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || 0x0000000000003773 || Seconds component&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;b03e e007 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || 0x0000000007e03eb0 || Nanoseconds component&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 0000 ... 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || All zeros || Echo data (default 64 bytes total - 24 byte header = 40 bytes data)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID 0x0041 shows traffic from server-side flow ID&lt;br /&gt;
* This is the first ping request (ID = 0x00000000)&lt;br /&gt;
* Timestamp captures when the ping was sent (seconds in network order)&lt;br /&gt;
* Default oping payload is 64 bytes total; 24 bytes header + 40 bytes data&lt;br /&gt;
&lt;br /&gt;
==== Packet 6: ECHO_REPLY - Plaintext Data ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server receives the ECHO_REQUEST and immediately sends back an ECHO_REPLY with the same ID and timestamps, echoing the client&#039;s message.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:50.181496 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0040 0040 0000 0001 0000 0000 7377 0000  .@.@........sw..&lt;br /&gt;
        0x0010:  0000 0000 b03e e007 0000 0000 0000 0000  .....&amp;gt;..........&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client ← server)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Oping payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REPLY Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The oping payload starts at offset 0x04:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0001&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000001 || Message type: ECHO_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Echo of request sequence number&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;7377 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || 0x0000000000003773 || Echo of original seconds&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;b03e e007 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || 0x0000000007e03eb0 || Echo of original nanoseconds&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 0000 ... 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || All zeros || Echo data (unchanged from request)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID 0x0040 shows traffic from client-side flow ID receiving the reply&lt;br /&gt;
* Type field changed from 0x00000000 (REQUEST) to 0x00000001 (REPLY)&lt;br /&gt;
* ID, timestamps, and payload data are identical to the request (echoed back)&lt;br /&gt;
* Round-trip time can be calculated by comparing current time with echoed timestamp&lt;br /&gt;
* Ping succeeded on first attempt with minimal latency (~1 millisecond between timestamps)&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 2 - No authentication, with encryption ===&lt;br /&gt;
&lt;br /&gt;
==== Packet 7: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates flow allocation with encryption enabled. This FLOW_REQ carries an OAP header with an ephemeral ECDHE P-384 public key (91 bytes) for encryption setup.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:53.808158 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 212:&lt;br /&gt;
        0x0000:  0000 00c2 0040 0000 0000 0001 0000 0000  .....@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8af1 766b 547c  ..f.i.._....vkT|&lt;br /&gt;
        0x0050:  fcb0 8fce 5d60 a01e e8be 0218 86a8 e253  ....]`.........S&lt;br /&gt;
        0x0060:  8b6c 9000 0000 5b30 5930 1306 072a 8648  .l....[0Y0...*.H&lt;br /&gt;
        0x0070:  ce3d 0201 0608 2a86 48ce 3d03 0107 0342  .=....*.H.=....B&lt;br /&gt;
        0x0080:  0004 c508 1c19 6106 b7e9 3074 57b9 bb16  ......a...0tW...&lt;br /&gt;
        0x0090:  6959 4a55 81f9 169b cc79 fe10 a882 41fe  iYJU.....y....A.&lt;br /&gt;
        0x00a0:  0697 c9b4 f8f0 5562 7fa2 c7a0 a020 1ac6  ......Ub........&lt;br /&gt;
        0x00b0:  939f 23ff b2fb 07a2 b747 aacc 474a 3dab  ..#......G..GJ=.&lt;br /&gt;
        0x00c0:  2598 0000 0000                           %.....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;00c2&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 194 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... d4c0&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Mixed || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x00 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload with Ephemeral Key&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;f1 766b 547c fcb0 8fce 5d60 a01e e8be 02&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e253 8b6c 90&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate in client request&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x005b (91) || Ephemeral key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0xc1 || &amp;lt;code&amp;gt;30 5930 ... 3dab 2598&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDHE P-384 || ECDHE P-384 public key (DER encoded)&lt;br /&gt;
|-&lt;br /&gt;
| 0xd3-0xd4 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0xd5-0xd6 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* Encryption enabled: ephemeral key present (91 bytes)&lt;br /&gt;
* Client sends no certificate, allowing anonymous encryption setup&lt;br /&gt;
* No signature (unsigned OAP)&lt;br /&gt;
* Ephemeral key is ECDHE P-384 for key exchange&lt;br /&gt;
&lt;br /&gt;
==== Packet 8: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server accepts the encrypted flow allocation request. FLOW_REPLY contains the server&#039;s ephemeral key but no certificate (since client didn&#039;t send one).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:53.810564 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 180:&lt;br /&gt;
        0x0000:  0000 00a2 0042 0040 0000 0000 0000 0000  .....B.@........&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 00f1 766b 547c  ............vkT|&lt;br /&gt;
        0x0030:  fcb0 8fce 5d60 a01e e8be 0218 86a8 e253  ....]`.........S&lt;br /&gt;
        0x0040:  b694 e800 0000 5b30 5930 1306 072a 8648  ......[0Y0...*.H&lt;br /&gt;
        0x0050:  ce3d 0201 0608 2a86 48ce 3d03 0107 0342  .=....*.H.=....B&lt;br /&gt;
        0x0060:  0004 5f3c 6929 cca2 024a ae9f 9aa1 dfc2  .._&amp;lt;i)...J......&lt;br /&gt;
        0x0070:  a493 3ff3 ff58 b054 74dc d2e2 47fc 7c5b  ..?..X.Tt...G.|[&lt;br /&gt;
        0x0080:  eff5 e129 72b4 de1e 7c09 bf8c fe38 5e8b  ...)r...|....8^.&lt;br /&gt;
        0x0090:  b22e 59ed 6eb9 dfda 369d 691e 6e2c 122c  ..Y.n...6.i.n,.,&lt;br /&gt;
        0x00a0:  9936 0000 0000                           .6....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;00a2&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 162 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0042&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0042 || Source Endpoint ID (allocated server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0x00000000 || Response code: 0 (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY with Ephemeral Key&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;f1 766b 547c fcb0 8fce 5d60 a01e e8be 02&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || Echo of client ID || Echoes client&#039;s flow ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;18 86a8 e253 b694 e8&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Echoed timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x46 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x005b (91) || Ephemeral key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x47-0xa1 || &amp;lt;code&amp;gt;30 5930...9936&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDHE P-384 || Server&#039;s ECDHE P-384 public key (DER encoded)&lt;br /&gt;
|-&lt;br /&gt;
| 0xd1-0xd2 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0xd3-0xd4 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID 0x0042 is the new server-side flow endpoint&lt;br /&gt;
* Both keys are now exchanged; client and server can derive shared secret&lt;br /&gt;
* No authentication (no certificates) but encryption is negotiated&lt;br /&gt;
* Response indicates successful allocation&lt;br /&gt;
&lt;br /&gt;
==== Packet 9: Encrypted ECHO_REQUEST ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends encrypted ping request after encryption keys are established. The payload is encrypted with the derived shared secret.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:54.815771 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0042 0060 a691 6d83 8446 cbeb ac95 c2eb  .B.`..m..F......&lt;br /&gt;
        0x0010:  4b42 e819 c67f 92c8 58d7 0641 d8a6 6e1f  KB......X..A..n.&lt;br /&gt;
        0x0020:  fc90 feed ef55 b791 4fbd a832 74bd 8bed  .....U..O..2t...&lt;br /&gt;
        0x0030:  249c 4cee 0fc0 cec6 2f1b aec1 2428 bdbd  $.L...../...$(..&lt;br /&gt;
        0x0040:  36b5 01b5 1257 004e 6ed6 7ecd f0c7 7d11  6....W.Nn.~...}.&lt;br /&gt;
        0x0050:  20ba e81b f43a 4de9 b141 1624 e1ba 0a84  .....:M..A.$....&lt;br /&gt;
        0x0060:  74b1 9a9a                                t...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0042&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0042 || Destination Endpoint ID (server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0060&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 96 bytes || Encrypted payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REQUEST (Encrypted)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x63 || &amp;lt;code&amp;gt;a691 6d83 8446 cbeb...74b1 9a9a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Encrypted Data&#039;&#039;&#039; || 96 || Ciphertext || All 96 bytes encrypted&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* All 96 bytes of oping data (type, ID, timestamps, payload) are encrypted&lt;br /&gt;
* No plaintext oping headers visible; entire packet is ciphertext&lt;br /&gt;
* Flow IDs (0x0042) identify which encryption context to use&lt;br /&gt;
* Ping still works with encryption transparently&lt;br /&gt;
&lt;br /&gt;
==== Packet 10: Encrypted ECHO_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server receives encrypted ping request, decrypts it, and sends encrypted ECHO_REPLY.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:54.819574 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0040 0060 c6ea 2222 5618 0268 b27e 9a91  .@.`..&amp;quot;&amp;quot;V..h.~..&lt;br /&gt;
        0x0010:  f124 1f8d bccc 478c 26fe 9b13 b3cb 5398  .$....G.&amp;amp;.....S.&lt;br /&gt;
        0x0020:  6869 3cdb 4928 510d 4de8 dc6a 3f3a 6a6d  hi&amp;lt;.I(Q.M..j?:jm&lt;br /&gt;
        0x0030:  6487 dcd8 c8cd 1a85 fba2 9ecd 3566 57d1  d...........5fW.&lt;br /&gt;
        0x0040:  1c94 ac35 518e 8509 873a 3a5e 04d9 8ee2  ...5Q....::^....&lt;br /&gt;
        0x0050:  9d74 2527 e425 5433 9d73 9ccd f56a 1f8d  .t%&#039;.%T3.s...j..&lt;br /&gt;
        0x0060:  f328 7237                                .(r7&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0060&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 96 bytes || Encrypted payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REPLY (Encrypted)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x63 || &amp;lt;code&amp;gt;c6ea 2222 5618 0268...f328 7237&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Encrypted Data&#039;&#039;&#039; || 96 || Ciphertext || All 96 bytes encrypted&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID 0x0040 shows reply going back to client-side flow&lt;br /&gt;
* Ciphertext is different from request (different plaintext: type field differs)&lt;br /&gt;
* Both encrypted packets are 96 bytes (same size as Packet 9)&lt;br /&gt;
* Client receives encrypted reply, decrypts it, verifies ID and timestamps match request&lt;br /&gt;
* Encryption is transparent at application layer: oping works exactly as with plaintext flows&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 3 - Authentication and encryption ===&lt;br /&gt;
&lt;br /&gt;
==== Packet 11: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates flow allocation request with encryption enabled. Sends ephemeral public key for ECDHE key exchange but no certificate (client is not authenticating in this tutorial). The management protocol now carries a valid allocated SEID (0x0040).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:58.827411 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 212:&lt;br /&gt;
        0x0000:  0000 00c2 0040 0000 0000 0001 0000 0000  .....@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a66 bb82 95fa  ..f.i.._...f....&lt;br /&gt;
        0x0050:  91a2 7bd3 bd60 1b3e 35f6 b918 86a8 e37e  ..{..`.&amp;gt;5......~&lt;br /&gt;
        0x0060:  c0d2 ad00 0000 5b30 5930 1306 072a 8648  ......[0Y0...*.H&lt;br /&gt;
        0x0070:  ce3d 0201 0608 2a86 48ce 3d03 0107 0342  .=....*.H.=....B&lt;br /&gt;
        0x0080:  0004 9dea c238 6732 4987 1cd4 7133 9614  .....8g2I...q3..&lt;br /&gt;
        0x0090:  9d04 4fde 3f68 42f1 54fb 7ef3 88d0 ffe6  ..O.?hB.T.~.....&lt;br /&gt;
        0x00a0:  7e01 432e 56c2 2d64 72c9 19fc b0cf 1eca  ~.C.V.-dr.......&lt;br /&gt;
        0x00b0:  689e 3536 771a 8041 726c 20e2 d9bb 3589  h.56w..Arl....5.&lt;br /&gt;
        0x00c0:  86e7 0000 0000                           ......&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;00c2&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 194 bytes || Total payload length (excluding DIX header)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID (client flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... d4c0&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Default values || QoS parameters&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || Response code (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x00 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ... 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload with Encryption Setup&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;66 bb82 95fa 91a2 7bd3 bd60 1b3e 35f6 b9&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier for Test 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e37e c0d2 ad&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || Client not authenticating&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 91 (0x005b) || Ephemeral public key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0xc1 || &amp;lt;code&amp;gt;30 5930 1306 ... 3589&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDP-384 public key || Client&#039;s ephemeral ECDHE public key for encryption negotiation&lt;br /&gt;
|-&lt;br /&gt;
| 0xc2-0xc3 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0xc4-0xc5 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0040 - Same as Test 2 (Encrypted) because this is the same client session reusing the same allocated ID from the previous test&lt;br /&gt;
* No Certificate - &amp;lt;code&amp;gt;crt_len = 0x0000&amp;lt;/code&amp;gt; because the client does not have authentication credentials; the server will authenticate instead&lt;br /&gt;
* Ephemeral Key Present - &amp;lt;code&amp;gt;eph_len = 0x005b (91)&amp;lt;/code&amp;gt; because encryption is enabled on the client&lt;br /&gt;
* No Signature - &amp;lt;code&amp;gt;sig_len = 0x0000&amp;lt;/code&amp;gt; because client is not signing (no certificate to sign with)&lt;br /&gt;
* FLOW_REQ Message Type - Code field is 0x00, and service hash is present because FLOW_REQ always includes the service hash&lt;br /&gt;
* Timestamp Consistency - Same OAP ID and timestamp structure as Test 2, but with additional security handshake&lt;br /&gt;
&lt;br /&gt;
==== Packet 12: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds to client&#039;s FLOW_REQ by sending FLOW_REPLY with its certificate for authentication, ephemeral public key for ECDHE encryption setup, and a digital signature proving ownership of the certificate. This is the full authentication response.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data (abbreviated):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:58.828806 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 843:&lt;br /&gt;
        0x0000:  0000 0339 0043 0040 0000 0000 0000 0000  ...9.C.@........&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 0066 bb82 95fa  ...........f....&lt;br /&gt;
        0x0030:  91a2 7bd3 bd60 1b3e 35f6 b918 86a8 e37e  ..{..`.&amp;gt;5......~&lt;br /&gt;
        0x0040:  d566 a002 2f30 8202 2b30 8201 b2a0 0302  .f../0..+0......&lt;br /&gt;
        (... certificate and signature bytes ...)&lt;br /&gt;
        0x0320:  ef11 c358 f5d0 5cd7 3906 adf1 8a2c 9b25  ...X..\.9....,.%&lt;br /&gt;
        0x0330:  dc78 6050 ab61 3a3f 81c0 254b d193 7827  .x`P.a:?..%K..x&#039;&lt;br /&gt;
        0x0340:  c0e9 38c7 e0d1 c517 d299 9992 07         ..8..........&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0339&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 825 bytes || Total payload length (excluding DIX header)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0043&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0043 || Source Endpoint ID (server-side allocated flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client&#039;s flow ID from FLOW_REQ)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Default values || QoS parameters&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0x00000000 || Response code: 0 (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY Payload with Full Authentication&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;66 bb82 95fa 91a2 7bd3 bd60 1b3e 35f6 b9&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || &#039;&#039;&#039;Same ID as client&#039;s FLOW_REQ&#039;&#039;&#039; (echoed back)&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;18 86a8 e37e d566 a0&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Server&#039;s timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;022f&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 559 (0x022f) || Server certificate length: 559 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x243 || &amp;lt;code&amp;gt;2f30 8202 2b ... 81c8 30&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Certificate&#039;&#039;&#039; || 559 || DER-encoded X.509 || Server&#039;s certificate (signed by intermediate CA)&lt;br /&gt;
|-&lt;br /&gt;
| 0x244-0x245 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 91 (0x005b) || Server&#039;s ephemeral public key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x246-0x2a0 || &amp;lt;code&amp;gt;30 5930 1306 ... 9936&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDP-384 public key || Server&#039;s ephemeral ECDHE public key&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a4-0x2a5 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a6-0x2a7 || &amp;lt;code&amp;gt;0068&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 104 (0x0068) || Digital signature length: 104 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a8-0x30f || &amp;lt;code&amp;gt;30 6602 3100 ... 07&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Signature&#039;&#039;&#039; || 104 || ECDSA signature (DER encoded) || Server&#039;s signature over OAP header proving certificate ownership&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0043 - New server-side endpoint ID allocated for this authenticated flow&lt;br /&gt;
* DEID is 0x0040 - Client&#039;s flow ID from the FLOW_REQ, establishing the bidirectional flow&lt;br /&gt;
* FLOW_REPLY Message Type - Code field is 0x01, &#039;&#039;&#039;no service hash&#039;&#039;&#039; (already identified in FLOW_REQ)&lt;br /&gt;
* Full Certificate - &amp;lt;code&amp;gt;crt_len = 0x022f (559)&amp;lt;/code&amp;gt; carrying server&#039;s complete X.509 certificate signed by intermediate CA&lt;br /&gt;
* Ephemeral Key Present - &amp;lt;code&amp;gt;eph_len = 0x005b (91)&amp;lt;/code&amp;gt; with server&#039;s ECDHE public key for encryption&lt;br /&gt;
* Signature Included - &amp;lt;code&amp;gt;sig_len = 0x0068 (104)&amp;lt;/code&amp;gt; containing ECDSA digital signature over the entire OAP header&lt;br /&gt;
* Same OAP ID - Server echoes back the exact ID from client&#039;s FLOW_REQ to confirm association (included in signature, binding response to this specific client request)&lt;br /&gt;
* Large Payload - Total of 825 bytes due to certificate (559) + ephemeral key (91) + signature (104) + overhead&lt;br /&gt;
* Authentication Complete - Client verifies: (1) certificate against CA store, (2) signature over entire response ensures authenticity and integrity, (3) echoed ID binds response to this specific request, (4) timestamp prevents replay attacks&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds with its certificate for authentication, ephemeral public key for ECDHE encryption, and a digital signature proving ownership of the certificate.&lt;br /&gt;
&lt;br /&gt;
==== Packet 13: Encrypted ECHO_REQUEST ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends encrypted ping request after authentication handshake. All application data is protected by encryption using the ephemeral keys established in Packets 11-12.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:59.836485 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0043 0060 3bed 0b48 1be1 6930 cf3d dee9  .C.`.;..H..i0.=..&lt;br /&gt;
        0x0010:  4fc9 774b 5d63 cc9b 5a34 6604 f9ac 1016  O.wK]c..Z4f.....&lt;br /&gt;
        0x0020:  1c6d c9ac f80e dc89 31c1 9634 1a4f b2c7  .m......1..4.O..&lt;br /&gt;
        0x0030:  4721 e402 8259 b0aa 8870 4566 33d1 9c18  G!...Y..  .pEf3...&lt;br /&gt;
        0x0040:  06da 50c3 8b75 86b0 f240 d109 840e a6cd  ..P..u...@......&lt;br /&gt;
        0x0050:  d115 77cb 5652 5bfb e6d5 0ca9 dbc3 d0b8  ..w.VR[.........&lt;br /&gt;
        0x0060:  0058 fd19                                .X..&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame Analysis:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Length !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Source EID || 0x00-0x01 || 2 bytes || &amp;lt;code&amp;gt;0x0043&amp;lt;/code&amp;gt; || Client flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| Destination EID || 0x02-0x03 || 2 bytes || &amp;lt;code&amp;gt;0x0060&amp;lt;/code&amp;gt; || Server flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Encrypted Payload&#039;&#039;&#039; || &#039;&#039;&#039;0x04-0x71&#039;&#039;&#039; || &#039;&#039;&#039;110 bytes&#039;&#039;&#039; || &#039;&#039;&#039;Ciphertext&#039;&#039;&#039; || &#039;&#039;&#039;AES-encrypted oping ECHO_REQUEST data&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* No visible protocol structure - all application data appears as ciphertext&lt;br /&gt;
* Uses the same source/destination EID pair (0x0043 → 0x0060) established in the FLOW_REQ/FLOW_REPLY handshake&lt;br /&gt;
* Encryption is done using the ephemeral key (91 bytes) exchanged in Packet 11&#039;s OAP header&lt;br /&gt;
* Unlike Packets 11-12, this packet contains no certificate, public keys, or signatures&lt;br /&gt;
* The 110-byte encrypted data corresponds to the original oping ECHO_REQUEST message&lt;br /&gt;
&lt;br /&gt;
==== Packet 14: Encrypted ECHO_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server sends encrypted ping reply. Note that the flow identifiers swap, demonstrating bidirectional encrypted communication.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:59.836930 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0040 0060 d552 e100 e681 940c e35a 07d0  .@.`..........Z..&lt;br /&gt;
        0x0010:  a293 1d73 33a5 854e 0fce 4f4d 6655 267a  ...s3..N..OMfU&amp;amp;z&lt;br /&gt;
        0x0020:  3de2 663b 709d 739a a696 2ddd 7b34 28b8  =.f;p.s...-{4(...&lt;br /&gt;
        0x0030:  5a98 eec2 52c6 4288 3885 ae16 e466 4181  Z...R.B.8...fA..&lt;br /&gt;
        0x0040:  f2d6 44c1 b51b 8728 58a4 7525 fb5e 3fd6  ..D...(X.u%.^?..&lt;br /&gt;
        0x0050:  7e49 532a d2a5 bea7 55e9 c274 f1b2 0412  ~IS*....U..t....&lt;br /&gt;
        0x0060:  73d4 6436                                s.d6&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame Analysis:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Length !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Source EID || 0x00-0x01 || 2 bytes || &amp;lt;code&amp;gt;0x0040&amp;lt;/code&amp;gt; || Client&#039;s inbound flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| Destination EID || 0x02-0x03 || 2 bytes || &amp;lt;code&amp;gt;0x0060&amp;lt;/code&amp;gt; || Server flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Encrypted Payload&#039;&#039;&#039; || &#039;&#039;&#039;0x04-0x71&#039;&#039;&#039; || &#039;&#039;&#039;110 bytes&#039;&#039;&#039; || &#039;&#039;&#039;Ciphertext&#039;&#039;&#039; || &#039;&#039;&#039;AES-encrypted oping ECHO_REPLY data&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* The EID in offset 0x00 is now 0x0040 (server&#039;s view of client&#039;s inbound flow)&lt;br /&gt;
* Uses the same ephemeral key material as Packet 13, but encryption direction is reversed&lt;br /&gt;
* Both packets use AES-GCM with keys derived from the ECDH exchange&lt;br /&gt;
* Timestamp 17:39:59.836930 is only 445 microseconds after Packet 13, indicating server-side processing&lt;br /&gt;
* The 110-byte encrypted ECHO_REPLY payload is the same size as the request&lt;br /&gt;
* All application data is protected by both authentication (X.509 + ECDSA) and encryption (AES)&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 4 - Authentication, no encryption ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Packet 15: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates flow allocation with authentication enabled but encryption disabled. This FLOW_REQ carries an OAP header but &#039;&#039;&#039;no ephemeral key&#039;&#039;&#039; since the client does not request encrypted communication.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:03.413372 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 121:&lt;br /&gt;
        0x0000:  0000 0067 0040 0000 0000 0001 0000 0000  ...g.@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a8f a6ab 6ea7  ..f.i.._........&lt;br /&gt;
        0x0050:  ef89 68e1 ed1e 2ede 0919 fa18 86a8 e490  .h..............&lt;br /&gt;
        0x0060:  0de6 6100 0000 0000 0000 00              ..a.....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0067&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 103 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID (allocated flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... dc40&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Mixed || &lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload (No Encryption)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;8f a6ab 6ea7 ef89 68e1 ed1e 2ede 0919 fa&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e490 0de6 61&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate in client request&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;No ephemeral key (no encryption)&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0x68 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x69-0x6a || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* No encryption enabled: ephemeral key absent (Eph_len = 0x0000)&lt;br /&gt;
* Client requests authentication only&lt;br /&gt;
* Server will respond with certificate + signature but no ephemeral key&lt;br /&gt;
* Packet is minimal compared to Packet 11 (Test 3) which includes 91-byte ephemeral key&lt;br /&gt;
&lt;br /&gt;
==== Packet 16: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server accepts the authenticated (but not encrypted) flow allocation request. FLOW_REPLY contains the server&#039;s X.509 certificate and ECDSA signature for client authentication, but &#039;&#039;&#039;no ephemeral key&#039;&#039;&#039; since encryption is not being used.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data (abbreviated):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:03.416675 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 751:&lt;br /&gt;
        0x0000:  0000 02dd 0041 0040 0000 0000 0000 0000  .......A.@......&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 008f a6ab 6ea7  ................&lt;br /&gt;
        0x0030:  ef89 68e1 ed1e 2ede 0919 fa18 86a8 e490  .h..............&lt;br /&gt;
        0x0040:  3754 a702 2f30 8202 2b30 8201 b2a0 0302  7T../0..+0......&lt;br /&gt;
        0x0050:  0102 0202 1000 300a 0608 2a86 48ce 3d04  ......0...*.H.=.&lt;br /&gt;
        (... certificate and signature bytes ...)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;02dd&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 733 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0041 || Source Endpoint ID (allocated server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0x00000000 || Response code: 0 (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY Payload with Certificate and Signature (No Ephemeral Key)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;8f a6ab 6ea7 ef89 68e1 ed1e 2ede 0919&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || &#039;&#039;&#039;Same ID as client&#039;s FLOW_REQ&#039;&#039;&#039; (Packet 15 echoed back)&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;fa18 86a8 e490 3754&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Server&#039;s timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;a702&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x02a7 (679 decimal) || &#039;&#039;&#039;Certificate length: 679 bytes&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x270 || &amp;lt;code&amp;gt;2f30 8202 2b30 8201 b2a0 0302 ... (DER certificate) ...&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Certificate&#039;&#039;&#039; || 679 || DER-encoded X.509 || Server&#039;s certificate signed by intermediate CA&lt;br /&gt;
|-&lt;br /&gt;
| 0x271-0x272 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;No ephemeral key&#039;&#039;&#039; (no encryption)&lt;br /&gt;
|-&lt;br /&gt;
| 0x273-0x274 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x275-0x276 || &amp;lt;code&amp;gt;0067&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0067 (103 decimal) || &#039;&#039;&#039;ECDSA signature length: 103 bytes&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x277-0x2dd || &amp;lt;code&amp;gt;3065 0230 75dc 5717 ... 83&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Signature&#039;&#039;&#039; || 103 || ECDSA signature (DER encoded) || Server&#039;s ECDSA signature proving certificate ownership&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0041 - New server-side endpoint ID allocated for this authenticated flow&lt;br /&gt;
* DEID is 0x0040 - Client&#039;s flow ID from Packet 15 FLOW_REQ, establishing the bidirectional flow&lt;br /&gt;
* FLOW_REPLY Message Type - Code field is 0x01, &#039;&#039;&#039;no service hash&#039;&#039;&#039; (already identified in FLOW_REQ)&lt;br /&gt;
* Certificate Field - &amp;lt;code&amp;gt;crt_len = 0x02a7 (679)&amp;lt;/code&amp;gt; carrying server&#039;s X.509 certificate signed by intermediate CA&lt;br /&gt;
* Separate Signature Field - &amp;lt;code&amp;gt;sig_len = 0x0067 (103)&amp;lt;/code&amp;gt; with ECDSA signature over entire OAP header&lt;br /&gt;
* No Ephemeral Key - &amp;lt;code&amp;gt;eph_len = 0x0000&amp;lt;/code&amp;gt; since encryption is &#039;&#039;&#039;not&#039;&#039;&#039; being used in Test 4&lt;br /&gt;
* Same OAP ID - Server echoes back the exact ID from client&#039;s FLOW_REQ (included in signature, binding response to this specific client request)&lt;br /&gt;
* Complete OAP Structure - Full OAP header with all standard fields, just without ephemeral key data&lt;br /&gt;
* Plaintext Data Exchange - After this FLOW_REPLY, all subsequent application data will be transmitted in plaintext (but authenticated via certificate + signature verification)&lt;br /&gt;
&lt;br /&gt;
==== Packet 17: ECHO_REQUEST ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends plaintext ECHO_REQUEST data through the authenticated (but unencrypted) flow. The oping application&#039;s ping request is transmitted directly without encryption, relying on the earlier certificate+signature authentication for security.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:04.419664 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0041 0040 0000 0000 0000 0000 8177 0000  .A.@............&lt;br /&gt;
        0x0010:  0000 0000 aa16 1c16 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0041 || Destination Endpoint ID (server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Total application data length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Application Data - Oping Echo Request (Plaintext)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000000 || Message type: ECHO_REQUEST&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Sequence number / message identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;8177 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Seconds component from CLOCK_REALTIME&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;aa16 1c16 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Nanoseconds component (0-999999999)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || Application data || Probe payload (zero-filled for 64 bytes total)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID Pair: 0x0041 → Server Flow - Data is directed to the server&#039;s endpoint ID allocated in Packet 16 FLOW_REPLY&lt;br /&gt;
* Plaintext Transmission - No encryption layer; oping payload is sent as-is (compare to Packet 13 which had encryption)&lt;br /&gt;
* Authenticated Flow - Although plaintext, this data travels on the authenticated flow established in Packet 16 (certificate + signature verified)&lt;br /&gt;
* Type = ECHO_REQUEST - 0x00000000 indicates client-to-server ping request&lt;br /&gt;
* ID = 0 - Sequence number for matching request/reply pairs&lt;br /&gt;
* Test 4 Characteristic - Demonstrates authenticated communication &#039;&#039;&#039;without&#039;&#039;&#039; encryption; application data is readable but cryptographically bound to the authenticated flow&lt;br /&gt;
* Contrast to Test 3 - Packet 13 (Test 3 encrypted ECHO_REQUEST) was 114 bytes with ciphertext; this packet is 82 bytes of plaintext&lt;br /&gt;
* Total Payload - 64 bytes total (4 + 4 + 8 + 8 + 40 bytes payload)&lt;br /&gt;
&lt;br /&gt;
==== Packet 18: ECHO_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds with plaintext ECHO_REPLY data, echoing back the client&#039;s request. This confirms successful bidirectional communication over the authenticated (but unencrypted) flow.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:04.420088 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0040 0040 0000 0001 0000 0000 8177 0000  .@.@............&lt;br /&gt;
        0x0010:  0000 0000 aa16 1c16 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Total application data length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Application Data - Oping Echo Reply (Plaintext)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0001&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000001 || Message type: ECHO_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Echo of request sequence number&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;8177 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Echoed request timestamp (seconds)&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;aa16 1c16 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Echoed request timestamp (nanoseconds)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || Application data || Echoed probe payload (zero-filled for 64 bytes total)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID Pair: 0x0040 → Client Flow - Server responds to client&#039;s endpoint ID from Packet 15 FLOW_REQ&lt;br /&gt;
* Type = ECHO_REPLY - 0x00000001 indicates server-to-client response&lt;br /&gt;
* ID = 0 - Echoes the request sequence number, matching this response to the request&lt;br /&gt;
* Timestamps Echo Request - Both timestamp fields are copied from Packet 17 unchanged (8177 0000 0000 0000 and aa16 1c16 0000 0000)&lt;br /&gt;
* Plaintext Reply - No encryption; server&#039;s response payload is readable (compare to Packet 14 which had encryption)&lt;br /&gt;
* Authenticated Channel - Although plaintext, this reply is part of the authenticated flow; client can verify integrity through earlier certificate+signature&lt;br /&gt;
* Test 4 Completion - Demonstrates &#039;&#039;&#039;full bidirectional plaintext communication&#039;&#039;&#039; over an authenticated (but unencrypted) flow&lt;br /&gt;
* Contrast to Test 3 - Packet 14 (Test 3 encrypted ECHO_REPLY) was 114 bytes with ciphertext; this packet is 82 bytes of plaintext&lt;br /&gt;
* Total Payload - 64 bytes total (4 + 4 + 8 + 8 + 40 bytes payload)&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Tutorial_06&amp;diff=1898</id>
		<title>Ouroboros Tutorial 06</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Tutorial_06&amp;diff=1898"/>
		<updated>2026-02-14T15:15:44Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* Test 2: No Authentication, With Encryption */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Ouroboros Tutorial 06 - Authenticated Flows =&lt;br /&gt;
&lt;br /&gt;
This tutorial demonstrates setting up and using authenticated flows in Ouroboros with certificate-based authentication.&lt;br /&gt;
&lt;br /&gt;
The overall flow of authenticated flow allocation is&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Client (IRMd)                              Server (IRMd)&lt;br /&gt;
     |                                           |&lt;br /&gt;
     | 1. Load client cert/key                   |&lt;br /&gt;
     | 2. Generate ephemeral keypair             |&lt;br /&gt;
     | 3. Build OAP_HDR (id, ts, crt, eph)       |&lt;br /&gt;
     | 4. Sign header with client key            |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |-------- FLOW_REQ (OAP_HDR) -------------&amp;gt; |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |                                           | 5. Load server cert/key&lt;br /&gt;
     |                                           | 6. Verify client cert against CA&lt;br /&gt;
     |                                           | 7. Verify client signature&lt;br /&gt;
     |                                           | 8. Generate ephemeral keypair&lt;br /&gt;
     |                                           | 9. Derive symmetric key (ECDHE)&lt;br /&gt;
     |                                           | 10. Build response OAP_HDR&lt;br /&gt;
     |                                           | 11. Sign with server key&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |&amp;lt;------- FLOW_REPLY (OAP_HDR) ------------ |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     | 12. Verify server cert against CA         |&lt;br /&gt;
     | 13. Verify server signature               |&lt;br /&gt;
     | 14. Derive symmetric key (ECDHE)          |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |===========================================|&lt;br /&gt;
     |         Encrypted data channel            |&lt;br /&gt;
     |===========================================|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Tutorial Directory:&#039;&#039;&#039; This tutorial will execute in &amp;lt;code&amp;gt;/tmp/o7s-tut06/&amp;lt;/code&amp;gt;. All configuration files, generated certificates, logs, and packet captures will be stored in this directory.&lt;br /&gt;
&lt;br /&gt;
We create a complete PKI (Public Key Infrastructure):&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Root CA&#039;&#039;&#039; (&amp;lt;code&amp;gt;ca.tut.o7s&amp;lt;/code&amp;gt;): Self-signed trust anchor&lt;br /&gt;
* &#039;&#039;&#039;Intermediate CA&#039;&#039;&#039; (&amp;lt;code&amp;gt;sign.tut.o7s&amp;lt;/code&amp;gt;): Signed by root with pathlen:0 constraint&lt;br /&gt;
* &#039;&#039;&#039;Server Certificate&#039;&#039;&#039; (&amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;): Signed by intermediate CA&lt;br /&gt;
&lt;br /&gt;
This tutorial uses ECDSA P-384 with SHA-384 hashing.&lt;br /&gt;
&lt;br /&gt;
== Setting Up the Tutorial ==&lt;br /&gt;
&lt;br /&gt;
To properly understand and debug the authenticated flows, this tutorial uses a debug build of Ouroboros with OAP protocol debugging enabled.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /path/to/ouroboros&lt;br /&gt;
mkdir build &amp;amp;&amp;amp; cd build&lt;br /&gt;
cmake -DCMAKE_BUILD_TYPE=Debug -DDEBUG_PROTO_OAP=ON ..&lt;br /&gt;
make -j$(nproc)&lt;br /&gt;
sudo make install&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When built with these options, the IRMd will output detailed OAP protocol information.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Files ===&lt;br /&gt;
&lt;br /&gt;
The following three files should be created in the tutorial directory (&amp;lt;code&amp;gt;/tmp/o7s-tut06/&amp;lt;/code&amp;gt;) before starting the tutorial:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;tut06.conf&#039;&#039;&#039; - IRMd configuration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
# Ouroboros Tutorial 06 - Authenticated Flows Configuration&lt;br /&gt;
# Uses system-installed certificates at /etc/ouroboros/security/&lt;br /&gt;
&lt;br /&gt;
[name.&amp;quot;sec.oping.tut.o7s&amp;quot;]&lt;br /&gt;
prog=[&amp;quot;/usr/bin/oping&amp;quot;]&lt;br /&gt;
args=[&amp;quot;--listen&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
[eth-dix.eth-dix-lo]&lt;br /&gt;
bootstrap=&amp;quot;eth-dix-network&amp;quot;&lt;br /&gt;
dev=&amp;quot;lo&amp;quot;&lt;br /&gt;
reg=[&amp;quot;sec.oping.tut.o7s&amp;quot;]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ca.tut.o7s.cnf&#039;&#039;&#039; - OpenSSL configuration for PKI generation&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
# Unified OpenSSL Configuration for Ouroboros Tutorial 06&lt;br /&gt;
# Named CA sections: CA_root (signs intermediate), CA_intermediate (signs server)&lt;br /&gt;
# Usage: openssl ca -name CA_root -config ca.tut.o7s.cnf ...&lt;br /&gt;
&lt;br /&gt;
[ req ]&lt;br /&gt;
default_bits       = 384&lt;br /&gt;
default_keyfile    = private/key.pem&lt;br /&gt;
distinguished_name = req_distinguished_name&lt;br /&gt;
string_mask        = utf8only&lt;br /&gt;
default_md         = sha384&lt;br /&gt;
x509_extensions    = v3_ca&lt;br /&gt;
&lt;br /&gt;
[ req_distinguished_name ]&lt;br /&gt;
countryName                 = Country Name (2 letter code)&lt;br /&gt;
stateOrProvinceName         = State or Province Name&lt;br /&gt;
localityName                = Locality Name&lt;br /&gt;
organizationName            = Organization Name&lt;br /&gt;
commonName                  = Common Name&lt;br /&gt;
&lt;br /&gt;
countryName_default         = BE&lt;br /&gt;
stateOrProvinceName_default = OVL&lt;br /&gt;
localityName_default        = Ghent&lt;br /&gt;
organizationName_default    = o7s&lt;br /&gt;
&lt;br /&gt;
[ ca ]&lt;br /&gt;
default_ca = CA_root&lt;br /&gt;
&lt;br /&gt;
[ CA_root ]&lt;br /&gt;
dir             = ./pki/root&lt;br /&gt;
database        = $dir/index.txt&lt;br /&gt;
serial          = $dir/serial&lt;br /&gt;
new_certs_dir   = $dir/certs&lt;br /&gt;
certificate     = $dir/certs/ca.tut.o7s.crt.pem&lt;br /&gt;
private_key     = $dir/private/ca.tut.o7s.key.pem&lt;br /&gt;
default_days    = 3650&lt;br /&gt;
default_md      = sha384&lt;br /&gt;
policy          = policy_loose&lt;br /&gt;
&lt;br /&gt;
[ CA_intermediate ]&lt;br /&gt;
dir             = ./pki/sign&lt;br /&gt;
database        = $dir/index.txt&lt;br /&gt;
serial          = $dir/serial&lt;br /&gt;
new_certs_dir   = $dir/certs&lt;br /&gt;
certificate     = $dir/certs/sign.tut.o7s.crt.pem&lt;br /&gt;
private_key     = $dir/private/sign.tut.o7s.key.pem&lt;br /&gt;
default_days    = 365&lt;br /&gt;
default_md      = sha384&lt;br /&gt;
policy          = policy_loose&lt;br /&gt;
&lt;br /&gt;
[ policy_loose ]&lt;br /&gt;
commonName = supplied&lt;br /&gt;
&lt;br /&gt;
[ v3_ca ]&lt;br /&gt;
subjectKeyIdentifier = hash&lt;br /&gt;
authorityKeyIdentifier = keyid:always,issuer&lt;br /&gt;
basicConstraints = critical, CA:true&lt;br /&gt;
keyUsage = critical, digitalSignature, cRLSign, keyCertSign&lt;br /&gt;
&lt;br /&gt;
[ v3_intermediate_ca ]&lt;br /&gt;
subjectKeyIdentifier = hash&lt;br /&gt;
authorityKeyIdentifier = keyid:always,issuer&lt;br /&gt;
basicConstraints = critical, CA:true, pathlen:0&lt;br /&gt;
keyUsage = critical, digitalSignature, cRLSign, keyCertSign&lt;br /&gt;
&lt;br /&gt;
[ server_cert ]&lt;br /&gt;
basicConstraints = CA:FALSE&lt;br /&gt;
subjectKeyIdentifier = hash&lt;br /&gt;
authorityKeyIdentifier = keyid,issuer:always&lt;br /&gt;
keyUsage = critical, digitalSignature, keyEncipherment&lt;br /&gt;
extendedKeyUsage = serverAuth&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;gen-pki.sh&#039;&#039;&#039; - PKI generation script&lt;br /&gt;
&lt;br /&gt;
This script will:&lt;br /&gt;
1. Create the directory structure&lt;br /&gt;
2. Generate the root CA key and certificate&lt;br /&gt;
3. Generate the intermediate CA key and CSR&lt;br /&gt;
4. Sign the intermediate CA certificate&lt;br /&gt;
5. Generate the server certificate key and CSR&lt;br /&gt;
6. Sign the server certificate&lt;br /&gt;
7. Verify the complete certificate chain&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
# Ouroboros Tutorial 06 - PKI Generation Script (Simplified)&lt;br /&gt;
# Generates: Root CA, Intermediate CA, and Server Certificate&lt;br /&gt;
&lt;br /&gt;
set -e&lt;br /&gt;
&lt;br /&gt;
if [ ! -f ca.tut.o7s.cnf ]; then&lt;br /&gt;
    echo &amp;quot;ERROR: ca.tut.o7s.cnf not found&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
mkdir -p pki/{root,sign,server}/{certs,private,csr}&lt;br /&gt;
&lt;br /&gt;
# Root CA&lt;br /&gt;
openssl ecparam -genkey -name secp384r1 -out pki/root/private/ca.tut.o7s.key.pem 2&amp;gt;/dev/null&lt;br /&gt;
openssl req -new -x509 -sha384 -days 7300 \&lt;br /&gt;
    -config ca.tut.o7s.cnf \&lt;br /&gt;
    -key pki/root/private/ca.tut.o7s.key.pem \&lt;br /&gt;
    -out pki/root/certs/ca.tut.o7s.crt.pem \&lt;br /&gt;
    -subj &amp;quot;/C=BE/ST=OVL/L=Ghent/O=o7s/CN=ca.tut.o7s&amp;quot; 2&amp;gt;/dev/null&lt;br /&gt;
&lt;br /&gt;
# Intermediate CA&lt;br /&gt;
openssl ecparam -genkey -name secp384r1 -out pki/sign/private/sign.tut.o7s.key.pem 2&amp;gt;/dev/null&lt;br /&gt;
openssl req -new -sha384 \&lt;br /&gt;
    -config ca.tut.o7s.cnf \&lt;br /&gt;
    -key pki/sign/private/sign.tut.o7s.key.pem \&lt;br /&gt;
    -out pki/sign/csr/sign.tut.o7s.csr \&lt;br /&gt;
    -subj &amp;quot;/C=BE/ST=OVL/L=Ghent/O=o7s/CN=sign.tut.o7s&amp;quot; 2&amp;gt;/dev/null&lt;br /&gt;
&lt;br /&gt;
touch pki/root/index.txt&lt;br /&gt;
echo 1000 &amp;gt; pki/root/serial&lt;br /&gt;
&lt;br /&gt;
openssl ca -name CA_root -config ca.tut.o7s.cnf \&lt;br /&gt;
    -extensions v3_intermediate_ca -days 3650 -md sha384 -batch \&lt;br /&gt;
    -in pki/sign/csr/sign.tut.o7s.csr \&lt;br /&gt;
    -out pki/sign/certs/sign.tut.o7s.crt.pem 2&amp;gt;&amp;amp;1 | grep -E &amp;quot;Signature ok|Database updated&amp;quot; || true&lt;br /&gt;
&lt;br /&gt;
# Server Certificate&lt;br /&gt;
openssl ecparam -genkey -name secp384r1 -out pki/server/private/sec.oping.tut.o7s.key.pem 2&amp;gt;/dev/null&lt;br /&gt;
openssl req -new -sha384 \&lt;br /&gt;
    -config ca.tut.o7s.cnf \&lt;br /&gt;
    -key pki/server/private/sec.oping.tut.o7s.key.pem \&lt;br /&gt;
    -out pki/server/csr/sec.oping.tut.o7s.csr \&lt;br /&gt;
    -subj &amp;quot;/C=BE/ST=OVL/L=Ghent/O=o7s/CN=sec.oping.tut.o7s&amp;quot; 2&amp;gt;/dev/null&lt;br /&gt;
&lt;br /&gt;
touch pki/sign/index.txt&lt;br /&gt;
echo 1000 &amp;gt; pki/sign/serial&lt;br /&gt;
&lt;br /&gt;
openssl ca -name CA_intermediate -config ca.tut.o7s.cnf \&lt;br /&gt;
    -extensions server_cert -days 365 -md sha384 -batch \&lt;br /&gt;
    -in pki/server/csr/sec.oping.tut.o7s.csr \&lt;br /&gt;
    -out pki/server/certs/sec.oping.tut.o7s.crt.pem 2&amp;gt;&amp;amp;1 | grep -E &amp;quot;Signature ok|Database updated&amp;quot; || true&lt;br /&gt;
&lt;br /&gt;
# Verify chain&lt;br /&gt;
openssl verify -CAfile pki/root/certs/ca.tut.o7s.crt.pem \&lt;br /&gt;
    -untrusted pki/sign/certs/sign.tut.o7s.crt.pem \&lt;br /&gt;
    pki/server/certs/sec.oping.tut.o7s.crt.pem &amp;gt; /dev/null 2&amp;gt;&amp;amp;1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;PKI generation complete.&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Part 1: Running the Tutorial - Single Session with 4 Tests ==&lt;br /&gt;
&lt;br /&gt;
This section demonstrates a single continuous session with one IRMd and tcpdump instance. The configuration file (&amp;lt;code&amp;gt;tut06.conf&amp;lt;/code&amp;gt;) includes autostart for oping, so the server is ready immediately when IRMd starts.&lt;br /&gt;
&lt;br /&gt;
First install the &#039;&#039;&#039;CA and Intermediate CA only&#039;&#039;&#039; to the system security directories. The server certificate will be installed later during Test 3 (authentication test):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo mkdir -p /etc/ouroboros/security/{cacert,untrusted,server/sec.oping.tut.o7s,client/sec.oping.tut.o7s}&lt;br /&gt;
&lt;br /&gt;
# Run the PKI generation script&lt;br /&gt;
cd /tmp/o7s-tut06&lt;br /&gt;
sudo chmod +x gen-pki.sh&lt;br /&gt;
sudo ./gen-pki.sh&lt;br /&gt;
&lt;br /&gt;
# Install Root CA (trust anchor)&lt;br /&gt;
sudo cp pki/root/certs/ca.tut.o7s.crt.pem /etc/ouroboros/security/cacert/&lt;br /&gt;
&lt;br /&gt;
# Install Intermediate CA (for certificate chain validation)&lt;br /&gt;
sudo cp pki/sign/certs/sign.tut.o7s.crt.pem /etc/ouroboros/security/untrusted/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Running the Tutorial (3 Terminals) ===&lt;br /&gt;
&lt;br /&gt;
In this tutorial, we run a single IRMd session with a concurrent tcpdump instance to capture it. We then run four oping client tests while the IRMd/tcpdump sessions are going, modifying the security configuration between tests. After the tests are complete, we can will down the IRMd and tcpdump sessions with Ctrl-C.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Terminal 1: Start tcpdump to capture all packets (runs continuously)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo tcpdump -i lo -n -A -v -U -w /tmp/o7s-tut06/tut06.pcap &amp;quot;ether proto 0xa000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Terminal 2: Start IRMd with debug output (runs continuously)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /tmp/o7s-tut06&lt;br /&gt;
sudo irmd --config tut06.conf --stdout 2&amp;gt;&amp;amp;1 | tee /tmp/o7s-tut06/irmd.log&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Terminal 3: Run the tests&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Test 1: No Authentication, No Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Verify directories are empty&lt;br /&gt;
sudo ls -la /etc/ouroboros/security/client/sec.oping.tut.o7s/*&lt;br /&gt;
sudo ls -la /etc/ouroboros/security/server/sec.oping.tut.o7s/*&lt;br /&gt;
&lt;br /&gt;
# Run first ping test&lt;br /&gt;
echo &amp;quot;=== Test 1: No Authentication, No Encryption ===&amp;quot;&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 1: Client initiates plaintext flow allocation&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [60e824383b3fbd6a] KEX config: algo=none, mode=server-encap, cipher=none.&lt;br /&gt;
irmd/oap(PP): OAP_HDR [60e824383b3fbd6a @ 2026-02-14 14:08:56 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Cipher: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   KDF: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 1: Server accepts and completes handshake&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [60e824383b3fbd6a @ 2026-02-14 14:08:57 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Cipher: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   KDF: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [48 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; All OAP fields are &amp;lt;code&amp;gt;&amp;lt;none&amp;gt;&amp;lt;/code&amp;gt; because no security is configured (except for the request hash in the response). Flow succeeds with plaintext communication.&lt;br /&gt;
&lt;br /&gt;
==== Test 2: No Authentication, With Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Enable encryption for client only&lt;br /&gt;
sudo touch /etc/ouroboros/security/client/sec.oping.tut.o7s/enc.conf&lt;br /&gt;
&lt;br /&gt;
# Run second ping test&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 2: Client initiates flow with encryption enabled&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(II): Encryption enabled for sec.oping.tut.o7s.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Generated ephemeral prime256v1 keys (91 bytes).&lt;br /&gt;
irmd/oap(PP): OAP_HDR [80fd6f9509a996b0 @ 2026-02-14 14:09:38 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: sha256&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 2: Server receives and responds with ephemeral key&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] No crt provided.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Selected client cipher aes-256-gcm.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Selected client KDF sha256.&lt;br /&gt;
irmd/oap(II): [80fd6f9509a996b0] No key exchange.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Generated prime256v1 ephemeral keys.&lt;br /&gt;
irmd/oap(PP): OAP_HDR [80fd6f9509a996b0 @ 2026-02-14 14:09:38 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes] [Server encaps]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [32 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; Both client and server generate ephemeral keys (91 bytes each) for encryption. No certificates because authentication is not required. Encryption and authentication are independent.&lt;br /&gt;
&lt;br /&gt;
==== Test 3: With Authentication, With Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Install server certificates and keys&lt;br /&gt;
sudo cp /tmp/o7s-tut06/pki/server/certs/sec.oping.tut.o7s.crt.pem \&lt;br /&gt;
        /etc/ouroboros/security/server/sec.oping.tut.o7s/crt.pem&lt;br /&gt;
sudo cp /tmp/o7s-tut06/pki/server/private/sec.oping.tut.o7s.key.pem \&lt;br /&gt;
        /etc/ouroboros/security/server/sec.oping.tut.o7s/key.pem&lt;br /&gt;
&lt;br /&gt;
# enc.conf is still in place from Test 2&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 3: Client initiates flow with encryption and server has certificate&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [c904b18b563dc1b0 @ 2026-02-14 14:09:47 (UTC) ] &amp;lt;--&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes] [Server encaps]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: sha256&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 3: Server responds with certificate + ephemeral key + signature&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [c904b18b563dc1b0 @ 2026-02-14 14:09:47 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: [560 bytes]&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes] [Server encaps]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [32 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: [103 bytes]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 3: Client verifies certificate and authenticates&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [c904b18b563dc1b0] Loaded peer crt.&lt;br /&gt;
irmd/oap(DB): [c904b18b563dc1b0] Got public key from crt.&lt;br /&gt;
irmd/oap(DB): [c904b18b563dc1b0] Successfully verified peer crt.&lt;br /&gt;
irmd/oap(DB): [c904b18b563dc1b0] Successfully authenticated peer.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; Full OAP handshake with certificate (560 bytes) + ephemeral keys (91 bytes) + signature (103 bytes). Client verifies server&#039;s certificate against CA store and confirms authentication success.&lt;br /&gt;
&lt;br /&gt;
==== Test 4: With Authentication, No Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Remove encryption config but keep certificates&lt;br /&gt;
sudo rm -f /etc/ouroboros/security/client/sec.oping.tut.o7s/enc.conf&lt;br /&gt;
&lt;br /&gt;
# Run fourth ping test&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 4: Client initiates plaintext flow (no encryption file)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [03e47baa966a5823 @ 2026-02-14 14:09:57 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Cipher: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   KDF: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 4: Server responds with certificate + signature (no ephemeral key)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [03e47baa966a5823 @ 2026-02-14 14:09:57 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: [560 bytes]&lt;br /&gt;
irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Cipher: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   KDF: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [48 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: [103 bytes]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 4: Client verifies certificate and authenticates&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [03e47baa966a5823] Loaded peer crt.&lt;br /&gt;
irmd/oap(DB): [03e47baa966a5823] Got public key from crt.&lt;br /&gt;
irmd/oap(DB): [03e47baa966a5823] Successfully verified peer crt.&lt;br /&gt;
irmd/oap(DB): [03e47baa966a5823] Successfully authenticated peer.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; Server sends certificate + signature for authentication, but NO ephemeral keys (plaintext data). Data exchanged without encryption even though authenticated. Demonstrates that authentication and encryption are independent mechanisms.&lt;br /&gt;
&lt;br /&gt;
=== Stop the IRMd and tcpdump, clean up the tutorial files ===&lt;br /&gt;
&lt;br /&gt;
Once all tests complete:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Stop IRMd in Terminal 2 (Ctrl+C)&lt;br /&gt;
# Stop tcpdump in Terminal 1 (Ctrl+C)&lt;br /&gt;
&lt;br /&gt;
# Clean up tutorial security files from system&lt;br /&gt;
sudo rm -f /etc/ouroboros/security/cacert/ca.tut.o7s.crt.pem&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Part 2: PCAP Trace Analysis ==&lt;br /&gt;
&lt;br /&gt;
After the tutorial, we now explain the trace in the tcpdump pcap file.&lt;br /&gt;
&lt;br /&gt;
=== Protocol Overview ===&lt;br /&gt;
&lt;br /&gt;
This section summarizes the four protocols that work together in the captured packet flow.&lt;br /&gt;
&lt;br /&gt;
==== Ethernet DIX Frame with EID Header ====&lt;br /&gt;
&lt;br /&gt;
Ouroboros extends the DIX frame with a flow identifier (EID - Endpoint Identifier) and length field.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Octets !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Destination MAC || 0-5 || 6 bytes || Hardware address of destination&lt;br /&gt;
|-&lt;br /&gt;
| Source MAC || 6-11 || 6 bytes || Hardware address of source&lt;br /&gt;
|-&lt;br /&gt;
| EtherType || 12-13 || 2 bytes || Protocol identifier (0xA000 for Ouroboros)&lt;br /&gt;
|-&lt;br /&gt;
| EID || 14-15 || 2 bytes || Destination Endpoint Identifier&lt;br /&gt;
|-&lt;br /&gt;
| Length || 16-17 || 2 bytes || Payload length (needed because of runt frame padding)&lt;br /&gt;
|-&lt;br /&gt;
| Payload || 18+ || Variable || Frame data (up to MTU size)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Ethernet Flow Allocator - Management Protocol ====&lt;br /&gt;
&lt;br /&gt;
The Ethernet DIX management protocol handles flow allocation, setup, and teardown. All management frames use destination EID &amp;lt;code&amp;gt;0x0000&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Management Frame Types:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Code !! Type !! Direction !! Service Hash !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x00&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FLOW_REQ&amp;lt;/code&amp;gt; || Client → Server || ✓ Included || Request new flow allocation&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x01&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FLOW_REPLY&amp;lt;/code&amp;gt; || Server → Client || – Not included || Respond to flow request (success/failure)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x02&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NAME_QUERY_REQ&amp;lt;/code&amp;gt; || Client → Server || ✓ Included || Query if a remote name is reachable&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x03&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NAME_QUERY_REPLY&amp;lt;/code&amp;gt; || Server → Client || ✓ Included || Response to name query&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The 32-byte service hash (SHA3-256) is appended after the management protocol header for NAME_QUERY_* and FLOW_REQ messages to identify which service is being queried or allocated. FLOW_REPLY does not include the service hash; the endpoints are already identified by the allocated EIDs (SEID/DEID) and the flow allocation ID in the OAP header (see below).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frame Layout by Field:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| SEID || 0-1 || 2 bytes || Source Endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| DEID || 2-3 || 2 bytes || Destination Endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| Loss || 4-7 || 4 bytes || Acceptable packet loss (ppm)&lt;br /&gt;
|-&lt;br /&gt;
| Bandwidth || 8-15 || 8 bytes || Required bandwidth (bps)&lt;br /&gt;
|-&lt;br /&gt;
| BER || 16-19 || 4 bytes || Bit error rate (ppm)&lt;br /&gt;
|-&lt;br /&gt;
| Max Gap || 20-23 || 4 bytes || Maximum consecutive lost packets&lt;br /&gt;
|-&lt;br /&gt;
| Delay || 24-27 || 4 bytes || Maximum latency (ms)&lt;br /&gt;
|-&lt;br /&gt;
| Timeout || 28-31 || 4 bytes || Flow idle timeout (seconds)&lt;br /&gt;
|-&lt;br /&gt;
| Response || 32-35 || 4 bytes || Response code (0=success, negative=error)&lt;br /&gt;
|-&lt;br /&gt;
| In-Order || 36 || 1 byte || In-order delivery requirement (boolean)&lt;br /&gt;
|-&lt;br /&gt;
| Code || 37 || 1 byte || Message type (FLOW_REQ, FLOW_REPLY, etc.)&lt;br /&gt;
|-&lt;br /&gt;
| Availability || 38 || 1 byte || Availability status&lt;br /&gt;
|-&lt;br /&gt;
| Service hash || 39-61 || 32 bytes || SHA3-256 hash (optional, see above)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Ouroboros Flow Allocation Protocol (OAP) ====&lt;br /&gt;
&lt;br /&gt;
The Ouroboros Application Protocol (OAP) is the flow allocation and authentication protocol. It carries flow negotiation requests, responses, and authentication credentials. OAP frames are encapsulated as data payload over the management protocol.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frame Layout by Field:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| ID || 0-15 || 16 bytes || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| Timestamp || 16-23 || 8 bytes || Creation timestamp (UTC, seconds and microseconds)&lt;br /&gt;
|-&lt;br /&gt;
| Crt Length || 24-25 || 2 bytes || Certificate length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Certificate || 26+ || Variable || X.509 certificate (DER encoded)&lt;br /&gt;
|-&lt;br /&gt;
| Eph Length || Variable || 2 bytes || Ephemeral public key length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Ephemeral Key || Variable || Variable || ECDHE public key (DER/raw encoded)&lt;br /&gt;
|-&lt;br /&gt;
| Data Length || Variable || 2 bytes || Application data length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Data || Variable || Variable || Piggybacked application-layer data&lt;br /&gt;
|-&lt;br /&gt;
| Sig Length || Variable || 2 bytes || Signature length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Signature || Variable || Variable || Digital signature (ECDSA, DER encoded)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Oping Application Protocol ====&lt;br /&gt;
&lt;br /&gt;
The Ouroboros Ping (oping) application is a simple echo/reply protocol used to measure round-trip time and validate connectivity between applications. It implements a request/reply pattern similar to ICMP ping.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frame Layout by Field:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Type || 0-3 || 4 bytes || Message type (ECHO_REQUEST=0 or ECHO_REPLY=1)&lt;br /&gt;
|-&lt;br /&gt;
| ID || 4-7 || 4 bytes || Sequence number / message identifier&lt;br /&gt;
|-&lt;br /&gt;
| Timestamp (seconds) || 8-15 || 8 bytes || Seconds when message was sent (CLOCK_REALTIME)&lt;br /&gt;
|-&lt;br /&gt;
| Timestamp (nanoseconds) || 16-23 || 8 bytes || Nanoseconds component of timestamp&lt;br /&gt;
|-&lt;br /&gt;
| Payload || 24+ || Variable || Application data (configurable size, default 64 bytes)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Field Definitions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Type&#039;&#039;&#039; (4 bytes): Message type selector&lt;br /&gt;
** &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt; (ECHO_REQUEST): Client-to-server ping request&lt;br /&gt;
** &amp;lt;code&amp;gt;0x00000001&amp;lt;/code&amp;gt; (ECHO_REPLY): Server-to-client response&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;ID&#039;&#039;&#039; (4 bytes): Sequence number for matching requests with replies. Incremented for each ping sent.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; (8 bytes): Network-byte-order 64-bit seconds component from when the ping was sent (CLOCK_REALTIME).&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; (8 bytes): Network-byte-order 64-bit nanoseconds component (0-999999999) for high-resolution timing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Payload&#039;&#039;&#039; (Variable): Application data echoed back by the server. Size is configurable (default 64 bytes, maximum 1500 bytes).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Usage:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Client sends ECHO_REQUEST with current timestamp&lt;br /&gt;
* Server receives request and echoes back as ECHO_REPLY with the same ID and timestamps&lt;br /&gt;
* Client calculates RTT by comparing reception time with original timestamps&lt;br /&gt;
* Out-of-order detection by tracking sequence numbers (ID field)&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 1 - No authentication/encryption ===&lt;br /&gt;
&lt;br /&gt;
==== Packet 1: NAME_QUERY_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends a NAME_QUERY_REQ message to discover if the service &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt; is available. This is a broadcast discovery query sent because the service is not yet known for the flow allocation process.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:48.165639 00:00:00:00:00:00 &amp;gt; ff:ff:ff:ff:ff:ff, ethertype Unknown (0xa000), length 89:&lt;br /&gt;
        0x0000:  0000 0047 0000 0000 0000 0000 0000 0000  ...G............&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0002 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a              ..f.i.._...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0047&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 71 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0000 || Source Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || Response code (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x03 || Message Type: NAME_QUERY_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Packet 2: NAME_QUERY_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds to the NAME_QUERY_REQ by sending a NAME_QUERY_REPLY for the service hash.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:48.166073 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 89:&lt;br /&gt;
        0x0000:  0000 0047 0000 0000 0000 0000 0000 0000  ...G............&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0003 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a              ..f.i.._...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0047&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 71 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0000 || Source Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || Response code&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x03 || Message Type: NAME_QUERY_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt; (echoed back)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Packet 3: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates a flow allocation request (FLOW_REQ) with minimal OAP headers since no authentication or encryption is being used.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:48.167222 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 121:&lt;br /&gt;
        0x0000:  0000 0067 0040 0000 0000 0001 0000 0000  ...g.@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a51 8a56 ff6f  ..f.i.._...Q.V.o&lt;br /&gt;
        0x0050:  5ba6 9d03 7da1 cfc3 0f30 7718 86a8 e103  [...}....0w.....&lt;br /&gt;
        0x0060:  3e52 3300 0000 0000 0000 00              &amp;gt;R3........&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0067&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 103 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID (allocated flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || -- || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0001 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x00 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The OAP payload starts at offset 0x4b (after management protocol + service hash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;51 8a56 ff6f 5ba6 9d03 7da1 cfc3 0f30 77&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e103 3e52 33&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp (seconds + microseconds)&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || No ephemeral key&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0x68 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x69-0x6a || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0040 (first allocated flow ID for this session)&lt;br /&gt;
* Service hash is carried in management protocol payload (32 bytes)&lt;br /&gt;
* OAP header is minimal: only ID and timestamp, no optional fields&lt;br /&gt;
* No certificate, ephemeral key, data, or signature in this initial request&lt;br /&gt;
* Client sends minimal OAP headers with no authentication or encryption setup at allocation time&lt;br /&gt;
&lt;br /&gt;
==== Packet 4: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds to FLOW_REQ by sending FLOW_REPLY with a new DEID (destination endpoint ID 0x0041) to establish the allocated flow for data transfer.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:49.178732 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 89:&lt;br /&gt;
        0x0000:  0000 0047 0041 0040 0000 0000 0000 0000  ...G.A.@........&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 0051 8a56 ff6f  ...........Q.V.o&lt;br /&gt;
        0x0030:  5ba6 9d03 7da1 cfc3 0f30 7718 86a8 e13f  [...}....0w....?&lt;br /&gt;
        0x0040:  a347 3800 0000 0000 0000 00              .G8........&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0047&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 71 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0041 || Source Endpoint ID (allocated server-side flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client&#039;s flow ID from FLOW_REQ)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0|| Response code (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The OAP payload starts at offset 0x2b (no service hash in FLOW_REPLY):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;51 8a56 ff6f 5ba6 9d03 7da1 cfc3 0f30 77&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Echo of client&#039;s flow ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;18 86a8 e13f a347 38&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Echoed timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x46 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || No ephemeral key&lt;br /&gt;
|-&lt;br /&gt;
| 0x47-0x48 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x49-0x4a || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID 0x0041 is the newly allocated server-side flow endpoint&lt;br /&gt;
* DEID 0x0040 reflects the client&#039;s flow ID, creating a bidirectional mapping&lt;br /&gt;
* No service hash included (FLOW_REPLY only needs the EIDs to identify the flow)&lt;br /&gt;
* OAP echoes the client&#039;s ID and timestamp, confirming the flow allocation&lt;br /&gt;
* Response code 0x00000000 indicates success&lt;br /&gt;
* Both client and server now have their respective flow IDs (0x0040 and 0x0041) for data transfer&lt;br /&gt;
* Response code 0x00000000 indicates success&lt;br /&gt;
&lt;br /&gt;
==== Packet 5: ECHO_REQUEST - Plaintext Data ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends an oping ECHO_REQUEST packet to the server using the allocated flow.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:50.180824 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0041 0040 0000 0000 0000 0000 7377 0000  .A.@........sw..&lt;br /&gt;
        0x0010:  0000 0000 b03e e007 0000 0000 0000 0000  .....&amp;gt;..........&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0041 || Source Endpoint ID (server → client)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Oping payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REQUEST Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The oping payload starts at offset 0x04:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000000 || Message type: ECHO_REQUEST&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Sequence number (first ping)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;7377 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || 0x0000000000003773 || Seconds component&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;b03e e007 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || 0x0000000007e03eb0 || Nanoseconds component&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 0000 ... 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || All zeros || Echo data (default 64 bytes total - 24 byte header = 40 bytes data)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID 0x0041 shows traffic from server-side flow ID&lt;br /&gt;
* This is the first ping request (ID = 0x00000000)&lt;br /&gt;
* Timestamp captures when the ping was sent (seconds in network order)&lt;br /&gt;
* Default oping payload is 64 bytes total; 24 bytes header + 40 bytes data&lt;br /&gt;
&lt;br /&gt;
==== Packet 6: ECHO_REPLY - Plaintext Data ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server receives the ECHO_REQUEST and immediately sends back an ECHO_REPLY with the same ID and timestamps, echoing the client&#039;s message.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:50.181496 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0040 0040 0000 0001 0000 0000 7377 0000  .@.@........sw..&lt;br /&gt;
        0x0010:  0000 0000 b03e e007 0000 0000 0000 0000  .....&amp;gt;..........&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client ← server)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Oping payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REPLY Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The oping payload starts at offset 0x04:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0001&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000001 || Message type: ECHO_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Echo of request sequence number&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;7377 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || 0x0000000000003773 || Echo of original seconds&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;b03e e007 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || 0x0000000007e03eb0 || Echo of original nanoseconds&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 0000 ... 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || All zeros || Echo data (unchanged from request)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID 0x0040 shows traffic from client-side flow ID receiving the reply&lt;br /&gt;
* Type field changed from 0x00000000 (REQUEST) to 0x00000001 (REPLY)&lt;br /&gt;
* ID, timestamps, and payload data are identical to the request (echoed back)&lt;br /&gt;
* Round-trip time can be calculated by comparing current time with echoed timestamp&lt;br /&gt;
* Ping succeeded on first attempt with minimal latency (~1 millisecond between timestamps)&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 2 - No authentication, with encryption ===&lt;br /&gt;
&lt;br /&gt;
==== Packet 7: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates flow allocation with encryption enabled. This FLOW_REQ carries an OAP header with an ephemeral ECDHE P-384 public key (91 bytes) for encryption setup.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:53.808158 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 212:&lt;br /&gt;
        0x0000:  0000 00c2 0040 0000 0000 0001 0000 0000  .....@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8af1 766b 547c  ..f.i.._....vkT|&lt;br /&gt;
        0x0050:  fcb0 8fce 5d60 a01e e8be 0218 86a8 e253  ....]`.........S&lt;br /&gt;
        0x0060:  8b6c 9000 0000 5b30 5930 1306 072a 8648  .l....[0Y0...*.H&lt;br /&gt;
        0x0070:  ce3d 0201 0608 2a86 48ce 3d03 0107 0342  .=....*.H.=....B&lt;br /&gt;
        0x0080:  0004 c508 1c19 6106 b7e9 3074 57b9 bb16  ......a...0tW...&lt;br /&gt;
        0x0090:  6959 4a55 81f9 169b cc79 fe10 a882 41fe  iYJU.....y....A.&lt;br /&gt;
        0x00a0:  0697 c9b4 f8f0 5562 7fa2 c7a0 a020 1ac6  ......Ub........&lt;br /&gt;
        0x00b0:  939f 23ff b2fb 07a2 b747 aacc 474a 3dab  ..#......G..GJ=.&lt;br /&gt;
        0x00c0:  2598 0000 0000                           %.....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;00c2&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 194 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... d4c0&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Mixed || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x00 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload with Ephemeral Key&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;f1 766b 547c fcb0 8fce 5d60 a01e e8be 02&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e253 8b6c 90&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate in client request&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x005b (91) || Ephemeral key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0xc1 || &amp;lt;code&amp;gt;30 5930 ... 3dab 2598&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDHE P-384 || ECDHE P-384 public key (DER encoded)&lt;br /&gt;
|-&lt;br /&gt;
| 0xd3-0xd4 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0xd5-0xd6 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* Encryption enabled: ephemeral key present (91 bytes)&lt;br /&gt;
* Client sends no certificate, allowing anonymous encryption setup&lt;br /&gt;
* No signature (unsigned OAP)&lt;br /&gt;
* Ephemeral key is ECDHE P-384 for key exchange&lt;br /&gt;
&lt;br /&gt;
==== Packet 8: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server accepts the encrypted flow allocation request. FLOW_REPLY contains the server&#039;s ephemeral key but no certificate (since client didn&#039;t send one).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:53.810564 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 180:&lt;br /&gt;
        0x0000:  0000 00a2 0042 0040 0000 0000 0000 0000  .....B.@........&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 00f1 766b 547c  ............vkT|&lt;br /&gt;
        0x0030:  fcb0 8fce 5d60 a01e e8be 0218 86a8 e253  ....]`.........S&lt;br /&gt;
        0x0040:  b694 e800 0000 5b30 5930 1306 072a 8648  ......[0Y0...*.H&lt;br /&gt;
        0x0050:  ce3d 0201 0608 2a86 48ce 3d03 0107 0342  .=....*.H.=....B&lt;br /&gt;
        0x0060:  0004 5f3c 6929 cca2 024a ae9f 9aa1 dfc2  .._&amp;lt;i)...J......&lt;br /&gt;
        0x0070:  a493 3ff3 ff58 b054 74dc d2e2 47fc 7c5b  ..?..X.Tt...G.|[&lt;br /&gt;
        0x0080:  eff5 e129 72b4 de1e 7c09 bf8c fe38 5e8b  ...)r...|....8^.&lt;br /&gt;
        0x0090:  b22e 59ed 6eb9 dfda 369d 691e 6e2c 122c  ..Y.n...6.i.n,.,&lt;br /&gt;
        0x00a0:  9936 0000 0000                           .6....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;00a2&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 162 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0042&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0042 || Source Endpoint ID (allocated server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0x00000000 || Response code: 0 (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY with Ephemeral Key&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;f1 766b 547c fcb0 8fce 5d60 a01e e8be 02&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || Echo of client ID || Echoes client&#039;s flow ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;18 86a8 e253 b694 e8&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Echoed timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x46 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x005b (91) || Ephemeral key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x47-0xa1 || &amp;lt;code&amp;gt;30 5930...9936&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDHE P-384 || Server&#039;s ECDHE P-384 public key (DER encoded)&lt;br /&gt;
|-&lt;br /&gt;
| 0xd1-0xd2 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0xd3-0xd4 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID 0x0042 is the new server-side flow endpoint&lt;br /&gt;
* Both keys are now exchanged; client and server can derive shared secret&lt;br /&gt;
* No authentication (no certificates) but encryption is negotiated&lt;br /&gt;
* Response indicates successful allocation&lt;br /&gt;
&lt;br /&gt;
==== Packet 9: Encrypted ECHO_REQUEST ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends encrypted ping request after encryption keys are established. The payload is encrypted with the derived shared secret.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:54.815771 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0042 0060 a691 6d83 8446 cbeb ac95 c2eb  .B.`..m..F......&lt;br /&gt;
        0x0010:  4b42 e819 c67f 92c8 58d7 0641 d8a6 6e1f  KB......X..A..n.&lt;br /&gt;
        0x0020:  fc90 feed ef55 b791 4fbd a832 74bd 8bed  .....U..O..2t...&lt;br /&gt;
        0x0030:  249c 4cee 0fc0 cec6 2f1b aec1 2428 bdbd  $.L...../...$(..&lt;br /&gt;
        0x0040:  36b5 01b5 1257 004e 6ed6 7ecd f0c7 7d11  6....W.Nn.~...}.&lt;br /&gt;
        0x0050:  20ba e81b f43a 4de9 b141 1624 e1ba 0a84  .....:M..A.$....&lt;br /&gt;
        0x0060:  74b1 9a9a                                t...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0042&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0042 || Destination Endpoint ID (server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0060&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 96 bytes || Encrypted payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REQUEST (Encrypted)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x63 || &amp;lt;code&amp;gt;a691 6d83 8446 cbeb...74b1 9a9a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Encrypted Data&#039;&#039;&#039; || 96 || Ciphertext || All 96 bytes encrypted&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* All 96 bytes of oping data (type, ID, timestamps, payload) are encrypted&lt;br /&gt;
* No plaintext oping headers visible; entire packet is ciphertext&lt;br /&gt;
* Flow IDs (0x0042) identify which encryption context to use&lt;br /&gt;
* Ping still works with encryption transparently&lt;br /&gt;
&lt;br /&gt;
==== Packet 10: Encrypted ECHO_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server receives encrypted ping request, decrypts it, and sends encrypted ECHO_REPLY.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:54.819574 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0040 0060 c6ea 2222 5618 0268 b27e 9a91  .@.`..&amp;quot;&amp;quot;V..h.~..&lt;br /&gt;
        0x0010:  f124 1f8d bccc 478c 26fe 9b13 b3cb 5398  .$....G.&amp;amp;.....S.&lt;br /&gt;
        0x0020:  6869 3cdb 4928 510d 4de8 dc6a 3f3a 6a6d  hi&amp;lt;.I(Q.M..j?:jm&lt;br /&gt;
        0x0030:  6487 dcd8 c8cd 1a85 fba2 9ecd 3566 57d1  d...........5fW.&lt;br /&gt;
        0x0040:  1c94 ac35 518e 8509 873a 3a5e 04d9 8ee2  ...5Q....::^....&lt;br /&gt;
        0x0050:  9d74 2527 e425 5433 9d73 9ccd f56a 1f8d  .t%&#039;.%T3.s...j..&lt;br /&gt;
        0x0060:  f328 7237                                .(r7&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0060&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 96 bytes || Encrypted payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REPLY (Encrypted)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x63 || &amp;lt;code&amp;gt;c6ea 2222 5618 0268...f328 7237&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Encrypted Data&#039;&#039;&#039; || 96 || Ciphertext || All 96 bytes encrypted&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID 0x0040 shows reply going back to client-side flow&lt;br /&gt;
* Ciphertext is different from request (different plaintext: type field differs)&lt;br /&gt;
* Both encrypted packets are 96 bytes (same size as Packet 9)&lt;br /&gt;
* Client receives encrypted reply, decrypts it, verifies ID and timestamps match request&lt;br /&gt;
* Encryption is transparent at application layer: oping works exactly as with plaintext flows&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 3 - Authentication and encryption ===&lt;br /&gt;
&lt;br /&gt;
==== Packet 11: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates flow allocation request with encryption enabled. Sends ephemeral public key for ECDHE key exchange but no certificate (client is not authenticating in this tutorial). The management protocol now carries a valid allocated SEID (0x0040).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:58.827411 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 212:&lt;br /&gt;
        0x0000:  0000 00c2 0040 0000 0000 0001 0000 0000  .....@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a66 bb82 95fa  ..f.i.._...f....&lt;br /&gt;
        0x0050:  91a2 7bd3 bd60 1b3e 35f6 b918 86a8 e37e  ..{..`.&amp;gt;5......~&lt;br /&gt;
        0x0060:  c0d2 ad00 0000 5b30 5930 1306 072a 8648  ......[0Y0...*.H&lt;br /&gt;
        0x0070:  ce3d 0201 0608 2a86 48ce 3d03 0107 0342  .=....*.H.=....B&lt;br /&gt;
        0x0080:  0004 9dea c238 6732 4987 1cd4 7133 9614  .....8g2I...q3..&lt;br /&gt;
        0x0090:  9d04 4fde 3f68 42f1 54fb 7ef3 88d0 ffe6  ..O.?hB.T.~.....&lt;br /&gt;
        0x00a0:  7e01 432e 56c2 2d64 72c9 19fc b0cf 1eca  ~.C.V.-dr.......&lt;br /&gt;
        0x00b0:  689e 3536 771a 8041 726c 20e2 d9bb 3589  h.56w..Arl....5.&lt;br /&gt;
        0x00c0:  86e7 0000 0000                           ......&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;00c2&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 194 bytes || Total payload length (excluding DIX header)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID (client flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... d4c0&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Default values || QoS parameters&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || Response code (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x00 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ... 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload with Encryption Setup&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;66 bb82 95fa 91a2 7bd3 bd60 1b3e 35f6 b9&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier for Test 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e37e c0d2 ad&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || Client not authenticating&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 91 (0x005b) || Ephemeral public key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0xc1 || &amp;lt;code&amp;gt;30 5930 1306 ... 3589&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDP-384 public key || Client&#039;s ephemeral ECDHE public key for encryption negotiation&lt;br /&gt;
|-&lt;br /&gt;
| 0xc2-0xc3 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0xc4-0xc5 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0040 - Same as Test 2 (Encrypted) because this is the same client session reusing the same allocated ID from the previous test&lt;br /&gt;
* No Certificate - &amp;lt;code&amp;gt;crt_len = 0x0000&amp;lt;/code&amp;gt; because the client does not have authentication credentials; the server will authenticate instead&lt;br /&gt;
* Ephemeral Key Present - &amp;lt;code&amp;gt;eph_len = 0x005b (91)&amp;lt;/code&amp;gt; because encryption is enabled on the client&lt;br /&gt;
* No Signature - &amp;lt;code&amp;gt;sig_len = 0x0000&amp;lt;/code&amp;gt; because client is not signing (no certificate to sign with)&lt;br /&gt;
* FLOW_REQ Message Type - Code field is 0x00, and service hash is present because FLOW_REQ always includes the service hash&lt;br /&gt;
* Timestamp Consistency - Same OAP ID and timestamp structure as Test 2, but with additional security handshake&lt;br /&gt;
&lt;br /&gt;
==== Packet 12: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds to client&#039;s FLOW_REQ by sending FLOW_REPLY with its certificate for authentication, ephemeral public key for ECDHE encryption setup, and a digital signature proving ownership of the certificate. This is the full authentication response.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data (abbreviated):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:58.828806 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 843:&lt;br /&gt;
        0x0000:  0000 0339 0043 0040 0000 0000 0000 0000  ...9.C.@........&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 0066 bb82 95fa  ...........f....&lt;br /&gt;
        0x0030:  91a2 7bd3 bd60 1b3e 35f6 b918 86a8 e37e  ..{..`.&amp;gt;5......~&lt;br /&gt;
        0x0040:  d566 a002 2f30 8202 2b30 8201 b2a0 0302  .f../0..+0......&lt;br /&gt;
        (... certificate and signature bytes ...)&lt;br /&gt;
        0x0320:  ef11 c358 f5d0 5cd7 3906 adf1 8a2c 9b25  ...X..\.9....,.%&lt;br /&gt;
        0x0330:  dc78 6050 ab61 3a3f 81c0 254b d193 7827  .x`P.a:?..%K..x&#039;&lt;br /&gt;
        0x0340:  c0e9 38c7 e0d1 c517 d299 9992 07         ..8..........&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0339&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 825 bytes || Total payload length (excluding DIX header)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0043&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0043 || Source Endpoint ID (server-side allocated flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client&#039;s flow ID from FLOW_REQ)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Default values || QoS parameters&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0x00000000 || Response code: 0 (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY Payload with Full Authentication&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;66 bb82 95fa 91a2 7bd3 bd60 1b3e 35f6 b9&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || &#039;&#039;&#039;Same ID as client&#039;s FLOW_REQ&#039;&#039;&#039; (echoed back)&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;18 86a8 e37e d566 a0&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Server&#039;s timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;022f&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 559 (0x022f) || Server certificate length: 559 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x243 || &amp;lt;code&amp;gt;2f30 8202 2b ... 81c8 30&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Certificate&#039;&#039;&#039; || 559 || DER-encoded X.509 || Server&#039;s certificate (signed by intermediate CA)&lt;br /&gt;
|-&lt;br /&gt;
| 0x244-0x245 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 91 (0x005b) || Server&#039;s ephemeral public key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x246-0x2a0 || &amp;lt;code&amp;gt;30 5930 1306 ... 9936&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDP-384 public key || Server&#039;s ephemeral ECDHE public key&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a4-0x2a5 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a6-0x2a7 || &amp;lt;code&amp;gt;0068&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 104 (0x0068) || Digital signature length: 104 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a8-0x30f || &amp;lt;code&amp;gt;30 6602 3100 ... 07&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Signature&#039;&#039;&#039; || 104 || ECDSA signature (DER encoded) || Server&#039;s signature over OAP header proving certificate ownership&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0043 - New server-side endpoint ID allocated for this authenticated flow&lt;br /&gt;
* DEID is 0x0040 - Client&#039;s flow ID from the FLOW_REQ, establishing the bidirectional flow&lt;br /&gt;
* FLOW_REPLY Message Type - Code field is 0x01, &#039;&#039;&#039;no service hash&#039;&#039;&#039; (already identified in FLOW_REQ)&lt;br /&gt;
* Full Certificate - &amp;lt;code&amp;gt;crt_len = 0x022f (559)&amp;lt;/code&amp;gt; carrying server&#039;s complete X.509 certificate signed by intermediate CA&lt;br /&gt;
* Ephemeral Key Present - &amp;lt;code&amp;gt;eph_len = 0x005b (91)&amp;lt;/code&amp;gt; with server&#039;s ECDHE public key for encryption&lt;br /&gt;
* Signature Included - &amp;lt;code&amp;gt;sig_len = 0x0068 (104)&amp;lt;/code&amp;gt; containing ECDSA digital signature over the entire OAP header&lt;br /&gt;
* Same OAP ID - Server echoes back the exact ID from client&#039;s FLOW_REQ to confirm association (included in signature, binding response to this specific client request)&lt;br /&gt;
* Large Payload - Total of 825 bytes due to certificate (559) + ephemeral key (91) + signature (104) + overhead&lt;br /&gt;
* Authentication Complete - Client verifies: (1) certificate against CA store, (2) signature over entire response ensures authenticity and integrity, (3) echoed ID binds response to this specific request, (4) timestamp prevents replay attacks&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds with its certificate for authentication, ephemeral public key for ECDHE encryption, and a digital signature proving ownership of the certificate.&lt;br /&gt;
&lt;br /&gt;
==== Packet 13: Encrypted ECHO_REQUEST ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends encrypted ping request after authentication handshake. All application data is protected by encryption using the ephemeral keys established in Packets 11-12.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:59.836485 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0043 0060 3bed 0b48 1be1 6930 cf3d dee9  .C.`.;..H..i0.=..&lt;br /&gt;
        0x0010:  4fc9 774b 5d63 cc9b 5a34 6604 f9ac 1016  O.wK]c..Z4f.....&lt;br /&gt;
        0x0020:  1c6d c9ac f80e dc89 31c1 9634 1a4f b2c7  .m......1..4.O..&lt;br /&gt;
        0x0030:  4721 e402 8259 b0aa 8870 4566 33d1 9c18  G!...Y..  .pEf3...&lt;br /&gt;
        0x0040:  06da 50c3 8b75 86b0 f240 d109 840e a6cd  ..P..u...@......&lt;br /&gt;
        0x0050:  d115 77cb 5652 5bfb e6d5 0ca9 dbc3 d0b8  ..w.VR[.........&lt;br /&gt;
        0x0060:  0058 fd19                                .X..&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame Analysis:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Length !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Source EID || 0x00-0x01 || 2 bytes || &amp;lt;code&amp;gt;0x0043&amp;lt;/code&amp;gt; || Client flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| Destination EID || 0x02-0x03 || 2 bytes || &amp;lt;code&amp;gt;0x0060&amp;lt;/code&amp;gt; || Server flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Encrypted Payload&#039;&#039;&#039; || &#039;&#039;&#039;0x04-0x71&#039;&#039;&#039; || &#039;&#039;&#039;110 bytes&#039;&#039;&#039; || &#039;&#039;&#039;Ciphertext&#039;&#039;&#039; || &#039;&#039;&#039;AES-encrypted oping ECHO_REQUEST data&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* No visible protocol structure - all application data appears as ciphertext&lt;br /&gt;
* Uses the same source/destination EID pair (0x0043 → 0x0060) established in the FLOW_REQ/FLOW_REPLY handshake&lt;br /&gt;
* Encryption is done using the ephemeral key (91 bytes) exchanged in Packet 11&#039;s OAP header&lt;br /&gt;
* Unlike Packets 11-12, this packet contains no certificate, public keys, or signatures&lt;br /&gt;
* The 110-byte encrypted data corresponds to the original oping ECHO_REQUEST message&lt;br /&gt;
&lt;br /&gt;
==== Packet 14: Encrypted ECHO_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server sends encrypted ping reply. Note that the flow identifiers swap, demonstrating bidirectional encrypted communication.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:59.836930 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0040 0060 d552 e100 e681 940c e35a 07d0  .@.`..........Z..&lt;br /&gt;
        0x0010:  a293 1d73 33a5 854e 0fce 4f4d 6655 267a  ...s3..N..OMfU&amp;amp;z&lt;br /&gt;
        0x0020:  3de2 663b 709d 739a a696 2ddd 7b34 28b8  =.f;p.s...-{4(...&lt;br /&gt;
        0x0030:  5a98 eec2 52c6 4288 3885 ae16 e466 4181  Z...R.B.8...fA..&lt;br /&gt;
        0x0040:  f2d6 44c1 b51b 8728 58a4 7525 fb5e 3fd6  ..D...(X.u%.^?..&lt;br /&gt;
        0x0050:  7e49 532a d2a5 bea7 55e9 c274 f1b2 0412  ~IS*....U..t....&lt;br /&gt;
        0x0060:  73d4 6436                                s.d6&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame Analysis:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Length !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Source EID || 0x00-0x01 || 2 bytes || &amp;lt;code&amp;gt;0x0040&amp;lt;/code&amp;gt; || Client&#039;s inbound flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| Destination EID || 0x02-0x03 || 2 bytes || &amp;lt;code&amp;gt;0x0060&amp;lt;/code&amp;gt; || Server flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Encrypted Payload&#039;&#039;&#039; || &#039;&#039;&#039;0x04-0x71&#039;&#039;&#039; || &#039;&#039;&#039;110 bytes&#039;&#039;&#039; || &#039;&#039;&#039;Ciphertext&#039;&#039;&#039; || &#039;&#039;&#039;AES-encrypted oping ECHO_REPLY data&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* The EID in offset 0x00 is now 0x0040 (server&#039;s view of client&#039;s inbound flow)&lt;br /&gt;
* Uses the same ephemeral key material as Packet 13, but encryption direction is reversed&lt;br /&gt;
* Both packets use AES-GCM with keys derived from the ECDH exchange&lt;br /&gt;
* Timestamp 17:39:59.836930 is only 445 microseconds after Packet 13, indicating server-side processing&lt;br /&gt;
* The 110-byte encrypted ECHO_REPLY payload is the same size as the request&lt;br /&gt;
* All application data is protected by both authentication (X.509 + ECDSA) and encryption (AES)&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 4 - Authentication, no encryption ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Packet 15: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates flow allocation with authentication enabled but encryption disabled. This FLOW_REQ carries an OAP header but &#039;&#039;&#039;no ephemeral key&#039;&#039;&#039; since the client does not request encrypted communication.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:03.413372 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 121:&lt;br /&gt;
        0x0000:  0000 0067 0040 0000 0000 0001 0000 0000  ...g.@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a8f a6ab 6ea7  ..f.i.._........&lt;br /&gt;
        0x0050:  ef89 68e1 ed1e 2ede 0919 fa18 86a8 e490  .h..............&lt;br /&gt;
        0x0060:  0de6 6100 0000 0000 0000 00              ..a.....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0067&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 103 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID (allocated flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... dc40&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Mixed || &lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload (No Encryption)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;8f a6ab 6ea7 ef89 68e1 ed1e 2ede 0919 fa&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e490 0de6 61&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate in client request&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;No ephemeral key (no encryption)&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0x68 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x69-0x6a || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* No encryption enabled: ephemeral key absent (Eph_len = 0x0000)&lt;br /&gt;
* Client requests authentication only&lt;br /&gt;
* Server will respond with certificate + signature but no ephemeral key&lt;br /&gt;
* Packet is minimal compared to Packet 11 (Test 3) which includes 91-byte ephemeral key&lt;br /&gt;
&lt;br /&gt;
==== Packet 16: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server accepts the authenticated (but not encrypted) flow allocation request. FLOW_REPLY contains the server&#039;s X.509 certificate and ECDSA signature for client authentication, but &#039;&#039;&#039;no ephemeral key&#039;&#039;&#039; since encryption is not being used.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data (abbreviated):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:03.416675 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 751:&lt;br /&gt;
        0x0000:  0000 02dd 0041 0040 0000 0000 0000 0000  .......A.@......&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 008f a6ab 6ea7  ................&lt;br /&gt;
        0x0030:  ef89 68e1 ed1e 2ede 0919 fa18 86a8 e490  .h..............&lt;br /&gt;
        0x0040:  3754 a702 2f30 8202 2b30 8201 b2a0 0302  7T../0..+0......&lt;br /&gt;
        0x0050:  0102 0202 1000 300a 0608 2a86 48ce 3d04  ......0...*.H.=.&lt;br /&gt;
        (... certificate and signature bytes ...)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;02dd&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 733 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0041 || Source Endpoint ID (allocated server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0x00000000 || Response code: 0 (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY Payload with Certificate and Signature (No Ephemeral Key)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;8f a6ab 6ea7 ef89 68e1 ed1e 2ede 0919&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || &#039;&#039;&#039;Same ID as client&#039;s FLOW_REQ&#039;&#039;&#039; (Packet 15 echoed back)&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;fa18 86a8 e490 3754&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Server&#039;s timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;a702&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x02a7 (679 decimal) || &#039;&#039;&#039;Certificate length: 679 bytes&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x270 || &amp;lt;code&amp;gt;2f30 8202 2b30 8201 b2a0 0302 ... (DER certificate) ...&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Certificate&#039;&#039;&#039; || 679 || DER-encoded X.509 || Server&#039;s certificate signed by intermediate CA&lt;br /&gt;
|-&lt;br /&gt;
| 0x271-0x272 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;No ephemeral key&#039;&#039;&#039; (no encryption)&lt;br /&gt;
|-&lt;br /&gt;
| 0x273-0x274 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x275-0x276 || &amp;lt;code&amp;gt;0067&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0067 (103 decimal) || &#039;&#039;&#039;ECDSA signature length: 103 bytes&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x277-0x2dd || &amp;lt;code&amp;gt;3065 0230 75dc 5717 ... 83&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Signature&#039;&#039;&#039; || 103 || ECDSA signature (DER encoded) || Server&#039;s ECDSA signature proving certificate ownership&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0041 - New server-side endpoint ID allocated for this authenticated flow&lt;br /&gt;
* DEID is 0x0040 - Client&#039;s flow ID from Packet 15 FLOW_REQ, establishing the bidirectional flow&lt;br /&gt;
* FLOW_REPLY Message Type - Code field is 0x01, &#039;&#039;&#039;no service hash&#039;&#039;&#039; (already identified in FLOW_REQ)&lt;br /&gt;
* Certificate Field - &amp;lt;code&amp;gt;crt_len = 0x02a7 (679)&amp;lt;/code&amp;gt; carrying server&#039;s X.509 certificate signed by intermediate CA&lt;br /&gt;
* Separate Signature Field - &amp;lt;code&amp;gt;sig_len = 0x0067 (103)&amp;lt;/code&amp;gt; with ECDSA signature over entire OAP header&lt;br /&gt;
* No Ephemeral Key - &amp;lt;code&amp;gt;eph_len = 0x0000&amp;lt;/code&amp;gt; since encryption is &#039;&#039;&#039;not&#039;&#039;&#039; being used in Test 4&lt;br /&gt;
* Same OAP ID - Server echoes back the exact ID from client&#039;s FLOW_REQ (included in signature, binding response to this specific client request)&lt;br /&gt;
* Complete OAP Structure - Full OAP header with all standard fields, just without ephemeral key data&lt;br /&gt;
* Plaintext Data Exchange - After this FLOW_REPLY, all subsequent application data will be transmitted in plaintext (but authenticated via certificate + signature verification)&lt;br /&gt;
&lt;br /&gt;
==== Packet 17: ECHO_REQUEST ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends plaintext ECHO_REQUEST data through the authenticated (but unencrypted) flow. The oping application&#039;s ping request is transmitted directly without encryption, relying on the earlier certificate+signature authentication for security.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:04.419664 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0041 0040 0000 0000 0000 0000 8177 0000  .A.@............&lt;br /&gt;
        0x0010:  0000 0000 aa16 1c16 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0041 || Destination Endpoint ID (server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Total application data length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Application Data - Oping Echo Request (Plaintext)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000000 || Message type: ECHO_REQUEST&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Sequence number / message identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;8177 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Seconds component from CLOCK_REALTIME&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;aa16 1c16 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Nanoseconds component (0-999999999)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || Application data || Probe payload (zero-filled for 64 bytes total)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID Pair: 0x0041 → Server Flow - Data is directed to the server&#039;s endpoint ID allocated in Packet 16 FLOW_REPLY&lt;br /&gt;
* Plaintext Transmission - No encryption layer; oping payload is sent as-is (compare to Packet 13 which had encryption)&lt;br /&gt;
* Authenticated Flow - Although plaintext, this data travels on the authenticated flow established in Packet 16 (certificate + signature verified)&lt;br /&gt;
* Type = ECHO_REQUEST - 0x00000000 indicates client-to-server ping request&lt;br /&gt;
* ID = 0 - Sequence number for matching request/reply pairs&lt;br /&gt;
* Test 4 Characteristic - Demonstrates authenticated communication &#039;&#039;&#039;without&#039;&#039;&#039; encryption; application data is readable but cryptographically bound to the authenticated flow&lt;br /&gt;
* Contrast to Test 3 - Packet 13 (Test 3 encrypted ECHO_REQUEST) was 114 bytes with ciphertext; this packet is 82 bytes of plaintext&lt;br /&gt;
* Total Payload - 64 bytes total (4 + 4 + 8 + 8 + 40 bytes payload)&lt;br /&gt;
&lt;br /&gt;
==== Packet 18: ECHO_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds with plaintext ECHO_REPLY data, echoing back the client&#039;s request. This confirms successful bidirectional communication over the authenticated (but unencrypted) flow.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:04.420088 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0040 0040 0000 0001 0000 0000 8177 0000  .@.@............&lt;br /&gt;
        0x0010:  0000 0000 aa16 1c16 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Total application data length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Application Data - Oping Echo Reply (Plaintext)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0001&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000001 || Message type: ECHO_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Echo of request sequence number&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;8177 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Echoed request timestamp (seconds)&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;aa16 1c16 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Echoed request timestamp (nanoseconds)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || Application data || Echoed probe payload (zero-filled for 64 bytes total)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID Pair: 0x0040 → Client Flow - Server responds to client&#039;s endpoint ID from Packet 15 FLOW_REQ&lt;br /&gt;
* Type = ECHO_REPLY - 0x00000001 indicates server-to-client response&lt;br /&gt;
* ID = 0 - Echoes the request sequence number, matching this response to the request&lt;br /&gt;
* Timestamps Echo Request - Both timestamp fields are copied from Packet 17 unchanged (8177 0000 0000 0000 and aa16 1c16 0000 0000)&lt;br /&gt;
* Plaintext Reply - No encryption; server&#039;s response payload is readable (compare to Packet 14 which had encryption)&lt;br /&gt;
* Authenticated Channel - Although plaintext, this reply is part of the authenticated flow; client can verify integrity through earlier certificate+signature&lt;br /&gt;
* Test 4 Completion - Demonstrates &#039;&#039;&#039;full bidirectional plaintext communication&#039;&#039;&#039; over an authenticated (but unencrypted) flow&lt;br /&gt;
* Contrast to Test 3 - Packet 14 (Test 3 encrypted ECHO_REPLY) was 114 bytes with ciphertext; this packet is 82 bytes of plaintext&lt;br /&gt;
* Total Payload - 64 bytes total (4 + 4 + 8 + 8 + 40 bytes payload)&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Tutorial_06&amp;diff=1897</id>
		<title>Ouroboros Tutorial 06</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Tutorial_06&amp;diff=1897"/>
		<updated>2026-02-14T15:14:50Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* Test 4: With Authentication, No Encryption */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Ouroboros Tutorial 06 - Authenticated Flows =&lt;br /&gt;
&lt;br /&gt;
This tutorial demonstrates setting up and using authenticated flows in Ouroboros with certificate-based authentication.&lt;br /&gt;
&lt;br /&gt;
The overall flow of authenticated flow allocation is&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Client (IRMd)                              Server (IRMd)&lt;br /&gt;
     |                                           |&lt;br /&gt;
     | 1. Load client cert/key                   |&lt;br /&gt;
     | 2. Generate ephemeral keypair             |&lt;br /&gt;
     | 3. Build OAP_HDR (id, ts, crt, eph)       |&lt;br /&gt;
     | 4. Sign header with client key            |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |-------- FLOW_REQ (OAP_HDR) -------------&amp;gt; |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |                                           | 5. Load server cert/key&lt;br /&gt;
     |                                           | 6. Verify client cert against CA&lt;br /&gt;
     |                                           | 7. Verify client signature&lt;br /&gt;
     |                                           | 8. Generate ephemeral keypair&lt;br /&gt;
     |                                           | 9. Derive symmetric key (ECDHE)&lt;br /&gt;
     |                                           | 10. Build response OAP_HDR&lt;br /&gt;
     |                                           | 11. Sign with server key&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |&amp;lt;------- FLOW_REPLY (OAP_HDR) ------------ |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     | 12. Verify server cert against CA         |&lt;br /&gt;
     | 13. Verify server signature               |&lt;br /&gt;
     | 14. Derive symmetric key (ECDHE)          |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |===========================================|&lt;br /&gt;
     |         Encrypted data channel            |&lt;br /&gt;
     |===========================================|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Tutorial Directory:&#039;&#039;&#039; This tutorial will execute in &amp;lt;code&amp;gt;/tmp/o7s-tut06/&amp;lt;/code&amp;gt;. All configuration files, generated certificates, logs, and packet captures will be stored in this directory.&lt;br /&gt;
&lt;br /&gt;
We create a complete PKI (Public Key Infrastructure):&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Root CA&#039;&#039;&#039; (&amp;lt;code&amp;gt;ca.tut.o7s&amp;lt;/code&amp;gt;): Self-signed trust anchor&lt;br /&gt;
* &#039;&#039;&#039;Intermediate CA&#039;&#039;&#039; (&amp;lt;code&amp;gt;sign.tut.o7s&amp;lt;/code&amp;gt;): Signed by root with pathlen:0 constraint&lt;br /&gt;
* &#039;&#039;&#039;Server Certificate&#039;&#039;&#039; (&amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;): Signed by intermediate CA&lt;br /&gt;
&lt;br /&gt;
This tutorial uses ECDSA P-384 with SHA-384 hashing.&lt;br /&gt;
&lt;br /&gt;
== Setting Up the Tutorial ==&lt;br /&gt;
&lt;br /&gt;
To properly understand and debug the authenticated flows, this tutorial uses a debug build of Ouroboros with OAP protocol debugging enabled.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /path/to/ouroboros&lt;br /&gt;
mkdir build &amp;amp;&amp;amp; cd build&lt;br /&gt;
cmake -DCMAKE_BUILD_TYPE=Debug -DDEBUG_PROTO_OAP=ON ..&lt;br /&gt;
make -j$(nproc)&lt;br /&gt;
sudo make install&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When built with these options, the IRMd will output detailed OAP protocol information.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Files ===&lt;br /&gt;
&lt;br /&gt;
The following three files should be created in the tutorial directory (&amp;lt;code&amp;gt;/tmp/o7s-tut06/&amp;lt;/code&amp;gt;) before starting the tutorial:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;tut06.conf&#039;&#039;&#039; - IRMd configuration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
# Ouroboros Tutorial 06 - Authenticated Flows Configuration&lt;br /&gt;
# Uses system-installed certificates at /etc/ouroboros/security/&lt;br /&gt;
&lt;br /&gt;
[name.&amp;quot;sec.oping.tut.o7s&amp;quot;]&lt;br /&gt;
prog=[&amp;quot;/usr/bin/oping&amp;quot;]&lt;br /&gt;
args=[&amp;quot;--listen&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
[eth-dix.eth-dix-lo]&lt;br /&gt;
bootstrap=&amp;quot;eth-dix-network&amp;quot;&lt;br /&gt;
dev=&amp;quot;lo&amp;quot;&lt;br /&gt;
reg=[&amp;quot;sec.oping.tut.o7s&amp;quot;]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ca.tut.o7s.cnf&#039;&#039;&#039; - OpenSSL configuration for PKI generation&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
# Unified OpenSSL Configuration for Ouroboros Tutorial 06&lt;br /&gt;
# Named CA sections: CA_root (signs intermediate), CA_intermediate (signs server)&lt;br /&gt;
# Usage: openssl ca -name CA_root -config ca.tut.o7s.cnf ...&lt;br /&gt;
&lt;br /&gt;
[ req ]&lt;br /&gt;
default_bits       = 384&lt;br /&gt;
default_keyfile    = private/key.pem&lt;br /&gt;
distinguished_name = req_distinguished_name&lt;br /&gt;
string_mask        = utf8only&lt;br /&gt;
default_md         = sha384&lt;br /&gt;
x509_extensions    = v3_ca&lt;br /&gt;
&lt;br /&gt;
[ req_distinguished_name ]&lt;br /&gt;
countryName                 = Country Name (2 letter code)&lt;br /&gt;
stateOrProvinceName         = State or Province Name&lt;br /&gt;
localityName                = Locality Name&lt;br /&gt;
organizationName            = Organization Name&lt;br /&gt;
commonName                  = Common Name&lt;br /&gt;
&lt;br /&gt;
countryName_default         = BE&lt;br /&gt;
stateOrProvinceName_default = OVL&lt;br /&gt;
localityName_default        = Ghent&lt;br /&gt;
organizationName_default    = o7s&lt;br /&gt;
&lt;br /&gt;
[ ca ]&lt;br /&gt;
default_ca = CA_root&lt;br /&gt;
&lt;br /&gt;
[ CA_root ]&lt;br /&gt;
dir             = ./pki/root&lt;br /&gt;
database        = $dir/index.txt&lt;br /&gt;
serial          = $dir/serial&lt;br /&gt;
new_certs_dir   = $dir/certs&lt;br /&gt;
certificate     = $dir/certs/ca.tut.o7s.crt.pem&lt;br /&gt;
private_key     = $dir/private/ca.tut.o7s.key.pem&lt;br /&gt;
default_days    = 3650&lt;br /&gt;
default_md      = sha384&lt;br /&gt;
policy          = policy_loose&lt;br /&gt;
&lt;br /&gt;
[ CA_intermediate ]&lt;br /&gt;
dir             = ./pki/sign&lt;br /&gt;
database        = $dir/index.txt&lt;br /&gt;
serial          = $dir/serial&lt;br /&gt;
new_certs_dir   = $dir/certs&lt;br /&gt;
certificate     = $dir/certs/sign.tut.o7s.crt.pem&lt;br /&gt;
private_key     = $dir/private/sign.tut.o7s.key.pem&lt;br /&gt;
default_days    = 365&lt;br /&gt;
default_md      = sha384&lt;br /&gt;
policy          = policy_loose&lt;br /&gt;
&lt;br /&gt;
[ policy_loose ]&lt;br /&gt;
commonName = supplied&lt;br /&gt;
&lt;br /&gt;
[ v3_ca ]&lt;br /&gt;
subjectKeyIdentifier = hash&lt;br /&gt;
authorityKeyIdentifier = keyid:always,issuer&lt;br /&gt;
basicConstraints = critical, CA:true&lt;br /&gt;
keyUsage = critical, digitalSignature, cRLSign, keyCertSign&lt;br /&gt;
&lt;br /&gt;
[ v3_intermediate_ca ]&lt;br /&gt;
subjectKeyIdentifier = hash&lt;br /&gt;
authorityKeyIdentifier = keyid:always,issuer&lt;br /&gt;
basicConstraints = critical, CA:true, pathlen:0&lt;br /&gt;
keyUsage = critical, digitalSignature, cRLSign, keyCertSign&lt;br /&gt;
&lt;br /&gt;
[ server_cert ]&lt;br /&gt;
basicConstraints = CA:FALSE&lt;br /&gt;
subjectKeyIdentifier = hash&lt;br /&gt;
authorityKeyIdentifier = keyid,issuer:always&lt;br /&gt;
keyUsage = critical, digitalSignature, keyEncipherment&lt;br /&gt;
extendedKeyUsage = serverAuth&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;gen-pki.sh&#039;&#039;&#039; - PKI generation script&lt;br /&gt;
&lt;br /&gt;
This script will:&lt;br /&gt;
1. Create the directory structure&lt;br /&gt;
2. Generate the root CA key and certificate&lt;br /&gt;
3. Generate the intermediate CA key and CSR&lt;br /&gt;
4. Sign the intermediate CA certificate&lt;br /&gt;
5. Generate the server certificate key and CSR&lt;br /&gt;
6. Sign the server certificate&lt;br /&gt;
7. Verify the complete certificate chain&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
# Ouroboros Tutorial 06 - PKI Generation Script (Simplified)&lt;br /&gt;
# Generates: Root CA, Intermediate CA, and Server Certificate&lt;br /&gt;
&lt;br /&gt;
set -e&lt;br /&gt;
&lt;br /&gt;
if [ ! -f ca.tut.o7s.cnf ]; then&lt;br /&gt;
    echo &amp;quot;ERROR: ca.tut.o7s.cnf not found&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
mkdir -p pki/{root,sign,server}/{certs,private,csr}&lt;br /&gt;
&lt;br /&gt;
# Root CA&lt;br /&gt;
openssl ecparam -genkey -name secp384r1 -out pki/root/private/ca.tut.o7s.key.pem 2&amp;gt;/dev/null&lt;br /&gt;
openssl req -new -x509 -sha384 -days 7300 \&lt;br /&gt;
    -config ca.tut.o7s.cnf \&lt;br /&gt;
    -key pki/root/private/ca.tut.o7s.key.pem \&lt;br /&gt;
    -out pki/root/certs/ca.tut.o7s.crt.pem \&lt;br /&gt;
    -subj &amp;quot;/C=BE/ST=OVL/L=Ghent/O=o7s/CN=ca.tut.o7s&amp;quot; 2&amp;gt;/dev/null&lt;br /&gt;
&lt;br /&gt;
# Intermediate CA&lt;br /&gt;
openssl ecparam -genkey -name secp384r1 -out pki/sign/private/sign.tut.o7s.key.pem 2&amp;gt;/dev/null&lt;br /&gt;
openssl req -new -sha384 \&lt;br /&gt;
    -config ca.tut.o7s.cnf \&lt;br /&gt;
    -key pki/sign/private/sign.tut.o7s.key.pem \&lt;br /&gt;
    -out pki/sign/csr/sign.tut.o7s.csr \&lt;br /&gt;
    -subj &amp;quot;/C=BE/ST=OVL/L=Ghent/O=o7s/CN=sign.tut.o7s&amp;quot; 2&amp;gt;/dev/null&lt;br /&gt;
&lt;br /&gt;
touch pki/root/index.txt&lt;br /&gt;
echo 1000 &amp;gt; pki/root/serial&lt;br /&gt;
&lt;br /&gt;
openssl ca -name CA_root -config ca.tut.o7s.cnf \&lt;br /&gt;
    -extensions v3_intermediate_ca -days 3650 -md sha384 -batch \&lt;br /&gt;
    -in pki/sign/csr/sign.tut.o7s.csr \&lt;br /&gt;
    -out pki/sign/certs/sign.tut.o7s.crt.pem 2&amp;gt;&amp;amp;1 | grep -E &amp;quot;Signature ok|Database updated&amp;quot; || true&lt;br /&gt;
&lt;br /&gt;
# Server Certificate&lt;br /&gt;
openssl ecparam -genkey -name secp384r1 -out pki/server/private/sec.oping.tut.o7s.key.pem 2&amp;gt;/dev/null&lt;br /&gt;
openssl req -new -sha384 \&lt;br /&gt;
    -config ca.tut.o7s.cnf \&lt;br /&gt;
    -key pki/server/private/sec.oping.tut.o7s.key.pem \&lt;br /&gt;
    -out pki/server/csr/sec.oping.tut.o7s.csr \&lt;br /&gt;
    -subj &amp;quot;/C=BE/ST=OVL/L=Ghent/O=o7s/CN=sec.oping.tut.o7s&amp;quot; 2&amp;gt;/dev/null&lt;br /&gt;
&lt;br /&gt;
touch pki/sign/index.txt&lt;br /&gt;
echo 1000 &amp;gt; pki/sign/serial&lt;br /&gt;
&lt;br /&gt;
openssl ca -name CA_intermediate -config ca.tut.o7s.cnf \&lt;br /&gt;
    -extensions server_cert -days 365 -md sha384 -batch \&lt;br /&gt;
    -in pki/server/csr/sec.oping.tut.o7s.csr \&lt;br /&gt;
    -out pki/server/certs/sec.oping.tut.o7s.crt.pem 2&amp;gt;&amp;amp;1 | grep -E &amp;quot;Signature ok|Database updated&amp;quot; || true&lt;br /&gt;
&lt;br /&gt;
# Verify chain&lt;br /&gt;
openssl verify -CAfile pki/root/certs/ca.tut.o7s.crt.pem \&lt;br /&gt;
    -untrusted pki/sign/certs/sign.tut.o7s.crt.pem \&lt;br /&gt;
    pki/server/certs/sec.oping.tut.o7s.crt.pem &amp;gt; /dev/null 2&amp;gt;&amp;amp;1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;PKI generation complete.&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Part 1: Running the Tutorial - Single Session with 4 Tests ==&lt;br /&gt;
&lt;br /&gt;
This section demonstrates a single continuous session with one IRMd and tcpdump instance. The configuration file (&amp;lt;code&amp;gt;tut06.conf&amp;lt;/code&amp;gt;) includes autostart for oping, so the server is ready immediately when IRMd starts.&lt;br /&gt;
&lt;br /&gt;
First install the &#039;&#039;&#039;CA and Intermediate CA only&#039;&#039;&#039; to the system security directories. The server certificate will be installed later during Test 3 (authentication test):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo mkdir -p /etc/ouroboros/security/{cacert,untrusted,server/sec.oping.tut.o7s,client/sec.oping.tut.o7s}&lt;br /&gt;
&lt;br /&gt;
# Run the PKI generation script&lt;br /&gt;
cd /tmp/o7s-tut06&lt;br /&gt;
sudo chmod +x gen-pki.sh&lt;br /&gt;
sudo ./gen-pki.sh&lt;br /&gt;
&lt;br /&gt;
# Install Root CA (trust anchor)&lt;br /&gt;
sudo cp pki/root/certs/ca.tut.o7s.crt.pem /etc/ouroboros/security/cacert/&lt;br /&gt;
&lt;br /&gt;
# Install Intermediate CA (for certificate chain validation)&lt;br /&gt;
sudo cp pki/sign/certs/sign.tut.o7s.crt.pem /etc/ouroboros/security/untrusted/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Running the Tutorial (3 Terminals) ===&lt;br /&gt;
&lt;br /&gt;
In this tutorial, we run a single IRMd session with a concurrent tcpdump instance to capture it. We then run four oping client tests while the IRMd/tcpdump sessions are going, modifying the security configuration between tests. After the tests are complete, we can will down the IRMd and tcpdump sessions with Ctrl-C.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Terminal 1: Start tcpdump to capture all packets (runs continuously)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo tcpdump -i lo -n -A -v -U -w /tmp/o7s-tut06/tut06.pcap &amp;quot;ether proto 0xa000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Terminal 2: Start IRMd with debug output (runs continuously)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /tmp/o7s-tut06&lt;br /&gt;
sudo irmd --config tut06.conf --stdout 2&amp;gt;&amp;amp;1 | tee /tmp/o7s-tut06/irmd.log&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Terminal 3: Run the tests&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Test 1: No Authentication, No Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Verify directories are empty&lt;br /&gt;
sudo ls -la /etc/ouroboros/security/client/sec.oping.tut.o7s/*&lt;br /&gt;
sudo ls -la /etc/ouroboros/security/server/sec.oping.tut.o7s/*&lt;br /&gt;
&lt;br /&gt;
# Run first ping test&lt;br /&gt;
echo &amp;quot;=== Test 1: No Authentication, No Encryption ===&amp;quot;&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 1: Client initiates plaintext flow allocation&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [60e824383b3fbd6a] KEX config: algo=none, mode=server-encap, cipher=none.&lt;br /&gt;
irmd/oap(PP): OAP_HDR [60e824383b3fbd6a @ 2026-02-14 14:08:56 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Cipher: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   KDF: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 1: Server accepts and completes handshake&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [60e824383b3fbd6a @ 2026-02-14 14:08:57 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Cipher: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   KDF: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [48 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; All OAP fields are &amp;lt;code&amp;gt;&amp;lt;none&amp;gt;&amp;lt;/code&amp;gt; because no security is configured (except for the request hash in the response). Flow succeeds with plaintext communication.&lt;br /&gt;
&lt;br /&gt;
==== Test 2: No Authentication, With Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Enable encryption for client only&lt;br /&gt;
sudo touch /etc/ouroboros/security/client/sec.oping.tut.o7s/enc.conf&lt;br /&gt;
&lt;br /&gt;
# Run second ping test&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 2: Client initiates flow with encryption enabled&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(II): Encryption enabled for sec.oping.tut.o7s.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] KEX config: algo=prime256v1, mode=server-encap, cipher=aes-256-gcm.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Generated ephemeral prime256v1 keys (91 bytes).&lt;br /&gt;
irmd/oap(PP): OAP_HDR [80fd6f9509a996b0 @ 2026-02-14 14:09:38 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes] [Server encaps]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: sha256&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 2: Server receives and responds with ephemeral key&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] No crt provided.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Selected client cipher aes-256-gcm.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Selected client KDF sha256.&lt;br /&gt;
irmd/oap(II): [80fd6f9509a996b0] No key exchange.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Generated prime256v1 ephemeral keys.&lt;br /&gt;
irmd/oap(PP): OAP_HDR [80fd6f9509a996b0 @ 2026-02-14 14:09:38 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes] [Server encaps]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [32 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; Both client and server generate ephemeral keys (91 bytes each) for encryption. No certificates because authentication is not required. Encryption and authentication are independent.&lt;br /&gt;
&lt;br /&gt;
==== Test 3: With Authentication, With Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Install server certificates and keys&lt;br /&gt;
sudo cp /tmp/o7s-tut06/pki/server/certs/sec.oping.tut.o7s.crt.pem \&lt;br /&gt;
        /etc/ouroboros/security/server/sec.oping.tut.o7s/crt.pem&lt;br /&gt;
sudo cp /tmp/o7s-tut06/pki/server/private/sec.oping.tut.o7s.key.pem \&lt;br /&gt;
        /etc/ouroboros/security/server/sec.oping.tut.o7s/key.pem&lt;br /&gt;
&lt;br /&gt;
# enc.conf is still in place from Test 2&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 3: Client initiates flow with encryption and server has certificate&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [c904b18b563dc1b0 @ 2026-02-14 14:09:47 (UTC) ] &amp;lt;--&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes] [Server encaps]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: sha256&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 3: Server responds with certificate + ephemeral key + signature&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [c904b18b563dc1b0 @ 2026-02-14 14:09:47 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: [560 bytes]&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes] [Server encaps]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [32 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: [103 bytes]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 3: Client verifies certificate and authenticates&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [c904b18b563dc1b0] Loaded peer crt.&lt;br /&gt;
irmd/oap(DB): [c904b18b563dc1b0] Got public key from crt.&lt;br /&gt;
irmd/oap(DB): [c904b18b563dc1b0] Successfully verified peer crt.&lt;br /&gt;
irmd/oap(DB): [c904b18b563dc1b0] Successfully authenticated peer.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; Full OAP handshake with certificate (560 bytes) + ephemeral keys (91 bytes) + signature (103 bytes). Client verifies server&#039;s certificate against CA store and confirms authentication success.&lt;br /&gt;
&lt;br /&gt;
==== Test 4: With Authentication, No Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Remove encryption config but keep certificates&lt;br /&gt;
sudo rm -f /etc/ouroboros/security/client/sec.oping.tut.o7s/enc.conf&lt;br /&gt;
&lt;br /&gt;
# Run fourth ping test&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 4: Client initiates plaintext flow (no encryption file)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [03e47baa966a5823 @ 2026-02-14 14:09:57 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Cipher: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   KDF: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 4: Server responds with certificate + signature (no ephemeral key)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [03e47baa966a5823 @ 2026-02-14 14:09:57 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: [560 bytes]&lt;br /&gt;
irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Cipher: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   KDF: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [48 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: [103 bytes]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 4: Client verifies certificate and authenticates&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [03e47baa966a5823] Loaded peer crt.&lt;br /&gt;
irmd/oap(DB): [03e47baa966a5823] Got public key from crt.&lt;br /&gt;
irmd/oap(DB): [03e47baa966a5823] Successfully verified peer crt.&lt;br /&gt;
irmd/oap(DB): [03e47baa966a5823] Successfully authenticated peer.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; Server sends certificate + signature for authentication, but NO ephemeral keys (plaintext data). Data exchanged without encryption even though authenticated. Demonstrates that authentication and encryption are independent mechanisms.&lt;br /&gt;
&lt;br /&gt;
=== Stop the IRMd and tcpdump, clean up the tutorial files ===&lt;br /&gt;
&lt;br /&gt;
Once all tests complete:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Stop IRMd in Terminal 2 (Ctrl+C)&lt;br /&gt;
# Stop tcpdump in Terminal 1 (Ctrl+C)&lt;br /&gt;
&lt;br /&gt;
# Clean up tutorial security files from system&lt;br /&gt;
sudo rm -f /etc/ouroboros/security/cacert/ca.tut.o7s.crt.pem&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Part 2: PCAP Trace Analysis ==&lt;br /&gt;
&lt;br /&gt;
After the tutorial, we now explain the trace in the tcpdump pcap file.&lt;br /&gt;
&lt;br /&gt;
=== Protocol Overview ===&lt;br /&gt;
&lt;br /&gt;
This section summarizes the four protocols that work together in the captured packet flow.&lt;br /&gt;
&lt;br /&gt;
==== Ethernet DIX Frame with EID Header ====&lt;br /&gt;
&lt;br /&gt;
Ouroboros extends the DIX frame with a flow identifier (EID - Endpoint Identifier) and length field.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Octets !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Destination MAC || 0-5 || 6 bytes || Hardware address of destination&lt;br /&gt;
|-&lt;br /&gt;
| Source MAC || 6-11 || 6 bytes || Hardware address of source&lt;br /&gt;
|-&lt;br /&gt;
| EtherType || 12-13 || 2 bytes || Protocol identifier (0xA000 for Ouroboros)&lt;br /&gt;
|-&lt;br /&gt;
| EID || 14-15 || 2 bytes || Destination Endpoint Identifier&lt;br /&gt;
|-&lt;br /&gt;
| Length || 16-17 || 2 bytes || Payload length (needed because of runt frame padding)&lt;br /&gt;
|-&lt;br /&gt;
| Payload || 18+ || Variable || Frame data (up to MTU size)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Ethernet Flow Allocator - Management Protocol ====&lt;br /&gt;
&lt;br /&gt;
The Ethernet DIX management protocol handles flow allocation, setup, and teardown. All management frames use destination EID &amp;lt;code&amp;gt;0x0000&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Management Frame Types:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Code !! Type !! Direction !! Service Hash !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x00&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FLOW_REQ&amp;lt;/code&amp;gt; || Client → Server || ✓ Included || Request new flow allocation&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x01&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FLOW_REPLY&amp;lt;/code&amp;gt; || Server → Client || – Not included || Respond to flow request (success/failure)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x02&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NAME_QUERY_REQ&amp;lt;/code&amp;gt; || Client → Server || ✓ Included || Query if a remote name is reachable&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x03&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NAME_QUERY_REPLY&amp;lt;/code&amp;gt; || Server → Client || ✓ Included || Response to name query&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The 32-byte service hash (SHA3-256) is appended after the management protocol header for NAME_QUERY_* and FLOW_REQ messages to identify which service is being queried or allocated. FLOW_REPLY does not include the service hash; the endpoints are already identified by the allocated EIDs (SEID/DEID) and the flow allocation ID in the OAP header (see below).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frame Layout by Field:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| SEID || 0-1 || 2 bytes || Source Endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| DEID || 2-3 || 2 bytes || Destination Endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| Loss || 4-7 || 4 bytes || Acceptable packet loss (ppm)&lt;br /&gt;
|-&lt;br /&gt;
| Bandwidth || 8-15 || 8 bytes || Required bandwidth (bps)&lt;br /&gt;
|-&lt;br /&gt;
| BER || 16-19 || 4 bytes || Bit error rate (ppm)&lt;br /&gt;
|-&lt;br /&gt;
| Max Gap || 20-23 || 4 bytes || Maximum consecutive lost packets&lt;br /&gt;
|-&lt;br /&gt;
| Delay || 24-27 || 4 bytes || Maximum latency (ms)&lt;br /&gt;
|-&lt;br /&gt;
| Timeout || 28-31 || 4 bytes || Flow idle timeout (seconds)&lt;br /&gt;
|-&lt;br /&gt;
| Response || 32-35 || 4 bytes || Response code (0=success, negative=error)&lt;br /&gt;
|-&lt;br /&gt;
| In-Order || 36 || 1 byte || In-order delivery requirement (boolean)&lt;br /&gt;
|-&lt;br /&gt;
| Code || 37 || 1 byte || Message type (FLOW_REQ, FLOW_REPLY, etc.)&lt;br /&gt;
|-&lt;br /&gt;
| Availability || 38 || 1 byte || Availability status&lt;br /&gt;
|-&lt;br /&gt;
| Service hash || 39-61 || 32 bytes || SHA3-256 hash (optional, see above)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Ouroboros Flow Allocation Protocol (OAP) ====&lt;br /&gt;
&lt;br /&gt;
The Ouroboros Application Protocol (OAP) is the flow allocation and authentication protocol. It carries flow negotiation requests, responses, and authentication credentials. OAP frames are encapsulated as data payload over the management protocol.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frame Layout by Field:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| ID || 0-15 || 16 bytes || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| Timestamp || 16-23 || 8 bytes || Creation timestamp (UTC, seconds and microseconds)&lt;br /&gt;
|-&lt;br /&gt;
| Crt Length || 24-25 || 2 bytes || Certificate length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Certificate || 26+ || Variable || X.509 certificate (DER encoded)&lt;br /&gt;
|-&lt;br /&gt;
| Eph Length || Variable || 2 bytes || Ephemeral public key length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Ephemeral Key || Variable || Variable || ECDHE public key (DER/raw encoded)&lt;br /&gt;
|-&lt;br /&gt;
| Data Length || Variable || 2 bytes || Application data length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Data || Variable || Variable || Piggybacked application-layer data&lt;br /&gt;
|-&lt;br /&gt;
| Sig Length || Variable || 2 bytes || Signature length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Signature || Variable || Variable || Digital signature (ECDSA, DER encoded)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Oping Application Protocol ====&lt;br /&gt;
&lt;br /&gt;
The Ouroboros Ping (oping) application is a simple echo/reply protocol used to measure round-trip time and validate connectivity between applications. It implements a request/reply pattern similar to ICMP ping.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frame Layout by Field:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Type || 0-3 || 4 bytes || Message type (ECHO_REQUEST=0 or ECHO_REPLY=1)&lt;br /&gt;
|-&lt;br /&gt;
| ID || 4-7 || 4 bytes || Sequence number / message identifier&lt;br /&gt;
|-&lt;br /&gt;
| Timestamp (seconds) || 8-15 || 8 bytes || Seconds when message was sent (CLOCK_REALTIME)&lt;br /&gt;
|-&lt;br /&gt;
| Timestamp (nanoseconds) || 16-23 || 8 bytes || Nanoseconds component of timestamp&lt;br /&gt;
|-&lt;br /&gt;
| Payload || 24+ || Variable || Application data (configurable size, default 64 bytes)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Field Definitions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Type&#039;&#039;&#039; (4 bytes): Message type selector&lt;br /&gt;
** &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt; (ECHO_REQUEST): Client-to-server ping request&lt;br /&gt;
** &amp;lt;code&amp;gt;0x00000001&amp;lt;/code&amp;gt; (ECHO_REPLY): Server-to-client response&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;ID&#039;&#039;&#039; (4 bytes): Sequence number for matching requests with replies. Incremented for each ping sent.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; (8 bytes): Network-byte-order 64-bit seconds component from when the ping was sent (CLOCK_REALTIME).&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; (8 bytes): Network-byte-order 64-bit nanoseconds component (0-999999999) for high-resolution timing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Payload&#039;&#039;&#039; (Variable): Application data echoed back by the server. Size is configurable (default 64 bytes, maximum 1500 bytes).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Usage:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Client sends ECHO_REQUEST with current timestamp&lt;br /&gt;
* Server receives request and echoes back as ECHO_REPLY with the same ID and timestamps&lt;br /&gt;
* Client calculates RTT by comparing reception time with original timestamps&lt;br /&gt;
* Out-of-order detection by tracking sequence numbers (ID field)&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 1 - No authentication/encryption ===&lt;br /&gt;
&lt;br /&gt;
==== Packet 1: NAME_QUERY_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends a NAME_QUERY_REQ message to discover if the service &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt; is available. This is a broadcast discovery query sent because the service is not yet known for the flow allocation process.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:48.165639 00:00:00:00:00:00 &amp;gt; ff:ff:ff:ff:ff:ff, ethertype Unknown (0xa000), length 89:&lt;br /&gt;
        0x0000:  0000 0047 0000 0000 0000 0000 0000 0000  ...G............&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0002 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a              ..f.i.._...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0047&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 71 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0000 || Source Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || Response code (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x03 || Message Type: NAME_QUERY_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Packet 2: NAME_QUERY_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds to the NAME_QUERY_REQ by sending a NAME_QUERY_REPLY for the service hash.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:48.166073 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 89:&lt;br /&gt;
        0x0000:  0000 0047 0000 0000 0000 0000 0000 0000  ...G............&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0003 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a              ..f.i.._...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0047&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 71 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0000 || Source Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || Response code&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x03 || Message Type: NAME_QUERY_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt; (echoed back)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Packet 3: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates a flow allocation request (FLOW_REQ) with minimal OAP headers since no authentication or encryption is being used.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:48.167222 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 121:&lt;br /&gt;
        0x0000:  0000 0067 0040 0000 0000 0001 0000 0000  ...g.@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a51 8a56 ff6f  ..f.i.._...Q.V.o&lt;br /&gt;
        0x0050:  5ba6 9d03 7da1 cfc3 0f30 7718 86a8 e103  [...}....0w.....&lt;br /&gt;
        0x0060:  3e52 3300 0000 0000 0000 00              &amp;gt;R3........&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0067&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 103 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID (allocated flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || -- || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0001 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x00 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The OAP payload starts at offset 0x4b (after management protocol + service hash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;51 8a56 ff6f 5ba6 9d03 7da1 cfc3 0f30 77&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e103 3e52 33&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp (seconds + microseconds)&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || No ephemeral key&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0x68 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x69-0x6a || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0040 (first allocated flow ID for this session)&lt;br /&gt;
* Service hash is carried in management protocol payload (32 bytes)&lt;br /&gt;
* OAP header is minimal: only ID and timestamp, no optional fields&lt;br /&gt;
* No certificate, ephemeral key, data, or signature in this initial request&lt;br /&gt;
* Client sends minimal OAP headers with no authentication or encryption setup at allocation time&lt;br /&gt;
&lt;br /&gt;
==== Packet 4: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds to FLOW_REQ by sending FLOW_REPLY with a new DEID (destination endpoint ID 0x0041) to establish the allocated flow for data transfer.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:49.178732 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 89:&lt;br /&gt;
        0x0000:  0000 0047 0041 0040 0000 0000 0000 0000  ...G.A.@........&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 0051 8a56 ff6f  ...........Q.V.o&lt;br /&gt;
        0x0030:  5ba6 9d03 7da1 cfc3 0f30 7718 86a8 e13f  [...}....0w....?&lt;br /&gt;
        0x0040:  a347 3800 0000 0000 0000 00              .G8........&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0047&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 71 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0041 || Source Endpoint ID (allocated server-side flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client&#039;s flow ID from FLOW_REQ)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0|| Response code (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The OAP payload starts at offset 0x2b (no service hash in FLOW_REPLY):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;51 8a56 ff6f 5ba6 9d03 7da1 cfc3 0f30 77&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Echo of client&#039;s flow ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;18 86a8 e13f a347 38&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Echoed timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x46 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || No ephemeral key&lt;br /&gt;
|-&lt;br /&gt;
| 0x47-0x48 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x49-0x4a || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID 0x0041 is the newly allocated server-side flow endpoint&lt;br /&gt;
* DEID 0x0040 reflects the client&#039;s flow ID, creating a bidirectional mapping&lt;br /&gt;
* No service hash included (FLOW_REPLY only needs the EIDs to identify the flow)&lt;br /&gt;
* OAP echoes the client&#039;s ID and timestamp, confirming the flow allocation&lt;br /&gt;
* Response code 0x00000000 indicates success&lt;br /&gt;
* Both client and server now have their respective flow IDs (0x0040 and 0x0041) for data transfer&lt;br /&gt;
* Response code 0x00000000 indicates success&lt;br /&gt;
&lt;br /&gt;
==== Packet 5: ECHO_REQUEST - Plaintext Data ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends an oping ECHO_REQUEST packet to the server using the allocated flow.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:50.180824 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0041 0040 0000 0000 0000 0000 7377 0000  .A.@........sw..&lt;br /&gt;
        0x0010:  0000 0000 b03e e007 0000 0000 0000 0000  .....&amp;gt;..........&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0041 || Source Endpoint ID (server → client)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Oping payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REQUEST Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The oping payload starts at offset 0x04:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000000 || Message type: ECHO_REQUEST&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Sequence number (first ping)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;7377 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || 0x0000000000003773 || Seconds component&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;b03e e007 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || 0x0000000007e03eb0 || Nanoseconds component&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 0000 ... 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || All zeros || Echo data (default 64 bytes total - 24 byte header = 40 bytes data)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID 0x0041 shows traffic from server-side flow ID&lt;br /&gt;
* This is the first ping request (ID = 0x00000000)&lt;br /&gt;
* Timestamp captures when the ping was sent (seconds in network order)&lt;br /&gt;
* Default oping payload is 64 bytes total; 24 bytes header + 40 bytes data&lt;br /&gt;
&lt;br /&gt;
==== Packet 6: ECHO_REPLY - Plaintext Data ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server receives the ECHO_REQUEST and immediately sends back an ECHO_REPLY with the same ID and timestamps, echoing the client&#039;s message.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:50.181496 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0040 0040 0000 0001 0000 0000 7377 0000  .@.@........sw..&lt;br /&gt;
        0x0010:  0000 0000 b03e e007 0000 0000 0000 0000  .....&amp;gt;..........&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client ← server)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Oping payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REPLY Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The oping payload starts at offset 0x04:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0001&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000001 || Message type: ECHO_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Echo of request sequence number&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;7377 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || 0x0000000000003773 || Echo of original seconds&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;b03e e007 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || 0x0000000007e03eb0 || Echo of original nanoseconds&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 0000 ... 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || All zeros || Echo data (unchanged from request)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID 0x0040 shows traffic from client-side flow ID receiving the reply&lt;br /&gt;
* Type field changed from 0x00000000 (REQUEST) to 0x00000001 (REPLY)&lt;br /&gt;
* ID, timestamps, and payload data are identical to the request (echoed back)&lt;br /&gt;
* Round-trip time can be calculated by comparing current time with echoed timestamp&lt;br /&gt;
* Ping succeeded on first attempt with minimal latency (~1 millisecond between timestamps)&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 2 - No authentication, with encryption ===&lt;br /&gt;
&lt;br /&gt;
==== Packet 7: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates flow allocation with encryption enabled. This FLOW_REQ carries an OAP header with an ephemeral ECDHE P-384 public key (91 bytes) for encryption setup.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:53.808158 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 212:&lt;br /&gt;
        0x0000:  0000 00c2 0040 0000 0000 0001 0000 0000  .....@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8af1 766b 547c  ..f.i.._....vkT|&lt;br /&gt;
        0x0050:  fcb0 8fce 5d60 a01e e8be 0218 86a8 e253  ....]`.........S&lt;br /&gt;
        0x0060:  8b6c 9000 0000 5b30 5930 1306 072a 8648  .l....[0Y0...*.H&lt;br /&gt;
        0x0070:  ce3d 0201 0608 2a86 48ce 3d03 0107 0342  .=....*.H.=....B&lt;br /&gt;
        0x0080:  0004 c508 1c19 6106 b7e9 3074 57b9 bb16  ......a...0tW...&lt;br /&gt;
        0x0090:  6959 4a55 81f9 169b cc79 fe10 a882 41fe  iYJU.....y....A.&lt;br /&gt;
        0x00a0:  0697 c9b4 f8f0 5562 7fa2 c7a0 a020 1ac6  ......Ub........&lt;br /&gt;
        0x00b0:  939f 23ff b2fb 07a2 b747 aacc 474a 3dab  ..#......G..GJ=.&lt;br /&gt;
        0x00c0:  2598 0000 0000                           %.....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;00c2&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 194 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... d4c0&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Mixed || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x00 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload with Ephemeral Key&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;f1 766b 547c fcb0 8fce 5d60 a01e e8be 02&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e253 8b6c 90&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate in client request&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x005b (91) || Ephemeral key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0xc1 || &amp;lt;code&amp;gt;30 5930 ... 3dab 2598&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDHE P-384 || ECDHE P-384 public key (DER encoded)&lt;br /&gt;
|-&lt;br /&gt;
| 0xd3-0xd4 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0xd5-0xd6 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* Encryption enabled: ephemeral key present (91 bytes)&lt;br /&gt;
* Client sends no certificate, allowing anonymous encryption setup&lt;br /&gt;
* No signature (unsigned OAP)&lt;br /&gt;
* Ephemeral key is ECDHE P-384 for key exchange&lt;br /&gt;
&lt;br /&gt;
==== Packet 8: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server accepts the encrypted flow allocation request. FLOW_REPLY contains the server&#039;s ephemeral key but no certificate (since client didn&#039;t send one).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:53.810564 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 180:&lt;br /&gt;
        0x0000:  0000 00a2 0042 0040 0000 0000 0000 0000  .....B.@........&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 00f1 766b 547c  ............vkT|&lt;br /&gt;
        0x0030:  fcb0 8fce 5d60 a01e e8be 0218 86a8 e253  ....]`.........S&lt;br /&gt;
        0x0040:  b694 e800 0000 5b30 5930 1306 072a 8648  ......[0Y0...*.H&lt;br /&gt;
        0x0050:  ce3d 0201 0608 2a86 48ce 3d03 0107 0342  .=....*.H.=....B&lt;br /&gt;
        0x0060:  0004 5f3c 6929 cca2 024a ae9f 9aa1 dfc2  .._&amp;lt;i)...J......&lt;br /&gt;
        0x0070:  a493 3ff3 ff58 b054 74dc d2e2 47fc 7c5b  ..?..X.Tt...G.|[&lt;br /&gt;
        0x0080:  eff5 e129 72b4 de1e 7c09 bf8c fe38 5e8b  ...)r...|....8^.&lt;br /&gt;
        0x0090:  b22e 59ed 6eb9 dfda 369d 691e 6e2c 122c  ..Y.n...6.i.n,.,&lt;br /&gt;
        0x00a0:  9936 0000 0000                           .6....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;00a2&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 162 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0042&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0042 || Source Endpoint ID (allocated server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0x00000000 || Response code: 0 (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY with Ephemeral Key&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;f1 766b 547c fcb0 8fce 5d60 a01e e8be 02&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || Echo of client ID || Echoes client&#039;s flow ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;18 86a8 e253 b694 e8&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Echoed timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x46 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x005b (91) || Ephemeral key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x47-0xa1 || &amp;lt;code&amp;gt;30 5930...9936&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDHE P-384 || Server&#039;s ECDHE P-384 public key (DER encoded)&lt;br /&gt;
|-&lt;br /&gt;
| 0xd1-0xd2 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0xd3-0xd4 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID 0x0042 is the new server-side flow endpoint&lt;br /&gt;
* Both keys are now exchanged; client and server can derive shared secret&lt;br /&gt;
* No authentication (no certificates) but encryption is negotiated&lt;br /&gt;
* Response indicates successful allocation&lt;br /&gt;
&lt;br /&gt;
==== Packet 9: Encrypted ECHO_REQUEST ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends encrypted ping request after encryption keys are established. The payload is encrypted with the derived shared secret.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:54.815771 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0042 0060 a691 6d83 8446 cbeb ac95 c2eb  .B.`..m..F......&lt;br /&gt;
        0x0010:  4b42 e819 c67f 92c8 58d7 0641 d8a6 6e1f  KB......X..A..n.&lt;br /&gt;
        0x0020:  fc90 feed ef55 b791 4fbd a832 74bd 8bed  .....U..O..2t...&lt;br /&gt;
        0x0030:  249c 4cee 0fc0 cec6 2f1b aec1 2428 bdbd  $.L...../...$(..&lt;br /&gt;
        0x0040:  36b5 01b5 1257 004e 6ed6 7ecd f0c7 7d11  6....W.Nn.~...}.&lt;br /&gt;
        0x0050:  20ba e81b f43a 4de9 b141 1624 e1ba 0a84  .....:M..A.$....&lt;br /&gt;
        0x0060:  74b1 9a9a                                t...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0042&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0042 || Destination Endpoint ID (server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0060&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 96 bytes || Encrypted payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REQUEST (Encrypted)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x63 || &amp;lt;code&amp;gt;a691 6d83 8446 cbeb...74b1 9a9a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Encrypted Data&#039;&#039;&#039; || 96 || Ciphertext || All 96 bytes encrypted&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* All 96 bytes of oping data (type, ID, timestamps, payload) are encrypted&lt;br /&gt;
* No plaintext oping headers visible; entire packet is ciphertext&lt;br /&gt;
* Flow IDs (0x0042) identify which encryption context to use&lt;br /&gt;
* Ping still works with encryption transparently&lt;br /&gt;
&lt;br /&gt;
==== Packet 10: Encrypted ECHO_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server receives encrypted ping request, decrypts it, and sends encrypted ECHO_REPLY.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:54.819574 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0040 0060 c6ea 2222 5618 0268 b27e 9a91  .@.`..&amp;quot;&amp;quot;V..h.~..&lt;br /&gt;
        0x0010:  f124 1f8d bccc 478c 26fe 9b13 b3cb 5398  .$....G.&amp;amp;.....S.&lt;br /&gt;
        0x0020:  6869 3cdb 4928 510d 4de8 dc6a 3f3a 6a6d  hi&amp;lt;.I(Q.M..j?:jm&lt;br /&gt;
        0x0030:  6487 dcd8 c8cd 1a85 fba2 9ecd 3566 57d1  d...........5fW.&lt;br /&gt;
        0x0040:  1c94 ac35 518e 8509 873a 3a5e 04d9 8ee2  ...5Q....::^....&lt;br /&gt;
        0x0050:  9d74 2527 e425 5433 9d73 9ccd f56a 1f8d  .t%&#039;.%T3.s...j..&lt;br /&gt;
        0x0060:  f328 7237                                .(r7&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0060&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 96 bytes || Encrypted payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REPLY (Encrypted)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x63 || &amp;lt;code&amp;gt;c6ea 2222 5618 0268...f328 7237&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Encrypted Data&#039;&#039;&#039; || 96 || Ciphertext || All 96 bytes encrypted&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID 0x0040 shows reply going back to client-side flow&lt;br /&gt;
* Ciphertext is different from request (different plaintext: type field differs)&lt;br /&gt;
* Both encrypted packets are 96 bytes (same size as Packet 9)&lt;br /&gt;
* Client receives encrypted reply, decrypts it, verifies ID and timestamps match request&lt;br /&gt;
* Encryption is transparent at application layer: oping works exactly as with plaintext flows&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 3 - Authentication and encryption ===&lt;br /&gt;
&lt;br /&gt;
==== Packet 11: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates flow allocation request with encryption enabled. Sends ephemeral public key for ECDHE key exchange but no certificate (client is not authenticating in this tutorial). The management protocol now carries a valid allocated SEID (0x0040).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:58.827411 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 212:&lt;br /&gt;
        0x0000:  0000 00c2 0040 0000 0000 0001 0000 0000  .....@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a66 bb82 95fa  ..f.i.._...f....&lt;br /&gt;
        0x0050:  91a2 7bd3 bd60 1b3e 35f6 b918 86a8 e37e  ..{..`.&amp;gt;5......~&lt;br /&gt;
        0x0060:  c0d2 ad00 0000 5b30 5930 1306 072a 8648  ......[0Y0...*.H&lt;br /&gt;
        0x0070:  ce3d 0201 0608 2a86 48ce 3d03 0107 0342  .=....*.H.=....B&lt;br /&gt;
        0x0080:  0004 9dea c238 6732 4987 1cd4 7133 9614  .....8g2I...q3..&lt;br /&gt;
        0x0090:  9d04 4fde 3f68 42f1 54fb 7ef3 88d0 ffe6  ..O.?hB.T.~.....&lt;br /&gt;
        0x00a0:  7e01 432e 56c2 2d64 72c9 19fc b0cf 1eca  ~.C.V.-dr.......&lt;br /&gt;
        0x00b0:  689e 3536 771a 8041 726c 20e2 d9bb 3589  h.56w..Arl....5.&lt;br /&gt;
        0x00c0:  86e7 0000 0000                           ......&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;00c2&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 194 bytes || Total payload length (excluding DIX header)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID (client flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... d4c0&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Default values || QoS parameters&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || Response code (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x00 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ... 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload with Encryption Setup&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;66 bb82 95fa 91a2 7bd3 bd60 1b3e 35f6 b9&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier for Test 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e37e c0d2 ad&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || Client not authenticating&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 91 (0x005b) || Ephemeral public key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0xc1 || &amp;lt;code&amp;gt;30 5930 1306 ... 3589&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDP-384 public key || Client&#039;s ephemeral ECDHE public key for encryption negotiation&lt;br /&gt;
|-&lt;br /&gt;
| 0xc2-0xc3 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0xc4-0xc5 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0040 - Same as Test 2 (Encrypted) because this is the same client session reusing the same allocated ID from the previous test&lt;br /&gt;
* No Certificate - &amp;lt;code&amp;gt;crt_len = 0x0000&amp;lt;/code&amp;gt; because the client does not have authentication credentials; the server will authenticate instead&lt;br /&gt;
* Ephemeral Key Present - &amp;lt;code&amp;gt;eph_len = 0x005b (91)&amp;lt;/code&amp;gt; because encryption is enabled on the client&lt;br /&gt;
* No Signature - &amp;lt;code&amp;gt;sig_len = 0x0000&amp;lt;/code&amp;gt; because client is not signing (no certificate to sign with)&lt;br /&gt;
* FLOW_REQ Message Type - Code field is 0x00, and service hash is present because FLOW_REQ always includes the service hash&lt;br /&gt;
* Timestamp Consistency - Same OAP ID and timestamp structure as Test 2, but with additional security handshake&lt;br /&gt;
&lt;br /&gt;
==== Packet 12: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds to client&#039;s FLOW_REQ by sending FLOW_REPLY with its certificate for authentication, ephemeral public key for ECDHE encryption setup, and a digital signature proving ownership of the certificate. This is the full authentication response.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data (abbreviated):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:58.828806 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 843:&lt;br /&gt;
        0x0000:  0000 0339 0043 0040 0000 0000 0000 0000  ...9.C.@........&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 0066 bb82 95fa  ...........f....&lt;br /&gt;
        0x0030:  91a2 7bd3 bd60 1b3e 35f6 b918 86a8 e37e  ..{..`.&amp;gt;5......~&lt;br /&gt;
        0x0040:  d566 a002 2f30 8202 2b30 8201 b2a0 0302  .f../0..+0......&lt;br /&gt;
        (... certificate and signature bytes ...)&lt;br /&gt;
        0x0320:  ef11 c358 f5d0 5cd7 3906 adf1 8a2c 9b25  ...X..\.9....,.%&lt;br /&gt;
        0x0330:  dc78 6050 ab61 3a3f 81c0 254b d193 7827  .x`P.a:?..%K..x&#039;&lt;br /&gt;
        0x0340:  c0e9 38c7 e0d1 c517 d299 9992 07         ..8..........&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0339&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 825 bytes || Total payload length (excluding DIX header)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0043&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0043 || Source Endpoint ID (server-side allocated flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client&#039;s flow ID from FLOW_REQ)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Default values || QoS parameters&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0x00000000 || Response code: 0 (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY Payload with Full Authentication&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;66 bb82 95fa 91a2 7bd3 bd60 1b3e 35f6 b9&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || &#039;&#039;&#039;Same ID as client&#039;s FLOW_REQ&#039;&#039;&#039; (echoed back)&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;18 86a8 e37e d566 a0&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Server&#039;s timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;022f&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 559 (0x022f) || Server certificate length: 559 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x243 || &amp;lt;code&amp;gt;2f30 8202 2b ... 81c8 30&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Certificate&#039;&#039;&#039; || 559 || DER-encoded X.509 || Server&#039;s certificate (signed by intermediate CA)&lt;br /&gt;
|-&lt;br /&gt;
| 0x244-0x245 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 91 (0x005b) || Server&#039;s ephemeral public key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x246-0x2a0 || &amp;lt;code&amp;gt;30 5930 1306 ... 9936&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDP-384 public key || Server&#039;s ephemeral ECDHE public key&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a4-0x2a5 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a6-0x2a7 || &amp;lt;code&amp;gt;0068&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 104 (0x0068) || Digital signature length: 104 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a8-0x30f || &amp;lt;code&amp;gt;30 6602 3100 ... 07&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Signature&#039;&#039;&#039; || 104 || ECDSA signature (DER encoded) || Server&#039;s signature over OAP header proving certificate ownership&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0043 - New server-side endpoint ID allocated for this authenticated flow&lt;br /&gt;
* DEID is 0x0040 - Client&#039;s flow ID from the FLOW_REQ, establishing the bidirectional flow&lt;br /&gt;
* FLOW_REPLY Message Type - Code field is 0x01, &#039;&#039;&#039;no service hash&#039;&#039;&#039; (already identified in FLOW_REQ)&lt;br /&gt;
* Full Certificate - &amp;lt;code&amp;gt;crt_len = 0x022f (559)&amp;lt;/code&amp;gt; carrying server&#039;s complete X.509 certificate signed by intermediate CA&lt;br /&gt;
* Ephemeral Key Present - &amp;lt;code&amp;gt;eph_len = 0x005b (91)&amp;lt;/code&amp;gt; with server&#039;s ECDHE public key for encryption&lt;br /&gt;
* Signature Included - &amp;lt;code&amp;gt;sig_len = 0x0068 (104)&amp;lt;/code&amp;gt; containing ECDSA digital signature over the entire OAP header&lt;br /&gt;
* Same OAP ID - Server echoes back the exact ID from client&#039;s FLOW_REQ to confirm association (included in signature, binding response to this specific client request)&lt;br /&gt;
* Large Payload - Total of 825 bytes due to certificate (559) + ephemeral key (91) + signature (104) + overhead&lt;br /&gt;
* Authentication Complete - Client verifies: (1) certificate against CA store, (2) signature over entire response ensures authenticity and integrity, (3) echoed ID binds response to this specific request, (4) timestamp prevents replay attacks&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds with its certificate for authentication, ephemeral public key for ECDHE encryption, and a digital signature proving ownership of the certificate.&lt;br /&gt;
&lt;br /&gt;
==== Packet 13: Encrypted ECHO_REQUEST ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends encrypted ping request after authentication handshake. All application data is protected by encryption using the ephemeral keys established in Packets 11-12.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:59.836485 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0043 0060 3bed 0b48 1be1 6930 cf3d dee9  .C.`.;..H..i0.=..&lt;br /&gt;
        0x0010:  4fc9 774b 5d63 cc9b 5a34 6604 f9ac 1016  O.wK]c..Z4f.....&lt;br /&gt;
        0x0020:  1c6d c9ac f80e dc89 31c1 9634 1a4f b2c7  .m......1..4.O..&lt;br /&gt;
        0x0030:  4721 e402 8259 b0aa 8870 4566 33d1 9c18  G!...Y..  .pEf3...&lt;br /&gt;
        0x0040:  06da 50c3 8b75 86b0 f240 d109 840e a6cd  ..P..u...@......&lt;br /&gt;
        0x0050:  d115 77cb 5652 5bfb e6d5 0ca9 dbc3 d0b8  ..w.VR[.........&lt;br /&gt;
        0x0060:  0058 fd19                                .X..&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame Analysis:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Length !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Source EID || 0x00-0x01 || 2 bytes || &amp;lt;code&amp;gt;0x0043&amp;lt;/code&amp;gt; || Client flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| Destination EID || 0x02-0x03 || 2 bytes || &amp;lt;code&amp;gt;0x0060&amp;lt;/code&amp;gt; || Server flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Encrypted Payload&#039;&#039;&#039; || &#039;&#039;&#039;0x04-0x71&#039;&#039;&#039; || &#039;&#039;&#039;110 bytes&#039;&#039;&#039; || &#039;&#039;&#039;Ciphertext&#039;&#039;&#039; || &#039;&#039;&#039;AES-encrypted oping ECHO_REQUEST data&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* No visible protocol structure - all application data appears as ciphertext&lt;br /&gt;
* Uses the same source/destination EID pair (0x0043 → 0x0060) established in the FLOW_REQ/FLOW_REPLY handshake&lt;br /&gt;
* Encryption is done using the ephemeral key (91 bytes) exchanged in Packet 11&#039;s OAP header&lt;br /&gt;
* Unlike Packets 11-12, this packet contains no certificate, public keys, or signatures&lt;br /&gt;
* The 110-byte encrypted data corresponds to the original oping ECHO_REQUEST message&lt;br /&gt;
&lt;br /&gt;
==== Packet 14: Encrypted ECHO_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server sends encrypted ping reply. Note that the flow identifiers swap, demonstrating bidirectional encrypted communication.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:59.836930 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0040 0060 d552 e100 e681 940c e35a 07d0  .@.`..........Z..&lt;br /&gt;
        0x0010:  a293 1d73 33a5 854e 0fce 4f4d 6655 267a  ...s3..N..OMfU&amp;amp;z&lt;br /&gt;
        0x0020:  3de2 663b 709d 739a a696 2ddd 7b34 28b8  =.f;p.s...-{4(...&lt;br /&gt;
        0x0030:  5a98 eec2 52c6 4288 3885 ae16 e466 4181  Z...R.B.8...fA..&lt;br /&gt;
        0x0040:  f2d6 44c1 b51b 8728 58a4 7525 fb5e 3fd6  ..D...(X.u%.^?..&lt;br /&gt;
        0x0050:  7e49 532a d2a5 bea7 55e9 c274 f1b2 0412  ~IS*....U..t....&lt;br /&gt;
        0x0060:  73d4 6436                                s.d6&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame Analysis:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Length !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Source EID || 0x00-0x01 || 2 bytes || &amp;lt;code&amp;gt;0x0040&amp;lt;/code&amp;gt; || Client&#039;s inbound flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| Destination EID || 0x02-0x03 || 2 bytes || &amp;lt;code&amp;gt;0x0060&amp;lt;/code&amp;gt; || Server flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Encrypted Payload&#039;&#039;&#039; || &#039;&#039;&#039;0x04-0x71&#039;&#039;&#039; || &#039;&#039;&#039;110 bytes&#039;&#039;&#039; || &#039;&#039;&#039;Ciphertext&#039;&#039;&#039; || &#039;&#039;&#039;AES-encrypted oping ECHO_REPLY data&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* The EID in offset 0x00 is now 0x0040 (server&#039;s view of client&#039;s inbound flow)&lt;br /&gt;
* Uses the same ephemeral key material as Packet 13, but encryption direction is reversed&lt;br /&gt;
* Both packets use AES-GCM with keys derived from the ECDH exchange&lt;br /&gt;
* Timestamp 17:39:59.836930 is only 445 microseconds after Packet 13, indicating server-side processing&lt;br /&gt;
* The 110-byte encrypted ECHO_REPLY payload is the same size as the request&lt;br /&gt;
* All application data is protected by both authentication (X.509 + ECDSA) and encryption (AES)&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 4 - Authentication, no encryption ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Packet 15: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates flow allocation with authentication enabled but encryption disabled. This FLOW_REQ carries an OAP header but &#039;&#039;&#039;no ephemeral key&#039;&#039;&#039; since the client does not request encrypted communication.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:03.413372 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 121:&lt;br /&gt;
        0x0000:  0000 0067 0040 0000 0000 0001 0000 0000  ...g.@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a8f a6ab 6ea7  ..f.i.._........&lt;br /&gt;
        0x0050:  ef89 68e1 ed1e 2ede 0919 fa18 86a8 e490  .h..............&lt;br /&gt;
        0x0060:  0de6 6100 0000 0000 0000 00              ..a.....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0067&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 103 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID (allocated flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... dc40&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Mixed || &lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload (No Encryption)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;8f a6ab 6ea7 ef89 68e1 ed1e 2ede 0919 fa&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e490 0de6 61&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate in client request&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;No ephemeral key (no encryption)&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0x68 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x69-0x6a || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* No encryption enabled: ephemeral key absent (Eph_len = 0x0000)&lt;br /&gt;
* Client requests authentication only&lt;br /&gt;
* Server will respond with certificate + signature but no ephemeral key&lt;br /&gt;
* Packet is minimal compared to Packet 11 (Test 3) which includes 91-byte ephemeral key&lt;br /&gt;
&lt;br /&gt;
==== Packet 16: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server accepts the authenticated (but not encrypted) flow allocation request. FLOW_REPLY contains the server&#039;s X.509 certificate and ECDSA signature for client authentication, but &#039;&#039;&#039;no ephemeral key&#039;&#039;&#039; since encryption is not being used.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data (abbreviated):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:03.416675 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 751:&lt;br /&gt;
        0x0000:  0000 02dd 0041 0040 0000 0000 0000 0000  .......A.@......&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 008f a6ab 6ea7  ................&lt;br /&gt;
        0x0030:  ef89 68e1 ed1e 2ede 0919 fa18 86a8 e490  .h..............&lt;br /&gt;
        0x0040:  3754 a702 2f30 8202 2b30 8201 b2a0 0302  7T../0..+0......&lt;br /&gt;
        0x0050:  0102 0202 1000 300a 0608 2a86 48ce 3d04  ......0...*.H.=.&lt;br /&gt;
        (... certificate and signature bytes ...)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;02dd&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 733 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0041 || Source Endpoint ID (allocated server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0x00000000 || Response code: 0 (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY Payload with Certificate and Signature (No Ephemeral Key)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;8f a6ab 6ea7 ef89 68e1 ed1e 2ede 0919&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || &#039;&#039;&#039;Same ID as client&#039;s FLOW_REQ&#039;&#039;&#039; (Packet 15 echoed back)&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;fa18 86a8 e490 3754&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Server&#039;s timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;a702&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x02a7 (679 decimal) || &#039;&#039;&#039;Certificate length: 679 bytes&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x270 || &amp;lt;code&amp;gt;2f30 8202 2b30 8201 b2a0 0302 ... (DER certificate) ...&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Certificate&#039;&#039;&#039; || 679 || DER-encoded X.509 || Server&#039;s certificate signed by intermediate CA&lt;br /&gt;
|-&lt;br /&gt;
| 0x271-0x272 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;No ephemeral key&#039;&#039;&#039; (no encryption)&lt;br /&gt;
|-&lt;br /&gt;
| 0x273-0x274 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x275-0x276 || &amp;lt;code&amp;gt;0067&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0067 (103 decimal) || &#039;&#039;&#039;ECDSA signature length: 103 bytes&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x277-0x2dd || &amp;lt;code&amp;gt;3065 0230 75dc 5717 ... 83&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Signature&#039;&#039;&#039; || 103 || ECDSA signature (DER encoded) || Server&#039;s ECDSA signature proving certificate ownership&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0041 - New server-side endpoint ID allocated for this authenticated flow&lt;br /&gt;
* DEID is 0x0040 - Client&#039;s flow ID from Packet 15 FLOW_REQ, establishing the bidirectional flow&lt;br /&gt;
* FLOW_REPLY Message Type - Code field is 0x01, &#039;&#039;&#039;no service hash&#039;&#039;&#039; (already identified in FLOW_REQ)&lt;br /&gt;
* Certificate Field - &amp;lt;code&amp;gt;crt_len = 0x02a7 (679)&amp;lt;/code&amp;gt; carrying server&#039;s X.509 certificate signed by intermediate CA&lt;br /&gt;
* Separate Signature Field - &amp;lt;code&amp;gt;sig_len = 0x0067 (103)&amp;lt;/code&amp;gt; with ECDSA signature over entire OAP header&lt;br /&gt;
* No Ephemeral Key - &amp;lt;code&amp;gt;eph_len = 0x0000&amp;lt;/code&amp;gt; since encryption is &#039;&#039;&#039;not&#039;&#039;&#039; being used in Test 4&lt;br /&gt;
* Same OAP ID - Server echoes back the exact ID from client&#039;s FLOW_REQ (included in signature, binding response to this specific client request)&lt;br /&gt;
* Complete OAP Structure - Full OAP header with all standard fields, just without ephemeral key data&lt;br /&gt;
* Plaintext Data Exchange - After this FLOW_REPLY, all subsequent application data will be transmitted in plaintext (but authenticated via certificate + signature verification)&lt;br /&gt;
&lt;br /&gt;
==== Packet 17: ECHO_REQUEST ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends plaintext ECHO_REQUEST data through the authenticated (but unencrypted) flow. The oping application&#039;s ping request is transmitted directly without encryption, relying on the earlier certificate+signature authentication for security.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:04.419664 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0041 0040 0000 0000 0000 0000 8177 0000  .A.@............&lt;br /&gt;
        0x0010:  0000 0000 aa16 1c16 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0041 || Destination Endpoint ID (server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Total application data length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Application Data - Oping Echo Request (Plaintext)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000000 || Message type: ECHO_REQUEST&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Sequence number / message identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;8177 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Seconds component from CLOCK_REALTIME&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;aa16 1c16 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Nanoseconds component (0-999999999)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || Application data || Probe payload (zero-filled for 64 bytes total)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID Pair: 0x0041 → Server Flow - Data is directed to the server&#039;s endpoint ID allocated in Packet 16 FLOW_REPLY&lt;br /&gt;
* Plaintext Transmission - No encryption layer; oping payload is sent as-is (compare to Packet 13 which had encryption)&lt;br /&gt;
* Authenticated Flow - Although plaintext, this data travels on the authenticated flow established in Packet 16 (certificate + signature verified)&lt;br /&gt;
* Type = ECHO_REQUEST - 0x00000000 indicates client-to-server ping request&lt;br /&gt;
* ID = 0 - Sequence number for matching request/reply pairs&lt;br /&gt;
* Test 4 Characteristic - Demonstrates authenticated communication &#039;&#039;&#039;without&#039;&#039;&#039; encryption; application data is readable but cryptographically bound to the authenticated flow&lt;br /&gt;
* Contrast to Test 3 - Packet 13 (Test 3 encrypted ECHO_REQUEST) was 114 bytes with ciphertext; this packet is 82 bytes of plaintext&lt;br /&gt;
* Total Payload - 64 bytes total (4 + 4 + 8 + 8 + 40 bytes payload)&lt;br /&gt;
&lt;br /&gt;
==== Packet 18: ECHO_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds with plaintext ECHO_REPLY data, echoing back the client&#039;s request. This confirms successful bidirectional communication over the authenticated (but unencrypted) flow.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:04.420088 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0040 0040 0000 0001 0000 0000 8177 0000  .@.@............&lt;br /&gt;
        0x0010:  0000 0000 aa16 1c16 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Total application data length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Application Data - Oping Echo Reply (Plaintext)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0001&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000001 || Message type: ECHO_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Echo of request sequence number&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;8177 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Echoed request timestamp (seconds)&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;aa16 1c16 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Echoed request timestamp (nanoseconds)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || Application data || Echoed probe payload (zero-filled for 64 bytes total)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID Pair: 0x0040 → Client Flow - Server responds to client&#039;s endpoint ID from Packet 15 FLOW_REQ&lt;br /&gt;
* Type = ECHO_REPLY - 0x00000001 indicates server-to-client response&lt;br /&gt;
* ID = 0 - Echoes the request sequence number, matching this response to the request&lt;br /&gt;
* Timestamps Echo Request - Both timestamp fields are copied from Packet 17 unchanged (8177 0000 0000 0000 and aa16 1c16 0000 0000)&lt;br /&gt;
* Plaintext Reply - No encryption; server&#039;s response payload is readable (compare to Packet 14 which had encryption)&lt;br /&gt;
* Authenticated Channel - Although plaintext, this reply is part of the authenticated flow; client can verify integrity through earlier certificate+signature&lt;br /&gt;
* Test 4 Completion - Demonstrates &#039;&#039;&#039;full bidirectional plaintext communication&#039;&#039;&#039; over an authenticated (but unencrypted) flow&lt;br /&gt;
* Contrast to Test 3 - Packet 14 (Test 3 encrypted ECHO_REPLY) was 114 bytes with ciphertext; this packet is 82 bytes of plaintext&lt;br /&gt;
* Total Payload - 64 bytes total (4 + 4 + 8 + 8 + 40 bytes payload)&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Tutorial_06&amp;diff=1896</id>
		<title>Ouroboros Tutorial 06</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Tutorial_06&amp;diff=1896"/>
		<updated>2026-02-14T14:59:54Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* Test 4: With Authentication, No Encryption */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Ouroboros Tutorial 06 - Authenticated Flows =&lt;br /&gt;
&lt;br /&gt;
This tutorial demonstrates setting up and using authenticated flows in Ouroboros with certificate-based authentication.&lt;br /&gt;
&lt;br /&gt;
The overall flow of authenticated flow allocation is&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Client (IRMd)                              Server (IRMd)&lt;br /&gt;
     |                                           |&lt;br /&gt;
     | 1. Load client cert/key                   |&lt;br /&gt;
     | 2. Generate ephemeral keypair             |&lt;br /&gt;
     | 3. Build OAP_HDR (id, ts, crt, eph)       |&lt;br /&gt;
     | 4. Sign header with client key            |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |-------- FLOW_REQ (OAP_HDR) -------------&amp;gt; |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |                                           | 5. Load server cert/key&lt;br /&gt;
     |                                           | 6. Verify client cert against CA&lt;br /&gt;
     |                                           | 7. Verify client signature&lt;br /&gt;
     |                                           | 8. Generate ephemeral keypair&lt;br /&gt;
     |                                           | 9. Derive symmetric key (ECDHE)&lt;br /&gt;
     |                                           | 10. Build response OAP_HDR&lt;br /&gt;
     |                                           | 11. Sign with server key&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |&amp;lt;------- FLOW_REPLY (OAP_HDR) ------------ |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     | 12. Verify server cert against CA         |&lt;br /&gt;
     | 13. Verify server signature               |&lt;br /&gt;
     | 14. Derive symmetric key (ECDHE)          |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |===========================================|&lt;br /&gt;
     |         Encrypted data channel            |&lt;br /&gt;
     |===========================================|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Tutorial Directory:&#039;&#039;&#039; This tutorial will execute in &amp;lt;code&amp;gt;/tmp/o7s-tut06/&amp;lt;/code&amp;gt;. All configuration files, generated certificates, logs, and packet captures will be stored in this directory.&lt;br /&gt;
&lt;br /&gt;
We create a complete PKI (Public Key Infrastructure):&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Root CA&#039;&#039;&#039; (&amp;lt;code&amp;gt;ca.tut.o7s&amp;lt;/code&amp;gt;): Self-signed trust anchor&lt;br /&gt;
* &#039;&#039;&#039;Intermediate CA&#039;&#039;&#039; (&amp;lt;code&amp;gt;sign.tut.o7s&amp;lt;/code&amp;gt;): Signed by root with pathlen:0 constraint&lt;br /&gt;
* &#039;&#039;&#039;Server Certificate&#039;&#039;&#039; (&amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;): Signed by intermediate CA&lt;br /&gt;
&lt;br /&gt;
This tutorial uses ECDSA P-384 with SHA-384 hashing.&lt;br /&gt;
&lt;br /&gt;
== Setting Up the Tutorial ==&lt;br /&gt;
&lt;br /&gt;
To properly understand and debug the authenticated flows, this tutorial uses a debug build of Ouroboros with OAP protocol debugging enabled.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /path/to/ouroboros&lt;br /&gt;
mkdir build &amp;amp;&amp;amp; cd build&lt;br /&gt;
cmake -DCMAKE_BUILD_TYPE=Debug -DDEBUG_PROTO_OAP=ON ..&lt;br /&gt;
make -j$(nproc)&lt;br /&gt;
sudo make install&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When built with these options, the IRMd will output detailed OAP protocol information.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Files ===&lt;br /&gt;
&lt;br /&gt;
The following three files should be created in the tutorial directory (&amp;lt;code&amp;gt;/tmp/o7s-tut06/&amp;lt;/code&amp;gt;) before starting the tutorial:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;tut06.conf&#039;&#039;&#039; - IRMd configuration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
# Ouroboros Tutorial 06 - Authenticated Flows Configuration&lt;br /&gt;
# Uses system-installed certificates at /etc/ouroboros/security/&lt;br /&gt;
&lt;br /&gt;
[name.&amp;quot;sec.oping.tut.o7s&amp;quot;]&lt;br /&gt;
prog=[&amp;quot;/usr/bin/oping&amp;quot;]&lt;br /&gt;
args=[&amp;quot;--listen&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
[eth-dix.eth-dix-lo]&lt;br /&gt;
bootstrap=&amp;quot;eth-dix-network&amp;quot;&lt;br /&gt;
dev=&amp;quot;lo&amp;quot;&lt;br /&gt;
reg=[&amp;quot;sec.oping.tut.o7s&amp;quot;]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ca.tut.o7s.cnf&#039;&#039;&#039; - OpenSSL configuration for PKI generation&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
# Unified OpenSSL Configuration for Ouroboros Tutorial 06&lt;br /&gt;
# Named CA sections: CA_root (signs intermediate), CA_intermediate (signs server)&lt;br /&gt;
# Usage: openssl ca -name CA_root -config ca.tut.o7s.cnf ...&lt;br /&gt;
&lt;br /&gt;
[ req ]&lt;br /&gt;
default_bits       = 384&lt;br /&gt;
default_keyfile    = private/key.pem&lt;br /&gt;
distinguished_name = req_distinguished_name&lt;br /&gt;
string_mask        = utf8only&lt;br /&gt;
default_md         = sha384&lt;br /&gt;
x509_extensions    = v3_ca&lt;br /&gt;
&lt;br /&gt;
[ req_distinguished_name ]&lt;br /&gt;
countryName                 = Country Name (2 letter code)&lt;br /&gt;
stateOrProvinceName         = State or Province Name&lt;br /&gt;
localityName                = Locality Name&lt;br /&gt;
organizationName            = Organization Name&lt;br /&gt;
commonName                  = Common Name&lt;br /&gt;
&lt;br /&gt;
countryName_default         = BE&lt;br /&gt;
stateOrProvinceName_default = OVL&lt;br /&gt;
localityName_default        = Ghent&lt;br /&gt;
organizationName_default    = o7s&lt;br /&gt;
&lt;br /&gt;
[ ca ]&lt;br /&gt;
default_ca = CA_root&lt;br /&gt;
&lt;br /&gt;
[ CA_root ]&lt;br /&gt;
dir             = ./pki/root&lt;br /&gt;
database        = $dir/index.txt&lt;br /&gt;
serial          = $dir/serial&lt;br /&gt;
new_certs_dir   = $dir/certs&lt;br /&gt;
certificate     = $dir/certs/ca.tut.o7s.crt.pem&lt;br /&gt;
private_key     = $dir/private/ca.tut.o7s.key.pem&lt;br /&gt;
default_days    = 3650&lt;br /&gt;
default_md      = sha384&lt;br /&gt;
policy          = policy_loose&lt;br /&gt;
&lt;br /&gt;
[ CA_intermediate ]&lt;br /&gt;
dir             = ./pki/sign&lt;br /&gt;
database        = $dir/index.txt&lt;br /&gt;
serial          = $dir/serial&lt;br /&gt;
new_certs_dir   = $dir/certs&lt;br /&gt;
certificate     = $dir/certs/sign.tut.o7s.crt.pem&lt;br /&gt;
private_key     = $dir/private/sign.tut.o7s.key.pem&lt;br /&gt;
default_days    = 365&lt;br /&gt;
default_md      = sha384&lt;br /&gt;
policy          = policy_loose&lt;br /&gt;
&lt;br /&gt;
[ policy_loose ]&lt;br /&gt;
commonName = supplied&lt;br /&gt;
&lt;br /&gt;
[ v3_ca ]&lt;br /&gt;
subjectKeyIdentifier = hash&lt;br /&gt;
authorityKeyIdentifier = keyid:always,issuer&lt;br /&gt;
basicConstraints = critical, CA:true&lt;br /&gt;
keyUsage = critical, digitalSignature, cRLSign, keyCertSign&lt;br /&gt;
&lt;br /&gt;
[ v3_intermediate_ca ]&lt;br /&gt;
subjectKeyIdentifier = hash&lt;br /&gt;
authorityKeyIdentifier = keyid:always,issuer&lt;br /&gt;
basicConstraints = critical, CA:true, pathlen:0&lt;br /&gt;
keyUsage = critical, digitalSignature, cRLSign, keyCertSign&lt;br /&gt;
&lt;br /&gt;
[ server_cert ]&lt;br /&gt;
basicConstraints = CA:FALSE&lt;br /&gt;
subjectKeyIdentifier = hash&lt;br /&gt;
authorityKeyIdentifier = keyid,issuer:always&lt;br /&gt;
keyUsage = critical, digitalSignature, keyEncipherment&lt;br /&gt;
extendedKeyUsage = serverAuth&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;gen-pki.sh&#039;&#039;&#039; - PKI generation script&lt;br /&gt;
&lt;br /&gt;
This script will:&lt;br /&gt;
1. Create the directory structure&lt;br /&gt;
2. Generate the root CA key and certificate&lt;br /&gt;
3. Generate the intermediate CA key and CSR&lt;br /&gt;
4. Sign the intermediate CA certificate&lt;br /&gt;
5. Generate the server certificate key and CSR&lt;br /&gt;
6. Sign the server certificate&lt;br /&gt;
7. Verify the complete certificate chain&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
# Ouroboros Tutorial 06 - PKI Generation Script (Simplified)&lt;br /&gt;
# Generates: Root CA, Intermediate CA, and Server Certificate&lt;br /&gt;
&lt;br /&gt;
set -e&lt;br /&gt;
&lt;br /&gt;
if [ ! -f ca.tut.o7s.cnf ]; then&lt;br /&gt;
    echo &amp;quot;ERROR: ca.tut.o7s.cnf not found&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
mkdir -p pki/{root,sign,server}/{certs,private,csr}&lt;br /&gt;
&lt;br /&gt;
# Root CA&lt;br /&gt;
openssl ecparam -genkey -name secp384r1 -out pki/root/private/ca.tut.o7s.key.pem 2&amp;gt;/dev/null&lt;br /&gt;
openssl req -new -x509 -sha384 -days 7300 \&lt;br /&gt;
    -config ca.tut.o7s.cnf \&lt;br /&gt;
    -key pki/root/private/ca.tut.o7s.key.pem \&lt;br /&gt;
    -out pki/root/certs/ca.tut.o7s.crt.pem \&lt;br /&gt;
    -subj &amp;quot;/C=BE/ST=OVL/L=Ghent/O=o7s/CN=ca.tut.o7s&amp;quot; 2&amp;gt;/dev/null&lt;br /&gt;
&lt;br /&gt;
# Intermediate CA&lt;br /&gt;
openssl ecparam -genkey -name secp384r1 -out pki/sign/private/sign.tut.o7s.key.pem 2&amp;gt;/dev/null&lt;br /&gt;
openssl req -new -sha384 \&lt;br /&gt;
    -config ca.tut.o7s.cnf \&lt;br /&gt;
    -key pki/sign/private/sign.tut.o7s.key.pem \&lt;br /&gt;
    -out pki/sign/csr/sign.tut.o7s.csr \&lt;br /&gt;
    -subj &amp;quot;/C=BE/ST=OVL/L=Ghent/O=o7s/CN=sign.tut.o7s&amp;quot; 2&amp;gt;/dev/null&lt;br /&gt;
&lt;br /&gt;
touch pki/root/index.txt&lt;br /&gt;
echo 1000 &amp;gt; pki/root/serial&lt;br /&gt;
&lt;br /&gt;
openssl ca -name CA_root -config ca.tut.o7s.cnf \&lt;br /&gt;
    -extensions v3_intermediate_ca -days 3650 -md sha384 -batch \&lt;br /&gt;
    -in pki/sign/csr/sign.tut.o7s.csr \&lt;br /&gt;
    -out pki/sign/certs/sign.tut.o7s.crt.pem 2&amp;gt;&amp;amp;1 | grep -E &amp;quot;Signature ok|Database updated&amp;quot; || true&lt;br /&gt;
&lt;br /&gt;
# Server Certificate&lt;br /&gt;
openssl ecparam -genkey -name secp384r1 -out pki/server/private/sec.oping.tut.o7s.key.pem 2&amp;gt;/dev/null&lt;br /&gt;
openssl req -new -sha384 \&lt;br /&gt;
    -config ca.tut.o7s.cnf \&lt;br /&gt;
    -key pki/server/private/sec.oping.tut.o7s.key.pem \&lt;br /&gt;
    -out pki/server/csr/sec.oping.tut.o7s.csr \&lt;br /&gt;
    -subj &amp;quot;/C=BE/ST=OVL/L=Ghent/O=o7s/CN=sec.oping.tut.o7s&amp;quot; 2&amp;gt;/dev/null&lt;br /&gt;
&lt;br /&gt;
touch pki/sign/index.txt&lt;br /&gt;
echo 1000 &amp;gt; pki/sign/serial&lt;br /&gt;
&lt;br /&gt;
openssl ca -name CA_intermediate -config ca.tut.o7s.cnf \&lt;br /&gt;
    -extensions server_cert -days 365 -md sha384 -batch \&lt;br /&gt;
    -in pki/server/csr/sec.oping.tut.o7s.csr \&lt;br /&gt;
    -out pki/server/certs/sec.oping.tut.o7s.crt.pem 2&amp;gt;&amp;amp;1 | grep -E &amp;quot;Signature ok|Database updated&amp;quot; || true&lt;br /&gt;
&lt;br /&gt;
# Verify chain&lt;br /&gt;
openssl verify -CAfile pki/root/certs/ca.tut.o7s.crt.pem \&lt;br /&gt;
    -untrusted pki/sign/certs/sign.tut.o7s.crt.pem \&lt;br /&gt;
    pki/server/certs/sec.oping.tut.o7s.crt.pem &amp;gt; /dev/null 2&amp;gt;&amp;amp;1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;PKI generation complete.&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Part 1: Running the Tutorial - Single Session with 4 Tests ==&lt;br /&gt;
&lt;br /&gt;
This section demonstrates a single continuous session with one IRMd and tcpdump instance. The configuration file (&amp;lt;code&amp;gt;tut06.conf&amp;lt;/code&amp;gt;) includes autostart for oping, so the server is ready immediately when IRMd starts.&lt;br /&gt;
&lt;br /&gt;
First install the &#039;&#039;&#039;CA and Intermediate CA only&#039;&#039;&#039; to the system security directories. The server certificate will be installed later during Test 3 (authentication test):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo mkdir -p /etc/ouroboros/security/{cacert,untrusted,server/sec.oping.tut.o7s,client/sec.oping.tut.o7s}&lt;br /&gt;
&lt;br /&gt;
# Run the PKI generation script&lt;br /&gt;
cd /tmp/o7s-tut06&lt;br /&gt;
sudo chmod +x gen-pki.sh&lt;br /&gt;
sudo ./gen-pki.sh&lt;br /&gt;
&lt;br /&gt;
# Install Root CA (trust anchor)&lt;br /&gt;
sudo cp pki/root/certs/ca.tut.o7s.crt.pem /etc/ouroboros/security/cacert/&lt;br /&gt;
&lt;br /&gt;
# Install Intermediate CA (for certificate chain validation)&lt;br /&gt;
sudo cp pki/sign/certs/sign.tut.o7s.crt.pem /etc/ouroboros/security/untrusted/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Running the Tutorial (3 Terminals) ===&lt;br /&gt;
&lt;br /&gt;
In this tutorial, we run a single IRMd session with a concurrent tcpdump instance to capture it. We then run four oping client tests while the IRMd/tcpdump sessions are going, modifying the security configuration between tests. After the tests are complete, we can will down the IRMd and tcpdump sessions with Ctrl-C.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Terminal 1: Start tcpdump to capture all packets (runs continuously)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo tcpdump -i lo -n -A -v -U -w /tmp/o7s-tut06/tut06.pcap &amp;quot;ether proto 0xa000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Terminal 2: Start IRMd with debug output (runs continuously)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /tmp/o7s-tut06&lt;br /&gt;
sudo irmd --config tut06.conf --stdout 2&amp;gt;&amp;amp;1 | tee /tmp/o7s-tut06/irmd.log&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Terminal 3: Run the tests&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Test 1: No Authentication, No Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Verify directories are empty&lt;br /&gt;
sudo ls -la /etc/ouroboros/security/client/sec.oping.tut.o7s/*&lt;br /&gt;
sudo ls -la /etc/ouroboros/security/server/sec.oping.tut.o7s/*&lt;br /&gt;
&lt;br /&gt;
# Run first ping test&lt;br /&gt;
echo &amp;quot;=== Test 1: No Authentication, No Encryption ===&amp;quot;&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 1: Client initiates plaintext flow allocation&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [60e824383b3fbd6a] KEX config: algo=none, mode=server-encap, cipher=none.&lt;br /&gt;
irmd/oap(PP): OAP_HDR [60e824383b3fbd6a @ 2026-02-14 14:08:56 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Cipher: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   KDF: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 1: Server accepts and completes handshake&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [60e824383b3fbd6a @ 2026-02-14 14:08:57 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Cipher: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   KDF: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [48 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; All OAP fields are &amp;lt;code&amp;gt;&amp;lt;none&amp;gt;&amp;lt;/code&amp;gt; because no security is configured (except for the request hash in the response). Flow succeeds with plaintext communication.&lt;br /&gt;
&lt;br /&gt;
==== Test 2: No Authentication, With Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Enable encryption for client only&lt;br /&gt;
sudo touch /etc/ouroboros/security/client/sec.oping.tut.o7s/enc.conf&lt;br /&gt;
&lt;br /&gt;
# Run second ping test&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 2: Client initiates flow with encryption enabled&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(II): Encryption enabled for sec.oping.tut.o7s.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] KEX config: algo=prime256v1, mode=server-encap, cipher=aes-256-gcm.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Generated ephemeral prime256v1 keys (91 bytes).&lt;br /&gt;
irmd/oap(PP): OAP_HDR [80fd6f9509a996b0 @ 2026-02-14 14:09:38 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes] [Server encaps]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: sha256&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 2: Server receives and responds with ephemeral key&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] No crt provided.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Selected client cipher aes-256-gcm.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Selected client KDF sha256.&lt;br /&gt;
irmd/oap(II): [80fd6f9509a996b0] No key exchange.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Generated prime256v1 ephemeral keys.&lt;br /&gt;
irmd/oap(PP): OAP_HDR [80fd6f9509a996b0 @ 2026-02-14 14:09:38 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes] [Server encaps]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [32 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; Both client and server generate ephemeral keys (91 bytes each) for encryption. No certificates because authentication is not required. Encryption and authentication are independent.&lt;br /&gt;
&lt;br /&gt;
==== Test 3: With Authentication, With Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Install server certificates and keys&lt;br /&gt;
sudo cp /tmp/o7s-tut06/pki/server/certs/sec.oping.tut.o7s.crt.pem \&lt;br /&gt;
        /etc/ouroboros/security/server/sec.oping.tut.o7s/crt.pem&lt;br /&gt;
sudo cp /tmp/o7s-tut06/pki/server/private/sec.oping.tut.o7s.key.pem \&lt;br /&gt;
        /etc/ouroboros/security/server/sec.oping.tut.o7s/key.pem&lt;br /&gt;
&lt;br /&gt;
# enc.conf is still in place from Test 2&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 3: Client initiates flow with encryption and server has certificate&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [c904b18b563dc1b0 @ 2026-02-14 14:09:47 (UTC) ] &amp;lt;--&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes] [Server encaps]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: sha256&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 3: Server responds with certificate + ephemeral key + signature&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [c904b18b563dc1b0 @ 2026-02-14 14:09:47 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: [560 bytes]&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes] [Server encaps]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [32 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: [103 bytes]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 3: Client verifies certificate and authenticates&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [c904b18b563dc1b0] Loaded peer crt.&lt;br /&gt;
irmd/oap(DB): [c904b18b563dc1b0] Got public key from crt.&lt;br /&gt;
irmd/oap(DB): [c904b18b563dc1b0] Successfully verified peer crt.&lt;br /&gt;
irmd/oap(DB): [c904b18b563dc1b0] Successfully authenticated peer.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; Full OAP handshake with certificate (560 bytes) + ephemeral keys (91 bytes) + signature (103 bytes). Client verifies server&#039;s certificate against CA store and confirms authentication success.&lt;br /&gt;
&lt;br /&gt;
==== Test 4: With Authentication, No Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Remove encryption config but keep certificates&lt;br /&gt;
sudo rm -f /etc/ouroboros/security/client/sec.oping.tut.o7s/enc.conf&lt;br /&gt;
&lt;br /&gt;
# Run fourth ping test&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 4: Client initiates plaintext flow (no encryption file)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [03e47baa966a5823] KEX config: algo=none, mode=server-encap, cipher=none.&lt;br /&gt;
irmd/oap(PP): OAP_HDR [03e47baa966a5823 @ 2026-02-14 14:09:57 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Cipher: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   KDF: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 4: Server responds with certificate + signature (no ephemeral key)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [03e47baa966a5823 @ 2026-02-14 14:09:57 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: [560 bytes]&lt;br /&gt;
irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Cipher: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   KDF: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [48 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: [103 bytes]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 4: Client verifies certificate and authenticates&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [03e47baa966a5823] Loaded peer crt.&lt;br /&gt;
irmd/oap(DB): [03e47baa966a5823] Got public key from crt.&lt;br /&gt;
irmd/oap(DB): [03e47baa966a5823] Successfully verified peer crt.&lt;br /&gt;
irmd/oap(DB): [03e47baa966a5823] Successfully authenticated peer.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; Server sends certificate + signature for authentication, but NO ephemeral keys (plaintext data). Data exchanged without encryption even though authenticated. Demonstrates that authentication and encryption are independent mechanisms.&lt;br /&gt;
&lt;br /&gt;
=== Stop the IRMd and tcpdump, clean up the tutorial files ===&lt;br /&gt;
&lt;br /&gt;
Once all tests complete:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Stop IRMd in Terminal 2 (Ctrl+C)&lt;br /&gt;
# Stop tcpdump in Terminal 1 (Ctrl+C)&lt;br /&gt;
&lt;br /&gt;
# Clean up tutorial security files from system&lt;br /&gt;
sudo rm -f /etc/ouroboros/security/cacert/ca.tut.o7s.crt.pem&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Part 2: PCAP Trace Analysis ==&lt;br /&gt;
&lt;br /&gt;
After the tutorial, we now explain the trace in the tcpdump pcap file.&lt;br /&gt;
&lt;br /&gt;
=== Protocol Overview ===&lt;br /&gt;
&lt;br /&gt;
This section summarizes the four protocols that work together in the captured packet flow.&lt;br /&gt;
&lt;br /&gt;
==== Ethernet DIX Frame with EID Header ====&lt;br /&gt;
&lt;br /&gt;
Ouroboros extends the DIX frame with a flow identifier (EID - Endpoint Identifier) and length field.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Octets !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Destination MAC || 0-5 || 6 bytes || Hardware address of destination&lt;br /&gt;
|-&lt;br /&gt;
| Source MAC || 6-11 || 6 bytes || Hardware address of source&lt;br /&gt;
|-&lt;br /&gt;
| EtherType || 12-13 || 2 bytes || Protocol identifier (0xA000 for Ouroboros)&lt;br /&gt;
|-&lt;br /&gt;
| EID || 14-15 || 2 bytes || Destination Endpoint Identifier&lt;br /&gt;
|-&lt;br /&gt;
| Length || 16-17 || 2 bytes || Payload length (needed because of runt frame padding)&lt;br /&gt;
|-&lt;br /&gt;
| Payload || 18+ || Variable || Frame data (up to MTU size)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Ethernet Flow Allocator - Management Protocol ====&lt;br /&gt;
&lt;br /&gt;
The Ethernet DIX management protocol handles flow allocation, setup, and teardown. All management frames use destination EID &amp;lt;code&amp;gt;0x0000&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Management Frame Types:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Code !! Type !! Direction !! Service Hash !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x00&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FLOW_REQ&amp;lt;/code&amp;gt; || Client → Server || ✓ Included || Request new flow allocation&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x01&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FLOW_REPLY&amp;lt;/code&amp;gt; || Server → Client || – Not included || Respond to flow request (success/failure)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x02&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NAME_QUERY_REQ&amp;lt;/code&amp;gt; || Client → Server || ✓ Included || Query if a remote name is reachable&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x03&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NAME_QUERY_REPLY&amp;lt;/code&amp;gt; || Server → Client || ✓ Included || Response to name query&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The 32-byte service hash (SHA3-256) is appended after the management protocol header for NAME_QUERY_* and FLOW_REQ messages to identify which service is being queried or allocated. FLOW_REPLY does not include the service hash; the endpoints are already identified by the allocated EIDs (SEID/DEID) and the flow allocation ID in the OAP header (see below).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frame Layout by Field:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| SEID || 0-1 || 2 bytes || Source Endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| DEID || 2-3 || 2 bytes || Destination Endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| Loss || 4-7 || 4 bytes || Acceptable packet loss (ppm)&lt;br /&gt;
|-&lt;br /&gt;
| Bandwidth || 8-15 || 8 bytes || Required bandwidth (bps)&lt;br /&gt;
|-&lt;br /&gt;
| BER || 16-19 || 4 bytes || Bit error rate (ppm)&lt;br /&gt;
|-&lt;br /&gt;
| Max Gap || 20-23 || 4 bytes || Maximum consecutive lost packets&lt;br /&gt;
|-&lt;br /&gt;
| Delay || 24-27 || 4 bytes || Maximum latency (ms)&lt;br /&gt;
|-&lt;br /&gt;
| Timeout || 28-31 || 4 bytes || Flow idle timeout (seconds)&lt;br /&gt;
|-&lt;br /&gt;
| Response || 32-35 || 4 bytes || Response code (0=success, negative=error)&lt;br /&gt;
|-&lt;br /&gt;
| In-Order || 36 || 1 byte || In-order delivery requirement (boolean)&lt;br /&gt;
|-&lt;br /&gt;
| Code || 37 || 1 byte || Message type (FLOW_REQ, FLOW_REPLY, etc.)&lt;br /&gt;
|-&lt;br /&gt;
| Availability || 38 || 1 byte || Availability status&lt;br /&gt;
|-&lt;br /&gt;
| Service hash || 39-61 || 32 bytes || SHA3-256 hash (optional, see above)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Ouroboros Flow Allocation Protocol (OAP) ====&lt;br /&gt;
&lt;br /&gt;
The Ouroboros Application Protocol (OAP) is the flow allocation and authentication protocol. It carries flow negotiation requests, responses, and authentication credentials. OAP frames are encapsulated as data payload over the management protocol.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frame Layout by Field:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| ID || 0-15 || 16 bytes || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| Timestamp || 16-23 || 8 bytes || Creation timestamp (UTC, seconds and microseconds)&lt;br /&gt;
|-&lt;br /&gt;
| Crt Length || 24-25 || 2 bytes || Certificate length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Certificate || 26+ || Variable || X.509 certificate (DER encoded)&lt;br /&gt;
|-&lt;br /&gt;
| Eph Length || Variable || 2 bytes || Ephemeral public key length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Ephemeral Key || Variable || Variable || ECDHE public key (DER/raw encoded)&lt;br /&gt;
|-&lt;br /&gt;
| Data Length || Variable || 2 bytes || Application data length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Data || Variable || Variable || Piggybacked application-layer data&lt;br /&gt;
|-&lt;br /&gt;
| Sig Length || Variable || 2 bytes || Signature length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Signature || Variable || Variable || Digital signature (ECDSA, DER encoded)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Oping Application Protocol ====&lt;br /&gt;
&lt;br /&gt;
The Ouroboros Ping (oping) application is a simple echo/reply protocol used to measure round-trip time and validate connectivity between applications. It implements a request/reply pattern similar to ICMP ping.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frame Layout by Field:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Type || 0-3 || 4 bytes || Message type (ECHO_REQUEST=0 or ECHO_REPLY=1)&lt;br /&gt;
|-&lt;br /&gt;
| ID || 4-7 || 4 bytes || Sequence number / message identifier&lt;br /&gt;
|-&lt;br /&gt;
| Timestamp (seconds) || 8-15 || 8 bytes || Seconds when message was sent (CLOCK_REALTIME)&lt;br /&gt;
|-&lt;br /&gt;
| Timestamp (nanoseconds) || 16-23 || 8 bytes || Nanoseconds component of timestamp&lt;br /&gt;
|-&lt;br /&gt;
| Payload || 24+ || Variable || Application data (configurable size, default 64 bytes)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Field Definitions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Type&#039;&#039;&#039; (4 bytes): Message type selector&lt;br /&gt;
** &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt; (ECHO_REQUEST): Client-to-server ping request&lt;br /&gt;
** &amp;lt;code&amp;gt;0x00000001&amp;lt;/code&amp;gt; (ECHO_REPLY): Server-to-client response&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;ID&#039;&#039;&#039; (4 bytes): Sequence number for matching requests with replies. Incremented for each ping sent.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; (8 bytes): Network-byte-order 64-bit seconds component from when the ping was sent (CLOCK_REALTIME).&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; (8 bytes): Network-byte-order 64-bit nanoseconds component (0-999999999) for high-resolution timing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Payload&#039;&#039;&#039; (Variable): Application data echoed back by the server. Size is configurable (default 64 bytes, maximum 1500 bytes).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Usage:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Client sends ECHO_REQUEST with current timestamp&lt;br /&gt;
* Server receives request and echoes back as ECHO_REPLY with the same ID and timestamps&lt;br /&gt;
* Client calculates RTT by comparing reception time with original timestamps&lt;br /&gt;
* Out-of-order detection by tracking sequence numbers (ID field)&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 1 - No authentication/encryption ===&lt;br /&gt;
&lt;br /&gt;
==== Packet 1: NAME_QUERY_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends a NAME_QUERY_REQ message to discover if the service &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt; is available. This is a broadcast discovery query sent because the service is not yet known for the flow allocation process.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:48.165639 00:00:00:00:00:00 &amp;gt; ff:ff:ff:ff:ff:ff, ethertype Unknown (0xa000), length 89:&lt;br /&gt;
        0x0000:  0000 0047 0000 0000 0000 0000 0000 0000  ...G............&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0002 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a              ..f.i.._...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0047&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 71 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0000 || Source Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || Response code (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x03 || Message Type: NAME_QUERY_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Packet 2: NAME_QUERY_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds to the NAME_QUERY_REQ by sending a NAME_QUERY_REPLY for the service hash.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:48.166073 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 89:&lt;br /&gt;
        0x0000:  0000 0047 0000 0000 0000 0000 0000 0000  ...G............&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0003 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a              ..f.i.._...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0047&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 71 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0000 || Source Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || Response code&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x03 || Message Type: NAME_QUERY_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt; (echoed back)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Packet 3: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates a flow allocation request (FLOW_REQ) with minimal OAP headers since no authentication or encryption is being used.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:48.167222 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 121:&lt;br /&gt;
        0x0000:  0000 0067 0040 0000 0000 0001 0000 0000  ...g.@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a51 8a56 ff6f  ..f.i.._...Q.V.o&lt;br /&gt;
        0x0050:  5ba6 9d03 7da1 cfc3 0f30 7718 86a8 e103  [...}....0w.....&lt;br /&gt;
        0x0060:  3e52 3300 0000 0000 0000 00              &amp;gt;R3........&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0067&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 103 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID (allocated flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || -- || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0001 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x00 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The OAP payload starts at offset 0x4b (after management protocol + service hash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;51 8a56 ff6f 5ba6 9d03 7da1 cfc3 0f30 77&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e103 3e52 33&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp (seconds + microseconds)&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || No ephemeral key&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0x68 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x69-0x6a || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0040 (first allocated flow ID for this session)&lt;br /&gt;
* Service hash is carried in management protocol payload (32 bytes)&lt;br /&gt;
* OAP header is minimal: only ID and timestamp, no optional fields&lt;br /&gt;
* No certificate, ephemeral key, data, or signature in this initial request&lt;br /&gt;
* Client sends minimal OAP headers with no authentication or encryption setup at allocation time&lt;br /&gt;
&lt;br /&gt;
==== Packet 4: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds to FLOW_REQ by sending FLOW_REPLY with a new DEID (destination endpoint ID 0x0041) to establish the allocated flow for data transfer.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:49.178732 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 89:&lt;br /&gt;
        0x0000:  0000 0047 0041 0040 0000 0000 0000 0000  ...G.A.@........&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 0051 8a56 ff6f  ...........Q.V.o&lt;br /&gt;
        0x0030:  5ba6 9d03 7da1 cfc3 0f30 7718 86a8 e13f  [...}....0w....?&lt;br /&gt;
        0x0040:  a347 3800 0000 0000 0000 00              .G8........&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0047&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 71 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0041 || Source Endpoint ID (allocated server-side flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client&#039;s flow ID from FLOW_REQ)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0|| Response code (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The OAP payload starts at offset 0x2b (no service hash in FLOW_REPLY):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;51 8a56 ff6f 5ba6 9d03 7da1 cfc3 0f30 77&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Echo of client&#039;s flow ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;18 86a8 e13f a347 38&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Echoed timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x46 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || No ephemeral key&lt;br /&gt;
|-&lt;br /&gt;
| 0x47-0x48 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x49-0x4a || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID 0x0041 is the newly allocated server-side flow endpoint&lt;br /&gt;
* DEID 0x0040 reflects the client&#039;s flow ID, creating a bidirectional mapping&lt;br /&gt;
* No service hash included (FLOW_REPLY only needs the EIDs to identify the flow)&lt;br /&gt;
* OAP echoes the client&#039;s ID and timestamp, confirming the flow allocation&lt;br /&gt;
* Response code 0x00000000 indicates success&lt;br /&gt;
* Both client and server now have their respective flow IDs (0x0040 and 0x0041) for data transfer&lt;br /&gt;
* Response code 0x00000000 indicates success&lt;br /&gt;
&lt;br /&gt;
==== Packet 5: ECHO_REQUEST - Plaintext Data ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends an oping ECHO_REQUEST packet to the server using the allocated flow.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:50.180824 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0041 0040 0000 0000 0000 0000 7377 0000  .A.@........sw..&lt;br /&gt;
        0x0010:  0000 0000 b03e e007 0000 0000 0000 0000  .....&amp;gt;..........&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0041 || Source Endpoint ID (server → client)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Oping payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REQUEST Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The oping payload starts at offset 0x04:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000000 || Message type: ECHO_REQUEST&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Sequence number (first ping)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;7377 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || 0x0000000000003773 || Seconds component&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;b03e e007 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || 0x0000000007e03eb0 || Nanoseconds component&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 0000 ... 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || All zeros || Echo data (default 64 bytes total - 24 byte header = 40 bytes data)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID 0x0041 shows traffic from server-side flow ID&lt;br /&gt;
* This is the first ping request (ID = 0x00000000)&lt;br /&gt;
* Timestamp captures when the ping was sent (seconds in network order)&lt;br /&gt;
* Default oping payload is 64 bytes total; 24 bytes header + 40 bytes data&lt;br /&gt;
&lt;br /&gt;
==== Packet 6: ECHO_REPLY - Plaintext Data ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server receives the ECHO_REQUEST and immediately sends back an ECHO_REPLY with the same ID and timestamps, echoing the client&#039;s message.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:50.181496 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0040 0040 0000 0001 0000 0000 7377 0000  .@.@........sw..&lt;br /&gt;
        0x0010:  0000 0000 b03e e007 0000 0000 0000 0000  .....&amp;gt;..........&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client ← server)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Oping payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REPLY Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The oping payload starts at offset 0x04:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0001&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000001 || Message type: ECHO_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Echo of request sequence number&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;7377 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || 0x0000000000003773 || Echo of original seconds&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;b03e e007 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || 0x0000000007e03eb0 || Echo of original nanoseconds&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 0000 ... 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || All zeros || Echo data (unchanged from request)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID 0x0040 shows traffic from client-side flow ID receiving the reply&lt;br /&gt;
* Type field changed from 0x00000000 (REQUEST) to 0x00000001 (REPLY)&lt;br /&gt;
* ID, timestamps, and payload data are identical to the request (echoed back)&lt;br /&gt;
* Round-trip time can be calculated by comparing current time with echoed timestamp&lt;br /&gt;
* Ping succeeded on first attempt with minimal latency (~1 millisecond between timestamps)&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 2 - No authentication, with encryption ===&lt;br /&gt;
&lt;br /&gt;
==== Packet 7: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates flow allocation with encryption enabled. This FLOW_REQ carries an OAP header with an ephemeral ECDHE P-384 public key (91 bytes) for encryption setup.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:53.808158 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 212:&lt;br /&gt;
        0x0000:  0000 00c2 0040 0000 0000 0001 0000 0000  .....@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8af1 766b 547c  ..f.i.._....vkT|&lt;br /&gt;
        0x0050:  fcb0 8fce 5d60 a01e e8be 0218 86a8 e253  ....]`.........S&lt;br /&gt;
        0x0060:  8b6c 9000 0000 5b30 5930 1306 072a 8648  .l....[0Y0...*.H&lt;br /&gt;
        0x0070:  ce3d 0201 0608 2a86 48ce 3d03 0107 0342  .=....*.H.=....B&lt;br /&gt;
        0x0080:  0004 c508 1c19 6106 b7e9 3074 57b9 bb16  ......a...0tW...&lt;br /&gt;
        0x0090:  6959 4a55 81f9 169b cc79 fe10 a882 41fe  iYJU.....y....A.&lt;br /&gt;
        0x00a0:  0697 c9b4 f8f0 5562 7fa2 c7a0 a020 1ac6  ......Ub........&lt;br /&gt;
        0x00b0:  939f 23ff b2fb 07a2 b747 aacc 474a 3dab  ..#......G..GJ=.&lt;br /&gt;
        0x00c0:  2598 0000 0000                           %.....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;00c2&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 194 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... d4c0&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Mixed || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x00 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload with Ephemeral Key&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;f1 766b 547c fcb0 8fce 5d60 a01e e8be 02&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e253 8b6c 90&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate in client request&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x005b (91) || Ephemeral key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0xc1 || &amp;lt;code&amp;gt;30 5930 ... 3dab 2598&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDHE P-384 || ECDHE P-384 public key (DER encoded)&lt;br /&gt;
|-&lt;br /&gt;
| 0xd3-0xd4 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0xd5-0xd6 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* Encryption enabled: ephemeral key present (91 bytes)&lt;br /&gt;
* Client sends no certificate, allowing anonymous encryption setup&lt;br /&gt;
* No signature (unsigned OAP)&lt;br /&gt;
* Ephemeral key is ECDHE P-384 for key exchange&lt;br /&gt;
&lt;br /&gt;
==== Packet 8: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server accepts the encrypted flow allocation request. FLOW_REPLY contains the server&#039;s ephemeral key but no certificate (since client didn&#039;t send one).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:53.810564 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 180:&lt;br /&gt;
        0x0000:  0000 00a2 0042 0040 0000 0000 0000 0000  .....B.@........&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 00f1 766b 547c  ............vkT|&lt;br /&gt;
        0x0030:  fcb0 8fce 5d60 a01e e8be 0218 86a8 e253  ....]`.........S&lt;br /&gt;
        0x0040:  b694 e800 0000 5b30 5930 1306 072a 8648  ......[0Y0...*.H&lt;br /&gt;
        0x0050:  ce3d 0201 0608 2a86 48ce 3d03 0107 0342  .=....*.H.=....B&lt;br /&gt;
        0x0060:  0004 5f3c 6929 cca2 024a ae9f 9aa1 dfc2  .._&amp;lt;i)...J......&lt;br /&gt;
        0x0070:  a493 3ff3 ff58 b054 74dc d2e2 47fc 7c5b  ..?..X.Tt...G.|[&lt;br /&gt;
        0x0080:  eff5 e129 72b4 de1e 7c09 bf8c fe38 5e8b  ...)r...|....8^.&lt;br /&gt;
        0x0090:  b22e 59ed 6eb9 dfda 369d 691e 6e2c 122c  ..Y.n...6.i.n,.,&lt;br /&gt;
        0x00a0:  9936 0000 0000                           .6....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;00a2&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 162 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0042&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0042 || Source Endpoint ID (allocated server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0x00000000 || Response code: 0 (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY with Ephemeral Key&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;f1 766b 547c fcb0 8fce 5d60 a01e e8be 02&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || Echo of client ID || Echoes client&#039;s flow ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;18 86a8 e253 b694 e8&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Echoed timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x46 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x005b (91) || Ephemeral key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x47-0xa1 || &amp;lt;code&amp;gt;30 5930...9936&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDHE P-384 || Server&#039;s ECDHE P-384 public key (DER encoded)&lt;br /&gt;
|-&lt;br /&gt;
| 0xd1-0xd2 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0xd3-0xd4 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID 0x0042 is the new server-side flow endpoint&lt;br /&gt;
* Both keys are now exchanged; client and server can derive shared secret&lt;br /&gt;
* No authentication (no certificates) but encryption is negotiated&lt;br /&gt;
* Response indicates successful allocation&lt;br /&gt;
&lt;br /&gt;
==== Packet 9: Encrypted ECHO_REQUEST ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends encrypted ping request after encryption keys are established. The payload is encrypted with the derived shared secret.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:54.815771 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0042 0060 a691 6d83 8446 cbeb ac95 c2eb  .B.`..m..F......&lt;br /&gt;
        0x0010:  4b42 e819 c67f 92c8 58d7 0641 d8a6 6e1f  KB......X..A..n.&lt;br /&gt;
        0x0020:  fc90 feed ef55 b791 4fbd a832 74bd 8bed  .....U..O..2t...&lt;br /&gt;
        0x0030:  249c 4cee 0fc0 cec6 2f1b aec1 2428 bdbd  $.L...../...$(..&lt;br /&gt;
        0x0040:  36b5 01b5 1257 004e 6ed6 7ecd f0c7 7d11  6....W.Nn.~...}.&lt;br /&gt;
        0x0050:  20ba e81b f43a 4de9 b141 1624 e1ba 0a84  .....:M..A.$....&lt;br /&gt;
        0x0060:  74b1 9a9a                                t...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0042&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0042 || Destination Endpoint ID (server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0060&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 96 bytes || Encrypted payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REQUEST (Encrypted)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x63 || &amp;lt;code&amp;gt;a691 6d83 8446 cbeb...74b1 9a9a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Encrypted Data&#039;&#039;&#039; || 96 || Ciphertext || All 96 bytes encrypted&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* All 96 bytes of oping data (type, ID, timestamps, payload) are encrypted&lt;br /&gt;
* No plaintext oping headers visible; entire packet is ciphertext&lt;br /&gt;
* Flow IDs (0x0042) identify which encryption context to use&lt;br /&gt;
* Ping still works with encryption transparently&lt;br /&gt;
&lt;br /&gt;
==== Packet 10: Encrypted ECHO_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server receives encrypted ping request, decrypts it, and sends encrypted ECHO_REPLY.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:54.819574 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0040 0060 c6ea 2222 5618 0268 b27e 9a91  .@.`..&amp;quot;&amp;quot;V..h.~..&lt;br /&gt;
        0x0010:  f124 1f8d bccc 478c 26fe 9b13 b3cb 5398  .$....G.&amp;amp;.....S.&lt;br /&gt;
        0x0020:  6869 3cdb 4928 510d 4de8 dc6a 3f3a 6a6d  hi&amp;lt;.I(Q.M..j?:jm&lt;br /&gt;
        0x0030:  6487 dcd8 c8cd 1a85 fba2 9ecd 3566 57d1  d...........5fW.&lt;br /&gt;
        0x0040:  1c94 ac35 518e 8509 873a 3a5e 04d9 8ee2  ...5Q....::^....&lt;br /&gt;
        0x0050:  9d74 2527 e425 5433 9d73 9ccd f56a 1f8d  .t%&#039;.%T3.s...j..&lt;br /&gt;
        0x0060:  f328 7237                                .(r7&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0060&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 96 bytes || Encrypted payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REPLY (Encrypted)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x63 || &amp;lt;code&amp;gt;c6ea 2222 5618 0268...f328 7237&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Encrypted Data&#039;&#039;&#039; || 96 || Ciphertext || All 96 bytes encrypted&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID 0x0040 shows reply going back to client-side flow&lt;br /&gt;
* Ciphertext is different from request (different plaintext: type field differs)&lt;br /&gt;
* Both encrypted packets are 96 bytes (same size as Packet 9)&lt;br /&gt;
* Client receives encrypted reply, decrypts it, verifies ID and timestamps match request&lt;br /&gt;
* Encryption is transparent at application layer: oping works exactly as with plaintext flows&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 3 - Authentication and encryption ===&lt;br /&gt;
&lt;br /&gt;
==== Packet 11: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates flow allocation request with encryption enabled. Sends ephemeral public key for ECDHE key exchange but no certificate (client is not authenticating in this tutorial). The management protocol now carries a valid allocated SEID (0x0040).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:58.827411 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 212:&lt;br /&gt;
        0x0000:  0000 00c2 0040 0000 0000 0001 0000 0000  .....@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a66 bb82 95fa  ..f.i.._...f....&lt;br /&gt;
        0x0050:  91a2 7bd3 bd60 1b3e 35f6 b918 86a8 e37e  ..{..`.&amp;gt;5......~&lt;br /&gt;
        0x0060:  c0d2 ad00 0000 5b30 5930 1306 072a 8648  ......[0Y0...*.H&lt;br /&gt;
        0x0070:  ce3d 0201 0608 2a86 48ce 3d03 0107 0342  .=....*.H.=....B&lt;br /&gt;
        0x0080:  0004 9dea c238 6732 4987 1cd4 7133 9614  .....8g2I...q3..&lt;br /&gt;
        0x0090:  9d04 4fde 3f68 42f1 54fb 7ef3 88d0 ffe6  ..O.?hB.T.~.....&lt;br /&gt;
        0x00a0:  7e01 432e 56c2 2d64 72c9 19fc b0cf 1eca  ~.C.V.-dr.......&lt;br /&gt;
        0x00b0:  689e 3536 771a 8041 726c 20e2 d9bb 3589  h.56w..Arl....5.&lt;br /&gt;
        0x00c0:  86e7 0000 0000                           ......&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;00c2&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 194 bytes || Total payload length (excluding DIX header)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID (client flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... d4c0&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Default values || QoS parameters&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || Response code (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x00 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ... 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload with Encryption Setup&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;66 bb82 95fa 91a2 7bd3 bd60 1b3e 35f6 b9&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier for Test 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e37e c0d2 ad&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || Client not authenticating&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 91 (0x005b) || Ephemeral public key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0xc1 || &amp;lt;code&amp;gt;30 5930 1306 ... 3589&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDP-384 public key || Client&#039;s ephemeral ECDHE public key for encryption negotiation&lt;br /&gt;
|-&lt;br /&gt;
| 0xc2-0xc3 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0xc4-0xc5 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0040 - Same as Test 2 (Encrypted) because this is the same client session reusing the same allocated ID from the previous test&lt;br /&gt;
* No Certificate - &amp;lt;code&amp;gt;crt_len = 0x0000&amp;lt;/code&amp;gt; because the client does not have authentication credentials; the server will authenticate instead&lt;br /&gt;
* Ephemeral Key Present - &amp;lt;code&amp;gt;eph_len = 0x005b (91)&amp;lt;/code&amp;gt; because encryption is enabled on the client&lt;br /&gt;
* No Signature - &amp;lt;code&amp;gt;sig_len = 0x0000&amp;lt;/code&amp;gt; because client is not signing (no certificate to sign with)&lt;br /&gt;
* FLOW_REQ Message Type - Code field is 0x00, and service hash is present because FLOW_REQ always includes the service hash&lt;br /&gt;
* Timestamp Consistency - Same OAP ID and timestamp structure as Test 2, but with additional security handshake&lt;br /&gt;
&lt;br /&gt;
==== Packet 12: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds to client&#039;s FLOW_REQ by sending FLOW_REPLY with its certificate for authentication, ephemeral public key for ECDHE encryption setup, and a digital signature proving ownership of the certificate. This is the full authentication response.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data (abbreviated):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:58.828806 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 843:&lt;br /&gt;
        0x0000:  0000 0339 0043 0040 0000 0000 0000 0000  ...9.C.@........&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 0066 bb82 95fa  ...........f....&lt;br /&gt;
        0x0030:  91a2 7bd3 bd60 1b3e 35f6 b918 86a8 e37e  ..{..`.&amp;gt;5......~&lt;br /&gt;
        0x0040:  d566 a002 2f30 8202 2b30 8201 b2a0 0302  .f../0..+0......&lt;br /&gt;
        (... certificate and signature bytes ...)&lt;br /&gt;
        0x0320:  ef11 c358 f5d0 5cd7 3906 adf1 8a2c 9b25  ...X..\.9....,.%&lt;br /&gt;
        0x0330:  dc78 6050 ab61 3a3f 81c0 254b d193 7827  .x`P.a:?..%K..x&#039;&lt;br /&gt;
        0x0340:  c0e9 38c7 e0d1 c517 d299 9992 07         ..8..........&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0339&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 825 bytes || Total payload length (excluding DIX header)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0043&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0043 || Source Endpoint ID (server-side allocated flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client&#039;s flow ID from FLOW_REQ)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Default values || QoS parameters&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0x00000000 || Response code: 0 (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY Payload with Full Authentication&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;66 bb82 95fa 91a2 7bd3 bd60 1b3e 35f6 b9&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || &#039;&#039;&#039;Same ID as client&#039;s FLOW_REQ&#039;&#039;&#039; (echoed back)&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;18 86a8 e37e d566 a0&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Server&#039;s timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;022f&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 559 (0x022f) || Server certificate length: 559 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x243 || &amp;lt;code&amp;gt;2f30 8202 2b ... 81c8 30&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Certificate&#039;&#039;&#039; || 559 || DER-encoded X.509 || Server&#039;s certificate (signed by intermediate CA)&lt;br /&gt;
|-&lt;br /&gt;
| 0x244-0x245 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 91 (0x005b) || Server&#039;s ephemeral public key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x246-0x2a0 || &amp;lt;code&amp;gt;30 5930 1306 ... 9936&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDP-384 public key || Server&#039;s ephemeral ECDHE public key&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a4-0x2a5 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a6-0x2a7 || &amp;lt;code&amp;gt;0068&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 104 (0x0068) || Digital signature length: 104 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a8-0x30f || &amp;lt;code&amp;gt;30 6602 3100 ... 07&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Signature&#039;&#039;&#039; || 104 || ECDSA signature (DER encoded) || Server&#039;s signature over OAP header proving certificate ownership&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0043 - New server-side endpoint ID allocated for this authenticated flow&lt;br /&gt;
* DEID is 0x0040 - Client&#039;s flow ID from the FLOW_REQ, establishing the bidirectional flow&lt;br /&gt;
* FLOW_REPLY Message Type - Code field is 0x01, &#039;&#039;&#039;no service hash&#039;&#039;&#039; (already identified in FLOW_REQ)&lt;br /&gt;
* Full Certificate - &amp;lt;code&amp;gt;crt_len = 0x022f (559)&amp;lt;/code&amp;gt; carrying server&#039;s complete X.509 certificate signed by intermediate CA&lt;br /&gt;
* Ephemeral Key Present - &amp;lt;code&amp;gt;eph_len = 0x005b (91)&amp;lt;/code&amp;gt; with server&#039;s ECDHE public key for encryption&lt;br /&gt;
* Signature Included - &amp;lt;code&amp;gt;sig_len = 0x0068 (104)&amp;lt;/code&amp;gt; containing ECDSA digital signature over the entire OAP header&lt;br /&gt;
* Same OAP ID - Server echoes back the exact ID from client&#039;s FLOW_REQ to confirm association (included in signature, binding response to this specific client request)&lt;br /&gt;
* Large Payload - Total of 825 bytes due to certificate (559) + ephemeral key (91) + signature (104) + overhead&lt;br /&gt;
* Authentication Complete - Client verifies: (1) certificate against CA store, (2) signature over entire response ensures authenticity and integrity, (3) echoed ID binds response to this specific request, (4) timestamp prevents replay attacks&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds with its certificate for authentication, ephemeral public key for ECDHE encryption, and a digital signature proving ownership of the certificate.&lt;br /&gt;
&lt;br /&gt;
==== Packet 13: Encrypted ECHO_REQUEST ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends encrypted ping request after authentication handshake. All application data is protected by encryption using the ephemeral keys established in Packets 11-12.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:59.836485 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0043 0060 3bed 0b48 1be1 6930 cf3d dee9  .C.`.;..H..i0.=..&lt;br /&gt;
        0x0010:  4fc9 774b 5d63 cc9b 5a34 6604 f9ac 1016  O.wK]c..Z4f.....&lt;br /&gt;
        0x0020:  1c6d c9ac f80e dc89 31c1 9634 1a4f b2c7  .m......1..4.O..&lt;br /&gt;
        0x0030:  4721 e402 8259 b0aa 8870 4566 33d1 9c18  G!...Y..  .pEf3...&lt;br /&gt;
        0x0040:  06da 50c3 8b75 86b0 f240 d109 840e a6cd  ..P..u...@......&lt;br /&gt;
        0x0050:  d115 77cb 5652 5bfb e6d5 0ca9 dbc3 d0b8  ..w.VR[.........&lt;br /&gt;
        0x0060:  0058 fd19                                .X..&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame Analysis:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Length !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Source EID || 0x00-0x01 || 2 bytes || &amp;lt;code&amp;gt;0x0043&amp;lt;/code&amp;gt; || Client flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| Destination EID || 0x02-0x03 || 2 bytes || &amp;lt;code&amp;gt;0x0060&amp;lt;/code&amp;gt; || Server flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Encrypted Payload&#039;&#039;&#039; || &#039;&#039;&#039;0x04-0x71&#039;&#039;&#039; || &#039;&#039;&#039;110 bytes&#039;&#039;&#039; || &#039;&#039;&#039;Ciphertext&#039;&#039;&#039; || &#039;&#039;&#039;AES-encrypted oping ECHO_REQUEST data&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* No visible protocol structure - all application data appears as ciphertext&lt;br /&gt;
* Uses the same source/destination EID pair (0x0043 → 0x0060) established in the FLOW_REQ/FLOW_REPLY handshake&lt;br /&gt;
* Encryption is done using the ephemeral key (91 bytes) exchanged in Packet 11&#039;s OAP header&lt;br /&gt;
* Unlike Packets 11-12, this packet contains no certificate, public keys, or signatures&lt;br /&gt;
* The 110-byte encrypted data corresponds to the original oping ECHO_REQUEST message&lt;br /&gt;
&lt;br /&gt;
==== Packet 14: Encrypted ECHO_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server sends encrypted ping reply. Note that the flow identifiers swap, demonstrating bidirectional encrypted communication.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:59.836930 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0040 0060 d552 e100 e681 940c e35a 07d0  .@.`..........Z..&lt;br /&gt;
        0x0010:  a293 1d73 33a5 854e 0fce 4f4d 6655 267a  ...s3..N..OMfU&amp;amp;z&lt;br /&gt;
        0x0020:  3de2 663b 709d 739a a696 2ddd 7b34 28b8  =.f;p.s...-{4(...&lt;br /&gt;
        0x0030:  5a98 eec2 52c6 4288 3885 ae16 e466 4181  Z...R.B.8...fA..&lt;br /&gt;
        0x0040:  f2d6 44c1 b51b 8728 58a4 7525 fb5e 3fd6  ..D...(X.u%.^?..&lt;br /&gt;
        0x0050:  7e49 532a d2a5 bea7 55e9 c274 f1b2 0412  ~IS*....U..t....&lt;br /&gt;
        0x0060:  73d4 6436                                s.d6&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame Analysis:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Length !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Source EID || 0x00-0x01 || 2 bytes || &amp;lt;code&amp;gt;0x0040&amp;lt;/code&amp;gt; || Client&#039;s inbound flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| Destination EID || 0x02-0x03 || 2 bytes || &amp;lt;code&amp;gt;0x0060&amp;lt;/code&amp;gt; || Server flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Encrypted Payload&#039;&#039;&#039; || &#039;&#039;&#039;0x04-0x71&#039;&#039;&#039; || &#039;&#039;&#039;110 bytes&#039;&#039;&#039; || &#039;&#039;&#039;Ciphertext&#039;&#039;&#039; || &#039;&#039;&#039;AES-encrypted oping ECHO_REPLY data&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* The EID in offset 0x00 is now 0x0040 (server&#039;s view of client&#039;s inbound flow)&lt;br /&gt;
* Uses the same ephemeral key material as Packet 13, but encryption direction is reversed&lt;br /&gt;
* Both packets use AES-GCM with keys derived from the ECDH exchange&lt;br /&gt;
* Timestamp 17:39:59.836930 is only 445 microseconds after Packet 13, indicating server-side processing&lt;br /&gt;
* The 110-byte encrypted ECHO_REPLY payload is the same size as the request&lt;br /&gt;
* All application data is protected by both authentication (X.509 + ECDSA) and encryption (AES)&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 4 - Authentication, no encryption ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Packet 15: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates flow allocation with authentication enabled but encryption disabled. This FLOW_REQ carries an OAP header but &#039;&#039;&#039;no ephemeral key&#039;&#039;&#039; since the client does not request encrypted communication.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:03.413372 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 121:&lt;br /&gt;
        0x0000:  0000 0067 0040 0000 0000 0001 0000 0000  ...g.@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a8f a6ab 6ea7  ..f.i.._........&lt;br /&gt;
        0x0050:  ef89 68e1 ed1e 2ede 0919 fa18 86a8 e490  .h..............&lt;br /&gt;
        0x0060:  0de6 6100 0000 0000 0000 00              ..a.....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0067&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 103 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID (allocated flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... dc40&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Mixed || &lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload (No Encryption)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;8f a6ab 6ea7 ef89 68e1 ed1e 2ede 0919 fa&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e490 0de6 61&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate in client request&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;No ephemeral key (no encryption)&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0x68 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x69-0x6a || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* No encryption enabled: ephemeral key absent (Eph_len = 0x0000)&lt;br /&gt;
* Client requests authentication only&lt;br /&gt;
* Server will respond with certificate + signature but no ephemeral key&lt;br /&gt;
* Packet is minimal compared to Packet 11 (Test 3) which includes 91-byte ephemeral key&lt;br /&gt;
&lt;br /&gt;
==== Packet 16: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server accepts the authenticated (but not encrypted) flow allocation request. FLOW_REPLY contains the server&#039;s X.509 certificate and ECDSA signature for client authentication, but &#039;&#039;&#039;no ephemeral key&#039;&#039;&#039; since encryption is not being used.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data (abbreviated):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:03.416675 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 751:&lt;br /&gt;
        0x0000:  0000 02dd 0041 0040 0000 0000 0000 0000  .......A.@......&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 008f a6ab 6ea7  ................&lt;br /&gt;
        0x0030:  ef89 68e1 ed1e 2ede 0919 fa18 86a8 e490  .h..............&lt;br /&gt;
        0x0040:  3754 a702 2f30 8202 2b30 8201 b2a0 0302  7T../0..+0......&lt;br /&gt;
        0x0050:  0102 0202 1000 300a 0608 2a86 48ce 3d04  ......0...*.H.=.&lt;br /&gt;
        (... certificate and signature bytes ...)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;02dd&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 733 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0041 || Source Endpoint ID (allocated server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0x00000000 || Response code: 0 (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY Payload with Certificate and Signature (No Ephemeral Key)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;8f a6ab 6ea7 ef89 68e1 ed1e 2ede 0919&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || &#039;&#039;&#039;Same ID as client&#039;s FLOW_REQ&#039;&#039;&#039; (Packet 15 echoed back)&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;fa18 86a8 e490 3754&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Server&#039;s timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;a702&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x02a7 (679 decimal) || &#039;&#039;&#039;Certificate length: 679 bytes&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x270 || &amp;lt;code&amp;gt;2f30 8202 2b30 8201 b2a0 0302 ... (DER certificate) ...&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Certificate&#039;&#039;&#039; || 679 || DER-encoded X.509 || Server&#039;s certificate signed by intermediate CA&lt;br /&gt;
|-&lt;br /&gt;
| 0x271-0x272 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;No ephemeral key&#039;&#039;&#039; (no encryption)&lt;br /&gt;
|-&lt;br /&gt;
| 0x273-0x274 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x275-0x276 || &amp;lt;code&amp;gt;0067&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0067 (103 decimal) || &#039;&#039;&#039;ECDSA signature length: 103 bytes&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x277-0x2dd || &amp;lt;code&amp;gt;3065 0230 75dc 5717 ... 83&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Signature&#039;&#039;&#039; || 103 || ECDSA signature (DER encoded) || Server&#039;s ECDSA signature proving certificate ownership&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0041 - New server-side endpoint ID allocated for this authenticated flow&lt;br /&gt;
* DEID is 0x0040 - Client&#039;s flow ID from Packet 15 FLOW_REQ, establishing the bidirectional flow&lt;br /&gt;
* FLOW_REPLY Message Type - Code field is 0x01, &#039;&#039;&#039;no service hash&#039;&#039;&#039; (already identified in FLOW_REQ)&lt;br /&gt;
* Certificate Field - &amp;lt;code&amp;gt;crt_len = 0x02a7 (679)&amp;lt;/code&amp;gt; carrying server&#039;s X.509 certificate signed by intermediate CA&lt;br /&gt;
* Separate Signature Field - &amp;lt;code&amp;gt;sig_len = 0x0067 (103)&amp;lt;/code&amp;gt; with ECDSA signature over entire OAP header&lt;br /&gt;
* No Ephemeral Key - &amp;lt;code&amp;gt;eph_len = 0x0000&amp;lt;/code&amp;gt; since encryption is &#039;&#039;&#039;not&#039;&#039;&#039; being used in Test 4&lt;br /&gt;
* Same OAP ID - Server echoes back the exact ID from client&#039;s FLOW_REQ (included in signature, binding response to this specific client request)&lt;br /&gt;
* Complete OAP Structure - Full OAP header with all standard fields, just without ephemeral key data&lt;br /&gt;
* Plaintext Data Exchange - After this FLOW_REPLY, all subsequent application data will be transmitted in plaintext (but authenticated via certificate + signature verification)&lt;br /&gt;
&lt;br /&gt;
==== Packet 17: ECHO_REQUEST ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends plaintext ECHO_REQUEST data through the authenticated (but unencrypted) flow. The oping application&#039;s ping request is transmitted directly without encryption, relying on the earlier certificate+signature authentication for security.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:04.419664 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0041 0040 0000 0000 0000 0000 8177 0000  .A.@............&lt;br /&gt;
        0x0010:  0000 0000 aa16 1c16 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0041 || Destination Endpoint ID (server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Total application data length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Application Data - Oping Echo Request (Plaintext)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000000 || Message type: ECHO_REQUEST&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Sequence number / message identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;8177 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Seconds component from CLOCK_REALTIME&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;aa16 1c16 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Nanoseconds component (0-999999999)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || Application data || Probe payload (zero-filled for 64 bytes total)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID Pair: 0x0041 → Server Flow - Data is directed to the server&#039;s endpoint ID allocated in Packet 16 FLOW_REPLY&lt;br /&gt;
* Plaintext Transmission - No encryption layer; oping payload is sent as-is (compare to Packet 13 which had encryption)&lt;br /&gt;
* Authenticated Flow - Although plaintext, this data travels on the authenticated flow established in Packet 16 (certificate + signature verified)&lt;br /&gt;
* Type = ECHO_REQUEST - 0x00000000 indicates client-to-server ping request&lt;br /&gt;
* ID = 0 - Sequence number for matching request/reply pairs&lt;br /&gt;
* Test 4 Characteristic - Demonstrates authenticated communication &#039;&#039;&#039;without&#039;&#039;&#039; encryption; application data is readable but cryptographically bound to the authenticated flow&lt;br /&gt;
* Contrast to Test 3 - Packet 13 (Test 3 encrypted ECHO_REQUEST) was 114 bytes with ciphertext; this packet is 82 bytes of plaintext&lt;br /&gt;
* Total Payload - 64 bytes total (4 + 4 + 8 + 8 + 40 bytes payload)&lt;br /&gt;
&lt;br /&gt;
==== Packet 18: ECHO_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds with plaintext ECHO_REPLY data, echoing back the client&#039;s request. This confirms successful bidirectional communication over the authenticated (but unencrypted) flow.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:04.420088 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0040 0040 0000 0001 0000 0000 8177 0000  .@.@............&lt;br /&gt;
        0x0010:  0000 0000 aa16 1c16 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Total application data length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Application Data - Oping Echo Reply (Plaintext)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0001&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000001 || Message type: ECHO_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Echo of request sequence number&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;8177 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Echoed request timestamp (seconds)&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;aa16 1c16 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Echoed request timestamp (nanoseconds)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || Application data || Echoed probe payload (zero-filled for 64 bytes total)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID Pair: 0x0040 → Client Flow - Server responds to client&#039;s endpoint ID from Packet 15 FLOW_REQ&lt;br /&gt;
* Type = ECHO_REPLY - 0x00000001 indicates server-to-client response&lt;br /&gt;
* ID = 0 - Echoes the request sequence number, matching this response to the request&lt;br /&gt;
* Timestamps Echo Request - Both timestamp fields are copied from Packet 17 unchanged (8177 0000 0000 0000 and aa16 1c16 0000 0000)&lt;br /&gt;
* Plaintext Reply - No encryption; server&#039;s response payload is readable (compare to Packet 14 which had encryption)&lt;br /&gt;
* Authenticated Channel - Although plaintext, this reply is part of the authenticated flow; client can verify integrity through earlier certificate+signature&lt;br /&gt;
* Test 4 Completion - Demonstrates &#039;&#039;&#039;full bidirectional plaintext communication&#039;&#039;&#039; over an authenticated (but unencrypted) flow&lt;br /&gt;
* Contrast to Test 3 - Packet 14 (Test 3 encrypted ECHO_REPLY) was 114 bytes with ciphertext; this packet is 82 bytes of plaintext&lt;br /&gt;
* Total Payload - 64 bytes total (4 + 4 + 8 + 8 + 40 bytes payload)&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Tutorial_06&amp;diff=1895</id>
		<title>Ouroboros Tutorial 06</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Tutorial_06&amp;diff=1895"/>
		<updated>2026-02-14T14:56:42Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* Test 3: With Authentication, With Encryption */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Ouroboros Tutorial 06 - Authenticated Flows =&lt;br /&gt;
&lt;br /&gt;
This tutorial demonstrates setting up and using authenticated flows in Ouroboros with certificate-based authentication.&lt;br /&gt;
&lt;br /&gt;
The overall flow of authenticated flow allocation is&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Client (IRMd)                              Server (IRMd)&lt;br /&gt;
     |                                           |&lt;br /&gt;
     | 1. Load client cert/key                   |&lt;br /&gt;
     | 2. Generate ephemeral keypair             |&lt;br /&gt;
     | 3. Build OAP_HDR (id, ts, crt, eph)       |&lt;br /&gt;
     | 4. Sign header with client key            |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |-------- FLOW_REQ (OAP_HDR) -------------&amp;gt; |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |                                           | 5. Load server cert/key&lt;br /&gt;
     |                                           | 6. Verify client cert against CA&lt;br /&gt;
     |                                           | 7. Verify client signature&lt;br /&gt;
     |                                           | 8. Generate ephemeral keypair&lt;br /&gt;
     |                                           | 9. Derive symmetric key (ECDHE)&lt;br /&gt;
     |                                           | 10. Build response OAP_HDR&lt;br /&gt;
     |                                           | 11. Sign with server key&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |&amp;lt;------- FLOW_REPLY (OAP_HDR) ------------ |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     | 12. Verify server cert against CA         |&lt;br /&gt;
     | 13. Verify server signature               |&lt;br /&gt;
     | 14. Derive symmetric key (ECDHE)          |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |===========================================|&lt;br /&gt;
     |         Encrypted data channel            |&lt;br /&gt;
     |===========================================|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Tutorial Directory:&#039;&#039;&#039; This tutorial will execute in &amp;lt;code&amp;gt;/tmp/o7s-tut06/&amp;lt;/code&amp;gt;. All configuration files, generated certificates, logs, and packet captures will be stored in this directory.&lt;br /&gt;
&lt;br /&gt;
We create a complete PKI (Public Key Infrastructure):&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Root CA&#039;&#039;&#039; (&amp;lt;code&amp;gt;ca.tut.o7s&amp;lt;/code&amp;gt;): Self-signed trust anchor&lt;br /&gt;
* &#039;&#039;&#039;Intermediate CA&#039;&#039;&#039; (&amp;lt;code&amp;gt;sign.tut.o7s&amp;lt;/code&amp;gt;): Signed by root with pathlen:0 constraint&lt;br /&gt;
* &#039;&#039;&#039;Server Certificate&#039;&#039;&#039; (&amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;): Signed by intermediate CA&lt;br /&gt;
&lt;br /&gt;
This tutorial uses ECDSA P-384 with SHA-384 hashing.&lt;br /&gt;
&lt;br /&gt;
== Setting Up the Tutorial ==&lt;br /&gt;
&lt;br /&gt;
To properly understand and debug the authenticated flows, this tutorial uses a debug build of Ouroboros with OAP protocol debugging enabled.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /path/to/ouroboros&lt;br /&gt;
mkdir build &amp;amp;&amp;amp; cd build&lt;br /&gt;
cmake -DCMAKE_BUILD_TYPE=Debug -DDEBUG_PROTO_OAP=ON ..&lt;br /&gt;
make -j$(nproc)&lt;br /&gt;
sudo make install&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When built with these options, the IRMd will output detailed OAP protocol information.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Files ===&lt;br /&gt;
&lt;br /&gt;
The following three files should be created in the tutorial directory (&amp;lt;code&amp;gt;/tmp/o7s-tut06/&amp;lt;/code&amp;gt;) before starting the tutorial:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;tut06.conf&#039;&#039;&#039; - IRMd configuration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
# Ouroboros Tutorial 06 - Authenticated Flows Configuration&lt;br /&gt;
# Uses system-installed certificates at /etc/ouroboros/security/&lt;br /&gt;
&lt;br /&gt;
[name.&amp;quot;sec.oping.tut.o7s&amp;quot;]&lt;br /&gt;
prog=[&amp;quot;/usr/bin/oping&amp;quot;]&lt;br /&gt;
args=[&amp;quot;--listen&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
[eth-dix.eth-dix-lo]&lt;br /&gt;
bootstrap=&amp;quot;eth-dix-network&amp;quot;&lt;br /&gt;
dev=&amp;quot;lo&amp;quot;&lt;br /&gt;
reg=[&amp;quot;sec.oping.tut.o7s&amp;quot;]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ca.tut.o7s.cnf&#039;&#039;&#039; - OpenSSL configuration for PKI generation&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
# Unified OpenSSL Configuration for Ouroboros Tutorial 06&lt;br /&gt;
# Named CA sections: CA_root (signs intermediate), CA_intermediate (signs server)&lt;br /&gt;
# Usage: openssl ca -name CA_root -config ca.tut.o7s.cnf ...&lt;br /&gt;
&lt;br /&gt;
[ req ]&lt;br /&gt;
default_bits       = 384&lt;br /&gt;
default_keyfile    = private/key.pem&lt;br /&gt;
distinguished_name = req_distinguished_name&lt;br /&gt;
string_mask        = utf8only&lt;br /&gt;
default_md         = sha384&lt;br /&gt;
x509_extensions    = v3_ca&lt;br /&gt;
&lt;br /&gt;
[ req_distinguished_name ]&lt;br /&gt;
countryName                 = Country Name (2 letter code)&lt;br /&gt;
stateOrProvinceName         = State or Province Name&lt;br /&gt;
localityName                = Locality Name&lt;br /&gt;
organizationName            = Organization Name&lt;br /&gt;
commonName                  = Common Name&lt;br /&gt;
&lt;br /&gt;
countryName_default         = BE&lt;br /&gt;
stateOrProvinceName_default = OVL&lt;br /&gt;
localityName_default        = Ghent&lt;br /&gt;
organizationName_default    = o7s&lt;br /&gt;
&lt;br /&gt;
[ ca ]&lt;br /&gt;
default_ca = CA_root&lt;br /&gt;
&lt;br /&gt;
[ CA_root ]&lt;br /&gt;
dir             = ./pki/root&lt;br /&gt;
database        = $dir/index.txt&lt;br /&gt;
serial          = $dir/serial&lt;br /&gt;
new_certs_dir   = $dir/certs&lt;br /&gt;
certificate     = $dir/certs/ca.tut.o7s.crt.pem&lt;br /&gt;
private_key     = $dir/private/ca.tut.o7s.key.pem&lt;br /&gt;
default_days    = 3650&lt;br /&gt;
default_md      = sha384&lt;br /&gt;
policy          = policy_loose&lt;br /&gt;
&lt;br /&gt;
[ CA_intermediate ]&lt;br /&gt;
dir             = ./pki/sign&lt;br /&gt;
database        = $dir/index.txt&lt;br /&gt;
serial          = $dir/serial&lt;br /&gt;
new_certs_dir   = $dir/certs&lt;br /&gt;
certificate     = $dir/certs/sign.tut.o7s.crt.pem&lt;br /&gt;
private_key     = $dir/private/sign.tut.o7s.key.pem&lt;br /&gt;
default_days    = 365&lt;br /&gt;
default_md      = sha384&lt;br /&gt;
policy          = policy_loose&lt;br /&gt;
&lt;br /&gt;
[ policy_loose ]&lt;br /&gt;
commonName = supplied&lt;br /&gt;
&lt;br /&gt;
[ v3_ca ]&lt;br /&gt;
subjectKeyIdentifier = hash&lt;br /&gt;
authorityKeyIdentifier = keyid:always,issuer&lt;br /&gt;
basicConstraints = critical, CA:true&lt;br /&gt;
keyUsage = critical, digitalSignature, cRLSign, keyCertSign&lt;br /&gt;
&lt;br /&gt;
[ v3_intermediate_ca ]&lt;br /&gt;
subjectKeyIdentifier = hash&lt;br /&gt;
authorityKeyIdentifier = keyid:always,issuer&lt;br /&gt;
basicConstraints = critical, CA:true, pathlen:0&lt;br /&gt;
keyUsage = critical, digitalSignature, cRLSign, keyCertSign&lt;br /&gt;
&lt;br /&gt;
[ server_cert ]&lt;br /&gt;
basicConstraints = CA:FALSE&lt;br /&gt;
subjectKeyIdentifier = hash&lt;br /&gt;
authorityKeyIdentifier = keyid,issuer:always&lt;br /&gt;
keyUsage = critical, digitalSignature, keyEncipherment&lt;br /&gt;
extendedKeyUsage = serverAuth&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;gen-pki.sh&#039;&#039;&#039; - PKI generation script&lt;br /&gt;
&lt;br /&gt;
This script will:&lt;br /&gt;
1. Create the directory structure&lt;br /&gt;
2. Generate the root CA key and certificate&lt;br /&gt;
3. Generate the intermediate CA key and CSR&lt;br /&gt;
4. Sign the intermediate CA certificate&lt;br /&gt;
5. Generate the server certificate key and CSR&lt;br /&gt;
6. Sign the server certificate&lt;br /&gt;
7. Verify the complete certificate chain&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
# Ouroboros Tutorial 06 - PKI Generation Script (Simplified)&lt;br /&gt;
# Generates: Root CA, Intermediate CA, and Server Certificate&lt;br /&gt;
&lt;br /&gt;
set -e&lt;br /&gt;
&lt;br /&gt;
if [ ! -f ca.tut.o7s.cnf ]; then&lt;br /&gt;
    echo &amp;quot;ERROR: ca.tut.o7s.cnf not found&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
mkdir -p pki/{root,sign,server}/{certs,private,csr}&lt;br /&gt;
&lt;br /&gt;
# Root CA&lt;br /&gt;
openssl ecparam -genkey -name secp384r1 -out pki/root/private/ca.tut.o7s.key.pem 2&amp;gt;/dev/null&lt;br /&gt;
openssl req -new -x509 -sha384 -days 7300 \&lt;br /&gt;
    -config ca.tut.o7s.cnf \&lt;br /&gt;
    -key pki/root/private/ca.tut.o7s.key.pem \&lt;br /&gt;
    -out pki/root/certs/ca.tut.o7s.crt.pem \&lt;br /&gt;
    -subj &amp;quot;/C=BE/ST=OVL/L=Ghent/O=o7s/CN=ca.tut.o7s&amp;quot; 2&amp;gt;/dev/null&lt;br /&gt;
&lt;br /&gt;
# Intermediate CA&lt;br /&gt;
openssl ecparam -genkey -name secp384r1 -out pki/sign/private/sign.tut.o7s.key.pem 2&amp;gt;/dev/null&lt;br /&gt;
openssl req -new -sha384 \&lt;br /&gt;
    -config ca.tut.o7s.cnf \&lt;br /&gt;
    -key pki/sign/private/sign.tut.o7s.key.pem \&lt;br /&gt;
    -out pki/sign/csr/sign.tut.o7s.csr \&lt;br /&gt;
    -subj &amp;quot;/C=BE/ST=OVL/L=Ghent/O=o7s/CN=sign.tut.o7s&amp;quot; 2&amp;gt;/dev/null&lt;br /&gt;
&lt;br /&gt;
touch pki/root/index.txt&lt;br /&gt;
echo 1000 &amp;gt; pki/root/serial&lt;br /&gt;
&lt;br /&gt;
openssl ca -name CA_root -config ca.tut.o7s.cnf \&lt;br /&gt;
    -extensions v3_intermediate_ca -days 3650 -md sha384 -batch \&lt;br /&gt;
    -in pki/sign/csr/sign.tut.o7s.csr \&lt;br /&gt;
    -out pki/sign/certs/sign.tut.o7s.crt.pem 2&amp;gt;&amp;amp;1 | grep -E &amp;quot;Signature ok|Database updated&amp;quot; || true&lt;br /&gt;
&lt;br /&gt;
# Server Certificate&lt;br /&gt;
openssl ecparam -genkey -name secp384r1 -out pki/server/private/sec.oping.tut.o7s.key.pem 2&amp;gt;/dev/null&lt;br /&gt;
openssl req -new -sha384 \&lt;br /&gt;
    -config ca.tut.o7s.cnf \&lt;br /&gt;
    -key pki/server/private/sec.oping.tut.o7s.key.pem \&lt;br /&gt;
    -out pki/server/csr/sec.oping.tut.o7s.csr \&lt;br /&gt;
    -subj &amp;quot;/C=BE/ST=OVL/L=Ghent/O=o7s/CN=sec.oping.tut.o7s&amp;quot; 2&amp;gt;/dev/null&lt;br /&gt;
&lt;br /&gt;
touch pki/sign/index.txt&lt;br /&gt;
echo 1000 &amp;gt; pki/sign/serial&lt;br /&gt;
&lt;br /&gt;
openssl ca -name CA_intermediate -config ca.tut.o7s.cnf \&lt;br /&gt;
    -extensions server_cert -days 365 -md sha384 -batch \&lt;br /&gt;
    -in pki/server/csr/sec.oping.tut.o7s.csr \&lt;br /&gt;
    -out pki/server/certs/sec.oping.tut.o7s.crt.pem 2&amp;gt;&amp;amp;1 | grep -E &amp;quot;Signature ok|Database updated&amp;quot; || true&lt;br /&gt;
&lt;br /&gt;
# Verify chain&lt;br /&gt;
openssl verify -CAfile pki/root/certs/ca.tut.o7s.crt.pem \&lt;br /&gt;
    -untrusted pki/sign/certs/sign.tut.o7s.crt.pem \&lt;br /&gt;
    pki/server/certs/sec.oping.tut.o7s.crt.pem &amp;gt; /dev/null 2&amp;gt;&amp;amp;1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;PKI generation complete.&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Part 1: Running the Tutorial - Single Session with 4 Tests ==&lt;br /&gt;
&lt;br /&gt;
This section demonstrates a single continuous session with one IRMd and tcpdump instance. The configuration file (&amp;lt;code&amp;gt;tut06.conf&amp;lt;/code&amp;gt;) includes autostart for oping, so the server is ready immediately when IRMd starts.&lt;br /&gt;
&lt;br /&gt;
First install the &#039;&#039;&#039;CA and Intermediate CA only&#039;&#039;&#039; to the system security directories. The server certificate will be installed later during Test 3 (authentication test):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo mkdir -p /etc/ouroboros/security/{cacert,untrusted,server/sec.oping.tut.o7s,client/sec.oping.tut.o7s}&lt;br /&gt;
&lt;br /&gt;
# Run the PKI generation script&lt;br /&gt;
cd /tmp/o7s-tut06&lt;br /&gt;
sudo chmod +x gen-pki.sh&lt;br /&gt;
sudo ./gen-pki.sh&lt;br /&gt;
&lt;br /&gt;
# Install Root CA (trust anchor)&lt;br /&gt;
sudo cp pki/root/certs/ca.tut.o7s.crt.pem /etc/ouroboros/security/cacert/&lt;br /&gt;
&lt;br /&gt;
# Install Intermediate CA (for certificate chain validation)&lt;br /&gt;
sudo cp pki/sign/certs/sign.tut.o7s.crt.pem /etc/ouroboros/security/untrusted/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Running the Tutorial (3 Terminals) ===&lt;br /&gt;
&lt;br /&gt;
In this tutorial, we run a single IRMd session with a concurrent tcpdump instance to capture it. We then run four oping client tests while the IRMd/tcpdump sessions are going, modifying the security configuration between tests. After the tests are complete, we can will down the IRMd and tcpdump sessions with Ctrl-C.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Terminal 1: Start tcpdump to capture all packets (runs continuously)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo tcpdump -i lo -n -A -v -U -w /tmp/o7s-tut06/tut06.pcap &amp;quot;ether proto 0xa000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Terminal 2: Start IRMd with debug output (runs continuously)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /tmp/o7s-tut06&lt;br /&gt;
sudo irmd --config tut06.conf --stdout 2&amp;gt;&amp;amp;1 | tee /tmp/o7s-tut06/irmd.log&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Terminal 3: Run the tests&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Test 1: No Authentication, No Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Verify directories are empty&lt;br /&gt;
sudo ls -la /etc/ouroboros/security/client/sec.oping.tut.o7s/*&lt;br /&gt;
sudo ls -la /etc/ouroboros/security/server/sec.oping.tut.o7s/*&lt;br /&gt;
&lt;br /&gt;
# Run first ping test&lt;br /&gt;
echo &amp;quot;=== Test 1: No Authentication, No Encryption ===&amp;quot;&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 1: Client initiates plaintext flow allocation&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [60e824383b3fbd6a] KEX config: algo=none, mode=server-encap, cipher=none.&lt;br /&gt;
irmd/oap(PP): OAP_HDR [60e824383b3fbd6a @ 2026-02-14 14:08:56 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Cipher: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   KDF: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 1: Server accepts and completes handshake&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [60e824383b3fbd6a @ 2026-02-14 14:08:57 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Cipher: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   KDF: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [48 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; All OAP fields are &amp;lt;code&amp;gt;&amp;lt;none&amp;gt;&amp;lt;/code&amp;gt; because no security is configured (except for the request hash in the response). Flow succeeds with plaintext communication.&lt;br /&gt;
&lt;br /&gt;
==== Test 2: No Authentication, With Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Enable encryption for client only&lt;br /&gt;
sudo touch /etc/ouroboros/security/client/sec.oping.tut.o7s/enc.conf&lt;br /&gt;
&lt;br /&gt;
# Run second ping test&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 2: Client initiates flow with encryption enabled&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(II): Encryption enabled for sec.oping.tut.o7s.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] KEX config: algo=prime256v1, mode=server-encap, cipher=aes-256-gcm.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Generated ephemeral prime256v1 keys (91 bytes).&lt;br /&gt;
irmd/oap(PP): OAP_HDR [80fd6f9509a996b0 @ 2026-02-14 14:09:38 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes] [Server encaps]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: sha256&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 2: Server receives and responds with ephemeral key&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] No crt provided.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Selected client cipher aes-256-gcm.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Selected client KDF sha256.&lt;br /&gt;
irmd/oap(II): [80fd6f9509a996b0] No key exchange.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Generated prime256v1 ephemeral keys.&lt;br /&gt;
irmd/oap(PP): OAP_HDR [80fd6f9509a996b0 @ 2026-02-14 14:09:38 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes] [Server encaps]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [32 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; Both client and server generate ephemeral keys (91 bytes each) for encryption. No certificates because authentication is not required. Encryption and authentication are independent.&lt;br /&gt;
&lt;br /&gt;
==== Test 3: With Authentication, With Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Install server certificates and keys&lt;br /&gt;
sudo cp /tmp/o7s-tut06/pki/server/certs/sec.oping.tut.o7s.crt.pem \&lt;br /&gt;
        /etc/ouroboros/security/server/sec.oping.tut.o7s/crt.pem&lt;br /&gt;
sudo cp /tmp/o7s-tut06/pki/server/private/sec.oping.tut.o7s.key.pem \&lt;br /&gt;
        /etc/ouroboros/security/server/sec.oping.tut.o7s/key.pem&lt;br /&gt;
&lt;br /&gt;
# enc.conf is still in place from Test 2&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 3: Client initiates flow with encryption and server has certificate&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [c904b18b563dc1b0 @ 2026-02-14 14:09:47 (UTC) ] &amp;lt;--&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes] [Server encaps]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: sha256&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 3: Server responds with certificate + ephemeral key + signature&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [c904b18b563dc1b0 @ 2026-02-14 14:09:47 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: [560 bytes]&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes] [Server encaps]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [32 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: [103 bytes]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 3: Client verifies certificate and authenticates&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [c904b18b563dc1b0] Loaded peer crt.&lt;br /&gt;
irmd/oap(DB): [c904b18b563dc1b0] Got public key from crt.&lt;br /&gt;
irmd/oap(DB): [c904b18b563dc1b0] Successfully verified peer crt.&lt;br /&gt;
irmd/oap(DB): [c904b18b563dc1b0] Successfully authenticated peer.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; Full OAP handshake with certificate (560 bytes) + ephemeral keys (91 bytes) + signature (103 bytes). Client verifies server&#039;s certificate against CA store and confirms authentication success.&lt;br /&gt;
&lt;br /&gt;
==== Test 4: With Authentication, No Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Remove encryption config but keep certificates&lt;br /&gt;
sudo rm -f /etc/ouroboros/security/client/sec.oping.tut.o7s/enc.conf&lt;br /&gt;
&lt;br /&gt;
# Run fourth ping test&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 4: Client initiates plaintext flow (no encryption file)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
==33047== irmd(II): Allocating flow for 33642 to sec.oping.tut.o7s.&lt;br /&gt;
==33047== irmd(DB): File /etc/ouroboros/security/client/sec.oping.tut.o7s/enc.conf does not exist.&lt;br /&gt;
==33047== irmd(DB): File /etc/ouroboros/security/client/sec.oping.tut.o7s/crt.pem does not exist.&lt;br /&gt;
==33047== irmd(II): No security info for sec.oping.tut.o7s.&lt;br /&gt;
==33047== irmd/oap(PP): OAP_HDR [9b383e855577d211 @ 2026-01-01 11:27:34 (UTC) ] --&amp;gt;&lt;br /&gt;
==33047== irmd/oap(PP):   Certificate: &amp;lt;none&amp;gt;&lt;br /&gt;
==33047== irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
==33047== irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
==33047== irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 4: Server responds with certificate + signature (no ephemeral key)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
==33047== irmd(II): No certificate provided by &amp;lt;client&amp;gt;.&lt;br /&gt;
==33047== irmd/oap(PP): OAP_HDR [9b383e855577d211] --&amp;gt;&lt;br /&gt;
==33047== irmd/oap(PP):   Certificate: [560 bytes]&lt;br /&gt;
==33047== irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
==33047== irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
==33047== irmd/oap(PP):   Signature: [103 bytes]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 4: Client verifies certificate and authenticates&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
==33047== irmd(DB): Loaded peer certificate for sec.oping.tut.o7s.&lt;br /&gt;
==33047== irmd(DB): Certificate matches name sec.oping.tut.o7s.&lt;br /&gt;
==33047== irmd(DB): Got public key from certificate for sec.oping.tut.o7s.&lt;br /&gt;
==33047== irmd(II): Successfully verified peer certificate for sec.oping.tut.o7s.&lt;br /&gt;
==33047== irmd(II): Successfully authenticated sec.oping.tut.o7s.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; Server sends certificate + signature for authentication, but NO ephemeral keys (plaintext data). Data exchanged without encryption even though authenticated. Demonstrates that authentication and encryption are independent mechanisms.&lt;br /&gt;
&lt;br /&gt;
=== Stop the IRMd and tcpdump, clean up the tutorial files ===&lt;br /&gt;
&lt;br /&gt;
Once all tests complete:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Stop IRMd in Terminal 2 (Ctrl+C)&lt;br /&gt;
# Stop tcpdump in Terminal 1 (Ctrl+C)&lt;br /&gt;
&lt;br /&gt;
# Clean up tutorial security files from system&lt;br /&gt;
sudo rm -f /etc/ouroboros/security/cacert/ca.tut.o7s.crt.pem&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Part 2: PCAP Trace Analysis ==&lt;br /&gt;
&lt;br /&gt;
After the tutorial, we now explain the trace in the tcpdump pcap file.&lt;br /&gt;
&lt;br /&gt;
=== Protocol Overview ===&lt;br /&gt;
&lt;br /&gt;
This section summarizes the four protocols that work together in the captured packet flow.&lt;br /&gt;
&lt;br /&gt;
==== Ethernet DIX Frame with EID Header ====&lt;br /&gt;
&lt;br /&gt;
Ouroboros extends the DIX frame with a flow identifier (EID - Endpoint Identifier) and length field.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Octets !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Destination MAC || 0-5 || 6 bytes || Hardware address of destination&lt;br /&gt;
|-&lt;br /&gt;
| Source MAC || 6-11 || 6 bytes || Hardware address of source&lt;br /&gt;
|-&lt;br /&gt;
| EtherType || 12-13 || 2 bytes || Protocol identifier (0xA000 for Ouroboros)&lt;br /&gt;
|-&lt;br /&gt;
| EID || 14-15 || 2 bytes || Destination Endpoint Identifier&lt;br /&gt;
|-&lt;br /&gt;
| Length || 16-17 || 2 bytes || Payload length (needed because of runt frame padding)&lt;br /&gt;
|-&lt;br /&gt;
| Payload || 18+ || Variable || Frame data (up to MTU size)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Ethernet Flow Allocator - Management Protocol ====&lt;br /&gt;
&lt;br /&gt;
The Ethernet DIX management protocol handles flow allocation, setup, and teardown. All management frames use destination EID &amp;lt;code&amp;gt;0x0000&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Management Frame Types:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Code !! Type !! Direction !! Service Hash !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x00&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FLOW_REQ&amp;lt;/code&amp;gt; || Client → Server || ✓ Included || Request new flow allocation&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x01&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FLOW_REPLY&amp;lt;/code&amp;gt; || Server → Client || – Not included || Respond to flow request (success/failure)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x02&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NAME_QUERY_REQ&amp;lt;/code&amp;gt; || Client → Server || ✓ Included || Query if a remote name is reachable&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x03&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NAME_QUERY_REPLY&amp;lt;/code&amp;gt; || Server → Client || ✓ Included || Response to name query&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The 32-byte service hash (SHA3-256) is appended after the management protocol header for NAME_QUERY_* and FLOW_REQ messages to identify which service is being queried or allocated. FLOW_REPLY does not include the service hash; the endpoints are already identified by the allocated EIDs (SEID/DEID) and the flow allocation ID in the OAP header (see below).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frame Layout by Field:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| SEID || 0-1 || 2 bytes || Source Endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| DEID || 2-3 || 2 bytes || Destination Endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| Loss || 4-7 || 4 bytes || Acceptable packet loss (ppm)&lt;br /&gt;
|-&lt;br /&gt;
| Bandwidth || 8-15 || 8 bytes || Required bandwidth (bps)&lt;br /&gt;
|-&lt;br /&gt;
| BER || 16-19 || 4 bytes || Bit error rate (ppm)&lt;br /&gt;
|-&lt;br /&gt;
| Max Gap || 20-23 || 4 bytes || Maximum consecutive lost packets&lt;br /&gt;
|-&lt;br /&gt;
| Delay || 24-27 || 4 bytes || Maximum latency (ms)&lt;br /&gt;
|-&lt;br /&gt;
| Timeout || 28-31 || 4 bytes || Flow idle timeout (seconds)&lt;br /&gt;
|-&lt;br /&gt;
| Response || 32-35 || 4 bytes || Response code (0=success, negative=error)&lt;br /&gt;
|-&lt;br /&gt;
| In-Order || 36 || 1 byte || In-order delivery requirement (boolean)&lt;br /&gt;
|-&lt;br /&gt;
| Code || 37 || 1 byte || Message type (FLOW_REQ, FLOW_REPLY, etc.)&lt;br /&gt;
|-&lt;br /&gt;
| Availability || 38 || 1 byte || Availability status&lt;br /&gt;
|-&lt;br /&gt;
| Service hash || 39-61 || 32 bytes || SHA3-256 hash (optional, see above)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Ouroboros Flow Allocation Protocol (OAP) ====&lt;br /&gt;
&lt;br /&gt;
The Ouroboros Application Protocol (OAP) is the flow allocation and authentication protocol. It carries flow negotiation requests, responses, and authentication credentials. OAP frames are encapsulated as data payload over the management protocol.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frame Layout by Field:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| ID || 0-15 || 16 bytes || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| Timestamp || 16-23 || 8 bytes || Creation timestamp (UTC, seconds and microseconds)&lt;br /&gt;
|-&lt;br /&gt;
| Crt Length || 24-25 || 2 bytes || Certificate length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Certificate || 26+ || Variable || X.509 certificate (DER encoded)&lt;br /&gt;
|-&lt;br /&gt;
| Eph Length || Variable || 2 bytes || Ephemeral public key length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Ephemeral Key || Variable || Variable || ECDHE public key (DER/raw encoded)&lt;br /&gt;
|-&lt;br /&gt;
| Data Length || Variable || 2 bytes || Application data length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Data || Variable || Variable || Piggybacked application-layer data&lt;br /&gt;
|-&lt;br /&gt;
| Sig Length || Variable || 2 bytes || Signature length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Signature || Variable || Variable || Digital signature (ECDSA, DER encoded)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Oping Application Protocol ====&lt;br /&gt;
&lt;br /&gt;
The Ouroboros Ping (oping) application is a simple echo/reply protocol used to measure round-trip time and validate connectivity between applications. It implements a request/reply pattern similar to ICMP ping.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frame Layout by Field:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Type || 0-3 || 4 bytes || Message type (ECHO_REQUEST=0 or ECHO_REPLY=1)&lt;br /&gt;
|-&lt;br /&gt;
| ID || 4-7 || 4 bytes || Sequence number / message identifier&lt;br /&gt;
|-&lt;br /&gt;
| Timestamp (seconds) || 8-15 || 8 bytes || Seconds when message was sent (CLOCK_REALTIME)&lt;br /&gt;
|-&lt;br /&gt;
| Timestamp (nanoseconds) || 16-23 || 8 bytes || Nanoseconds component of timestamp&lt;br /&gt;
|-&lt;br /&gt;
| Payload || 24+ || Variable || Application data (configurable size, default 64 bytes)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Field Definitions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Type&#039;&#039;&#039; (4 bytes): Message type selector&lt;br /&gt;
** &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt; (ECHO_REQUEST): Client-to-server ping request&lt;br /&gt;
** &amp;lt;code&amp;gt;0x00000001&amp;lt;/code&amp;gt; (ECHO_REPLY): Server-to-client response&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;ID&#039;&#039;&#039; (4 bytes): Sequence number for matching requests with replies. Incremented for each ping sent.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; (8 bytes): Network-byte-order 64-bit seconds component from when the ping was sent (CLOCK_REALTIME).&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; (8 bytes): Network-byte-order 64-bit nanoseconds component (0-999999999) for high-resolution timing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Payload&#039;&#039;&#039; (Variable): Application data echoed back by the server. Size is configurable (default 64 bytes, maximum 1500 bytes).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Usage:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Client sends ECHO_REQUEST with current timestamp&lt;br /&gt;
* Server receives request and echoes back as ECHO_REPLY with the same ID and timestamps&lt;br /&gt;
* Client calculates RTT by comparing reception time with original timestamps&lt;br /&gt;
* Out-of-order detection by tracking sequence numbers (ID field)&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 1 - No authentication/encryption ===&lt;br /&gt;
&lt;br /&gt;
==== Packet 1: NAME_QUERY_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends a NAME_QUERY_REQ message to discover if the service &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt; is available. This is a broadcast discovery query sent because the service is not yet known for the flow allocation process.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:48.165639 00:00:00:00:00:00 &amp;gt; ff:ff:ff:ff:ff:ff, ethertype Unknown (0xa000), length 89:&lt;br /&gt;
        0x0000:  0000 0047 0000 0000 0000 0000 0000 0000  ...G............&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0002 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a              ..f.i.._...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0047&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 71 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0000 || Source Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || Response code (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x03 || Message Type: NAME_QUERY_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Packet 2: NAME_QUERY_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds to the NAME_QUERY_REQ by sending a NAME_QUERY_REPLY for the service hash.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:48.166073 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 89:&lt;br /&gt;
        0x0000:  0000 0047 0000 0000 0000 0000 0000 0000  ...G............&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0003 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a              ..f.i.._...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0047&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 71 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0000 || Source Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || Response code&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x03 || Message Type: NAME_QUERY_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt; (echoed back)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Packet 3: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates a flow allocation request (FLOW_REQ) with minimal OAP headers since no authentication or encryption is being used.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:48.167222 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 121:&lt;br /&gt;
        0x0000:  0000 0067 0040 0000 0000 0001 0000 0000  ...g.@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a51 8a56 ff6f  ..f.i.._...Q.V.o&lt;br /&gt;
        0x0050:  5ba6 9d03 7da1 cfc3 0f30 7718 86a8 e103  [...}....0w.....&lt;br /&gt;
        0x0060:  3e52 3300 0000 0000 0000 00              &amp;gt;R3........&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0067&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 103 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID (allocated flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || -- || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0001 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x00 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The OAP payload starts at offset 0x4b (after management protocol + service hash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;51 8a56 ff6f 5ba6 9d03 7da1 cfc3 0f30 77&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e103 3e52 33&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp (seconds + microseconds)&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || No ephemeral key&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0x68 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x69-0x6a || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0040 (first allocated flow ID for this session)&lt;br /&gt;
* Service hash is carried in management protocol payload (32 bytes)&lt;br /&gt;
* OAP header is minimal: only ID and timestamp, no optional fields&lt;br /&gt;
* No certificate, ephemeral key, data, or signature in this initial request&lt;br /&gt;
* Client sends minimal OAP headers with no authentication or encryption setup at allocation time&lt;br /&gt;
&lt;br /&gt;
==== Packet 4: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds to FLOW_REQ by sending FLOW_REPLY with a new DEID (destination endpoint ID 0x0041) to establish the allocated flow for data transfer.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:49.178732 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 89:&lt;br /&gt;
        0x0000:  0000 0047 0041 0040 0000 0000 0000 0000  ...G.A.@........&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 0051 8a56 ff6f  ...........Q.V.o&lt;br /&gt;
        0x0030:  5ba6 9d03 7da1 cfc3 0f30 7718 86a8 e13f  [...}....0w....?&lt;br /&gt;
        0x0040:  a347 3800 0000 0000 0000 00              .G8........&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0047&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 71 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0041 || Source Endpoint ID (allocated server-side flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client&#039;s flow ID from FLOW_REQ)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0|| Response code (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The OAP payload starts at offset 0x2b (no service hash in FLOW_REPLY):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;51 8a56 ff6f 5ba6 9d03 7da1 cfc3 0f30 77&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Echo of client&#039;s flow ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;18 86a8 e13f a347 38&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Echoed timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x46 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || No ephemeral key&lt;br /&gt;
|-&lt;br /&gt;
| 0x47-0x48 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x49-0x4a || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID 0x0041 is the newly allocated server-side flow endpoint&lt;br /&gt;
* DEID 0x0040 reflects the client&#039;s flow ID, creating a bidirectional mapping&lt;br /&gt;
* No service hash included (FLOW_REPLY only needs the EIDs to identify the flow)&lt;br /&gt;
* OAP echoes the client&#039;s ID and timestamp, confirming the flow allocation&lt;br /&gt;
* Response code 0x00000000 indicates success&lt;br /&gt;
* Both client and server now have their respective flow IDs (0x0040 and 0x0041) for data transfer&lt;br /&gt;
* Response code 0x00000000 indicates success&lt;br /&gt;
&lt;br /&gt;
==== Packet 5: ECHO_REQUEST - Plaintext Data ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends an oping ECHO_REQUEST packet to the server using the allocated flow.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:50.180824 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0041 0040 0000 0000 0000 0000 7377 0000  .A.@........sw..&lt;br /&gt;
        0x0010:  0000 0000 b03e e007 0000 0000 0000 0000  .....&amp;gt;..........&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0041 || Source Endpoint ID (server → client)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Oping payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REQUEST Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The oping payload starts at offset 0x04:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000000 || Message type: ECHO_REQUEST&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Sequence number (first ping)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;7377 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || 0x0000000000003773 || Seconds component&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;b03e e007 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || 0x0000000007e03eb0 || Nanoseconds component&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 0000 ... 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || All zeros || Echo data (default 64 bytes total - 24 byte header = 40 bytes data)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID 0x0041 shows traffic from server-side flow ID&lt;br /&gt;
* This is the first ping request (ID = 0x00000000)&lt;br /&gt;
* Timestamp captures when the ping was sent (seconds in network order)&lt;br /&gt;
* Default oping payload is 64 bytes total; 24 bytes header + 40 bytes data&lt;br /&gt;
&lt;br /&gt;
==== Packet 6: ECHO_REPLY - Plaintext Data ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server receives the ECHO_REQUEST and immediately sends back an ECHO_REPLY with the same ID and timestamps, echoing the client&#039;s message.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:50.181496 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0040 0040 0000 0001 0000 0000 7377 0000  .@.@........sw..&lt;br /&gt;
        0x0010:  0000 0000 b03e e007 0000 0000 0000 0000  .....&amp;gt;..........&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client ← server)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Oping payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REPLY Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The oping payload starts at offset 0x04:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0001&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000001 || Message type: ECHO_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Echo of request sequence number&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;7377 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || 0x0000000000003773 || Echo of original seconds&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;b03e e007 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || 0x0000000007e03eb0 || Echo of original nanoseconds&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 0000 ... 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || All zeros || Echo data (unchanged from request)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID 0x0040 shows traffic from client-side flow ID receiving the reply&lt;br /&gt;
* Type field changed from 0x00000000 (REQUEST) to 0x00000001 (REPLY)&lt;br /&gt;
* ID, timestamps, and payload data are identical to the request (echoed back)&lt;br /&gt;
* Round-trip time can be calculated by comparing current time with echoed timestamp&lt;br /&gt;
* Ping succeeded on first attempt with minimal latency (~1 millisecond between timestamps)&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 2 - No authentication, with encryption ===&lt;br /&gt;
&lt;br /&gt;
==== Packet 7: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates flow allocation with encryption enabled. This FLOW_REQ carries an OAP header with an ephemeral ECDHE P-384 public key (91 bytes) for encryption setup.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:53.808158 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 212:&lt;br /&gt;
        0x0000:  0000 00c2 0040 0000 0000 0001 0000 0000  .....@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8af1 766b 547c  ..f.i.._....vkT|&lt;br /&gt;
        0x0050:  fcb0 8fce 5d60 a01e e8be 0218 86a8 e253  ....]`.........S&lt;br /&gt;
        0x0060:  8b6c 9000 0000 5b30 5930 1306 072a 8648  .l....[0Y0...*.H&lt;br /&gt;
        0x0070:  ce3d 0201 0608 2a86 48ce 3d03 0107 0342  .=....*.H.=....B&lt;br /&gt;
        0x0080:  0004 c508 1c19 6106 b7e9 3074 57b9 bb16  ......a...0tW...&lt;br /&gt;
        0x0090:  6959 4a55 81f9 169b cc79 fe10 a882 41fe  iYJU.....y....A.&lt;br /&gt;
        0x00a0:  0697 c9b4 f8f0 5562 7fa2 c7a0 a020 1ac6  ......Ub........&lt;br /&gt;
        0x00b0:  939f 23ff b2fb 07a2 b747 aacc 474a 3dab  ..#......G..GJ=.&lt;br /&gt;
        0x00c0:  2598 0000 0000                           %.....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;00c2&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 194 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... d4c0&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Mixed || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x00 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload with Ephemeral Key&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;f1 766b 547c fcb0 8fce 5d60 a01e e8be 02&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e253 8b6c 90&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate in client request&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x005b (91) || Ephemeral key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0xc1 || &amp;lt;code&amp;gt;30 5930 ... 3dab 2598&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDHE P-384 || ECDHE P-384 public key (DER encoded)&lt;br /&gt;
|-&lt;br /&gt;
| 0xd3-0xd4 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0xd5-0xd6 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* Encryption enabled: ephemeral key present (91 bytes)&lt;br /&gt;
* Client sends no certificate, allowing anonymous encryption setup&lt;br /&gt;
* No signature (unsigned OAP)&lt;br /&gt;
* Ephemeral key is ECDHE P-384 for key exchange&lt;br /&gt;
&lt;br /&gt;
==== Packet 8: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server accepts the encrypted flow allocation request. FLOW_REPLY contains the server&#039;s ephemeral key but no certificate (since client didn&#039;t send one).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:53.810564 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 180:&lt;br /&gt;
        0x0000:  0000 00a2 0042 0040 0000 0000 0000 0000  .....B.@........&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 00f1 766b 547c  ............vkT|&lt;br /&gt;
        0x0030:  fcb0 8fce 5d60 a01e e8be 0218 86a8 e253  ....]`.........S&lt;br /&gt;
        0x0040:  b694 e800 0000 5b30 5930 1306 072a 8648  ......[0Y0...*.H&lt;br /&gt;
        0x0050:  ce3d 0201 0608 2a86 48ce 3d03 0107 0342  .=....*.H.=....B&lt;br /&gt;
        0x0060:  0004 5f3c 6929 cca2 024a ae9f 9aa1 dfc2  .._&amp;lt;i)...J......&lt;br /&gt;
        0x0070:  a493 3ff3 ff58 b054 74dc d2e2 47fc 7c5b  ..?..X.Tt...G.|[&lt;br /&gt;
        0x0080:  eff5 e129 72b4 de1e 7c09 bf8c fe38 5e8b  ...)r...|....8^.&lt;br /&gt;
        0x0090:  b22e 59ed 6eb9 dfda 369d 691e 6e2c 122c  ..Y.n...6.i.n,.,&lt;br /&gt;
        0x00a0:  9936 0000 0000                           .6....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;00a2&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 162 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0042&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0042 || Source Endpoint ID (allocated server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0x00000000 || Response code: 0 (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY with Ephemeral Key&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;f1 766b 547c fcb0 8fce 5d60 a01e e8be 02&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || Echo of client ID || Echoes client&#039;s flow ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;18 86a8 e253 b694 e8&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Echoed timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x46 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x005b (91) || Ephemeral key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x47-0xa1 || &amp;lt;code&amp;gt;30 5930...9936&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDHE P-384 || Server&#039;s ECDHE P-384 public key (DER encoded)&lt;br /&gt;
|-&lt;br /&gt;
| 0xd1-0xd2 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0xd3-0xd4 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID 0x0042 is the new server-side flow endpoint&lt;br /&gt;
* Both keys are now exchanged; client and server can derive shared secret&lt;br /&gt;
* No authentication (no certificates) but encryption is negotiated&lt;br /&gt;
* Response indicates successful allocation&lt;br /&gt;
&lt;br /&gt;
==== Packet 9: Encrypted ECHO_REQUEST ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends encrypted ping request after encryption keys are established. The payload is encrypted with the derived shared secret.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:54.815771 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0042 0060 a691 6d83 8446 cbeb ac95 c2eb  .B.`..m..F......&lt;br /&gt;
        0x0010:  4b42 e819 c67f 92c8 58d7 0641 d8a6 6e1f  KB......X..A..n.&lt;br /&gt;
        0x0020:  fc90 feed ef55 b791 4fbd a832 74bd 8bed  .....U..O..2t...&lt;br /&gt;
        0x0030:  249c 4cee 0fc0 cec6 2f1b aec1 2428 bdbd  $.L...../...$(..&lt;br /&gt;
        0x0040:  36b5 01b5 1257 004e 6ed6 7ecd f0c7 7d11  6....W.Nn.~...}.&lt;br /&gt;
        0x0050:  20ba e81b f43a 4de9 b141 1624 e1ba 0a84  .....:M..A.$....&lt;br /&gt;
        0x0060:  74b1 9a9a                                t...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0042&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0042 || Destination Endpoint ID (server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0060&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 96 bytes || Encrypted payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REQUEST (Encrypted)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x63 || &amp;lt;code&amp;gt;a691 6d83 8446 cbeb...74b1 9a9a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Encrypted Data&#039;&#039;&#039; || 96 || Ciphertext || All 96 bytes encrypted&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* All 96 bytes of oping data (type, ID, timestamps, payload) are encrypted&lt;br /&gt;
* No plaintext oping headers visible; entire packet is ciphertext&lt;br /&gt;
* Flow IDs (0x0042) identify which encryption context to use&lt;br /&gt;
* Ping still works with encryption transparently&lt;br /&gt;
&lt;br /&gt;
==== Packet 10: Encrypted ECHO_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server receives encrypted ping request, decrypts it, and sends encrypted ECHO_REPLY.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:54.819574 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0040 0060 c6ea 2222 5618 0268 b27e 9a91  .@.`..&amp;quot;&amp;quot;V..h.~..&lt;br /&gt;
        0x0010:  f124 1f8d bccc 478c 26fe 9b13 b3cb 5398  .$....G.&amp;amp;.....S.&lt;br /&gt;
        0x0020:  6869 3cdb 4928 510d 4de8 dc6a 3f3a 6a6d  hi&amp;lt;.I(Q.M..j?:jm&lt;br /&gt;
        0x0030:  6487 dcd8 c8cd 1a85 fba2 9ecd 3566 57d1  d...........5fW.&lt;br /&gt;
        0x0040:  1c94 ac35 518e 8509 873a 3a5e 04d9 8ee2  ...5Q....::^....&lt;br /&gt;
        0x0050:  9d74 2527 e425 5433 9d73 9ccd f56a 1f8d  .t%&#039;.%T3.s...j..&lt;br /&gt;
        0x0060:  f328 7237                                .(r7&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0060&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 96 bytes || Encrypted payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REPLY (Encrypted)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x63 || &amp;lt;code&amp;gt;c6ea 2222 5618 0268...f328 7237&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Encrypted Data&#039;&#039;&#039; || 96 || Ciphertext || All 96 bytes encrypted&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID 0x0040 shows reply going back to client-side flow&lt;br /&gt;
* Ciphertext is different from request (different plaintext: type field differs)&lt;br /&gt;
* Both encrypted packets are 96 bytes (same size as Packet 9)&lt;br /&gt;
* Client receives encrypted reply, decrypts it, verifies ID and timestamps match request&lt;br /&gt;
* Encryption is transparent at application layer: oping works exactly as with plaintext flows&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 3 - Authentication and encryption ===&lt;br /&gt;
&lt;br /&gt;
==== Packet 11: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates flow allocation request with encryption enabled. Sends ephemeral public key for ECDHE key exchange but no certificate (client is not authenticating in this tutorial). The management protocol now carries a valid allocated SEID (0x0040).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:58.827411 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 212:&lt;br /&gt;
        0x0000:  0000 00c2 0040 0000 0000 0001 0000 0000  .....@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a66 bb82 95fa  ..f.i.._...f....&lt;br /&gt;
        0x0050:  91a2 7bd3 bd60 1b3e 35f6 b918 86a8 e37e  ..{..`.&amp;gt;5......~&lt;br /&gt;
        0x0060:  c0d2 ad00 0000 5b30 5930 1306 072a 8648  ......[0Y0...*.H&lt;br /&gt;
        0x0070:  ce3d 0201 0608 2a86 48ce 3d03 0107 0342  .=....*.H.=....B&lt;br /&gt;
        0x0080:  0004 9dea c238 6732 4987 1cd4 7133 9614  .....8g2I...q3..&lt;br /&gt;
        0x0090:  9d04 4fde 3f68 42f1 54fb 7ef3 88d0 ffe6  ..O.?hB.T.~.....&lt;br /&gt;
        0x00a0:  7e01 432e 56c2 2d64 72c9 19fc b0cf 1eca  ~.C.V.-dr.......&lt;br /&gt;
        0x00b0:  689e 3536 771a 8041 726c 20e2 d9bb 3589  h.56w..Arl....5.&lt;br /&gt;
        0x00c0:  86e7 0000 0000                           ......&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;00c2&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 194 bytes || Total payload length (excluding DIX header)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID (client flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... d4c0&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Default values || QoS parameters&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || Response code (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x00 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ... 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload with Encryption Setup&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;66 bb82 95fa 91a2 7bd3 bd60 1b3e 35f6 b9&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier for Test 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e37e c0d2 ad&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || Client not authenticating&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 91 (0x005b) || Ephemeral public key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0xc1 || &amp;lt;code&amp;gt;30 5930 1306 ... 3589&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDP-384 public key || Client&#039;s ephemeral ECDHE public key for encryption negotiation&lt;br /&gt;
|-&lt;br /&gt;
| 0xc2-0xc3 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0xc4-0xc5 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0040 - Same as Test 2 (Encrypted) because this is the same client session reusing the same allocated ID from the previous test&lt;br /&gt;
* No Certificate - &amp;lt;code&amp;gt;crt_len = 0x0000&amp;lt;/code&amp;gt; because the client does not have authentication credentials; the server will authenticate instead&lt;br /&gt;
* Ephemeral Key Present - &amp;lt;code&amp;gt;eph_len = 0x005b (91)&amp;lt;/code&amp;gt; because encryption is enabled on the client&lt;br /&gt;
* No Signature - &amp;lt;code&amp;gt;sig_len = 0x0000&amp;lt;/code&amp;gt; because client is not signing (no certificate to sign with)&lt;br /&gt;
* FLOW_REQ Message Type - Code field is 0x00, and service hash is present because FLOW_REQ always includes the service hash&lt;br /&gt;
* Timestamp Consistency - Same OAP ID and timestamp structure as Test 2, but with additional security handshake&lt;br /&gt;
&lt;br /&gt;
==== Packet 12: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds to client&#039;s FLOW_REQ by sending FLOW_REPLY with its certificate for authentication, ephemeral public key for ECDHE encryption setup, and a digital signature proving ownership of the certificate. This is the full authentication response.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data (abbreviated):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:58.828806 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 843:&lt;br /&gt;
        0x0000:  0000 0339 0043 0040 0000 0000 0000 0000  ...9.C.@........&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 0066 bb82 95fa  ...........f....&lt;br /&gt;
        0x0030:  91a2 7bd3 bd60 1b3e 35f6 b918 86a8 e37e  ..{..`.&amp;gt;5......~&lt;br /&gt;
        0x0040:  d566 a002 2f30 8202 2b30 8201 b2a0 0302  .f../0..+0......&lt;br /&gt;
        (... certificate and signature bytes ...)&lt;br /&gt;
        0x0320:  ef11 c358 f5d0 5cd7 3906 adf1 8a2c 9b25  ...X..\.9....,.%&lt;br /&gt;
        0x0330:  dc78 6050 ab61 3a3f 81c0 254b d193 7827  .x`P.a:?..%K..x&#039;&lt;br /&gt;
        0x0340:  c0e9 38c7 e0d1 c517 d299 9992 07         ..8..........&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0339&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 825 bytes || Total payload length (excluding DIX header)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0043&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0043 || Source Endpoint ID (server-side allocated flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client&#039;s flow ID from FLOW_REQ)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Default values || QoS parameters&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0x00000000 || Response code: 0 (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY Payload with Full Authentication&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;66 bb82 95fa 91a2 7bd3 bd60 1b3e 35f6 b9&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || &#039;&#039;&#039;Same ID as client&#039;s FLOW_REQ&#039;&#039;&#039; (echoed back)&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;18 86a8 e37e d566 a0&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Server&#039;s timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;022f&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 559 (0x022f) || Server certificate length: 559 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x243 || &amp;lt;code&amp;gt;2f30 8202 2b ... 81c8 30&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Certificate&#039;&#039;&#039; || 559 || DER-encoded X.509 || Server&#039;s certificate (signed by intermediate CA)&lt;br /&gt;
|-&lt;br /&gt;
| 0x244-0x245 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 91 (0x005b) || Server&#039;s ephemeral public key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x246-0x2a0 || &amp;lt;code&amp;gt;30 5930 1306 ... 9936&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDP-384 public key || Server&#039;s ephemeral ECDHE public key&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a4-0x2a5 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a6-0x2a7 || &amp;lt;code&amp;gt;0068&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 104 (0x0068) || Digital signature length: 104 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a8-0x30f || &amp;lt;code&amp;gt;30 6602 3100 ... 07&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Signature&#039;&#039;&#039; || 104 || ECDSA signature (DER encoded) || Server&#039;s signature over OAP header proving certificate ownership&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0043 - New server-side endpoint ID allocated for this authenticated flow&lt;br /&gt;
* DEID is 0x0040 - Client&#039;s flow ID from the FLOW_REQ, establishing the bidirectional flow&lt;br /&gt;
* FLOW_REPLY Message Type - Code field is 0x01, &#039;&#039;&#039;no service hash&#039;&#039;&#039; (already identified in FLOW_REQ)&lt;br /&gt;
* Full Certificate - &amp;lt;code&amp;gt;crt_len = 0x022f (559)&amp;lt;/code&amp;gt; carrying server&#039;s complete X.509 certificate signed by intermediate CA&lt;br /&gt;
* Ephemeral Key Present - &amp;lt;code&amp;gt;eph_len = 0x005b (91)&amp;lt;/code&amp;gt; with server&#039;s ECDHE public key for encryption&lt;br /&gt;
* Signature Included - &amp;lt;code&amp;gt;sig_len = 0x0068 (104)&amp;lt;/code&amp;gt; containing ECDSA digital signature over the entire OAP header&lt;br /&gt;
* Same OAP ID - Server echoes back the exact ID from client&#039;s FLOW_REQ to confirm association (included in signature, binding response to this specific client request)&lt;br /&gt;
* Large Payload - Total of 825 bytes due to certificate (559) + ephemeral key (91) + signature (104) + overhead&lt;br /&gt;
* Authentication Complete - Client verifies: (1) certificate against CA store, (2) signature over entire response ensures authenticity and integrity, (3) echoed ID binds response to this specific request, (4) timestamp prevents replay attacks&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds with its certificate for authentication, ephemeral public key for ECDHE encryption, and a digital signature proving ownership of the certificate.&lt;br /&gt;
&lt;br /&gt;
==== Packet 13: Encrypted ECHO_REQUEST ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends encrypted ping request after authentication handshake. All application data is protected by encryption using the ephemeral keys established in Packets 11-12.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:59.836485 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0043 0060 3bed 0b48 1be1 6930 cf3d dee9  .C.`.;..H..i0.=..&lt;br /&gt;
        0x0010:  4fc9 774b 5d63 cc9b 5a34 6604 f9ac 1016  O.wK]c..Z4f.....&lt;br /&gt;
        0x0020:  1c6d c9ac f80e dc89 31c1 9634 1a4f b2c7  .m......1..4.O..&lt;br /&gt;
        0x0030:  4721 e402 8259 b0aa 8870 4566 33d1 9c18  G!...Y..  .pEf3...&lt;br /&gt;
        0x0040:  06da 50c3 8b75 86b0 f240 d109 840e a6cd  ..P..u...@......&lt;br /&gt;
        0x0050:  d115 77cb 5652 5bfb e6d5 0ca9 dbc3 d0b8  ..w.VR[.........&lt;br /&gt;
        0x0060:  0058 fd19                                .X..&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame Analysis:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Length !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Source EID || 0x00-0x01 || 2 bytes || &amp;lt;code&amp;gt;0x0043&amp;lt;/code&amp;gt; || Client flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| Destination EID || 0x02-0x03 || 2 bytes || &amp;lt;code&amp;gt;0x0060&amp;lt;/code&amp;gt; || Server flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Encrypted Payload&#039;&#039;&#039; || &#039;&#039;&#039;0x04-0x71&#039;&#039;&#039; || &#039;&#039;&#039;110 bytes&#039;&#039;&#039; || &#039;&#039;&#039;Ciphertext&#039;&#039;&#039; || &#039;&#039;&#039;AES-encrypted oping ECHO_REQUEST data&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* No visible protocol structure - all application data appears as ciphertext&lt;br /&gt;
* Uses the same source/destination EID pair (0x0043 → 0x0060) established in the FLOW_REQ/FLOW_REPLY handshake&lt;br /&gt;
* Encryption is done using the ephemeral key (91 bytes) exchanged in Packet 11&#039;s OAP header&lt;br /&gt;
* Unlike Packets 11-12, this packet contains no certificate, public keys, or signatures&lt;br /&gt;
* The 110-byte encrypted data corresponds to the original oping ECHO_REQUEST message&lt;br /&gt;
&lt;br /&gt;
==== Packet 14: Encrypted ECHO_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server sends encrypted ping reply. Note that the flow identifiers swap, demonstrating bidirectional encrypted communication.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:59.836930 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0040 0060 d552 e100 e681 940c e35a 07d0  .@.`..........Z..&lt;br /&gt;
        0x0010:  a293 1d73 33a5 854e 0fce 4f4d 6655 267a  ...s3..N..OMfU&amp;amp;z&lt;br /&gt;
        0x0020:  3de2 663b 709d 739a a696 2ddd 7b34 28b8  =.f;p.s...-{4(...&lt;br /&gt;
        0x0030:  5a98 eec2 52c6 4288 3885 ae16 e466 4181  Z...R.B.8...fA..&lt;br /&gt;
        0x0040:  f2d6 44c1 b51b 8728 58a4 7525 fb5e 3fd6  ..D...(X.u%.^?..&lt;br /&gt;
        0x0050:  7e49 532a d2a5 bea7 55e9 c274 f1b2 0412  ~IS*....U..t....&lt;br /&gt;
        0x0060:  73d4 6436                                s.d6&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame Analysis:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Length !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Source EID || 0x00-0x01 || 2 bytes || &amp;lt;code&amp;gt;0x0040&amp;lt;/code&amp;gt; || Client&#039;s inbound flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| Destination EID || 0x02-0x03 || 2 bytes || &amp;lt;code&amp;gt;0x0060&amp;lt;/code&amp;gt; || Server flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Encrypted Payload&#039;&#039;&#039; || &#039;&#039;&#039;0x04-0x71&#039;&#039;&#039; || &#039;&#039;&#039;110 bytes&#039;&#039;&#039; || &#039;&#039;&#039;Ciphertext&#039;&#039;&#039; || &#039;&#039;&#039;AES-encrypted oping ECHO_REPLY data&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* The EID in offset 0x00 is now 0x0040 (server&#039;s view of client&#039;s inbound flow)&lt;br /&gt;
* Uses the same ephemeral key material as Packet 13, but encryption direction is reversed&lt;br /&gt;
* Both packets use AES-GCM with keys derived from the ECDH exchange&lt;br /&gt;
* Timestamp 17:39:59.836930 is only 445 microseconds after Packet 13, indicating server-side processing&lt;br /&gt;
* The 110-byte encrypted ECHO_REPLY payload is the same size as the request&lt;br /&gt;
* All application data is protected by both authentication (X.509 + ECDSA) and encryption (AES)&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 4 - Authentication, no encryption ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Packet 15: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates flow allocation with authentication enabled but encryption disabled. This FLOW_REQ carries an OAP header but &#039;&#039;&#039;no ephemeral key&#039;&#039;&#039; since the client does not request encrypted communication.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:03.413372 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 121:&lt;br /&gt;
        0x0000:  0000 0067 0040 0000 0000 0001 0000 0000  ...g.@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a8f a6ab 6ea7  ..f.i.._........&lt;br /&gt;
        0x0050:  ef89 68e1 ed1e 2ede 0919 fa18 86a8 e490  .h..............&lt;br /&gt;
        0x0060:  0de6 6100 0000 0000 0000 00              ..a.....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0067&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 103 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID (allocated flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... dc40&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Mixed || &lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload (No Encryption)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;8f a6ab 6ea7 ef89 68e1 ed1e 2ede 0919 fa&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e490 0de6 61&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate in client request&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;No ephemeral key (no encryption)&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0x68 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x69-0x6a || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* No encryption enabled: ephemeral key absent (Eph_len = 0x0000)&lt;br /&gt;
* Client requests authentication only&lt;br /&gt;
* Server will respond with certificate + signature but no ephemeral key&lt;br /&gt;
* Packet is minimal compared to Packet 11 (Test 3) which includes 91-byte ephemeral key&lt;br /&gt;
&lt;br /&gt;
==== Packet 16: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server accepts the authenticated (but not encrypted) flow allocation request. FLOW_REPLY contains the server&#039;s X.509 certificate and ECDSA signature for client authentication, but &#039;&#039;&#039;no ephemeral key&#039;&#039;&#039; since encryption is not being used.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data (abbreviated):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:03.416675 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 751:&lt;br /&gt;
        0x0000:  0000 02dd 0041 0040 0000 0000 0000 0000  .......A.@......&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 008f a6ab 6ea7  ................&lt;br /&gt;
        0x0030:  ef89 68e1 ed1e 2ede 0919 fa18 86a8 e490  .h..............&lt;br /&gt;
        0x0040:  3754 a702 2f30 8202 2b30 8201 b2a0 0302  7T../0..+0......&lt;br /&gt;
        0x0050:  0102 0202 1000 300a 0608 2a86 48ce 3d04  ......0...*.H.=.&lt;br /&gt;
        (... certificate and signature bytes ...)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;02dd&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 733 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0041 || Source Endpoint ID (allocated server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0x00000000 || Response code: 0 (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY Payload with Certificate and Signature (No Ephemeral Key)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;8f a6ab 6ea7 ef89 68e1 ed1e 2ede 0919&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || &#039;&#039;&#039;Same ID as client&#039;s FLOW_REQ&#039;&#039;&#039; (Packet 15 echoed back)&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;fa18 86a8 e490 3754&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Server&#039;s timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;a702&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x02a7 (679 decimal) || &#039;&#039;&#039;Certificate length: 679 bytes&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x270 || &amp;lt;code&amp;gt;2f30 8202 2b30 8201 b2a0 0302 ... (DER certificate) ...&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Certificate&#039;&#039;&#039; || 679 || DER-encoded X.509 || Server&#039;s certificate signed by intermediate CA&lt;br /&gt;
|-&lt;br /&gt;
| 0x271-0x272 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;No ephemeral key&#039;&#039;&#039; (no encryption)&lt;br /&gt;
|-&lt;br /&gt;
| 0x273-0x274 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x275-0x276 || &amp;lt;code&amp;gt;0067&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0067 (103 decimal) || &#039;&#039;&#039;ECDSA signature length: 103 bytes&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x277-0x2dd || &amp;lt;code&amp;gt;3065 0230 75dc 5717 ... 83&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Signature&#039;&#039;&#039; || 103 || ECDSA signature (DER encoded) || Server&#039;s ECDSA signature proving certificate ownership&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0041 - New server-side endpoint ID allocated for this authenticated flow&lt;br /&gt;
* DEID is 0x0040 - Client&#039;s flow ID from Packet 15 FLOW_REQ, establishing the bidirectional flow&lt;br /&gt;
* FLOW_REPLY Message Type - Code field is 0x01, &#039;&#039;&#039;no service hash&#039;&#039;&#039; (already identified in FLOW_REQ)&lt;br /&gt;
* Certificate Field - &amp;lt;code&amp;gt;crt_len = 0x02a7 (679)&amp;lt;/code&amp;gt; carrying server&#039;s X.509 certificate signed by intermediate CA&lt;br /&gt;
* Separate Signature Field - &amp;lt;code&amp;gt;sig_len = 0x0067 (103)&amp;lt;/code&amp;gt; with ECDSA signature over entire OAP header&lt;br /&gt;
* No Ephemeral Key - &amp;lt;code&amp;gt;eph_len = 0x0000&amp;lt;/code&amp;gt; since encryption is &#039;&#039;&#039;not&#039;&#039;&#039; being used in Test 4&lt;br /&gt;
* Same OAP ID - Server echoes back the exact ID from client&#039;s FLOW_REQ (included in signature, binding response to this specific client request)&lt;br /&gt;
* Complete OAP Structure - Full OAP header with all standard fields, just without ephemeral key data&lt;br /&gt;
* Plaintext Data Exchange - After this FLOW_REPLY, all subsequent application data will be transmitted in plaintext (but authenticated via certificate + signature verification)&lt;br /&gt;
&lt;br /&gt;
==== Packet 17: ECHO_REQUEST ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends plaintext ECHO_REQUEST data through the authenticated (but unencrypted) flow. The oping application&#039;s ping request is transmitted directly without encryption, relying on the earlier certificate+signature authentication for security.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:04.419664 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0041 0040 0000 0000 0000 0000 8177 0000  .A.@............&lt;br /&gt;
        0x0010:  0000 0000 aa16 1c16 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0041 || Destination Endpoint ID (server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Total application data length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Application Data - Oping Echo Request (Plaintext)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000000 || Message type: ECHO_REQUEST&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Sequence number / message identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;8177 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Seconds component from CLOCK_REALTIME&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;aa16 1c16 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Nanoseconds component (0-999999999)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || Application data || Probe payload (zero-filled for 64 bytes total)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID Pair: 0x0041 → Server Flow - Data is directed to the server&#039;s endpoint ID allocated in Packet 16 FLOW_REPLY&lt;br /&gt;
* Plaintext Transmission - No encryption layer; oping payload is sent as-is (compare to Packet 13 which had encryption)&lt;br /&gt;
* Authenticated Flow - Although plaintext, this data travels on the authenticated flow established in Packet 16 (certificate + signature verified)&lt;br /&gt;
* Type = ECHO_REQUEST - 0x00000000 indicates client-to-server ping request&lt;br /&gt;
* ID = 0 - Sequence number for matching request/reply pairs&lt;br /&gt;
* Test 4 Characteristic - Demonstrates authenticated communication &#039;&#039;&#039;without&#039;&#039;&#039; encryption; application data is readable but cryptographically bound to the authenticated flow&lt;br /&gt;
* Contrast to Test 3 - Packet 13 (Test 3 encrypted ECHO_REQUEST) was 114 bytes with ciphertext; this packet is 82 bytes of plaintext&lt;br /&gt;
* Total Payload - 64 bytes total (4 + 4 + 8 + 8 + 40 bytes payload)&lt;br /&gt;
&lt;br /&gt;
==== Packet 18: ECHO_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds with plaintext ECHO_REPLY data, echoing back the client&#039;s request. This confirms successful bidirectional communication over the authenticated (but unencrypted) flow.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:04.420088 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0040 0040 0000 0001 0000 0000 8177 0000  .@.@............&lt;br /&gt;
        0x0010:  0000 0000 aa16 1c16 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Total application data length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Application Data - Oping Echo Reply (Plaintext)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0001&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000001 || Message type: ECHO_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Echo of request sequence number&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;8177 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Echoed request timestamp (seconds)&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;aa16 1c16 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Echoed request timestamp (nanoseconds)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || Application data || Echoed probe payload (zero-filled for 64 bytes total)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID Pair: 0x0040 → Client Flow - Server responds to client&#039;s endpoint ID from Packet 15 FLOW_REQ&lt;br /&gt;
* Type = ECHO_REPLY - 0x00000001 indicates server-to-client response&lt;br /&gt;
* ID = 0 - Echoes the request sequence number, matching this response to the request&lt;br /&gt;
* Timestamps Echo Request - Both timestamp fields are copied from Packet 17 unchanged (8177 0000 0000 0000 and aa16 1c16 0000 0000)&lt;br /&gt;
* Plaintext Reply - No encryption; server&#039;s response payload is readable (compare to Packet 14 which had encryption)&lt;br /&gt;
* Authenticated Channel - Although plaintext, this reply is part of the authenticated flow; client can verify integrity through earlier certificate+signature&lt;br /&gt;
* Test 4 Completion - Demonstrates &#039;&#039;&#039;full bidirectional plaintext communication&#039;&#039;&#039; over an authenticated (but unencrypted) flow&lt;br /&gt;
* Contrast to Test 3 - Packet 14 (Test 3 encrypted ECHO_REPLY) was 114 bytes with ciphertext; this packet is 82 bytes of plaintext&lt;br /&gt;
* Total Payload - 64 bytes total (4 + 4 + 8 + 8 + 40 bytes payload)&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
	<entry>
		<id>https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Tutorial_06&amp;diff=1894</id>
		<title>Ouroboros Tutorial 06</title>
		<link rel="alternate" type="text/html" href="https://ouroboros.rocks/mediawiki/index.php?title=Ouroboros_Tutorial_06&amp;diff=1894"/>
		<updated>2026-02-14T14:51:49Z</updated>

		<summary type="html">&lt;p&gt;Dimitri: /* Test 2: No Authentication, With Encryption */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Ouroboros Tutorial 06 - Authenticated Flows =&lt;br /&gt;
&lt;br /&gt;
This tutorial demonstrates setting up and using authenticated flows in Ouroboros with certificate-based authentication.&lt;br /&gt;
&lt;br /&gt;
The overall flow of authenticated flow allocation is&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Client (IRMd)                              Server (IRMd)&lt;br /&gt;
     |                                           |&lt;br /&gt;
     | 1. Load client cert/key                   |&lt;br /&gt;
     | 2. Generate ephemeral keypair             |&lt;br /&gt;
     | 3. Build OAP_HDR (id, ts, crt, eph)       |&lt;br /&gt;
     | 4. Sign header with client key            |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |-------- FLOW_REQ (OAP_HDR) -------------&amp;gt; |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |                                           | 5. Load server cert/key&lt;br /&gt;
     |                                           | 6. Verify client cert against CA&lt;br /&gt;
     |                                           | 7. Verify client signature&lt;br /&gt;
     |                                           | 8. Generate ephemeral keypair&lt;br /&gt;
     |                                           | 9. Derive symmetric key (ECDHE)&lt;br /&gt;
     |                                           | 10. Build response OAP_HDR&lt;br /&gt;
     |                                           | 11. Sign with server key&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |&amp;lt;------- FLOW_REPLY (OAP_HDR) ------------ |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     | 12. Verify server cert against CA         |&lt;br /&gt;
     | 13. Verify server signature               |&lt;br /&gt;
     | 14. Derive symmetric key (ECDHE)          |&lt;br /&gt;
     |                                           |&lt;br /&gt;
     |===========================================|&lt;br /&gt;
     |         Encrypted data channel            |&lt;br /&gt;
     |===========================================|&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Tutorial Directory:&#039;&#039;&#039; This tutorial will execute in &amp;lt;code&amp;gt;/tmp/o7s-tut06/&amp;lt;/code&amp;gt;. All configuration files, generated certificates, logs, and packet captures will be stored in this directory.&lt;br /&gt;
&lt;br /&gt;
We create a complete PKI (Public Key Infrastructure):&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Root CA&#039;&#039;&#039; (&amp;lt;code&amp;gt;ca.tut.o7s&amp;lt;/code&amp;gt;): Self-signed trust anchor&lt;br /&gt;
* &#039;&#039;&#039;Intermediate CA&#039;&#039;&#039; (&amp;lt;code&amp;gt;sign.tut.o7s&amp;lt;/code&amp;gt;): Signed by root with pathlen:0 constraint&lt;br /&gt;
* &#039;&#039;&#039;Server Certificate&#039;&#039;&#039; (&amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;): Signed by intermediate CA&lt;br /&gt;
&lt;br /&gt;
This tutorial uses ECDSA P-384 with SHA-384 hashing.&lt;br /&gt;
&lt;br /&gt;
== Setting Up the Tutorial ==&lt;br /&gt;
&lt;br /&gt;
To properly understand and debug the authenticated flows, this tutorial uses a debug build of Ouroboros with OAP protocol debugging enabled.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /path/to/ouroboros&lt;br /&gt;
mkdir build &amp;amp;&amp;amp; cd build&lt;br /&gt;
cmake -DCMAKE_BUILD_TYPE=Debug -DDEBUG_PROTO_OAP=ON ..&lt;br /&gt;
make -j$(nproc)&lt;br /&gt;
sudo make install&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When built with these options, the IRMd will output detailed OAP protocol information.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Files ===&lt;br /&gt;
&lt;br /&gt;
The following three files should be created in the tutorial directory (&amp;lt;code&amp;gt;/tmp/o7s-tut06/&amp;lt;/code&amp;gt;) before starting the tutorial:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;tut06.conf&#039;&#039;&#039; - IRMd configuration&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ini&amp;quot;&amp;gt;&lt;br /&gt;
# Ouroboros Tutorial 06 - Authenticated Flows Configuration&lt;br /&gt;
# Uses system-installed certificates at /etc/ouroboros/security/&lt;br /&gt;
&lt;br /&gt;
[name.&amp;quot;sec.oping.tut.o7s&amp;quot;]&lt;br /&gt;
prog=[&amp;quot;/usr/bin/oping&amp;quot;]&lt;br /&gt;
args=[&amp;quot;--listen&amp;quot;]&lt;br /&gt;
&lt;br /&gt;
[eth-dix.eth-dix-lo]&lt;br /&gt;
bootstrap=&amp;quot;eth-dix-network&amp;quot;&lt;br /&gt;
dev=&amp;quot;lo&amp;quot;&lt;br /&gt;
reg=[&amp;quot;sec.oping.tut.o7s&amp;quot;]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ca.tut.o7s.cnf&#039;&#039;&#039; - OpenSSL configuration for PKI generation&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
# Unified OpenSSL Configuration for Ouroboros Tutorial 06&lt;br /&gt;
# Named CA sections: CA_root (signs intermediate), CA_intermediate (signs server)&lt;br /&gt;
# Usage: openssl ca -name CA_root -config ca.tut.o7s.cnf ...&lt;br /&gt;
&lt;br /&gt;
[ req ]&lt;br /&gt;
default_bits       = 384&lt;br /&gt;
default_keyfile    = private/key.pem&lt;br /&gt;
distinguished_name = req_distinguished_name&lt;br /&gt;
string_mask        = utf8only&lt;br /&gt;
default_md         = sha384&lt;br /&gt;
x509_extensions    = v3_ca&lt;br /&gt;
&lt;br /&gt;
[ req_distinguished_name ]&lt;br /&gt;
countryName                 = Country Name (2 letter code)&lt;br /&gt;
stateOrProvinceName         = State or Province Name&lt;br /&gt;
localityName                = Locality Name&lt;br /&gt;
organizationName            = Organization Name&lt;br /&gt;
commonName                  = Common Name&lt;br /&gt;
&lt;br /&gt;
countryName_default         = BE&lt;br /&gt;
stateOrProvinceName_default = OVL&lt;br /&gt;
localityName_default        = Ghent&lt;br /&gt;
organizationName_default    = o7s&lt;br /&gt;
&lt;br /&gt;
[ ca ]&lt;br /&gt;
default_ca = CA_root&lt;br /&gt;
&lt;br /&gt;
[ CA_root ]&lt;br /&gt;
dir             = ./pki/root&lt;br /&gt;
database        = $dir/index.txt&lt;br /&gt;
serial          = $dir/serial&lt;br /&gt;
new_certs_dir   = $dir/certs&lt;br /&gt;
certificate     = $dir/certs/ca.tut.o7s.crt.pem&lt;br /&gt;
private_key     = $dir/private/ca.tut.o7s.key.pem&lt;br /&gt;
default_days    = 3650&lt;br /&gt;
default_md      = sha384&lt;br /&gt;
policy          = policy_loose&lt;br /&gt;
&lt;br /&gt;
[ CA_intermediate ]&lt;br /&gt;
dir             = ./pki/sign&lt;br /&gt;
database        = $dir/index.txt&lt;br /&gt;
serial          = $dir/serial&lt;br /&gt;
new_certs_dir   = $dir/certs&lt;br /&gt;
certificate     = $dir/certs/sign.tut.o7s.crt.pem&lt;br /&gt;
private_key     = $dir/private/sign.tut.o7s.key.pem&lt;br /&gt;
default_days    = 365&lt;br /&gt;
default_md      = sha384&lt;br /&gt;
policy          = policy_loose&lt;br /&gt;
&lt;br /&gt;
[ policy_loose ]&lt;br /&gt;
commonName = supplied&lt;br /&gt;
&lt;br /&gt;
[ v3_ca ]&lt;br /&gt;
subjectKeyIdentifier = hash&lt;br /&gt;
authorityKeyIdentifier = keyid:always,issuer&lt;br /&gt;
basicConstraints = critical, CA:true&lt;br /&gt;
keyUsage = critical, digitalSignature, cRLSign, keyCertSign&lt;br /&gt;
&lt;br /&gt;
[ v3_intermediate_ca ]&lt;br /&gt;
subjectKeyIdentifier = hash&lt;br /&gt;
authorityKeyIdentifier = keyid:always,issuer&lt;br /&gt;
basicConstraints = critical, CA:true, pathlen:0&lt;br /&gt;
keyUsage = critical, digitalSignature, cRLSign, keyCertSign&lt;br /&gt;
&lt;br /&gt;
[ server_cert ]&lt;br /&gt;
basicConstraints = CA:FALSE&lt;br /&gt;
subjectKeyIdentifier = hash&lt;br /&gt;
authorityKeyIdentifier = keyid,issuer:always&lt;br /&gt;
keyUsage = critical, digitalSignature, keyEncipherment&lt;br /&gt;
extendedKeyUsage = serverAuth&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;gen-pki.sh&#039;&#039;&#039; - PKI generation script&lt;br /&gt;
&lt;br /&gt;
This script will:&lt;br /&gt;
1. Create the directory structure&lt;br /&gt;
2. Generate the root CA key and certificate&lt;br /&gt;
3. Generate the intermediate CA key and CSR&lt;br /&gt;
4. Sign the intermediate CA certificate&lt;br /&gt;
5. Generate the server certificate key and CSR&lt;br /&gt;
6. Sign the server certificate&lt;br /&gt;
7. Verify the complete certificate chain&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
# Ouroboros Tutorial 06 - PKI Generation Script (Simplified)&lt;br /&gt;
# Generates: Root CA, Intermediate CA, and Server Certificate&lt;br /&gt;
&lt;br /&gt;
set -e&lt;br /&gt;
&lt;br /&gt;
if [ ! -f ca.tut.o7s.cnf ]; then&lt;br /&gt;
    echo &amp;quot;ERROR: ca.tut.o7s.cnf not found&amp;quot;&lt;br /&gt;
    exit 1&lt;br /&gt;
fi&lt;br /&gt;
&lt;br /&gt;
mkdir -p pki/{root,sign,server}/{certs,private,csr}&lt;br /&gt;
&lt;br /&gt;
# Root CA&lt;br /&gt;
openssl ecparam -genkey -name secp384r1 -out pki/root/private/ca.tut.o7s.key.pem 2&amp;gt;/dev/null&lt;br /&gt;
openssl req -new -x509 -sha384 -days 7300 \&lt;br /&gt;
    -config ca.tut.o7s.cnf \&lt;br /&gt;
    -key pki/root/private/ca.tut.o7s.key.pem \&lt;br /&gt;
    -out pki/root/certs/ca.tut.o7s.crt.pem \&lt;br /&gt;
    -subj &amp;quot;/C=BE/ST=OVL/L=Ghent/O=o7s/CN=ca.tut.o7s&amp;quot; 2&amp;gt;/dev/null&lt;br /&gt;
&lt;br /&gt;
# Intermediate CA&lt;br /&gt;
openssl ecparam -genkey -name secp384r1 -out pki/sign/private/sign.tut.o7s.key.pem 2&amp;gt;/dev/null&lt;br /&gt;
openssl req -new -sha384 \&lt;br /&gt;
    -config ca.tut.o7s.cnf \&lt;br /&gt;
    -key pki/sign/private/sign.tut.o7s.key.pem \&lt;br /&gt;
    -out pki/sign/csr/sign.tut.o7s.csr \&lt;br /&gt;
    -subj &amp;quot;/C=BE/ST=OVL/L=Ghent/O=o7s/CN=sign.tut.o7s&amp;quot; 2&amp;gt;/dev/null&lt;br /&gt;
&lt;br /&gt;
touch pki/root/index.txt&lt;br /&gt;
echo 1000 &amp;gt; pki/root/serial&lt;br /&gt;
&lt;br /&gt;
openssl ca -name CA_root -config ca.tut.o7s.cnf \&lt;br /&gt;
    -extensions v3_intermediate_ca -days 3650 -md sha384 -batch \&lt;br /&gt;
    -in pki/sign/csr/sign.tut.o7s.csr \&lt;br /&gt;
    -out pki/sign/certs/sign.tut.o7s.crt.pem 2&amp;gt;&amp;amp;1 | grep -E &amp;quot;Signature ok|Database updated&amp;quot; || true&lt;br /&gt;
&lt;br /&gt;
# Server Certificate&lt;br /&gt;
openssl ecparam -genkey -name secp384r1 -out pki/server/private/sec.oping.tut.o7s.key.pem 2&amp;gt;/dev/null&lt;br /&gt;
openssl req -new -sha384 \&lt;br /&gt;
    -config ca.tut.o7s.cnf \&lt;br /&gt;
    -key pki/server/private/sec.oping.tut.o7s.key.pem \&lt;br /&gt;
    -out pki/server/csr/sec.oping.tut.o7s.csr \&lt;br /&gt;
    -subj &amp;quot;/C=BE/ST=OVL/L=Ghent/O=o7s/CN=sec.oping.tut.o7s&amp;quot; 2&amp;gt;/dev/null&lt;br /&gt;
&lt;br /&gt;
touch pki/sign/index.txt&lt;br /&gt;
echo 1000 &amp;gt; pki/sign/serial&lt;br /&gt;
&lt;br /&gt;
openssl ca -name CA_intermediate -config ca.tut.o7s.cnf \&lt;br /&gt;
    -extensions server_cert -days 365 -md sha384 -batch \&lt;br /&gt;
    -in pki/server/csr/sec.oping.tut.o7s.csr \&lt;br /&gt;
    -out pki/server/certs/sec.oping.tut.o7s.crt.pem 2&amp;gt;&amp;amp;1 | grep -E &amp;quot;Signature ok|Database updated&amp;quot; || true&lt;br /&gt;
&lt;br /&gt;
# Verify chain&lt;br /&gt;
openssl verify -CAfile pki/root/certs/ca.tut.o7s.crt.pem \&lt;br /&gt;
    -untrusted pki/sign/certs/sign.tut.o7s.crt.pem \&lt;br /&gt;
    pki/server/certs/sec.oping.tut.o7s.crt.pem &amp;gt; /dev/null 2&amp;gt;&amp;amp;1&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;PKI generation complete.&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Part 1: Running the Tutorial - Single Session with 4 Tests ==&lt;br /&gt;
&lt;br /&gt;
This section demonstrates a single continuous session with one IRMd and tcpdump instance. The configuration file (&amp;lt;code&amp;gt;tut06.conf&amp;lt;/code&amp;gt;) includes autostart for oping, so the server is ready immediately when IRMd starts.&lt;br /&gt;
&lt;br /&gt;
First install the &#039;&#039;&#039;CA and Intermediate CA only&#039;&#039;&#039; to the system security directories. The server certificate will be installed later during Test 3 (authentication test):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo mkdir -p /etc/ouroboros/security/{cacert,untrusted,server/sec.oping.tut.o7s,client/sec.oping.tut.o7s}&lt;br /&gt;
&lt;br /&gt;
# Run the PKI generation script&lt;br /&gt;
cd /tmp/o7s-tut06&lt;br /&gt;
sudo chmod +x gen-pki.sh&lt;br /&gt;
sudo ./gen-pki.sh&lt;br /&gt;
&lt;br /&gt;
# Install Root CA (trust anchor)&lt;br /&gt;
sudo cp pki/root/certs/ca.tut.o7s.crt.pem /etc/ouroboros/security/cacert/&lt;br /&gt;
&lt;br /&gt;
# Install Intermediate CA (for certificate chain validation)&lt;br /&gt;
sudo cp pki/sign/certs/sign.tut.o7s.crt.pem /etc/ouroboros/security/untrusted/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Running the Tutorial (3 Terminals) ===&lt;br /&gt;
&lt;br /&gt;
In this tutorial, we run a single IRMd session with a concurrent tcpdump instance to capture it. We then run four oping client tests while the IRMd/tcpdump sessions are going, modifying the security configuration between tests. After the tests are complete, we can will down the IRMd and tcpdump sessions with Ctrl-C.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Terminal 1: Start tcpdump to capture all packets (runs continuously)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo tcpdump -i lo -n -A -v -U -w /tmp/o7s-tut06/tut06.pcap &amp;quot;ether proto 0xa000&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Terminal 2: Start IRMd with debug output (runs continuously)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
cd /tmp/o7s-tut06&lt;br /&gt;
sudo irmd --config tut06.conf --stdout 2&amp;gt;&amp;amp;1 | tee /tmp/o7s-tut06/irmd.log&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Terminal 3: Run the tests&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Test 1: No Authentication, No Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Verify directories are empty&lt;br /&gt;
sudo ls -la /etc/ouroboros/security/client/sec.oping.tut.o7s/*&lt;br /&gt;
sudo ls -la /etc/ouroboros/security/server/sec.oping.tut.o7s/*&lt;br /&gt;
&lt;br /&gt;
# Run first ping test&lt;br /&gt;
echo &amp;quot;=== Test 1: No Authentication, No Encryption ===&amp;quot;&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 1: Client initiates plaintext flow allocation&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [60e824383b3fbd6a] KEX config: algo=none, mode=server-encap, cipher=none.&lt;br /&gt;
irmd/oap(PP): OAP_HDR [60e824383b3fbd6a @ 2026-02-14 14:08:56 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Cipher: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   KDF: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 1: Server accepts and completes handshake&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(PP): OAP_HDR [60e824383b3fbd6a @ 2026-02-14 14:08:57 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Cipher: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   KDF: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [48 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; All OAP fields are &amp;lt;code&amp;gt;&amp;lt;none&amp;gt;&amp;lt;/code&amp;gt; because no security is configured (except for the request hash in the response). Flow succeeds with plaintext communication.&lt;br /&gt;
&lt;br /&gt;
==== Test 2: No Authentication, With Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Enable encryption for client only&lt;br /&gt;
sudo touch /etc/ouroboros/security/client/sec.oping.tut.o7s/enc.conf&lt;br /&gt;
&lt;br /&gt;
# Run second ping test&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 2: Client initiates flow with encryption enabled&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(II): Encryption enabled for sec.oping.tut.o7s.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] KEX config: algo=prime256v1, mode=server-encap, cipher=aes-256-gcm.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Generated ephemeral prime256v1 keys (91 bytes).&lt;br /&gt;
irmd/oap(PP): OAP_HDR [80fd6f9509a996b0 @ 2026-02-14 14:09:38 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes] [Server encaps]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: sha256&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 2: Server receives and responds with ephemeral key&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] No crt provided.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Selected client cipher aes-256-gcm.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Selected client KDF sha256.&lt;br /&gt;
irmd/oap(II): [80fd6f9509a996b0] No key exchange.&lt;br /&gt;
irmd/oap(DB): [80fd6f9509a996b0] Generated prime256v1 ephemeral keys.&lt;br /&gt;
irmd/oap(PP): OAP_HDR [80fd6f9509a996b0 @ 2026-02-14 14:09:38 (UTC) ] --&amp;gt;&lt;br /&gt;
irmd/oap(PP):   crt: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Key Exchange Data: [91 bytes] [Server encaps]&lt;br /&gt;
irmd/oap(PP):   Cipher: aes-256-gcm&lt;br /&gt;
irmd/oap(PP):   KDF: HKDF-sha256&lt;br /&gt;
irmd/oap(PP):   Digest: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
irmd/oap(PP):   Req Hash: [32 bytes]&lt;br /&gt;
irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; Both client and server generate ephemeral keys (91 bytes each) for encryption. No certificates because authentication is not required. Encryption and authentication are independent.&lt;br /&gt;
&lt;br /&gt;
==== Test 3: With Authentication, With Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Install server certificates and keys&lt;br /&gt;
sudo cp /tmp/o7s-tut06/pki/server/certs/sec.oping.tut.o7s.crt.pem \&lt;br /&gt;
        /etc/ouroboros/security/server/sec.oping.tut.o7s/crt.pem&lt;br /&gt;
sudo cp /tmp/o7s-tut06/pki/server/private/sec.oping.tut.o7s.key.pem \&lt;br /&gt;
        /etc/ouroboros/security/server/sec.oping.tut.o7s/key.pem&lt;br /&gt;
&lt;br /&gt;
# enc.conf is still in place from Test 2&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 3: Client initiates flow with encryption and server has certificate&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
==33047== irmd(II): Allocating flow for 33500 to sec.oping.tut.o7s.&lt;br /&gt;
==33047== irmd(II): Encryption enabled for sec.oping.tut.o7s.&lt;br /&gt;
==33047== irmd(DB): File /etc/ouroboros/security/client/sec.oping.tut.o7s/crt.pem does not exist.&lt;br /&gt;
==33047== irmd(II): No security info for sec.oping.tut.o7s.&lt;br /&gt;
==33047== irmd(DB): Generated ephemeral keys for 33500.&lt;br /&gt;
==33047== irmd/oap(PP): OAP_HDR [3f89a905c0e5571b @ 2026-01-01 11:27:25 (UTC) ] --&amp;gt;&lt;br /&gt;
==33047== irmd/oap(PP):   Certificate: &amp;lt;none&amp;gt;&lt;br /&gt;
==33047== irmd/oap(PP):   Ephemeral Public Key: [91 bytes]&lt;br /&gt;
==33047== irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
==33047== irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 3: Server responds with certificate + ephemeral key + signature&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
==33047== irmd(DB): Generated ephemeral keys for 33198.&lt;br /&gt;
==33047== irmd(II): No certificate provided by &amp;lt;client&amp;gt;.&lt;br /&gt;
==33047== irmd/oap(PP): OAP_HDR [3f89a905c0e5571b] --&amp;gt;&lt;br /&gt;
==33047== irmd/oap(PP):   Certificate: [560 bytes]&lt;br /&gt;
==33047== irmd/oap(PP):   Ephemeral Public Key: [91 bytes]&lt;br /&gt;
==33047== irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
==33047== irmd/oap(PP):   Signature: [103 bytes]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 3: Client verifies certificate and authenticates&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
==33047== irmd(DB): Loaded peer certificate for sec.oping.tut.o7s.&lt;br /&gt;
==33047== irmd(DB): Certificate matches name sec.oping.tut.o7s.&lt;br /&gt;
==33047== irmd(DB): Got public key from certificate for sec.oping.tut.o7s.&lt;br /&gt;
==33047== irmd(II): Successfully verified peer certificate for sec.oping.tut.o7s.&lt;br /&gt;
==33047== irmd(II): Successfully authenticated sec.oping.tut.o7s.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; Full OAP handshake with certificate (560 bytes) + ephemeral keys (91 bytes) + signature (103 bytes). Client verifies server&#039;s certificate against CA store and confirms authentication success.&lt;br /&gt;
&lt;br /&gt;
==== Test 4: With Authentication, No Encryption ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Remove encryption config but keep certificates&lt;br /&gt;
sudo rm -f /etc/ouroboros/security/client/sec.oping.tut.o7s/enc.conf&lt;br /&gt;
&lt;br /&gt;
# Run fourth ping test&lt;br /&gt;
oping -n sec.oping.tut.o7s -c 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 4: Client initiates plaintext flow (no encryption file)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
==33047== irmd(II): Allocating flow for 33642 to sec.oping.tut.o7s.&lt;br /&gt;
==33047== irmd(DB): File /etc/ouroboros/security/client/sec.oping.tut.o7s/enc.conf does not exist.&lt;br /&gt;
==33047== irmd(DB): File /etc/ouroboros/security/client/sec.oping.tut.o7s/crt.pem does not exist.&lt;br /&gt;
==33047== irmd(II): No security info for sec.oping.tut.o7s.&lt;br /&gt;
==33047== irmd/oap(PP): OAP_HDR [9b383e855577d211 @ 2026-01-01 11:27:34 (UTC) ] --&amp;gt;&lt;br /&gt;
==33047== irmd/oap(PP):   Certificate: &amp;lt;none&amp;gt;&lt;br /&gt;
==33047== irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
==33047== irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
==33047== irmd/oap(PP):   Signature: &amp;lt;none&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 4: Server responds with certificate + signature (no ephemeral key)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
==33047== irmd(II): No certificate provided by &amp;lt;client&amp;gt;.&lt;br /&gt;
==33047== irmd/oap(PP): OAP_HDR [9b383e855577d211] --&amp;gt;&lt;br /&gt;
==33047== irmd/oap(PP):   Certificate: [560 bytes]&lt;br /&gt;
==33047== irmd/oap(PP):   Ephemeral Public Key: &amp;lt;none&amp;gt;&lt;br /&gt;
==33047== irmd/oap(PP):   Data: &amp;lt;none&amp;gt;&lt;br /&gt;
==33047== irmd/oap(PP):   Signature: [103 bytes]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IRMd Output - Test 4: Client verifies certificate and authenticates&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
==33047== irmd(DB): Loaded peer certificate for sec.oping.tut.o7s.&lt;br /&gt;
==33047== irmd(DB): Certificate matches name sec.oping.tut.o7s.&lt;br /&gt;
==33047== irmd(DB): Got public key from certificate for sec.oping.tut.o7s.&lt;br /&gt;
==33047== irmd(II): Successfully verified peer certificate for sec.oping.tut.o7s.&lt;br /&gt;
==33047== irmd(II): Successfully authenticated sec.oping.tut.o7s.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039; Server sends certificate + signature for authentication, but NO ephemeral keys (plaintext data). Data exchanged without encryption even though authenticated. Demonstrates that authentication and encryption are independent mechanisms.&lt;br /&gt;
&lt;br /&gt;
=== Stop the IRMd and tcpdump, clean up the tutorial files ===&lt;br /&gt;
&lt;br /&gt;
Once all tests complete:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
# Stop IRMd in Terminal 2 (Ctrl+C)&lt;br /&gt;
# Stop tcpdump in Terminal 1 (Ctrl+C)&lt;br /&gt;
&lt;br /&gt;
# Clean up tutorial security files from system&lt;br /&gt;
sudo rm -f /etc/ouroboros/security/cacert/ca.tut.o7s.crt.pem&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Part 2: PCAP Trace Analysis ==&lt;br /&gt;
&lt;br /&gt;
After the tutorial, we now explain the trace in the tcpdump pcap file.&lt;br /&gt;
&lt;br /&gt;
=== Protocol Overview ===&lt;br /&gt;
&lt;br /&gt;
This section summarizes the four protocols that work together in the captured packet flow.&lt;br /&gt;
&lt;br /&gt;
==== Ethernet DIX Frame with EID Header ====&lt;br /&gt;
&lt;br /&gt;
Ouroboros extends the DIX frame with a flow identifier (EID - Endpoint Identifier) and length field.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Octets !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Destination MAC || 0-5 || 6 bytes || Hardware address of destination&lt;br /&gt;
|-&lt;br /&gt;
| Source MAC || 6-11 || 6 bytes || Hardware address of source&lt;br /&gt;
|-&lt;br /&gt;
| EtherType || 12-13 || 2 bytes || Protocol identifier (0xA000 for Ouroboros)&lt;br /&gt;
|-&lt;br /&gt;
| EID || 14-15 || 2 bytes || Destination Endpoint Identifier&lt;br /&gt;
|-&lt;br /&gt;
| Length || 16-17 || 2 bytes || Payload length (needed because of runt frame padding)&lt;br /&gt;
|-&lt;br /&gt;
| Payload || 18+ || Variable || Frame data (up to MTU size)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Ethernet Flow Allocator - Management Protocol ====&lt;br /&gt;
&lt;br /&gt;
The Ethernet DIX management protocol handles flow allocation, setup, and teardown. All management frames use destination EID &amp;lt;code&amp;gt;0x0000&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Management Frame Types:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Code !! Type !! Direction !! Service Hash !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x00&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FLOW_REQ&amp;lt;/code&amp;gt; || Client → Server || ✓ Included || Request new flow allocation&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x01&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;FLOW_REPLY&amp;lt;/code&amp;gt; || Server → Client || – Not included || Respond to flow request (success/failure)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x02&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NAME_QUERY_REQ&amp;lt;/code&amp;gt; || Client → Server || ✓ Included || Query if a remote name is reachable&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x03&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;NAME_QUERY_REPLY&amp;lt;/code&amp;gt; || Server → Client || ✓ Included || Response to name query&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The 32-byte service hash (SHA3-256) is appended after the management protocol header for NAME_QUERY_* and FLOW_REQ messages to identify which service is being queried or allocated. FLOW_REPLY does not include the service hash; the endpoints are already identified by the allocated EIDs (SEID/DEID) and the flow allocation ID in the OAP header (see below).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frame Layout by Field:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| SEID || 0-1 || 2 bytes || Source Endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| DEID || 2-3 || 2 bytes || Destination Endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| Loss || 4-7 || 4 bytes || Acceptable packet loss (ppm)&lt;br /&gt;
|-&lt;br /&gt;
| Bandwidth || 8-15 || 8 bytes || Required bandwidth (bps)&lt;br /&gt;
|-&lt;br /&gt;
| BER || 16-19 || 4 bytes || Bit error rate (ppm)&lt;br /&gt;
|-&lt;br /&gt;
| Max Gap || 20-23 || 4 bytes || Maximum consecutive lost packets&lt;br /&gt;
|-&lt;br /&gt;
| Delay || 24-27 || 4 bytes || Maximum latency (ms)&lt;br /&gt;
|-&lt;br /&gt;
| Timeout || 28-31 || 4 bytes || Flow idle timeout (seconds)&lt;br /&gt;
|-&lt;br /&gt;
| Response || 32-35 || 4 bytes || Response code (0=success, negative=error)&lt;br /&gt;
|-&lt;br /&gt;
| In-Order || 36 || 1 byte || In-order delivery requirement (boolean)&lt;br /&gt;
|-&lt;br /&gt;
| Code || 37 || 1 byte || Message type (FLOW_REQ, FLOW_REPLY, etc.)&lt;br /&gt;
|-&lt;br /&gt;
| Availability || 38 || 1 byte || Availability status&lt;br /&gt;
|-&lt;br /&gt;
| Service hash || 39-61 || 32 bytes || SHA3-256 hash (optional, see above)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Ouroboros Flow Allocation Protocol (OAP) ====&lt;br /&gt;
&lt;br /&gt;
The Ouroboros Application Protocol (OAP) is the flow allocation and authentication protocol. It carries flow negotiation requests, responses, and authentication credentials. OAP frames are encapsulated as data payload over the management protocol.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frame Layout by Field:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| ID || 0-15 || 16 bytes || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| Timestamp || 16-23 || 8 bytes || Creation timestamp (UTC, seconds and microseconds)&lt;br /&gt;
|-&lt;br /&gt;
| Crt Length || 24-25 || 2 bytes || Certificate length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Certificate || 26+ || Variable || X.509 certificate (DER encoded)&lt;br /&gt;
|-&lt;br /&gt;
| Eph Length || Variable || 2 bytes || Ephemeral public key length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Ephemeral Key || Variable || Variable || ECDHE public key (DER/raw encoded)&lt;br /&gt;
|-&lt;br /&gt;
| Data Length || Variable || 2 bytes || Application data length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Data || Variable || Variable || Piggybacked application-layer data&lt;br /&gt;
|-&lt;br /&gt;
| Sig Length || Variable || 2 bytes || Signature length (bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Signature || Variable || Variable || Digital signature (ECDSA, DER encoded)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Oping Application Protocol ====&lt;br /&gt;
&lt;br /&gt;
The Ouroboros Ping (oping) application is a simple echo/reply protocol used to measure round-trip time and validate connectivity between applications. It implements a request/reply pattern similar to ICMP ping.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Frame Layout by Field:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Size !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Type || 0-3 || 4 bytes || Message type (ECHO_REQUEST=0 or ECHO_REPLY=1)&lt;br /&gt;
|-&lt;br /&gt;
| ID || 4-7 || 4 bytes || Sequence number / message identifier&lt;br /&gt;
|-&lt;br /&gt;
| Timestamp (seconds) || 8-15 || 8 bytes || Seconds when message was sent (CLOCK_REALTIME)&lt;br /&gt;
|-&lt;br /&gt;
| Timestamp (nanoseconds) || 16-23 || 8 bytes || Nanoseconds component of timestamp&lt;br /&gt;
|-&lt;br /&gt;
| Payload || 24+ || Variable || Application data (configurable size, default 64 bytes)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Field Definitions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Type&#039;&#039;&#039; (4 bytes): Message type selector&lt;br /&gt;
** &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt; (ECHO_REQUEST): Client-to-server ping request&lt;br /&gt;
** &amp;lt;code&amp;gt;0x00000001&amp;lt;/code&amp;gt; (ECHO_REPLY): Server-to-client response&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;ID&#039;&#039;&#039; (4 bytes): Sequence number for matching requests with replies. Incremented for each ping sent.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; (8 bytes): Network-byte-order 64-bit seconds component from when the ping was sent (CLOCK_REALTIME).&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; (8 bytes): Network-byte-order 64-bit nanoseconds component (0-999999999) for high-resolution timing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Payload&#039;&#039;&#039; (Variable): Application data echoed back by the server. Size is configurable (default 64 bytes, maximum 1500 bytes).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Usage:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Client sends ECHO_REQUEST with current timestamp&lt;br /&gt;
* Server receives request and echoes back as ECHO_REPLY with the same ID and timestamps&lt;br /&gt;
* Client calculates RTT by comparing reception time with original timestamps&lt;br /&gt;
* Out-of-order detection by tracking sequence numbers (ID field)&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 1 - No authentication/encryption ===&lt;br /&gt;
&lt;br /&gt;
==== Packet 1: NAME_QUERY_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends a NAME_QUERY_REQ message to discover if the service &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt; is available. This is a broadcast discovery query sent because the service is not yet known for the flow allocation process.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:48.165639 00:00:00:00:00:00 &amp;gt; ff:ff:ff:ff:ff:ff, ethertype Unknown (0xa000), length 89:&lt;br /&gt;
        0x0000:  0000 0047 0000 0000 0000 0000 0000 0000  ...G............&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0002 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a              ..f.i.._...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0047&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 71 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0000 || Source Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || Response code (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x03 || Message Type: NAME_QUERY_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Packet 2: NAME_QUERY_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds to the NAME_QUERY_REQ by sending a NAME_QUERY_REPLY for the service hash.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:48.166073 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 89:&lt;br /&gt;
        0x0000:  0000 0047 0000 0000 0000 0000 0000 0000  ...G............&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0003 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a              ..f.i.._...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0047&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 71 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0000 || Source Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || Response code&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;03&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x03 || Message Type: NAME_QUERY_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt; (echoed back)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Packet 3: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates a flow allocation request (FLOW_REQ) with minimal OAP headers since no authentication or encryption is being used.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:48.167222 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 121:&lt;br /&gt;
        0x0000:  0000 0067 0040 0000 0000 0001 0000 0000  ...g.@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a51 8a56 ff6f  ..f.i.._...Q.V.o&lt;br /&gt;
        0x0050:  5ba6 9d03 7da1 cfc3 0f30 7718 86a8 e103  [...}....0w.....&lt;br /&gt;
        0x0060:  3e52 3300 0000 0000 0000 00              &amp;gt;R3........&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0067&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 103 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID (allocated flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || -- || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0001 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x00 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The OAP payload starts at offset 0x4b (after management protocol + service hash):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;51 8a56 ff6f 5ba6 9d03 7da1 cfc3 0f30 77&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e103 3e52 33&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp (seconds + microseconds)&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || No ephemeral key&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0x68 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x69-0x6a || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0040 (first allocated flow ID for this session)&lt;br /&gt;
* Service hash is carried in management protocol payload (32 bytes)&lt;br /&gt;
* OAP header is minimal: only ID and timestamp, no optional fields&lt;br /&gt;
* No certificate, ephemeral key, data, or signature in this initial request&lt;br /&gt;
* Client sends minimal OAP headers with no authentication or encryption setup at allocation time&lt;br /&gt;
&lt;br /&gt;
==== Packet 4: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds to FLOW_REQ by sending FLOW_REPLY with a new DEID (destination endpoint ID 0x0041) to establish the allocated flow for data transfer.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:49.178732 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 89:&lt;br /&gt;
        0x0000:  0000 0047 0041 0040 0000 0000 0000 0000  ...G.A.@........&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 0051 8a56 ff6f  ...........Q.V.o&lt;br /&gt;
        0x0030:  5ba6 9d03 7da1 cfc3 0f30 7718 86a8 e13f  [...}....0w....?&lt;br /&gt;
        0x0040:  a347 3800 0000 0000 0000 00              .G8........&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0047&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 71 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0041 || Source Endpoint ID (allocated server-side flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client&#039;s flow ID from FLOW_REQ)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0|| Response code (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The OAP payload starts at offset 0x2b (no service hash in FLOW_REPLY):&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;51 8a56 ff6f 5ba6 9d03 7da1 cfc3 0f30 77&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Echo of client&#039;s flow ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;18 86a8 e13f a347 38&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Echoed timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x46 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || No ephemeral key&lt;br /&gt;
|-&lt;br /&gt;
| 0x47-0x48 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x49-0x4a || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID 0x0041 is the newly allocated server-side flow endpoint&lt;br /&gt;
* DEID 0x0040 reflects the client&#039;s flow ID, creating a bidirectional mapping&lt;br /&gt;
* No service hash included (FLOW_REPLY only needs the EIDs to identify the flow)&lt;br /&gt;
* OAP echoes the client&#039;s ID and timestamp, confirming the flow allocation&lt;br /&gt;
* Response code 0x00000000 indicates success&lt;br /&gt;
* Both client and server now have their respective flow IDs (0x0040 and 0x0041) for data transfer&lt;br /&gt;
* Response code 0x00000000 indicates success&lt;br /&gt;
&lt;br /&gt;
==== Packet 5: ECHO_REQUEST - Plaintext Data ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends an oping ECHO_REQUEST packet to the server using the allocated flow.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:50.180824 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0041 0040 0000 0000 0000 0000 7377 0000  .A.@........sw..&lt;br /&gt;
        0x0010:  0000 0000 b03e e007 0000 0000 0000 0000  .....&amp;gt;..........&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0041 || Source Endpoint ID (server → client)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Oping payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REQUEST Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The oping payload starts at offset 0x04:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000000 || Message type: ECHO_REQUEST&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Sequence number (first ping)&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;7377 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || 0x0000000000003773 || Seconds component&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;b03e e007 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || 0x0000000007e03eb0 || Nanoseconds component&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 0000 ... 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || All zeros || Echo data (default 64 bytes total - 24 byte header = 40 bytes data)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID 0x0041 shows traffic from server-side flow ID&lt;br /&gt;
* This is the first ping request (ID = 0x00000000)&lt;br /&gt;
* Timestamp captures when the ping was sent (seconds in network order)&lt;br /&gt;
* Default oping payload is 64 bytes total; 24 bytes header + 40 bytes data&lt;br /&gt;
&lt;br /&gt;
==== Packet 6: ECHO_REPLY - Plaintext Data ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server receives the ECHO_REQUEST and immediately sends back an ECHO_REPLY with the same ID and timestamps, echoing the client&#039;s message.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:50.181496 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0040 0040 0000 0001 0000 0000 7377 0000  .@.@........sw..&lt;br /&gt;
        0x0010:  0000 0000 b03e e007 0000 0000 0000 0000  .....&amp;gt;..........&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client ← server)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Oping payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REPLY Payload&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The oping payload starts at offset 0x04:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0001&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000001 || Message type: ECHO_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Echo of request sequence number&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;7377 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || 0x0000000000003773 || Echo of original seconds&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;b03e e007 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || 0x0000000007e03eb0 || Echo of original nanoseconds&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 0000 ... 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || All zeros || Echo data (unchanged from request)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID 0x0040 shows traffic from client-side flow ID receiving the reply&lt;br /&gt;
* Type field changed from 0x00000000 (REQUEST) to 0x00000001 (REPLY)&lt;br /&gt;
* ID, timestamps, and payload data are identical to the request (echoed back)&lt;br /&gt;
* Round-trip time can be calculated by comparing current time with echoed timestamp&lt;br /&gt;
* Ping succeeded on first attempt with minimal latency (~1 millisecond between timestamps)&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 2 - No authentication, with encryption ===&lt;br /&gt;
&lt;br /&gt;
==== Packet 7: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates flow allocation with encryption enabled. This FLOW_REQ carries an OAP header with an ephemeral ECDHE P-384 public key (91 bytes) for encryption setup.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:53.808158 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 212:&lt;br /&gt;
        0x0000:  0000 00c2 0040 0000 0000 0001 0000 0000  .....@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8af1 766b 547c  ..f.i.._....vkT|&lt;br /&gt;
        0x0050:  fcb0 8fce 5d60 a01e e8be 0218 86a8 e253  ....]`.........S&lt;br /&gt;
        0x0060:  8b6c 9000 0000 5b30 5930 1306 072a 8648  .l....[0Y0...*.H&lt;br /&gt;
        0x0070:  ce3d 0201 0608 2a86 48ce 3d03 0107 0342  .=....*.H.=....B&lt;br /&gt;
        0x0080:  0004 c508 1c19 6106 b7e9 3074 57b9 bb16  ......a...0tW...&lt;br /&gt;
        0x0090:  6959 4a55 81f9 169b cc79 fe10 a882 41fe  iYJU.....y....A.&lt;br /&gt;
        0x00a0:  0697 c9b4 f8f0 5562 7fa2 c7a0 a020 1ac6  ......Ub........&lt;br /&gt;
        0x00b0:  939f 23ff b2fb 07a2 b747 aacc 474a 3dab  ..#......G..GJ=.&lt;br /&gt;
        0x00c0:  2598 0000 0000                           %.....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;00c2&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 194 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... d4c0&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Mixed || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x00 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload with Ephemeral Key&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;f1 766b 547c fcb0 8fce 5d60 a01e e8be 02&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e253 8b6c 90&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate in client request&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x005b (91) || Ephemeral key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0xc1 || &amp;lt;code&amp;gt;30 5930 ... 3dab 2598&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDHE P-384 || ECDHE P-384 public key (DER encoded)&lt;br /&gt;
|-&lt;br /&gt;
| 0xd3-0xd4 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0xd5-0xd6 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* Encryption enabled: ephemeral key present (91 bytes)&lt;br /&gt;
* Client sends no certificate, allowing anonymous encryption setup&lt;br /&gt;
* No signature (unsigned OAP)&lt;br /&gt;
* Ephemeral key is ECDHE P-384 for key exchange&lt;br /&gt;
&lt;br /&gt;
==== Packet 8: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server accepts the encrypted flow allocation request. FLOW_REPLY contains the server&#039;s ephemeral key but no certificate (since client didn&#039;t send one).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:53.810564 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 180:&lt;br /&gt;
        0x0000:  0000 00a2 0042 0040 0000 0000 0000 0000  .....B.@........&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 00f1 766b 547c  ............vkT|&lt;br /&gt;
        0x0030:  fcb0 8fce 5d60 a01e e8be 0218 86a8 e253  ....]`.........S&lt;br /&gt;
        0x0040:  b694 e800 0000 5b30 5930 1306 072a 8648  ......[0Y0...*.H&lt;br /&gt;
        0x0050:  ce3d 0201 0608 2a86 48ce 3d03 0107 0342  .=....*.H.=....B&lt;br /&gt;
        0x0060:  0004 5f3c 6929 cca2 024a ae9f 9aa1 dfc2  .._&amp;lt;i)...J......&lt;br /&gt;
        0x0070:  a493 3ff3 ff58 b054 74dc d2e2 47fc 7c5b  ..?..X.Tt...G.|[&lt;br /&gt;
        0x0080:  eff5 e129 72b4 de1e 7c09 bf8c fe38 5e8b  ...)r...|....8^.&lt;br /&gt;
        0x0090:  b22e 59ed 6eb9 dfda 369d 691e 6e2c 122c  ..Y.n...6.i.n,.,&lt;br /&gt;
        0x00a0:  9936 0000 0000                           .6....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;00a2&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 162 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0042&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0042 || Source Endpoint ID (allocated server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0x00000000 || Response code: 0 (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY with Ephemeral Key&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;f1 766b 547c fcb0 8fce 5d60 a01e e8be 02&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || Echo of client ID || Echoes client&#039;s flow ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;18 86a8 e253 b694 e8&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Echoed timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x46 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x005b (91) || Ephemeral key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x47-0xa1 || &amp;lt;code&amp;gt;30 5930...9936&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDHE P-384 || Server&#039;s ECDHE P-384 public key (DER encoded)&lt;br /&gt;
|-&lt;br /&gt;
| 0xd1-0xd2 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0xd3-0xd4 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID 0x0042 is the new server-side flow endpoint&lt;br /&gt;
* Both keys are now exchanged; client and server can derive shared secret&lt;br /&gt;
* No authentication (no certificates) but encryption is negotiated&lt;br /&gt;
* Response indicates successful allocation&lt;br /&gt;
&lt;br /&gt;
==== Packet 9: Encrypted ECHO_REQUEST ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends encrypted ping request after encryption keys are established. The payload is encrypted with the derived shared secret.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:54.815771 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0042 0060 a691 6d83 8446 cbeb ac95 c2eb  .B.`..m..F......&lt;br /&gt;
        0x0010:  4b42 e819 c67f 92c8 58d7 0641 d8a6 6e1f  KB......X..A..n.&lt;br /&gt;
        0x0020:  fc90 feed ef55 b791 4fbd a832 74bd 8bed  .....U..O..2t...&lt;br /&gt;
        0x0030:  249c 4cee 0fc0 cec6 2f1b aec1 2428 bdbd  $.L...../...$(..&lt;br /&gt;
        0x0040:  36b5 01b5 1257 004e 6ed6 7ecd f0c7 7d11  6....W.Nn.~...}.&lt;br /&gt;
        0x0050:  20ba e81b f43a 4de9 b141 1624 e1ba 0a84  .....:M..A.$....&lt;br /&gt;
        0x0060:  74b1 9a9a                                t...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0042&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0042 || Destination Endpoint ID (server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0060&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 96 bytes || Encrypted payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REQUEST (Encrypted)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x63 || &amp;lt;code&amp;gt;a691 6d83 8446 cbeb...74b1 9a9a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Encrypted Data&#039;&#039;&#039; || 96 || Ciphertext || All 96 bytes encrypted&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* All 96 bytes of oping data (type, ID, timestamps, payload) are encrypted&lt;br /&gt;
* No plaintext oping headers visible; entire packet is ciphertext&lt;br /&gt;
* Flow IDs (0x0042) identify which encryption context to use&lt;br /&gt;
* Ping still works with encryption transparently&lt;br /&gt;
&lt;br /&gt;
==== Packet 10: Encrypted ECHO_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server receives encrypted ping request, decrypts it, and sends encrypted ECHO_REPLY.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:54.819574 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0040 0060 c6ea 2222 5618 0268 b27e 9a91  .@.`..&amp;quot;&amp;quot;V..h.~..&lt;br /&gt;
        0x0010:  f124 1f8d bccc 478c 26fe 9b13 b3cb 5398  .$....G.&amp;amp;.....S.&lt;br /&gt;
        0x0020:  6869 3cdb 4928 510d 4de8 dc6a 3f3a 6a6d  hi&amp;lt;.I(Q.M..j?:jm&lt;br /&gt;
        0x0030:  6487 dcd8 c8cd 1a85 fba2 9ecd 3566 57d1  d...........5fW.&lt;br /&gt;
        0x0040:  1c94 ac35 518e 8509 873a 3a5e 04d9 8ee2  ...5Q....::^....&lt;br /&gt;
        0x0050:  9d74 2527 e425 5433 9d73 9ccd f56a 1f8d  .t%&#039;.%T3.s...j..&lt;br /&gt;
        0x0060:  f328 7237                                .(r7&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0060&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 96 bytes || Encrypted payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Oping Application Protocol - ECHO_REPLY (Encrypted)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x63 || &amp;lt;code&amp;gt;c6ea 2222 5618 0268...f328 7237&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Encrypted Data&#039;&#039;&#039; || 96 || Ciphertext || All 96 bytes encrypted&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID 0x0040 shows reply going back to client-side flow&lt;br /&gt;
* Ciphertext is different from request (different plaintext: type field differs)&lt;br /&gt;
* Both encrypted packets are 96 bytes (same size as Packet 9)&lt;br /&gt;
* Client receives encrypted reply, decrypts it, verifies ID and timestamps match request&lt;br /&gt;
* Encryption is transparent at application layer: oping works exactly as with plaintext flows&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 3 - Authentication and encryption ===&lt;br /&gt;
&lt;br /&gt;
==== Packet 11: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates flow allocation request with encryption enabled. Sends ephemeral public key for ECDHE key exchange but no certificate (client is not authenticating in this tutorial). The management protocol now carries a valid allocated SEID (0x0040).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:58.827411 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 212:&lt;br /&gt;
        0x0000:  0000 00c2 0040 0000 0000 0001 0000 0000  .....@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a66 bb82 95fa  ..f.i.._...f....&lt;br /&gt;
        0x0050:  91a2 7bd3 bd60 1b3e 35f6 b918 86a8 e37e  ..{..`.&amp;gt;5......~&lt;br /&gt;
        0x0060:  c0d2 ad00 0000 5b30 5930 1306 072a 8648  ......[0Y0...*.H&lt;br /&gt;
        0x0070:  ce3d 0201 0608 2a86 48ce 3d03 0107 0342  .=....*.H.=....B&lt;br /&gt;
        0x0080:  0004 9dea c238 6732 4987 1cd4 7133 9614  .....8g2I...q3..&lt;br /&gt;
        0x0090:  9d04 4fde 3f68 42f1 54fb 7ef3 88d0 ffe6  ..O.?hB.T.~.....&lt;br /&gt;
        0x00a0:  7e01 432e 56c2 2d64 72c9 19fc b0cf 1eca  ~.C.V.-dr.......&lt;br /&gt;
        0x00b0:  689e 3536 771a 8041 726c 20e2 d9bb 3589  h.56w..Arl....5.&lt;br /&gt;
        0x00c0:  86e7 0000 0000                           ......&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;00c2&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 194 bytes || Total payload length (excluding DIX header)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID (client flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... d4c0&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Default values || QoS parameters&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || Response code (&#039;&#039;&#039;UNUSED&#039;&#039;&#039;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x00 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ... 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload with Encryption Setup&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;66 bb82 95fa 91a2 7bd3 bd60 1b3e 35f6 b9&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier for Test 3&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e37e c0d2 ad&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || Client not authenticating&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 91 (0x005b) || Ephemeral public key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0xc1 || &amp;lt;code&amp;gt;30 5930 1306 ... 3589&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDP-384 public key || Client&#039;s ephemeral ECDHE public key for encryption negotiation&lt;br /&gt;
|-&lt;br /&gt;
| 0xc2-0xc3 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0xc4-0xc5 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0040 - Same as Test 2 (Encrypted) because this is the same client session reusing the same allocated ID from the previous test&lt;br /&gt;
* No Certificate - &amp;lt;code&amp;gt;crt_len = 0x0000&amp;lt;/code&amp;gt; because the client does not have authentication credentials; the server will authenticate instead&lt;br /&gt;
* Ephemeral Key Present - &amp;lt;code&amp;gt;eph_len = 0x005b (91)&amp;lt;/code&amp;gt; because encryption is enabled on the client&lt;br /&gt;
* No Signature - &amp;lt;code&amp;gt;sig_len = 0x0000&amp;lt;/code&amp;gt; because client is not signing (no certificate to sign with)&lt;br /&gt;
* FLOW_REQ Message Type - Code field is 0x00, and service hash is present because FLOW_REQ always includes the service hash&lt;br /&gt;
* Timestamp Consistency - Same OAP ID and timestamp structure as Test 2, but with additional security handshake&lt;br /&gt;
&lt;br /&gt;
==== Packet 12: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds to client&#039;s FLOW_REQ by sending FLOW_REPLY with its certificate for authentication, ephemeral public key for ECDHE encryption setup, and a digital signature proving ownership of the certificate. This is the full authentication response.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data (abbreviated):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:58.828806 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 843:&lt;br /&gt;
        0x0000:  0000 0339 0043 0040 0000 0000 0000 0000  ...9.C.@........&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 0066 bb82 95fa  ...........f....&lt;br /&gt;
        0x0030:  91a2 7bd3 bd60 1b3e 35f6 b918 86a8 e37e  ..{..`.&amp;gt;5......~&lt;br /&gt;
        0x0040:  d566 a002 2f30 8202 2b30 8201 b2a0 0302  .f../0..+0......&lt;br /&gt;
        (... certificate and signature bytes ...)&lt;br /&gt;
        0x0320:  ef11 c358 f5d0 5cd7 3906 adf1 8a2c 9b25  ...X..\.9....,.%&lt;br /&gt;
        0x0330:  dc78 6050 ab61 3a3f 81c0 254b d193 7827  .x`P.a:?..%K..x&#039;&lt;br /&gt;
        0x0340:  c0e9 38c7 e0d1 c517 d299 9992 07         ..8..........&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0339&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 825 bytes || Total payload length (excluding DIX header)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0043&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0043 || Source Endpoint ID (server-side allocated flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client&#039;s flow ID from FLOW_REQ)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Default values || QoS parameters&lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0x00000000 || Response code: 0 (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY Payload with Full Authentication&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;66 bb82 95fa 91a2 7bd3 bd60 1b3e 35f6 b9&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || &#039;&#039;&#039;Same ID as client&#039;s FLOW_REQ&#039;&#039;&#039; (echoed back)&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;18 86a8 e37e d566 a0&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Server&#039;s timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;022f&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 559 (0x022f) || Server certificate length: 559 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x243 || &amp;lt;code&amp;gt;2f30 8202 2b ... 81c8 30&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Certificate&#039;&#039;&#039; || 559 || DER-encoded X.509 || Server&#039;s certificate (signed by intermediate CA)&lt;br /&gt;
|-&lt;br /&gt;
| 0x244-0x245 || &amp;lt;code&amp;gt;005b&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 91 (0x005b) || Server&#039;s ephemeral public key length: 91 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x246-0x2a0 || &amp;lt;code&amp;gt;30 5930 1306 ... 9936&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Ephemeral Key&#039;&#039;&#039; || 91 || ECDP-384 public key || Server&#039;s ephemeral ECDHE public key&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a4-0x2a5 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a6-0x2a7 || &amp;lt;code&amp;gt;0068&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 104 (0x0068) || Digital signature length: 104 bytes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a8-0x30f || &amp;lt;code&amp;gt;30 6602 3100 ... 07&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Signature&#039;&#039;&#039; || 104 || ECDSA signature (DER encoded) || Server&#039;s signature over OAP header proving certificate ownership&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0043 - New server-side endpoint ID allocated for this authenticated flow&lt;br /&gt;
* DEID is 0x0040 - Client&#039;s flow ID from the FLOW_REQ, establishing the bidirectional flow&lt;br /&gt;
* FLOW_REPLY Message Type - Code field is 0x01, &#039;&#039;&#039;no service hash&#039;&#039;&#039; (already identified in FLOW_REQ)&lt;br /&gt;
* Full Certificate - &amp;lt;code&amp;gt;crt_len = 0x022f (559)&amp;lt;/code&amp;gt; carrying server&#039;s complete X.509 certificate signed by intermediate CA&lt;br /&gt;
* Ephemeral Key Present - &amp;lt;code&amp;gt;eph_len = 0x005b (91)&amp;lt;/code&amp;gt; with server&#039;s ECDHE public key for encryption&lt;br /&gt;
* Signature Included - &amp;lt;code&amp;gt;sig_len = 0x0068 (104)&amp;lt;/code&amp;gt; containing ECDSA digital signature over the entire OAP header&lt;br /&gt;
* Same OAP ID - Server echoes back the exact ID from client&#039;s FLOW_REQ to confirm association (included in signature, binding response to this specific client request)&lt;br /&gt;
* Large Payload - Total of 825 bytes due to certificate (559) + ephemeral key (91) + signature (104) + overhead&lt;br /&gt;
* Authentication Complete - Client verifies: (1) certificate against CA store, (2) signature over entire response ensures authenticity and integrity, (3) echoed ID binds response to this specific request, (4) timestamp prevents replay attacks&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds with its certificate for authentication, ephemeral public key for ECDHE encryption, and a digital signature proving ownership of the certificate.&lt;br /&gt;
&lt;br /&gt;
==== Packet 13: Encrypted ECHO_REQUEST ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends encrypted ping request after authentication handshake. All application data is protected by encryption using the ephemeral keys established in Packets 11-12.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:59.836485 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0043 0060 3bed 0b48 1be1 6930 cf3d dee9  .C.`.;..H..i0.=..&lt;br /&gt;
        0x0010:  4fc9 774b 5d63 cc9b 5a34 6604 f9ac 1016  O.wK]c..Z4f.....&lt;br /&gt;
        0x0020:  1c6d c9ac f80e dc89 31c1 9634 1a4f b2c7  .m......1..4.O..&lt;br /&gt;
        0x0030:  4721 e402 8259 b0aa 8870 4566 33d1 9c18  G!...Y..  .pEf3...&lt;br /&gt;
        0x0040:  06da 50c3 8b75 86b0 f240 d109 840e a6cd  ..P..u...@......&lt;br /&gt;
        0x0050:  d115 77cb 5652 5bfb e6d5 0ca9 dbc3 d0b8  ..w.VR[.........&lt;br /&gt;
        0x0060:  0058 fd19                                .X..&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame Analysis:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Length !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Source EID || 0x00-0x01 || 2 bytes || &amp;lt;code&amp;gt;0x0043&amp;lt;/code&amp;gt; || Client flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| Destination EID || 0x02-0x03 || 2 bytes || &amp;lt;code&amp;gt;0x0060&amp;lt;/code&amp;gt; || Server flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Encrypted Payload&#039;&#039;&#039; || &#039;&#039;&#039;0x04-0x71&#039;&#039;&#039; || &#039;&#039;&#039;110 bytes&#039;&#039;&#039; || &#039;&#039;&#039;Ciphertext&#039;&#039;&#039; || &#039;&#039;&#039;AES-encrypted oping ECHO_REQUEST data&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* No visible protocol structure - all application data appears as ciphertext&lt;br /&gt;
* Uses the same source/destination EID pair (0x0043 → 0x0060) established in the FLOW_REQ/FLOW_REPLY handshake&lt;br /&gt;
* Encryption is done using the ephemeral key (91 bytes) exchanged in Packet 11&#039;s OAP header&lt;br /&gt;
* Unlike Packets 11-12, this packet contains no certificate, public keys, or signatures&lt;br /&gt;
* The 110-byte encrypted data corresponds to the original oping ECHO_REQUEST message&lt;br /&gt;
&lt;br /&gt;
==== Packet 14: Encrypted ECHO_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server sends encrypted ping reply. Note that the flow identifiers swap, demonstrating bidirectional encrypted communication.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:39:59.836930 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 114:&lt;br /&gt;
        0x0000:  0040 0060 d552 e100 e681 940c e35a 07d0  .@.`..........Z..&lt;br /&gt;
        0x0010:  a293 1d73 33a5 854e 0fce 4f4d 6655 267a  ...s3..N..OMfU&amp;amp;z&lt;br /&gt;
        0x0020:  3de2 663b 709d 739a a696 2ddd 7b34 28b8  =.f;p.s...-{4(...&lt;br /&gt;
        0x0030:  5a98 eec2 52c6 4288 3885 ae16 e466 4181  Z...R.B.8...fA..&lt;br /&gt;
        0x0040:  f2d6 44c1 b51b 8728 58a4 7525 fb5e 3fd6  ..D...(X.u%.^?..&lt;br /&gt;
        0x0050:  7e49 532a d2a5 bea7 55e9 c274 f1b2 0412  ~IS*....U..t....&lt;br /&gt;
        0x0060:  73d4 6436                                s.d6&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame Analysis:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Offset !! Length !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Source EID || 0x00-0x01 || 2 bytes || &amp;lt;code&amp;gt;0x0040&amp;lt;/code&amp;gt; || Client&#039;s inbound flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| Destination EID || 0x02-0x03 || 2 bytes || &amp;lt;code&amp;gt;0x0060&amp;lt;/code&amp;gt; || Server flow endpoint ID&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Encrypted Payload&#039;&#039;&#039; || &#039;&#039;&#039;0x04-0x71&#039;&#039;&#039; || &#039;&#039;&#039;110 bytes&#039;&#039;&#039; || &#039;&#039;&#039;Ciphertext&#039;&#039;&#039; || &#039;&#039;&#039;AES-encrypted oping ECHO_REPLY data&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* The EID in offset 0x00 is now 0x0040 (server&#039;s view of client&#039;s inbound flow)&lt;br /&gt;
* Uses the same ephemeral key material as Packet 13, but encryption direction is reversed&lt;br /&gt;
* Both packets use AES-GCM with keys derived from the ECDH exchange&lt;br /&gt;
* Timestamp 17:39:59.836930 is only 445 microseconds after Packet 13, indicating server-side processing&lt;br /&gt;
* The 110-byte encrypted ECHO_REPLY payload is the same size as the request&lt;br /&gt;
* All application data is protected by both authentication (X.509 + ECDSA) and encryption (AES)&lt;br /&gt;
&lt;br /&gt;
=== Packet Analysis: Test 4 - Authentication, no encryption ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Packet 15: FLOW_REQ ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client initiates flow allocation with authentication enabled but encryption disabled. This FLOW_REQ carries an OAP header but &#039;&#039;&#039;no ephemeral key&#039;&#039;&#039; since the client does not request encrypted communication.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:03.413372 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 121:&lt;br /&gt;
        0x0000:  0000 0067 0040 0000 0000 0001 0000 0000  ...g.@..........&lt;br /&gt;
        0x0010:  0000 0000 0000 0001 ffff ffff ffff ffff  ................&lt;br /&gt;
        0x0020:  0001 d4c0 0000 0000 0000 00ec f815 ad98  ................&lt;br /&gt;
        0x0030:  3df6 bf81 fdc7 cb63 6eec 1563 6e60 5a94  =......cn..cn`Z.&lt;br /&gt;
        0x0040:  b1ad 66c1 690c 9e5f 9282 8a8f a6ab 6ea7  ..f.i.._........&lt;br /&gt;
        0x0050:  ef89 68e1 ed1e 2ede 0919 fa18 86a8 e490  .h..............&lt;br /&gt;
        0x0060:  0de6 6100 0000 0000 0000 00              ..a.....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0067&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 103 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0040 || Source Endpoint ID (allocated flow ID)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... dc40&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || Mixed || &lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0 || &#039;&#039;&#039;UNUSED&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REQ&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x4a || &amp;lt;code&amp;gt;ec f815 ad98 3df6 bf81...9282 8a&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Service Hash&#039;&#039;&#039; || 32 || SHA3-256 || Hash of &amp;lt;code&amp;gt;sec.oping.tut.o7s&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REQ Payload (No Encryption)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x4b-0x5a || &amp;lt;code&amp;gt;8f a6ab 6ea7 ef89 68e1 ed1e 2ede 0919 fa&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || Unique flow allocation identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x5b-0x62 || &amp;lt;code&amp;gt;18 86a8 e490 0de6 61&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x63-0x64 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x0000 || No certificate in client request&lt;br /&gt;
|-&lt;br /&gt;
| 0x65-0x66 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;No ephemeral key (no encryption)&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x67-0x68 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x69-0x6a || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0000 || No signature&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* No encryption enabled: ephemeral key absent (Eph_len = 0x0000)&lt;br /&gt;
* Client requests authentication only&lt;br /&gt;
* Server will respond with certificate + signature but no ephemeral key&lt;br /&gt;
* Packet is minimal compared to Packet 11 (Test 3) which includes 91-byte ephemeral key&lt;br /&gt;
&lt;br /&gt;
==== Packet 16: FLOW_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server accepts the authenticated (but not encrypted) flow allocation request. FLOW_REPLY contains the server&#039;s X.509 certificate and ECDSA signature for client authentication, but &#039;&#039;&#039;no ephemeral key&#039;&#039;&#039; since encryption is not being used.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data (abbreviated):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:03.416675 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 751:&lt;br /&gt;
        0x0000:  0000 02dd 0041 0040 0000 0000 0000 0000  .......A.@......&lt;br /&gt;
        0x0010:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0001 008f a6ab 6ea7  ................&lt;br /&gt;
        0x0030:  ef89 68e1 ed1e 2ede 0919 fa18 86a8 e490  .h..............&lt;br /&gt;
        0x0040:  3754 a702 2f30 8202 2b30 8201 b2a0 0302  7T../0..+0......&lt;br /&gt;
        0x0050:  0102 0202 1000 300a 0608 2a86 48ce 3d04  ......0...*.H.=.&lt;br /&gt;
        (... certificate and signature bytes ...)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0000 || Destination Endpoint ID (management channel)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;02dd&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 733 bytes || Total payload length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet Flow Allocator - Management Protocol&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x05 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;SEID&#039;&#039;&#039; || 2 || 0x0041 || Source Endpoint ID (allocated server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06-0x07 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;DEID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x23 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Various)&#039;&#039;&#039; || 28 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x24-0x27 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Response&#039;&#039;&#039; || 4 || 0x00000000 || Response code: 0 (success)&lt;br /&gt;
|-&lt;br /&gt;
| 0x28 || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (In-Order)&#039;&#039;&#039; || 1 || 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 0x29 || &amp;lt;code&amp;gt;01&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Code&#039;&#039;&#039; || 1 || 0x01 || Message Type: FLOW_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x2a || &amp;lt;code&amp;gt;00&amp;lt;/code&amp;gt; || &#039;&#039;&#039;QoS (Availability)&#039;&#039;&#039; || 1 || 0x00 || &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ouroboros Allocation Protocol (OAP) - FLOW_REPLY Payload with Certificate and Signature (No Ephemeral Key)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x2b-0x3a || &amp;lt;code&amp;gt;8f a6ab 6ea7 ef89 68e1 ed1e 2ede 0919&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 16 || 128-bit identifier || &#039;&#039;&#039;Same ID as client&#039;s FLOW_REQ&#039;&#039;&#039; (Packet 15 echoed back)&lt;br /&gt;
|-&lt;br /&gt;
| 0x3b-0x42 || &amp;lt;code&amp;gt;fa18 86a8 e490 3754&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp&#039;&#039;&#039; || 8 || Network order || Server&#039;s timestamp&lt;br /&gt;
|-&lt;br /&gt;
| 0x43-0x44 || &amp;lt;code&amp;gt;a702&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Crt_len&#039;&#039;&#039; || 2 || 0x02a7 (679 decimal) || &#039;&#039;&#039;Certificate length: 679 bytes&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x45-0x270 || &amp;lt;code&amp;gt;2f30 8202 2b30 8201 b2a0 0302 ... (DER certificate) ...&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Certificate&#039;&#039;&#039; || 679 || DER-encoded X.509 || Server&#039;s certificate signed by intermediate CA&lt;br /&gt;
|-&lt;br /&gt;
| 0x271-0x272 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Eph_len&#039;&#039;&#039; || 2 || 0x0000 || &#039;&#039;&#039;No ephemeral key&#039;&#039;&#039; (no encryption)&lt;br /&gt;
|-&lt;br /&gt;
| 0x273-0x274 || &amp;lt;code&amp;gt;0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Data_len&#039;&#039;&#039; || 2 || 0x0000 || No application data&lt;br /&gt;
|-&lt;br /&gt;
| 0x275-0x276 || &amp;lt;code&amp;gt;0067&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Sig_len&#039;&#039;&#039; || 2 || 0x0067 (103 decimal) || &#039;&#039;&#039;ECDSA signature length: 103 bytes&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 0x277-0x2dd || &amp;lt;code&amp;gt;3065 0230 75dc 5717 ... 83&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Signature&#039;&#039;&#039; || 103 || ECDSA signature (DER encoded) || Server&#039;s ECDSA signature proving certificate ownership&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* SEID is 0x0041 - New server-side endpoint ID allocated for this authenticated flow&lt;br /&gt;
* DEID is 0x0040 - Client&#039;s flow ID from Packet 15 FLOW_REQ, establishing the bidirectional flow&lt;br /&gt;
* FLOW_REPLY Message Type - Code field is 0x01, &#039;&#039;&#039;no service hash&#039;&#039;&#039; (already identified in FLOW_REQ)&lt;br /&gt;
* Certificate Field - &amp;lt;code&amp;gt;crt_len = 0x02a7 (679)&amp;lt;/code&amp;gt; carrying server&#039;s X.509 certificate signed by intermediate CA&lt;br /&gt;
* Separate Signature Field - &amp;lt;code&amp;gt;sig_len = 0x0067 (103)&amp;lt;/code&amp;gt; with ECDSA signature over entire OAP header&lt;br /&gt;
* No Ephemeral Key - &amp;lt;code&amp;gt;eph_len = 0x0000&amp;lt;/code&amp;gt; since encryption is &#039;&#039;&#039;not&#039;&#039;&#039; being used in Test 4&lt;br /&gt;
* Same OAP ID - Server echoes back the exact ID from client&#039;s FLOW_REQ (included in signature, binding response to this specific client request)&lt;br /&gt;
* Complete OAP Structure - Full OAP header with all standard fields, just without ephemeral key data&lt;br /&gt;
* Plaintext Data Exchange - After this FLOW_REPLY, all subsequent application data will be transmitted in plaintext (but authenticated via certificate + signature verification)&lt;br /&gt;
&lt;br /&gt;
==== Packet 17: ECHO_REQUEST ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Client sends plaintext ECHO_REQUEST data through the authenticated (but unencrypted) flow. The oping application&#039;s ping request is transmitted directly without encryption, relying on the earlier certificate+signature authentication for security.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:04.419664 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0041 0040 0000 0000 0000 0000 8177 0000  .A.@............&lt;br /&gt;
        0x0010:  0000 0000 aa16 1c16 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0041&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0041 || Destination Endpoint ID (server flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Total application data length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Application Data - Oping Echo Request (Plaintext)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000000 || Message type: ECHO_REQUEST&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Sequence number / message identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;8177 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Seconds component from CLOCK_REALTIME&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;aa16 1c16 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Nanoseconds component (0-999999999)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || Application data || Probe payload (zero-filled for 64 bytes total)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID Pair: 0x0041 → Server Flow - Data is directed to the server&#039;s endpoint ID allocated in Packet 16 FLOW_REPLY&lt;br /&gt;
* Plaintext Transmission - No encryption layer; oping payload is sent as-is (compare to Packet 13 which had encryption)&lt;br /&gt;
* Authenticated Flow - Although plaintext, this data travels on the authenticated flow established in Packet 16 (certificate + signature verified)&lt;br /&gt;
* Type = ECHO_REQUEST - 0x00000000 indicates client-to-server ping request&lt;br /&gt;
* ID = 0 - Sequence number for matching request/reply pairs&lt;br /&gt;
* Test 4 Characteristic - Demonstrates authenticated communication &#039;&#039;&#039;without&#039;&#039;&#039; encryption; application data is readable but cryptographically bound to the authenticated flow&lt;br /&gt;
* Contrast to Test 3 - Packet 13 (Test 3 encrypted ECHO_REQUEST) was 114 bytes with ciphertext; this packet is 82 bytes of plaintext&lt;br /&gt;
* Total Payload - 64 bytes total (4 + 4 + 8 + 8 + 40 bytes payload)&lt;br /&gt;
&lt;br /&gt;
==== Packet 18: ECHO_REPLY ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Summary:&#039;&#039;&#039; Server responds with plaintext ECHO_REPLY data, echoing back the client&#039;s request. This confirms successful bidirectional communication over the authenticated (but unencrypted) flow.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Raw Data:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
17:40:04.420088 00:00:00:00:00:00 &amp;gt; 00:00:00:00:00:00, ethertype Unknown (0xa000), length 82:&lt;br /&gt;
        0x0000:  0040 0040 0000 0001 0000 0000 8177 0000  .@.@............&lt;br /&gt;
        0x0010:  0000 0000 aa16 1c16 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0020:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0030:  0000 0000 0000 0000 0000 0000 0000 0000  ................&lt;br /&gt;
        0x0040:  0000 0000                                ....&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ethernet DIX Frame with EID Header&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x00-0x01 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;EID&#039;&#039;&#039; || 2 || 0x0040 || Destination Endpoint ID (client flow)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02-0x03 || &amp;lt;code&amp;gt;0040&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Length&#039;&#039;&#039; || 2 || 64 bytes || Total application data length&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Application Data - Oping Echo Reply (Plaintext)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Hex !! Field !! Size !! Value !! Notes&lt;br /&gt;
|-&lt;br /&gt;
| 0x04-0x07 || &amp;lt;code&amp;gt;0000 0001&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Type&#039;&#039;&#039; || 4 || 0x00000001 || Message type: ECHO_REPLY&lt;br /&gt;
|-&lt;br /&gt;
| 0x08-0x0b || &amp;lt;code&amp;gt;0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;ID&#039;&#039;&#039; || 4 || 0x00000000 || Echo of request sequence number&lt;br /&gt;
|-&lt;br /&gt;
| 0x0c-0x13 || &amp;lt;code&amp;gt;8177 0000 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (seconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Echoed request timestamp (seconds)&lt;br /&gt;
|-&lt;br /&gt;
| 0x14-0x1b || &amp;lt;code&amp;gt;aa16 1c16 0000 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Timestamp (nanoseconds)&#039;&#039;&#039; || 8 || Network-byte-order 64-bit || Echoed request timestamp (nanoseconds)&lt;br /&gt;
|-&lt;br /&gt;
| 0x1c-0x43 || &amp;lt;code&amp;gt;0000 ... 0000&amp;lt;/code&amp;gt; || &#039;&#039;&#039;Payload&#039;&#039;&#039; || 40 || Application data || Echoed probe payload (zero-filled for 64 bytes total)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key observations:&#039;&#039;&#039;&lt;br /&gt;
* EID Pair: 0x0040 → Client Flow - Server responds to client&#039;s endpoint ID from Packet 15 FLOW_REQ&lt;br /&gt;
* Type = ECHO_REPLY - 0x00000001 indicates server-to-client response&lt;br /&gt;
* ID = 0 - Echoes the request sequence number, matching this response to the request&lt;br /&gt;
* Timestamps Echo Request - Both timestamp fields are copied from Packet 17 unchanged (8177 0000 0000 0000 and aa16 1c16 0000 0000)&lt;br /&gt;
* Plaintext Reply - No encryption; server&#039;s response payload is readable (compare to Packet 14 which had encryption)&lt;br /&gt;
* Authenticated Channel - Although plaintext, this reply is part of the authenticated flow; client can verify integrity through earlier certificate+signature&lt;br /&gt;
* Test 4 Completion - Demonstrates &#039;&#039;&#039;full bidirectional plaintext communication&#039;&#039;&#039; over an authenticated (but unencrypted) flow&lt;br /&gt;
* Contrast to Test 3 - Packet 14 (Test 3 encrypted ECHO_REPLY) was 114 bytes with ciphertext; this packet is 82 bytes of plaintext&lt;br /&gt;
* Total Payload - 64 bytes total (4 + 4 + 8 + 8 + 40 bytes payload)&lt;/div&gt;</summary>
		<author><name>Dimitri</name></author>
	</entry>
</feed>