Summary

  • RFC 2360 was a Best Current Practice for standards writers, not a protocol. It aimed to improve the chance that independent implementations would interoperate and explicitly refused to promise that clear prose could guarantee the result.
  • Its sharpest lesson concerned the negative path: a specification had to say which malformed inputs could be partly accepted, which must be rejected, what an error did to connection or adjacency state, and how implementations behaved when a critical resource or scale limit was exceeded.
  • Packet diagrams, requirement words, formal grammar, summary tables and state machines were complementary evidence. None was self-executing, and running code still had to show whether separate implementations made the same decisions.

The disagreement began after the parser succeeded

The familiar picture of a protocol is a row of boxes. Version occupies these bits; length occupies those; a flag changes the meaning of what follows. If two teams read the same diagram and emit the same byte order, they appear to share a contract.

RFC 2360 looked beyond that normal path. Gregor D. Scott edited the guide, published in June 1998 as BCP 22. It gathered lessons from successful and unsuccessful IETF specification work and addressed the people who had to turn a document into implementations, tests, products and operational expectations. Its claim was deliberately limited: better writing could raise the probability of interoperability. It could not guarantee it.

That limit made the guide more useful, not less. A protocol does not act when a page is published. Independent programs interpret the page, receive inputs the author did not expect and encounter resource conditions the diagram does not show. The hidden contract is revealed when those programs must decide what to do next.

Suppose a routing update declares three tuples but carries bytes that look like a fourth. One receiver may trust the count and ignore the tail. Another may recover an extra route. A third may reject the whole update as suspect. All three may parse the specified fields correctly. They still create different network state.

RFC 2360 used that kind of case to make a precise demand: the specification should draw the line. It should say when useful content may be retained, when an error procedure takes over, and what happens to existing state. “Discard” is not enough if one implementation treats the message as never received while another tears down an adjacency.

Error handling was shared behaviour

The guide called out behaviour that deviated from or exceeded the specification because implementers frequently differed there. The available choices were not morally ranked in the abstract. Partial recovery can preserve service; strict rejection can stop ambiguous data from contaminating shared state. Either can be correct for a particular protocol.

The interoperability requirement was common disposition. If an invalid frame is ignored, does a timer continue? If an impossible transition arrives, is the current state preserved? If a malformed routing message contains some intelligible fields, can any of them affect forwarding? If a critical queue, identifier space or memory budget is exhausted, does the peer receive an error, does new work stop, or does existing state collapse?

These are control decisions. They determine who absorbs uncertainty and how far a fault travels. A permissive receiver may keep one session alive while importing an interpretation its peer never intended. A strict receiver may protect state while converting a minor extension mismatch into a full outage. The document must identify the intended boundary before vendors can accuse one another of being insufficiently tolerant or insufficiently disciplined.

That was why RFC 2360 treated the liberal-acceptance, conservative-sending maxim with caution. Robustness could not mean that every receiver improvised its own generosity. Send and receive rules had to be stated separately, especially where flawed input could change routing or other shared network state.

The state machine held what the packet picture omitted

Packet diagrams answer where. They do not necessarily answer when, under whose memory, or with which consequence. RFC 2360 therefore recommended state-machine descriptions: named states, variables, events, transitions and ordered actions.

The distinction matters even when the wire image is identical. A message accepted in an initial state can be illegal after closure. The same timeout may trigger a retry in one state and a reset in another. Two actions performed in opposite order can expose different intermediate states to a peer. A dash in a state table may mean an illegal transition, but the surrounding text still has to say how the implementation responds.

The guide did not turn diagrams into a higher authority than prose. It called state models adjuncts and made the detailed specification controlling in a conflict. That choice left a burden on the editor: multiple descriptions of one requirement had to identify which one bound the implementation.

Summary tables served another purpose. In long specifications, they could list what was mandatory, optional or prohibited and point the implementer to the controlling section. They reduced omissions. They did not prove conformance.

A capitalised word could not supply the missing mechanism

