docs(protocol): airlock server black-box findings
Transfer independently black-box-verified server behavior from a source-assisted research phase conducted outside this repository. Only a sanitizer-candidate report of black-box findings and the packet captures it referenced were admitted; no upstream source, tests, history, harnesses, build trees, result files or source-assisted analysis crossed the boundary. Provenance and the boundary discipline are recorded in docs/clean-room/CLEANROOM_NOTES.md. New upstream robustness defect (Section 22.5). An open selecting timestamps while omitting the Clock parameter is accepted with a valid token and no Clock echoed, and the first echo on that session is fatal to the server process, taking unrelated sessions on the same process with it. An explicit Clock=0 is instead rejected at open, so parameter presence is externally meaningful. Recorded as an observed defect with a must-not-crash recommendation; the clean server's resolution is deliberately left to the implementation work. Also established or strengthened: - the idle grace and maximum-duration grace are fixed additive margins across the settings swept, and the maximum-duration clock starts at the first echo that is actually served; - a rate-limited request is the only tested drop class that keeps a session alive, and in one documented configuration a conforming client can observe the difference; - a post-deadline packet of several tested classes consumes an expired session's one remaining reply, including packets that are themselves dropped; - the reply-length cap is the bind interface's MTU on the interfaces where it was reachable, and a length the host cannot transmit ends the process; - DSCP marking follows the session, with unmarked open replies and no cross-session leakage across interleaved sessions; - repeated tags are last-valid-wins, with invalid-duplicate handling scoped to the orders actually tested, and unknown tag values are consumed as varints. Three captures were admitted, each rewritten to the container convention already used here and verified byte-identical in packet data and timestamps: server-clock-absent (3 packets), server-expiry-consumption (14) and server-dscp-interleaved (32). Two further captures were reviewed and rejected as redundant or unsupporting; their textual results were kept instead. Host-specific figures are labelled as such throughout, upstream defects are not promoted to protocol requirements, observations are separated from inferences and from recommendations, and no implementation policy is chosen here. The client specification gains matching refinements where the two compatibility baselines would otherwise state a lifecycle or marking rule differently. Documentation and evidence only; no production code changed.
This commit is contained in:
8 files changed
+1715
-102
No files matched your search
@@ -693,15 +693,18 @@ The upstream count is six.)*
|
||||
A post-open request is bound to a session by its token **and** by an exact match
|
||||
on the source address, source port, and address family.
|
||||
|
||||
The identity trials below used live, non-expired sessions. Finding S-21 records
|
||||
the separately observed expired-session release case.
|
||||
|
||||
| Case | Result |
|
||||
|------|--------|
|
||||
| Correct token, original endpoint | Served |
|
||||
| Correct token, different source port | **Dropped, no reply; session intact** |
|
||||
| Correct token, different source port | **Dropped, no reply; the live session remained usable from its original endpoint** |
|
||||
| Correct token, other address family's listener on the same port | Dropped |
|
||||
| Unknown token | Dropped |
|
||||
| Zero token | Dropped |
|
||||
| Token of a closed session | Dropped |
|
||||
| **Close** bearing a valid token from a foreign source port | **Dropped; session not closed** |
|
||||
| **Close** bearing a valid token from a foreign source port | **Dropped; does not close an otherwise live session** |
|
||||
|
||||
Endpoint binding protects the close path as well as the echo path. A capture is
|
||||
in `captures/server-session-identity.pcapng`.
|
||||
@@ -751,8 +754,9 @@ produced 5 replies with counts 1–5, and three later requests continued 6–8.
|
||||
## Finding S-4: Server-initiated close exists and is triggered by the duration limit
|
||||
|
||||
Resolves client-spec open question 19.3. A server with a maximum test duration
|
||||
answers the first echo request past its hard deadline with a complete echo reply
|
||||
carrying flags `0x06` (Reply|Close), and removes the session at that moment.
|
||||
answers the first otherwise-serviceable echo request past its hard deadline with
|
||||
a complete echo reply carrying flags `0x06` (Reply|Close), and removes the
|
||||
session at that moment.
|
||||
|
||||
| Configured maximum duration | Close flag observed at |
|
||||
|---|---|
|
||||
@@ -760,7 +764,8 @@ carrying flags `0x06` (Reply|Close), and removes the session at that moment.
|
||||
| 2 s | 4.06 s / 4.10 s after the first echo |
|
||||
|
||||
The deadline is the maximum duration plus a 2-second grace, measured from the
|
||||
session's first echo request — not from open. Capture:
|
||||
session's first **served** echo — not from open. A deadline-crossing echo that is
|
||||
itself rate-limited is dropped; the next served echo carries Close. Capture:
|
||||
`captures/server-close.pcapng`, frame 36.
|
||||
|
||||
Driving an upstream 0.9.1 client against a server that sets the flag mid-test:
|
||||
@@ -871,8 +876,9 @@ consecutive failing probes and served the next valid request normally.
|
||||
## Finding S-11: Idle expiry has a 5-second grace, and never-used sessions do not expire
|
||||
|
||||
With a 2-second idle timeout, the boundary was measured between 6.9 s and 7.1 s,
|
||||
i.e. timeout + 5 s. The first request past the deadline is still answered and
|
||||
the session is released while handling it; only the next request is dropped.
|
||||
i.e. timeout + 5 s. A post-deadline echo that would otherwise be served is
|
||||
answered and releases the session while handling it; only the next request is
|
||||
dropped.
|
||||
|
||||
A session that has never carried an echo request does not expire at all: opened
|
||||
and left idle for 9 seconds under a 2-second timeout, it was served normally.
|
||||
@@ -1181,3 +1187,521 @@ disregards it loses nothing normative.
|
||||
**Classification:** SERVER SPEC CORRECTION (server spec Sections 9.2, 11.3.1,
|
||||
21.1, 21.2, 23.4, 24) **and** CLIENT SPEC CORRECTION (client spec Sections 8.5,
|
||||
19.14).
|
||||
|
||||
---
|
||||
|
||||
# Part IV — Server Deep-Dive Black-Box Pass (2026-08-12)
|
||||
|
||||
A further black-box pass over the server, aimed at behavior that only a
|
||||
non-conforming peer can reach: parameter presence, the lifetime boundaries, the
|
||||
drop classes, reply sizing, and IP marking. It resolves several statements that
|
||||
earlier parts left thin, and it records one previously undocumented upstream
|
||||
robustness defect that is reachable from two unauthenticated datagrams.
|
||||
|
||||
## Part IV Method and Environment
|
||||
|
||||
**Builds exercised**, all unmodified, all run as ordinary server processes with
|
||||
documented command-line options only:
|
||||
|
||||
| Build | Role in this part |
|
||||
|-------|-------------------|
|
||||
| **0.9.1 release** | the baseline; every unqualified statement refers to it |
|
||||
| **0.9.0 release** | reproduction check |
|
||||
| **development tree, six upstream commits past the 0.9.1 tag** | reproduction check; post-release work, not part of any release |
|
||||
|
||||
**Platform:** macOS Darwin 25.5.0 arm64. Loopback except where an Ethernet or
|
||||
tunnel interface is named.
|
||||
|
||||
**Instrument.** An independent raw-UDP harness sharing no code with any IRTT
|
||||
implementation. It performs the open negotiation itself, emits exact request
|
||||
bytes, and records exact reply bytes and monotonic arrival times. For IP marking
|
||||
it reads the received TOS / traffic-class byte from the kernel.
|
||||
|
||||
**Server provenance.** Each server under test is an unmodified build of the
|
||||
revision named in the table above — the 0.9.0 release, the 0.9.1 release, or the
|
||||
development tree six upstream commits past the 0.9.1 tag. No source change,
|
||||
patch, instrumentation or test hook was applied to any of them, and each was run
|
||||
as an ordinary server process configured only through documented command-line
|
||||
options.
|
||||
|
||||
**What counts as evidence here.** Request bytes, reply bytes, the presence or
|
||||
absence of a reply, the reply's IP header marking, arrival times, and whether a
|
||||
server process was still serving afterwards. Timing boundaries were established
|
||||
by sweeping probe offsets, not by reading any value out of anything. Where a
|
||||
result depends on the tested host — interface MTUs, the maximum outbound datagram
|
||||
size, socket handling of out-of-range values — it is labelled host-specific below
|
||||
and in the specification.
|
||||
|
||||
**Repeatability.** Timing boundaries were swept at 50 ms spacing with three
|
||||
repetitions per configured value. Behavioral results are single-shot unless a
|
||||
repetition count is given.
|
||||
|
||||
## Finding S-19: An open whose restricted StampAt remains non-none without a Clock is accepted, then fatal
|
||||
|
||||
The most serious result of this pass.
|
||||
|
||||
When timestamp restriction leaves the restricted (effective) `StampAt` non-none,
|
||||
an open request carrying `StampAt` = 1, 2, 3 or 4 and **no Clock tag at all** is
|
||||
accepted: the server returns an ordinary session-creating reply with a valid
|
||||
non-zero token, and the reply carries no Clock parameter — none is synthesized.
|
||||
The **first** echo request on that session receives no reply and the server
|
||||
process terminates. On a wildcard bind, the listener for the other address family
|
||||
dies with it, so unrelated sessions stop being served at the same moment.
|
||||
|
||||
```
|
||||
C -> S 14a75b01 05 06 06 06 04 00
|
||||
open; ReceivedStats=3, StampAt=3 (both), Length=0, no Clock tag
|
||||
S -> C 14a75b03 7ca44127b2cd7eab 01 02 05 06 06 06
|
||||
open reply; non-zero token, ProtocolVersion 1, ReceivedStats 3,
|
||||
StampAt 3 — no Clock parameter
|
||||
C -> S 14a75b00 7ca44127b2cd7eab 00000000
|
||||
first echo, sequence 0 -> no reply; server no longer serving
|
||||
```
|
||||
|
||||
| Input | Outcome |
|
||||
|-------|---------|
|
||||
| Requested StampAt 1 / 2 / 3 / 4, Clock tag absent; restricted StampAt remains non-none | open accepted, first echo fatal |
|
||||
| Requested StampAt 0, Clock tag absent | open accepted, serves normally, no timestamps |
|
||||
| StampAt 3, **explicit `Clock = 0`** | **open dropped** — no reply, no session |
|
||||
| Requested StampAt 1 / 2 / 3 / 4, Clock tag absent; no-timestamp policy restricts StampAt to none | open accepted, serves normally, no timestamps |
|
||||
| Requested StampAt 1 / 2 / 3 / 4, Clock tag absent; single-timestamp policy leaves restricted StampAt non-none | open accepted, first echo still fatal |
|
||||
|
||||
Reproduced for all four non-none StampAt values on **0.9.0, 0.9.1 and the
|
||||
development build**. Capture: `captures/server-clock-absent.pcapng` (3 packets).
|
||||
|
||||
The absent-versus-explicit-zero contrast is the load-bearing part: the two inputs
|
||||
differ by one encoded tag and produce opposite outcomes, so parameter *presence*
|
||||
is externally meaningful in this upstream behavior. An implementation that
|
||||
chooses to reproduce those distinct outcomes needs request validation that can
|
||||
distinguish the two forms; this observation does not prescribe a clean server's
|
||||
representation or policy for adversarial inputs.
|
||||
|
||||
Nothing here describes why the server stops. That is not observable from the
|
||||
wire, it is not stated, and no conclusion in this report depends on it.
|
||||
|
||||
**Classification:** SERVER-ONLY NEW INFORMATION — upstream robustness defect
|
||||
(server spec Sections 7.2, 22.5, 24.3; vectors Section 4.7).
|
||||
|
||||
## Finding S-20: A rate-limited request is the only tested drop class that keeps a session alive
|
||||
|
||||
For each drop class: open a session, send one echo, inject the packet under test,
|
||||
send another echo, and read the second reply's count and window. Separate timing
|
||||
trials established whether the injected packet moved the expiry boundary.
|
||||
|
||||
Every class tested advanced neither the received count nor the received window.
|
||||
They divide on the second question:
|
||||
|
||||
| Injected packet | Extends the observed idle lifetime |
|
||||
|-----------------|------------------------------------|
|
||||
| Bad magic, invalid flag bits, Reply/Open flag on a data packet | no |
|
||||
| Unknown token, zero token | no |
|
||||
| Valid token from a foreign source endpoint | no (but see S-21) |
|
||||
| Truncated echo, oversized echo | no |
|
||||
| Bad, missing, zeroed or truncated MAC | no |
|
||||
| **Request arriving with no rate allowance left** | **yes** |
|
||||
|
||||
The rate-limit result was separated from its alternatives three ways rather than
|
||||
asserted from one trial: a session fed *only* too-fast requests outlived its idle
|
||||
deadline; a session fed the same cadence of oversized requests expired on
|
||||
schedule; and a silent session expired on schedule. A two-probe method was used
|
||||
at the boundary so that "still alive" is distinguished from "expired but not yet
|
||||
touched" — the distinction S-21 turns on. Reproduced on all three builds.
|
||||
|
||||
This is a statement about how long the session kept being served. What a server
|
||||
records when it declines a request is not observable and is not claimed.
|
||||
|
||||
**Classification:** SERVER-ONLY NEW INFORMATION; strengthens the previously
|
||||
unelaborated statement in server spec Section 18.2 (now Sections 9.3, 18.2;
|
||||
vectors Section 8.2).
|
||||
|
||||
## Finding S-21: The first packet to release an expired session determines whether a final reply is emitted
|
||||
|
||||
Part II established that a post-deadline echo that would otherwise be served is
|
||||
answered and releases the session while handling it. This pass establishes that
|
||||
a packet which would normally be dropped can instead release the session without
|
||||
receiving a reply, so no later request receives a final reply.
|
||||
|
||||
Both trials are in one capture, `captures/server-expiry-consumption.pcapng`
|
||||
(14 packets), with a 3-second idle timeout — an 8-second effective boundary:
|
||||
|
||||
| Trial | Sequence | Client's first post-deadline echo |
|
||||
|-------|----------|-----------------------------------|
|
||||
| Control | nothing between the deadline and the client's request | **answered** (count 2); the next one dropped |
|
||||
| Test | an echo bearing the correct token from a **foreign source port** arrived 0.3 s earlier, just past the same deadline | **no reply**, and none for the one after it |
|
||||
|
||||
The foreign-endpoint packet was dropped, as such a packet is at any time, and it
|
||||
released the session without receiving a reply. Whether it advanced the session's
|
||||
received count before the release is **not** observable here: no reply was ever
|
||||
emitted for that session again, so no statistic carries the answer. Finding S-1's
|
||||
result that a foreign-endpoint packet is not counted was measured on a **live**
|
||||
session, where a later reply exposed the count, and it does not transfer to this
|
||||
expired-session path. The same
|
||||
release-without-a-final-reply outcome was produced by a truncated echo, an
|
||||
oversized echo, a close from a foreign endpoint, and a burst of unrelated
|
||||
ordinary opens.
|
||||
|
||||
Two scope limits are stated deliberately. These are the classes that were tested,
|
||||
and the effect is not claimed for every possible packet. And nothing here
|
||||
describes the order in which a server performs any internal step: what was
|
||||
observed is that the session was gone when the legitimate request arrived, and
|
||||
that in the control, with no intervening packet, it was not.
|
||||
|
||||
Separately, a burst of unrelated **ordinary opens** released an expired session
|
||||
with no packet bearing its token involved at all, while a burst of **no-test**
|
||||
opens did not — consistent with a no-test open creating no session.
|
||||
|
||||
**Classification:** SERVER-ONLY NEW INFORMATION (server spec Sections 16, 18.2;
|
||||
vectors Section 8.3).
|
||||
|
||||
## Finding S-22: Both lifetime margins are fixed additive constants
|
||||
|
||||
Part II measured the idle grace at one timeout and the maximum-duration grace at
|
||||
two maxima. Sweeping both across several configured values shows each margin is a
|
||||
fixed addition, not a proportion, and not an artifact of when the session began.
|
||||
|
||||
Idle session-release sweep, with gaps measured from the last accepted echo. Each
|
||||
probe used a first otherwise-serviceable echo after the gap and an immediate
|
||||
follow-up; the boundary is inferred from whether that follow-up was dropped.
|
||||
|
||||
| Configured idle timeout | Longest gap with follow-up served | Shortest gap with follow-up dropped |
|
||||
|-------------------------|----------------------------------|------------------------------------|
|
||||
| 500 ms | 5.45 s | 5.50 s |
|
||||
| 2 s | 6.95 s | 7.00 s |
|
||||
| 4 s | 8.95 s | 9.00 s |
|
||||
|
||||
At and beyond the observed release boundary, the first otherwise-serviceable
|
||||
post-gap echo may still receive a final lazy-release reply; the immediate
|
||||
follow-up is then dropped. These values are not first-request rejection
|
||||
thresholds.
|
||||
|
||||
Maximum duration, offsets measured from the first **served** echo:
|
||||
|
||||
| Configured maximum duration | Last offset answered normally | First offset carrying Close |
|
||||
|-----------------------------|-------------------------------|-----------------------------|
|
||||
| 500 ms | 2.45 s | 2.50 s |
|
||||
| 1 s | 2.95 s | 3.00 s |
|
||||
| 3 s | 4.95 s | 5.00 s |
|
||||
|
||||
Across the settings swept — idle timeouts of 500 ms, 2 s and 4 s, and maximum
|
||||
durations of 500 ms, 1 s and 3 s — both margins held at five seconds and two
|
||||
seconds respectively, to within the 50 ms probe spacing, and reproduced on all
|
||||
three builds. The idle boundary did not shift with when the session was opened.
|
||||
|
||||
Each margin rests on three measured points. That is enough to rule out a margin
|
||||
proportional to the configured value over the range tested, and it is what the
|
||||
"additive" description means here. It does not establish the margin for every
|
||||
configurable value: a threshold, a cap, or different behavior at much larger or
|
||||
much smaller settings would not have shown up in these sweeps and remains
|
||||
untested.
|
||||
|
||||
These are measured properties of the tested builds, stated with their tolerance.
|
||||
They are upstream policy, not protocol constants, and no requirement in either
|
||||
specification depends on either figure.
|
||||
|
||||
**Classification:** SERVER-ONLY NEW INFORMATION; strengthens Findings S-4 and
|
||||
S-11 (server spec Sections 15.2, 18.2, 19; vectors Sections 3.2, 8.1).
|
||||
|
||||
## Finding S-23: Maximum duration starts at the first served echo and closes on the first later served echo
|
||||
|
||||
The boundary sweep establishes two distinct points. The **origin** is the first
|
||||
echo request that is actually **served**. The **trigger** is the first echo after
|
||||
the resulting deadline that would otherwise be served.
|
||||
|
||||
| Case | Result |
|
||||
|------|--------|
|
||||
| Session opened 5 s before its first echo | deadline unchanged — the open does not start the clock |
|
||||
| First request oversized, dropped by the maximum-length policy | clock not started; a later served echo starts it |
|
||||
| First request from a foreign source endpoint, dropped | clock not started; a later served echo starts it |
|
||||
| Echo that crosses the deadline | full normal reply with Close set, and **counted** in the statistics it reports |
|
||||
| Deadline-crossing echo that is itself rate-limited | dropped; the **next served** echo carries the Close flag |
|
||||
| Client's next echo, and its close, after the flag | both unanswered — the token is released |
|
||||
|
||||
Two drop classes were tested in the first-request position — oversized and
|
||||
foreign-endpoint — and neither started the clock. That is the extent of the
|
||||
result: the remaining drop classes were not exercised in that position, and no
|
||||
general "any dropped request" rule is claimed. The rate-limit row is a different
|
||||
question, about a request crossing an already-running deadline rather than one
|
||||
that would start it.
|
||||
|
||||
The close-flagged reply itself is in `captures/server-close.pcapng`, frame 36,
|
||||
from Part II; the boundary sweep is a timing measurement with no capture of its
|
||||
own.
|
||||
|
||||
**Classification:** SERVER-ONLY NEW INFORMATION; refines Finding S-4 (server spec
|
||||
Section 15.2; vectors Section 3.2).
|
||||
|
||||
## Finding S-24: Repeated tags — last valid wins; invalid cases are limited
|
||||
|
||||
A tag may appear more than once in one open payload. Among individually valid
|
||||
occurrences the **last** takes effect; this was confirmed for ProtocolVersion,
|
||||
Duration, Interval, Length, ReceivedStats, StampAt, Clock, DSCP and ServerFill.
|
||||
|
||||
| Payload | Result |
|
||||
|---------|--------|
|
||||
| Duration 1 s, then 2 s | Duration 2 s |
|
||||
| Duration 2 s, then 1 s | Duration 1 s |
|
||||
| Clock 1, then 3 | Clock 3 |
|
||||
| Clock 3, then 4 (out of range) | **no reply** |
|
||||
| Duration 1 s, then 0 (invalid) | **no reply** |
|
||||
| Duration 0 (invalid), then 1 s | **no reply** |
|
||||
| ServerFill `rand`, then a 33-character string | **no reply** |
|
||||
|
||||
**Invalid repeated values were tested more narrowly.** An invalid Duration zero
|
||||
occurrence was dropped in both tested positions: valid then invalid, and invalid
|
||||
then valid. The Clock and ServerFill rows each tested an invalid second occurrence
|
||||
and were also dropped. The opposite ordering was not tested for Clock or
|
||||
ServerFill, and invalid duplicate occurrences were not tested for
|
||||
ProtocolVersion, Interval, Length, ReceivedStats, StampAt or DSCP. These results
|
||||
therefore do not establish a parser-wide, position-independent invalid-duplicate
|
||||
rule for every parameter.
|
||||
|
||||
No conforming client emits a repeated tag; the result is recorded because a
|
||||
decoder that stops at the first occurrence, or validates only the occurrence it
|
||||
keeps, can diverge on the measured adversarial inputs.
|
||||
|
||||
Unknown tags were re-tested at 0, 10, 11, 100, 127, 128, 1000 and 2³². Each was
|
||||
ignored with the rest of the payload still parsed, and an open carrying only
|
||||
unknown tags created an ordinary default session. The value of an unknown tag is
|
||||
consumed as a varint; no length-prefixed or string-shaped value form was accepted
|
||||
for one.
|
||||
|
||||
Finally, a rejected open did not poison the next open: after each malformed
|
||||
payload — incomplete trailing varint, tag with no value, length-prefixed string
|
||||
running past the buffer, varint overflow — a well-formed open from the same
|
||||
endpoint immediately afterwards was served normally. This establishes no
|
||||
persistent externally visible effect on the following request; it does not
|
||||
establish whether hidden accounting or temporary state exists.
|
||||
|
||||
**Classification:** SERVER-ONLY NEW INFORMATION (server spec Section 7.2; vectors
|
||||
Section 4.6).
|
||||
|
||||
## Finding S-25: Reply sizing — negative lengths, the MTU cap, and the fatal ceiling
|
||||
|
||||
Three separate results that are easy to conflate.
|
||||
|
||||
**A negative Length is accepted and produces a minimum-size reply.** It is
|
||||
returned in the open reply as sent. Echo replies for such a session are the size
|
||||
of the field block the negotiated parameters imply — the ordinary rule of server
|
||||
spec Section 9.2 applied to a negotiated length below that block. There is no
|
||||
negative-size concept on the wire.
|
||||
|
||||
**On the two interfaces where the cap was reachable, the emitted reply is capped
|
||||
at the bind interface's MTU.** Re-measured across three interfaces of one host:
|
||||
|
||||
| Bind interface (MTU) | negotiate 1500 | 1501 | 2000 | 8000 |
|
||||
|----------------------|----------------|------|------|------|
|
||||
| loopback (16384) | 1500 | 1501 | 2000 | 8000 |
|
||||
| ethernet (1500) | 1500 | **1500** | **1500** | **1500** |
|
||||
| tunnel (1280) | **1280** | **1280** | **1280** | **1280** |
|
||||
|
||||
On the 1500-byte and 1280-byte interfaces the cap is byte-exact — a negotiated
|
||||
1281 produces 1280 — and the open reply still returns the requested value
|
||||
unclamped, which is the Part II Finding S-14 result confirmed on one further
|
||||
interface.
|
||||
|
||||
The loopback row is weaker evidence and is **not** an observation of a
|
||||
16384-byte cap. It shows only that lengths up to 8000 were emitted unclamped. The
|
||||
loopback cap cannot be approached on this host, because a reply beyond roughly
|
||||
9300 bytes ends the server process before any such length is reached — see the
|
||||
next result. **Host-specific:** these are this host's interface MTUs.
|
||||
|
||||
**A length the host cannot transmit terminates the process.** On the same host,
|
||||
whose maximum outbound datagram size is 9216 bytes, replies up to 9216 bytes were
|
||||
sent normally and negotiated lengths producing replies from roughly 9300 bytes
|
||||
upward failed on the first echo and ended the server. With a wildcard bind, a
|
||||
fatal send on the IPv4 listener took the healthy IPv6 listener down with it —
|
||||
sessions on a different address family, unrelated to the offending one, stopped
|
||||
being served at the same instant. **Host-specific:** 9216 is this host's outbound
|
||||
datagram limit, not a protocol figure.
|
||||
|
||||
On the tested host, this failure was reachable in configurations whose effective
|
||||
reply cap exceeded the host's outbound datagram ceiling, including the tested
|
||||
loopback and wildcard cases. The tested 1500- and 1280-byte interface-bound
|
||||
listeners could not reach it because their reply caps were lower. Jumbo interfaces
|
||||
and other platforms were not tested.
|
||||
|
||||
**Classification:** SERVER-ONLY NEW INFORMATION; strengthens Findings S-14 and
|
||||
S-15 (server spec Sections 7.3, 9.2, 22.1, 23.5).
|
||||
|
||||
## Finding S-26: Over-MTU requests show an interface-MTU effective length boundary
|
||||
|
||||
Bound to an interface with a 1500-byte MTU, a 3000-byte request was dropped with
|
||||
a configured maximum of 1499 and accepted with a maximum of 1500. On a 1280-byte
|
||||
tunnel the same decision knee was at 1280. The observed maximum-length decision
|
||||
therefore behaved as though its comparison length was capped at the tested
|
||||
interface boundary. Separately, the policy was strict at the ordinary tested
|
||||
sizes: with a maximum of 1000, a 1000-byte request was answered and a 1001-byte
|
||||
request was dropped.
|
||||
|
||||
> **Black-box inference.** Receive-path truncation before the maximum-length
|
||||
> decision is one explanation consistent with the 1499/1500 and 1279/1280
|
||||
> boundaries, but the experiments cannot distinguish it from another mechanism
|
||||
> that yields the same effective comparison length.
|
||||
|
||||
**Host-specific and environment-specific.** This is an observation on the
|
||||
tested host and interfaces, not a wire rule. It does not prescribe a clean
|
||||
server's receive strategy or maximum-length policy. Negotiated Length is not by
|
||||
itself a valid receive-buffer upper bound, because mandatory protocol structure
|
||||
can require a larger datagram.
|
||||
|
||||
**No capture accompanies this finding.** The capture taken during the experiment
|
||||
was recorded on loopback with no maximum-length policy configured, and there all
|
||||
three oversized requests were answered normally at the negotiated length; it
|
||||
demonstrates neither the effective boundary nor a policy drop, and the behavior it does
|
||||
show is already covered by server spec Section 5.5 and vectors Section 5.4. It
|
||||
was reviewed and not admitted. The textual result above is the evidence.
|
||||
|
||||
**Classification:** SERVER-ONLY NEW INFORMATION, environment-specific (server spec
|
||||
Section 9.4).
|
||||
|
||||
## Finding S-27: DSCP marking is per session, with unmarked opens and marked closes
|
||||
|
||||
Four sessions were opened on one listener negotiating 46, 8, 0 and 184, then
|
||||
driven in three interleaved rounds. Every reply's IP header was read from the
|
||||
kernel. Capture: `captures/server-dscp-interleaved.pcapng` (32 packets).
|
||||
|
||||
| Session | Requested | Open reply TOS | Echo reply TOS, rounds 1–3 |
|
||||
|---------|-----------|----------------|-----------------------------|
|
||||
| 1 | 46 | **0x00** | 0x2e, 0x2e, 0x2e |
|
||||
| 2 | 8 | **0x00** | 0x08, 0x08, 0x08 |
|
||||
| 3 | not requested | **0x00** | 0x00, 0x00, 0x00 |
|
||||
| 4 | 184 | **0x00** | 0xb8, 0xb8, 0xb8 |
|
||||
|
||||
For each of these sessions, whose negotiated values are in `0..=255`, every echo
|
||||
reply carried its own session's value verbatim; no reply carried another session's
|
||||
value in any round, and every open reply was unmarked. Negative and out-of-range
|
||||
values are addressed separately below.
|
||||
|
||||
**Server-initiated close replies: measured, but not shown by any admitted
|
||||
capture.** The DSCP experiment recorded that a close-flagged echo reply carries
|
||||
the same in-range session mark as an ordinary one. Neither admitted capture
|
||||
demonstrates it, and a reader checking them will find nothing that does: the
|
||||
interleaved capture above contains no close-flagged reply at all, and
|
||||
`captures/server-close.pcapng` — which does contain one, at frame 36 — is TOS 0
|
||||
throughout, because that session negotiated no DSCP. The result therefore rests
|
||||
on the measurement, not on capture evidence, and the earlier wording that
|
||||
explained it as "an ordinary marked echo reply that also sets the Close flag" has
|
||||
been dropped: that phrasing argued from the flag rather than reporting what was
|
||||
observed. A capture of a close-flagged reply on a session with a nonzero
|
||||
negotiated DSCP would settle it directly and was not taken.
|
||||
|
||||
**On a listener that has already sent marked replies, an open reply is still
|
||||
unmarked — evidenced separately.** This capture does not show that ordering: its
|
||||
four open exchanges are frames 1–8 and its first marked echo reply is frame 10,
|
||||
so no open reply in it follows a marked one. The ordering is instead established
|
||||
by `captures/dscp-values.pcapng` from Part I, where one listener serves two
|
||||
sessions in sequence: frame 4 is a marked echo reply (TOS `0x08`) and frame 7 is
|
||||
the next session's open reply, unmarked, on the same port. The two pieces of
|
||||
evidence are cited separately rather than merged.
|
||||
|
||||
Observed negative and out-of-range DSCP handling is host-specific, not protocol
|
||||
behavior: a requested −1 appeared as TOS 255 on IPv4 and as traffic class 0 on
|
||||
IPv6. A value of 256 or above was negotiated and echoed back, but no echo reply
|
||||
was observed for that session while the server continued serving its other
|
||||
sessions. The black-box result does not identify whether the absence arose while
|
||||
applying the marking, during transmission, or at another stage.
|
||||
|
||||
**Classification:** SERVER-ONLY NEW INFORMATION; extends Findings B and C of
|
||||
Part I (server spec Section 20; vectors Sections 10.1–10.3).
|
||||
|
||||
## Finding S-28: The default fill phase is continuous across the tested listeners
|
||||
|
||||
Finding S-16 recorded that the default fill pattern's phase is not reset per
|
||||
packet or per session and carries over between sessions on one listener. It also
|
||||
carries over between **listeners**: in the tested configuration — one server
|
||||
bound to both an IPv4 and an IPv6 listener — successive echo replies drawn from a
|
||||
session on each continued the same repeating pattern without resetting.
|
||||
|
||||
That is the scope of the result: phase continuity across the sessions tested and
|
||||
across those two listeners. It is not established that a single stream is shared
|
||||
by every session of a process in every configuration, and nothing here describes
|
||||
how the bytes are produced.
|
||||
|
||||
**No recommendation is drawn from it.** The default fill is a fixed, public
|
||||
pattern, so phase continuity across sessions discloses nothing about another
|
||||
peer's traffic — unlike the no-fill mode of Finding S-16's second paragraph and
|
||||
server spec Section 13.4, which is a real disclosure risk and carries its own
|
||||
requirement there. A compatible server may reset the phase, continue it, or
|
||||
derive the payload some other way. Those choices can produce different payload
|
||||
bytes, making the phase policy observable in a packet capture, but they are
|
||||
interoperability-equivalent because a conforming client cannot depend on payload
|
||||
phase. No fill-state arrangement is prescribed.
|
||||
|
||||
**Classification:** SERVER-ONLY NEW INFORMATION; extends Finding S-16 (server spec
|
||||
Section 13.3).
|
||||
|
||||
## Part IV Version Comparison
|
||||
|
||||
Every result in this part was checked against the 0.9.0 release and the tested
|
||||
development build wherever a reproduction check was meaningful. All of the
|
||||
following were **identical** on all three builds:
|
||||
|
||||
| Behavior | 0.9.0 | 0.9.1 | development build |
|
||||
|----------|-------|-------|-------------------|
|
||||
| Open whose restricted StampAt remains non-none and has no Clock is accepted, first echo fatal (S-19) | yes | yes | yes |
|
||||
| Idle boundary at timeout + 5 s (S-22) | yes | yes | yes |
|
||||
| Maximum-duration close at maximum + 2 s (S-23) | yes | yes | yes |
|
||||
| A rate-limited request keeps the session alive (S-20) | yes | yes | yes |
|
||||
| Interface-MTU effective length boundary (S-26) | yes | yes | yes |
|
||||
| Burst allowance behavior | yes | yes | yes |
|
||||
| Fill phase continuous across the tested listeners (S-28) | yes | yes | yes |
|
||||
|
||||
No new version difference was found. The single known wire-visible difference
|
||||
between these builds remains the single-clock midpoint layout of Findings S-7 and
|
||||
S-18, which this pass re-observed unchanged and which is not restated here.
|
||||
|
||||
## Part IV Captures Created
|
||||
|
||||
| File | Packets | Contents |
|
||||
|------|---------|----------|
|
||||
| `captures/server-clock-absent.pcapng` | 3 | An open selecting timestamps with no Clock tag is answered with a token and no Clock parameter; the first echo is the last packet in the exchange |
|
||||
| `captures/server-expiry-consumption.pcapng` | 14 | Control versus foreign-endpoint-first, in one file: whether the packet that releases an expired session emits a final reply |
|
||||
| `captures/server-dscp-interleaved.pcapng` | 32 | Four interleaved sessions with distinct DSCP values over three rounds; no cross-session leakage; unmarked open replies |
|
||||
|
||||
Each was container-rewritten to the convention already used for the captures of
|
||||
Parts I–III — packet records and the rewriting tool's version string only, with
|
||||
no capture comment, host, user, interface or filter metadata and no filesystem
|
||||
path. Packet bytes and per-frame timestamps were verified identical before and
|
||||
after, and the packet counts above are unchanged from the originals.
|
||||
|
||||
Two further captures from this pass were **not** admitted. One was materially
|
||||
redundant with `captures/server-close.pcapng`, which already shows the
|
||||
close-flagged reply, the trigger being counted, and the following packet
|
||||
unanswered; a single-maximum capture cannot evidence the additive-margin result,
|
||||
which is the new part. The other is described under Finding S-26. In both cases
|
||||
the textual result is the evidence, which is the better trade.
|
||||
|
||||
## Part IV Client Spec Change Applied
|
||||
|
||||
| Section | Change | Class |
|
||||
|---------|--------|-------|
|
||||
| 19.3 | Maximum-duration deadline origin is the first echo request that is **served**; the close trigger is the first later echo that would otherwise be served. The two tested first-request drops do not start the clock, and a deadline-crossing rate-limited echo does not carry Close. The 2-second margin is additive across configured maxima | **CLARIFICATION** |
|
||||
|
||||
The client specification and the server specification are both compatibility
|
||||
baselines, so the lifecycle rule is stated the same way in each. The earlier
|
||||
client-side measurements are unaffected — the first request was served in those
|
||||
runs — and no client behavior depends on the distinction, since a conforming
|
||||
client stops sending at the negotiated duration and never reaches the deadline.
|
||||
|
||||
## Part IV Summary
|
||||
|
||||
- New upstream robustness defect documented: **S-19**, an accepted open that is
|
||||
fatal on its first echo, reachable in the default configuration from two
|
||||
unauthenticated datagrams, present in all three builds.
|
||||
- Statements strengthened from thin or single-point evidence: the idle grace and
|
||||
maximum-duration grace (**S-22**), the maximum-duration origin (**S-23**), the
|
||||
rate-limit/idle interaction (**S-20**), the reply-length cap (**S-25**), and the
|
||||
fill-phase scope (**S-28**).
|
||||
- New parser-edge results: repeated tags and unknown-tag value encoding
|
||||
(**S-24**), and the presence-versus-explicit-zero distinction that **S-19**
|
||||
turns on.
|
||||
- No result contradicted any statement in Parts I–III. **S-23** narrows the
|
||||
origin recorded in Finding S-4 from "the first echo request" to "the first
|
||||
echo request that is served"; the earlier measurement is unaffected, because
|
||||
the first request was served in that experiment.
|
||||
- Host-specific figures — interface MTUs, the 9216-byte outbound datagram limit,
|
||||
the effective-length decision knees, and the socket handling of out-of-range DSCP — are
|
||||
labelled as such wherever they appear and are not protocol constants.
|
||||
- Nothing in this part rests on anything but observed request bytes, reply bytes,
|
||||
reply presence, IP header markings, timings, and whether the server was still
|
||||
serving afterwards.
|
||||
Reference in new issue
Block a user