Summary

  • RFC 1977 could send an eligible packet in ordinary PPP form when compression would enlarge it, but both endpoints still had to process that packet into the same continuing BSD Compress dictionary.
  • Because a native packet carried no LZW CLEAR code, adaptive clearing depended on identical counters and ratio decisions at both ends; Version 1 and a 9–16-bit ceiling were only the starting parameters for that shared history.
  • A 16-bit sequence number could expose loss or reordering before a later compressed packet was decoded, while Reset-Request/Reset-Ack abandoned the old history and began a new one. Detection and recovery did not authenticate or certify the traffic already seen.

The most important packet did not look compressed

The easiest way to misunderstand RFC 1977 is to sort a packet trace into two piles: BSD-compressed traffic and everything else. That division follows the visible PPP Protocol field, but not the state machine. An eligible network-layer datagram could cross the link in its native encapsulation and still change the compression dictionary at both ends.

This was deliberate. BSD Unix compress used LZW and could discover, only after processing input, that its output was no smaller. RFC 1977 preserved the useful refusal: when expansion was significant, the sender had to transmit the original PPP packet. Even an increase of fewer than three bytes normally favoured native form. The purpose was practical. If incompressible traffic had to carry a larger compression wrapper, upper layers would need to behave as though the link had a smaller MTU.

But the sender did not learn that compression was unprofitable by looking at the packet. It learned by attempting the compression. The attempted encoding added phrases to the dictionary, moved code-width thresholds and updated the counters used to judge when the dictionary had become ineffective. Throwing away the larger wire representation could not also throw away those changes. If it did, the next compressed datagram would be encoded from a history the receiver had never built.

RFC 1977 therefore instructed the receiver to treat a native packet in the eligible protocol range as a packet that would have expanded. It locally compressed the received bytes, not to produce output, but to reproduce the sender's state transition. The appendix names the operation plainly: pf_bsd_incomp updates the BSD Compress dictionary for incompressible data by pretending to compress it.

The wire packet was native. The protocol event was not.

A file compressor became a two-ended history

Classic compress operated on a file. RFC 1977 adapted its code to an effectively infinite sequence of PPP datagrams. That change removed a natural boundary. The codebook was no longer private working memory that disappeared when one file ended; it became shared history continuing across packet boundaries.

The history included more than dictionary entries. Both sides had to agree on which original protocol values were eligible, apply Protocol-Field-Compression to the inner protocol before compression regardless of whether that PPP option had been negotiated, advance the code width at the same threshold and count the same input and virtual output bytes. The reference code begins with nine-bit codes, adds entries until the negotiated ceiling is reached and keeps a compression ratio for later clearing decisions.

This is why the configuration option was small but consequential. CCP option type 21 identified BSD Compress. Its third octet carried Version 001 and the largest code width, from 9 through 16 bits; 12 was described as common. The appendix implementation supported 9 through 15, an implementation limit that should not be mistaken for the specification's negotiated range. A receiver could not safely choose a larger table merely because it had more memory. Dictionary fullness and clearing time were shared facts.

Negotiation established those common starting rules after PPP reached the Network-Layer Protocol phase and CCP reached Opened. It did not prove that daemon and kernel, driver and user process, or two separately written implementations would apply every later transition in the same order.

CLEAR disappeared from the wire

The classic LZW vocabulary reserved code 256 for CLEAR. In a compressed packet, the sender could put that code at the end and tell the decoder to discard its table. Native encapsulation created a harder case. A packet carrying an ordinary PPP protocol number had no LZW code stream in which a CLEAR could reliably appear.

RFC 1977 answered by making the clear decision reproducible rather than always visible. Its appendix checked the compression ratio at 10,000-byte intervals once the dictionary was full. If the new ratio worsened, or fell below one-to-one, the state was cleared. The sender's trial compressor and the receiver's local simulation counted the same eligible bytes and the same notional encoded size, so they were expected to cross the threshold together.

This was a deterministic bargain. It saved an extra header and preserved the native MTU, but it made implementation identity depend on more than negotiated labels. Counter ageing, packet eligibility, code allocation, code-width change and clear timing all became interoperability inputs. A native packet omitted the compressed representation precisely while retaining the representation's state effects.

The first packets after a clear sharpened the ambiguity. A nearly empty dictionary usually offered little gain, so those packets were likely to appear natively—just like packets sent before compression was enabled. An observer could not infer active history from encapsulation alone. The relevant evidence was the control state and the ordered stream of every eligible input.

Sixteen bits can stop damage, not certify history

A BSD-compressed packet carried a 16-bit sequence number. It began at zero after a clear, advanced once for every eligible packet, including native ones, and wrapped after 65535. RFC 1977 advised checking the value before decoding. If the next compressed packet announced a count different from the receiver's expectation, the receiver could refuse to feed codes into a known-wrong dictionary.

That is valuable, but narrow. Native packets did not acquire a BSD Compress sequence field. If one such packet vanished, the loss might become visible only when a later compressed datagram exposed the gap. The number did not say which input was absent, whether the cause was loss or reordering, what bytes should have been learned, or whether earlier accepted packets were authentic. It was a discontinuity detector, not a transcript signature.

RFC 1977 relied on the HDLC FCS and ordinary discard mechanisms for corrupted frames. Its Security Considerations section simply says that security issues are not discussed. A wrapping counter and a link checksum therefore cannot be promoted into end-to-end integrity, peer authentication or authorization.

Reset was an abandonment protocol

When the receiver first saw an unexpected sequence, it should send a CCP Reset-Request and discard compressed packets until Reset-Ack arrived. Both sides then reset the sequence to zero and clear the dictionary. The RFC described the result as abandoning transmission of one “file” and beginning another.

The word matters. Reset did not reconstruct missing state or certify the old state. It established a new shared beginning. The transmitter had to clear and answer every Reset-Request because it could not know whether an earlier Reset-Ack reached the receiver. The receiver likewise had to clear on each matching acknowledgement because the transmitter would already have cleared.

On a busy link, one error could be followed by several unusable compressed packets before the acknowledgement returned. The receiver needed to retransmit enough requests to get one through, but not more frequently than the round-trip time: redundant requests meant redundant clears. One second appeared only as an example, not a universal timer. A new CCP Configure-Request could force renegotiation instead, at higher cost.

There was also a software boundary hidden behind the packets. If a control daemon handled Configure-Ack or Reset-Ack while kernel code handled data, the clear had to be synchronized with the control message that reopened compression. The receiver had to clear before processing the next packet. A perfectly formed acknowledgement could still be operationally late relative to the data path.

What the record proves

RFC 1977 is a compact example of a minimum shared mechanism. It defined eligibility, Version 1, code-width bounds, sequence arithmetic, ratio checkpoints and reset behaviour tightly enough for independent implementations to attempt the same history. The IANA registry still records CCP option 21 as BSD Compress, with 0x00FD and 0x00FB assigned to compressed datagrams in their respective placement.

Read through Lu Heng's emphasis on running-code evidence, the lesson is not that hidden state is illegitimate. It is that declared configuration and observable execution are different records. A peer may acknowledge Version 1 and 12-bit codes; an operator still needs packet order, native-packet coverage, counter transitions and clear receipts to show that both codebooks moved together.

This article does not retell RFC 1962's general CCP negotiation, RFC 1963's serial-data framing or RFC 1990's multilink reordering. It makes no claim about current deployment, a named product or measured savings. Its historical claim is narrower: in RFC 1977, “uncompressed” described the chosen wire form, not the absence of compression state. The protocol worked only if both ends understood that distinction at exactly the same packets.

Sources