RFC 2119 had already given the IETF a vocabulary of requirement levels. RFC 2360 told editors not to bend those definitions. Capitalisation could expose an accidental or unreasonable requirement, and a tester could use a clear normative sentence to derive a check.

But “MUST reject” still needs an object, condition and consequence. Reject which unit? Before or after authentication? With an error response or in silence? Does rejection consume a sequence number, preserve an adjacency, roll back partial state or close the association? The strength of the keyword cannot repair an absent state model.

Formal notation had the same boundary. A grammar can define which strings belong to a language. It cannot by itself explain the purpose of a message, the authority of its sender, the effect of a valid command or the recovery from a resource failure. RFC 2360 even warned that machine-readable notation could be difficult for humans and should not replace a clear protocol description.

Options were decisions deferred, not decisions erased

Optional features looked like a way to preserve consensus. The guide asked what happened when the resulting combinations met. An option needed a real requirement, an explicit default and an account of what use and non-use meant. Mutually exclusive choices had to be identified. Omitting an option was not supposed to create an interoperability failure for an otherwise conforming implementation.

This is a deeper governance point. Calling a feature optional moves a decision; it does not remove it. The receiver must discover whether the peer supports the feature. Both sides need a fallback whose semantics are known. Operators need to understand the loss of function. Security-sensitive choices need defaults that do not silently trade safety for convenience.

Later work such as RFC 6709 examined extension risk in greater depth. It is useful as a comparison, not evidence of a direct causal line. RFC 2360’s historical contribution was already visible: compatibility costs had to be written where implementers could see them, not hidden inside the word “optional.”

Rationale was part of the maintenance surface

The guide also asked for change logs, version differences and decision history. This was not an attempt to make an old consensus permanent. It was a way to preserve why a tempting alternative had been rejected and what implementation experience had changed.

Without that record, a future maintainer sees only the chosen mechanism. A shortcut may look equivalent because the failure it once caused is no longer in the room. A requirement may look ceremonial because its protected boundary is unnamed. Recording the reason gives later implementers evidence with which to challenge or retain the choice.

The same discipline applied to security, management, scaling, network stability and internationalisation. These were not decorative closing sections. They forced a specification to state its environment, limits and external authorities: who assigns identifiers, what topology destabilises the mechanism, which resource bounds are assumed, what an operator can observe, and which language or character assumptions can break a global deployment.

A specification was still only one layer of reality

Lu Heng’s distinction between symbolic authority, running code and observed outcome clarifies RFC 2360’s restraint. The IETF can publish a common behavioural claim. An implementation can demonstrate one interpretation. An interoperability test can show that selected versions agreed under selected cases. Operations can reveal what happened under live load and hostile input.

None of those receipts should impersonate the next. A complete state table does not prove that code follows it. Passing normal-path tests does not prove consistent exhaustion behaviour. Two implementations agreeing with each other does not prove that either follows the document. A deployment surviving malformed traffic does not prove every permitted option combination is safe.

The minimum useful common layer is therefore behavioural, not merely grammatical. It must be small enough that independent teams can implement and test it, yet explicit enough to settle the decisions that affect shared state. RFC 2360 belongs in Internet history because it located ambiguity where networks actually fracture: not only in the bits sent, but in the action taken when the bits, timing or resources cease to be ideal.

Sources and limits

The publication status and writing guidance are in the RFC Editor record and full text of RFC 2360. Its standards-process context comes from RFC 2026, requirement vocabulary from RFC 2119, architectural context from RFC 1958, and then-current author instructions from RFC 2223. The examples it cited include the host requirements in RFC 1122 and OSPF specification in RFC 2328. RFC 6709 supplies a later comparison on extension design. The separation of specification, implementation and observed result follows Lu Heng on Running-Code Primacy, Minimum Initial Specification and Reality Layers. These sources do not establish a present conformance rate, a named incident behind every recommendation, or a guarantee that later RFCs followed the guide.