a positive test proves Ze does what the requirement demands and a negative one proves it refuses what the requirement forbids
RFC 7606 - Revised Error Handling for BGP UPDATE Messages
Every requirement this repository extracted from RFC 7606, the tests bound to it, and what a reader has verified about them. This summary is enrolled and gated by ./le rfc check.
Overview
Positive
what Ze has
the requirement admits no counter-case, so one polarity plus a recorded reason is the whole proof available for it
one direction is tested, the other is neither tested nor excused, and nothing states which
scoped tests exist; the remaining obligation is unmet or unproven, with zero whole-requirement credit
a red was observed once under a recorded procedure, and the unit, the claim and the producer it rested on still hash to what was recorded. The break is not re-run. A test pair is not a proof until one has been observed
Neutral
measures that are neither good news nor bad
MUST-level requirements the gate HOLDS. A population, not a result: the shares beside it are what says how Ze stands
an obligation that does not bind Ze. A {not-applicable} annotation says it never bound; a {feature-declined} annotation says its condition is an optional feature Ze does not offer, and quotes the RFC sentence that makes it optional. Scope, not coverage: it stays in the denominator every share on this page is taken over
a {not-applicable} annotation says the obligation does not bind Ze, so no test is owed for it. It stays in the denominator every share here is taken over
a {lower-layer} annotation says a layer under Ze performs the behavior, on state Ze installs into that layer, and names the producer that installs it. The obligation binds Ze and is met; Ze proves none of it, because its own boundary carries no value the behavior reads
a {feature-declined} annotation says the obligation is conditional on a feature the RFC makes optional and Ze does not offer, and it quotes the sentence that makes it optional. The condition is false, so nothing is owed and nothing is missing. It stays in the denominator every share here is taken over
Negative
what Ze owes
no test carries the requirement id, whether or not a gap states why
1 weak, wrong or unimplemented, 6 no longer current. Each is named below under its own requirement id
The 8 shares marked as a part above are the whole of the 53 gated MUSTs: they add to 100%. Proven by a recorded break is a share of TAGGED UNITS, a different population, so it is not one of them.
How to read the colors
A color names what the measure MEANS, not how well Ze scores on it. Green is a good outcome at any value, red is a bad one, and neither a population nor a scope count is an outcome, so both take no color. The number under the label is what says how far Ze has got.
| Card | Tone here | Why that color |
|---|---|---|
| Gated MUSTs | neutral | no color: a population is a scale, and a larger one is neither good news nor bad. It is the accounting total |
| Out of scope | neutral | no color: an obligation that never bound Ze is neither an achievement nor a failure, and counting it either way would be a claim |
| Tested both ways | ok | green at every value: a test pair is the outcome this gate exists to produce, and the share under the label is what says how far Ze has got |
| One polarity plus reason | ok | green at every value: where no counter-case exists, one polarity IS the complete answer, and a recorded reason is what the gate demands beside it |
| One polarity, unexcused | ok | green at zero, RED above it: half a proof with no reason for the other half |
| Partial proof; remaining gap | ok | green at zero, RED above it: a tested clause cannot prove the whole requirement |
| No test at all | bad | green at zero, RED above it: a binding obligation nothing exercises is a claim with nothing behind it, whether or not a reason is stated |
| Not applicable | neutral | no color: an obligation that never bound Ze is neither an achievement nor a failure, and counting it either way would be a claim |
| Met below Ze | neutral | no color: an obligation met below Ze is neither a test Ze wrote nor work Ze owes, and the two green shares above are what says how much Ze proves itself |
| Optional feature declined | neutral | no color: an obligation whose condition Ze never meets is neither an achievement nor a failure. The absent FEATURE is disclosed on the RFC's own status row, as an implementation gap a later scope decision can revisit |
| Proven by a recorded break | ok | green at every value: an observed break is the outcome the discrimination gate exists to produce. The denominator is TAGGED UNITS, not obligations, so this share is not one of the parts above |
| Audit verdicts | bad | RED on the first weak, wrong or unimplemented verdict, amber while a verdict is no longer current or a gated MUST is unjudged, green when every one is judged sound and current |
At a glance
| Field | Value |
|---|---|
| Public status | Partial |
| Enrolment | Enrolled |
| Requirements | 57 |
| Gated MUST-level | 53 |
| Not applicable, so out of scope | 1 |
| Declared gaps | 1 |
| Declared gaps a test demonstrates | 1 |
| Gated with no test | 0 |
| Nightly-only evidence | 0 |
| Test tags | 220 |
| Tagged units | 218 |
| Recorded audit verdicts | 53 |
| Discrimination records | 57 |
| Summary | rfc/short/rfc7606.md |
| Requirement shard | rfc/requirements/rfc7606.md |
| RFC text | rfc/full/rfc7606.txt |
Enrolment
Enrolled: Revised UPDATE error handling: the error-handling contract for every UPDATE Ze parses. Pilot for this system -- ~60 near-1:1 tests already exist and the one real divergence (5.1 MP ordering, docs/architecture/wire/mp-nlri-ordering.md) proves the {gap} path is honest rather than an escape hatch.
What the public ledger says
Status: Partial
What the ledger says is covered
Structural UPDATE validation, treat-as-withdraw (routes are synthesized into withdrawals and removed from the Adj-RIB-In), attribute-discard decisions, session-reset with NOTIFICATION UPDATE Message Error, per-attribute validation for 15 attribute codes, §3.c attribute-flags validation for every attribute whose own specification fixes an Optional or Transitive value (internal/core/bgp/attribute/flags_spec.go, which also carries the two attributes whose RFC mandates other handling: the MP pair and AIGP), inner MP_REACH/MP_UNREACH NLRI overrun and RFC 4760 flag-consistency validation (§5.3, session reset via §3.j) for IPv4/IPv6 unicast and multicast (ADD-PATH aware, RFC 7911 path-ids skipped when negotiated), tests bound per requirement in rfc/requirements/rfc7606.md.
What the ledger says remains
One MUST-level gap, annotated in rfc/short/rfc7606.md and gated by ./le rfc check: §5.1 first bullet: Ze emits MP_UNREACH first and MP_REACH in type-code order after ORIGIN and AS_PATH, so the MP attribute of an announcement is not the very first path attribute (docs/architecture/wire/mp-nlri-ordering.md). TestRFC7606Section51MPAttributeEncodedFirst demonstrates the gap and fails the day it closes.
Closed 2026-08-01: §5.4. A route whose NLRI type Ze does not implement is now discarded at ingress, in enforceRFC7606 (internal/component/bgp/reactor/session_validation.go), so it reaches neither the RIB nor the forward path. The RIB alone would not have sufficed, because reactorForwardRS relays the received wire without consulting it. The ruling is per family, because §5.4 discards "unless the relevant specification for that address family specifies otherwise" and RFC 9552 §5.2 uses that clause to REQUIRE preserve-and-propagate for BGP-LS. Each NLRI plugin registers its own recognizer (internal/core/bgp/nlri/nlritype), so the three families §5.4 binds each carry their own ruling: EVPN discards route types outside 1..5 (internal/component/bgp/plugins/nlri/evpn/rfc7606.go), MCAST-VPN outside 1..7 (.../nlri/mvpn/rfc7606.go, RFC 6514 §4), and BGP-MUP anything but Architecture Type 1 with Route Type 1..4 (.../nlri/mup/rfc7606.go, draft-ietf-bess-mup-safi §3.1). BGP-LS registers no recognizer and propagates unchanged, and a family nobody has ruled on keeps its previous behavior. Judging a route type needs the section carved into single NLRIs, so MCAST-VPN and BGP-MUP gained nlrisplit splitters, which also made nlrisplit.Supported true for them: ze now installs both as opaque Adj-RIB-In entries the way it already installed EVPN (insertPoolNLRIs, internal/component/bgp/plugins/rib/rib.go). This reverses the divergence disclosed here until 2026-08-01.
Closed 2026-07-20: §7.13, §7.15 and §7.16 gained attribute validators for codes 24, 25 and 128, §7.15's unrecognized-type tolerance is now met by design rather than by omission (the validator reads length only), and §6's debugging facility now logs the NLRI involved and the entire malformed UPDATE.
Also closed 2026-07-20: §5.1's second bullet. Ze already originated only compliant UPDATEs; the relay now splits a received mixed shape as well, in both the zero-copy same-context branch and the re-encode branch, so every UPDATE ze sends carries at most one NLRI-bearing field. A compliant single-field UPDATE keeps the zero-copy forward: the shape verdict is cached per received message and the received bytes are handed on unchanged. Receive-side tolerance of any position or combination (§5.1 third bullet) is unchanged.
Coverage
| Bucket | Count | What it counts |
|---|---|---|
| Positive and negative tests | 46 | one part of the gated population |
| Annotated (including scoped evidence) | 7 | one part of the gated population |
| One polarity only | 0 | one part of the gated population |
| No test and no annotation | 0 | one part of the gated population |
| Partial proof; remaining gap (subset of annotated; zero whole-requirement credit) | 0 | an overlay: each of these is also counted by the part it falls in |
| Evidence that runs nightly only | 0 | an overlay: each of these is also counted by the part it falls in |
| Derived from other rows | 0 | outside the gated population: each asserts nothing and derives its state from the rows it names, which the parts above already count |
| Gated MUST-level requirements | 53 | every gated MUST falls in exactly one bucket above |
Positive and negative tests (46): RFC7606-2-1, RFC7606-2-2, RFC7606-2-3, RFC7606-3.b-1, RFC7606-3.c-1, RFC7606-3.d-1, RFC7606-3.e-1, RFC7606-3.f-1, RFC7606-3.g-1, RFC7606-3.i-1, RFC7606-3.j-1, RFC7606-4-1, RFC7606-5.1-2, RFC7606-5.2-1, RFC7606-5.4-1, RFC7606-7.1-1, RFC7606-7.2-1, RFC7606-7.3-1, RFC7606-7.4-1, RFC7606-7.5-1, RFC7606-7.5-2, RFC7606-7.6-1, RFC7606-7.7-1, RFC7606-7.8-1, RFC7606-7.9-1, RFC7606-7.9-2, RFC7606-7.10-1, RFC7606-7.10-2, RFC7606-7.11-1, RFC7606-7.13-1, RFC7606-7.14-1, RFC7606-7.15-1, RFC7606-7.16-1, RFC7606-4-3, RFC7606-4-2, RFC7606-3.g-2, RFC7606-6-1, RFC7606-3.a-1, RFC7606-2-5, RFC7606-5.3-1, RFC7606-5.3-2, RFC7606-5.3-3, RFC7606-5.3-4, RFC7606-5.3-5, RFC7606-5.3-6, RFC7606-2-6
Annotated (including scoped evidence) (7): RFC7606-3.h-1, RFC7606-3.h-2, RFC7606-5.1-1, RFC7606-5.1-3, RFC7606-7.14-2, RFC7606-7.15-2, RFC7606-8-1
Requirements
| Requirement | Level | Section | Tests |
|---|---|---|---|
RFC7606-2-1 | o Treat-as-withdraw: In this approach, the UPDATE message containing the path attribute in question MUST be treated as though all contained routes had been withdrawn just as if they had been listed in the WITHDRAWN ROUTES field (or in the MP_UNREACH_NLRI attribute if appropriate) of the UPDATE message, thus causing them to be removed from the Adj-RIB-In according to the procedures of [RFC4271]. (§2) | ||
| MUST | 2 | ||
RFC7606-2-2 | o Attribute discard: In this approach, the malformed attribute MUST be discarded and the UPDATE message continues to be processed. (§2) | ||
| MUST | 2 | ||
RFC7606-2-3 | This approach MUST NOT be used except in the case of an attribute that has no effect on route selection or installation. (§2) | ||
| MUST NOT | 2 | ||
RFC7606-3.b-1 | If the Withdrawn Routes Length or Total Attribute Length is too large (i.e., if Withdrawn Routes Length + Total Attribute Length + 23 exceeds the message Length), then the Error Subcode MUST be set to Malformed Attribute List. (§3.b) | ||
| MUST | 3.b | ||
RFC7606-3.c-1 | If the value of either the Optional or Transitive bits in the Attribute Flags is in conflict with their specified values, then the attribute MUST be treated as malformed and the "treat-as-withdraw" approach used, unless the specification for the attribute mandates different handling for incorrect Attribute Flags. (§3.c) | ||
| MUST | 3.c | ||
RFC7606-3.d-1 | If any of the well-known mandatory attributes are not present in an UPDATE message, then "treat-as-withdraw" MUST be used. (§3.d) | ||
| MUST | 3.d | ||
RFC7606-3.e-1 | "Treat-as-withdraw" MUST be used for the cases that specify a session reset and involve any of the attributes ORIGIN, AS_PATH, NEXT_HOP, MULTI_EXIT_DISC, or LOCAL_PREF. (§3.e) | ||
| MUST | 3.e | ||
RFC7606-3.f-1 | "Attribute discard" MUST be used for any of the cases that specify a session reset and involve ATOMIC_AGGREGATE or AGGREGATOR. (§3.f) | ||
| MUST | 3.f | ||
RFC7606-3.g-1 | If the MP_REACH_NLRI attribute or the MP_UNREACH_NLRI [RFC4760] attribute appears more than once in the UPDATE message, then a NOTIFICATION message MUST be sent with the Error Subcode "Malformed Attribute List". (§3.g) | ||
| MUST | 3.g | ||
RFC7606-3.h-1 | When multiple attribute errors exist in an UPDATE message, if the same approach (as described in Section 2) is specified for the handling of these malformed attributes, then the specified approach MUST be used. (§3.h) | ||
| MUST | 3.h | positive
no testno positive test{single-polarity}: "multiple attribute errors exist" has no conforming instance -- an UPDATE with zero errors is not a case of this rule, it is the absence of the rule |
|
RFC7606-3.h-2 | Otherwise, the approach with the strongest action MUST be used. (§3.h) | ||
| MUST | 3.h | positive
no testno positive test{single-polarity}: as 3.h-1: the premise of the rule is that errors exist, so no positive case can be constructed |
|
RFC7606-3.i-1 | The Withdrawn Routes field MUST be checked for syntactic correctness in the same manner as the NLRI field (§3.i) | ||
| MUST | 3.i | ||
RFC7606-3.j-1 | If this is not possible, the procedures of [RFC4271] and/or [RFC4760] continue to apply, meaning that the "session reset" approach (or the "AFI/SAFI disable" approach) MUST be followed. (§3.j) | ||
| MUST | 3.j | ||
RFC7606-4-1 | In either of these cases, an error condition exists and the "treat- as-withdraw" approach MUST be used (unless some other, more severe error is encountered dictating a stronger approach) (§4) | ||
| MUST | 4 | ||
RFC7606-5.1-1 | o The MP_REACH_NLRI or MP_UNREACH_NLRI attribute (if present) SHALL be encoded as the very first path attribute in an UPDATE message. (§5.1) | ||
| MUST | 5.1 | positive
no testno positive testnegative
no testno negative test{gap}: Ze emits MP_UNREACH first and every other attribute by type code, so MP_REACH follows ORIGIN and AS_PATH and is not the very first path attribute; recorded in docs/architecture/wire/mp-nlri-ordering.md and disclosed publicly in the docs/features/rfc-status.md RFC 7606 row |
|
RFC7606-5.1-2 | o An UPDATE message MUST NOT contain more than one of the following: non-empty Withdrawn Routes field, non-empty Network Layer Reachability Information field, MP_REACH_NLRI attribute, and MP_UNREACH_NLRI attribute. (§5.1) | ||
| MUST NOT | 5.1 | ||
RFC7606-5.1-3 | Since older BGP speakers may not implement these restrictions, an implementation MUST still be prepared to receive these fields in any position or combination. (§5.1) | ||
| MUST | 5.1 | negative
no testno negative test{single-polarity}: the obligation is to accept the four named fields, non-empty Withdrawn Routes, non-empty NLRI, MP_REACH_NLRI and MP_UNREACH_NLRI, in any permitted position or combination. Rejecting a shape solely for the sender restrictions would contradict this receiver obligation, not provide a conforming negative. |
|
RFC7606-5.2-1 | Thus, if an UPDATE message is encountered that does contain path attributes other than MP_UNREACH_NLRI and doesn't encode any reachable NLRI, we cannot be confident that the NLRI have been successfully parsed as Section 3 (j) requires. For this reason, if any path attribute errors are encountered in such an UPDATE message and if any encountered error specifies an error-handling approach other than "attribute discard", then the "session reset" approach MUST be used. (§5.2) | ||
| MUST | 5.2 | ||
RFC7606-5.4-1 | A BGP speaker advertising support for such a typed address family MUST handle routes with unrecognized NLRI types within that address family by discarding them, unless the relevant specification for that address family specifies otherwise. (§5.4) | ||
| MUST | 5.4 | ||
RFC7606-7.1-1 | The attribute is considered malformed if its length is not 1 or if it has an undefined value [RFC4271]. An UPDATE message with a malformed ORIGIN attribute SHALL be handled using the approach of "treat-as-withdraw". (§7.1) | ||
| MUST | 7.1 | ||
RFC7606-7.2-1 | An AS_PATH is considered malformed if an unrecognized segment type is encountered or if it contains a malformed segment. A segment is considered malformed if any of the following are true: o There is an overrun where the Path Segment Length field of the last segment encountered would cause the Attribute Length to be exceeded. o There is an underrun where after the last successfully parsed segment there is only a single octet remaining (that is, there is not enough unconsumed data to provide even an empty segment header). o It has a Path Segment Length field of zero. An UPDATE message with a malformed AS_PATH attribute SHALL be handled using the approach of "treat-as-withdraw". (§7.2) | ||
| MUST | 7.2 | ||
RFC7606-7.3-1 | The attribute is considered malformed if its length is not 4 [RFC4271]. An UPDATE message with a malformed NEXT_HOP attribute SHALL be handled using the approach of "treat-as-withdraw". (§7.3) | ||
| MUST | 7.3 | ||
RFC7606-7.4-1 | The attribute is considered malformed if its length is not 4 [RFC4271]. An UPDATE message with a malformed MULTI_EXIT_DISC attribute SHALL be handled using the approach of "treat-as-withdraw". (§7.4) | ||
| MUST | 7.4 | ||
RFC7606-7.5-1 | o if the LOCAL_PREF attribute is received from an external neighbor, it SHALL be discarded using the approach of "attribute discard"; or (§7.5) | ||
| MUST | 7.5 | ||
RFC7606-7.5-2 | if received from an internal neighbor, it SHALL be considered malformed if its length is not equal to 4. If malformed, the UPDATE message SHALL be handled using the approach of "treat-as- withdraw". (§7.5) | ||
| MUST | 7.5 | ||
RFC7606-7.6-1 | The attribute SHALL be considered malformed if its length is not 0 [RFC4271]. An UPDATE message with a malformed ATOMIC_AGGREGATE attribute SHALL be handled using the approach of "attribute discard". (§7.6) | ||
| MUST | 7.6 | ||
RFC7606-7.7-1 | The AGGREGATOR attribute SHALL be considered malformed if any of the following applies: o Its length is not 6 (when the 4-octet AS number capability is not advertised to or not received from the peer [RFC6793]). o Its length is not 8 (when the 4-octet AS number capability is both advertised to and received from the peer). An UPDATE message with a malformed AGGREGATOR attribute SHALL be handled using the approach of "attribute discard". (§7.7) | ||
| MUST | 7.7 | ||
RFC7606-7.8-1 | The Community attribute SHALL be considered malformed if its length is not a non-zero multiple of 4. o An UPDATE message with a malformed Community attribute SHALL be handled using the approach of "treat-as-withdraw". (§7.8) | ||
| MUST | 7.8 | ||
RFC7606-7.9-1 | o if the ORIGINATOR_ID attribute is received from an external neighbor, it SHALL be discarded using the approach of "attribute discard"; or (§7.9) | ||
| MUST | 7.9 | ||
RFC7606-7.9-2 | if received from an internal neighbor, it SHALL be considered malformed if its length is not equal to 4. If malformed, the UPDATE message SHALL be handled using the approach of "treat-as- withdraw". (§7.9) | ||
| MUST | 7.9 | ||
RFC7606-7.10-1 | if the CLUSTER_LIST attribute is received from an external neighbor, it SHALL be discarded using the approach of "attribute discard" (§7.10) | ||
| MUST | 7.10 | ||
RFC7606-7.10-2 | if received from an internal neighbor, it SHALL be considered malformed if its length is not a non-zero multiple of 4. If malformed, the UPDATE message SHALL be handled using the approach of "treat-as-withdraw". (§7.10) | ||
| MUST | 7.10 | ||
RFC7606-7.11-1 | If the Length of Next Hop Network Address field of the MP_REACH attribute is inconsistent with that which was expected, the attribute is considered malformed. Since the next hop precedes the NLRI field in the attribute, in this case it will not be possible to reliably locate the NLRI; thus, the "session reset" or "AFI/SAFI disable" approach MUST be used. (§7.11) | ||
| MUST | 7.11 | ||
RFC7606-7.13-1 | In the interim, an implementation that determines (for whatever reason) that an UPDATE message contains a malformed Traffic Engineering path attribute MUST handle it using the approach of "treat-as-withdraw". (§7.13) | ||
| MUST | 7.13 | ||
RFC7606-7.14-1 | The Extended Community attribute SHALL be considered malformed if its length is not a non-zero multiple of 8. o An UPDATE message with a malformed Extended Community attribute SHALL be handled using the approach of "treat-as-withdraw". (§7.14) | ||
| MUST | 7.14 | ||
RFC7606-7.14-2 | Note that a BGP speaker MUST NOT treat an unrecognized Extended Community Type or Sub-Type as an error. (§7.14) | ||
| MUST NOT | 7.14 | negative
no testno negative test{single-polarity}: the rule is a prohibition (MUST NOT treat an unrecognized type as an error), not a detection duty -- the only conforming observation is acceptance, and a negative would assert the rejection the RFC forbids |
|
RFC7606-7.15-1 | The IPv6 Address Specific Extended Community attribute SHALL be considered malformed if its length is not a non-zero multiple of 20. o An UPDATE message with a malformed IPv6 Address Specific Extended Community attribute SHALL be handled using the approach of "treat- as-withdraw". (§7.15) | ||
| MUST | 7.15 | ||
RFC7606-7.15-2 | Note that a BGP speaker MUST NOT treat an unrecognized IPv6 Address Specific Extended Community Type or Sub-Type as an error. (§7.15) | ||
| MUST NOT | 7.15 | negative
no testno negative test{single-polarity}: the rule is a prohibition (MUST NOT treat an unrecognized type as an error), not a detection duty -- the only conforming observation is acceptance, and a negative would assert the rejection the RFC forbids. Now met BY DESIGN rather than by omission: validateIPv6ExtCommunityAttr takes the attribute value as `_` and tests length alone, so no Type or Sub-Type can reach a rejection |
|
RFC7606-7.16-1 | An UPDATE message with a malformed ATTR_SET attribute SHALL be handled using the approach of "treat as withdraw". (§7.16) | ||
| MUST | 7.16 | ||
RFC7606-4-3 | the Total Attribute Length MUST be relied upon to enable the beginning of the NLRI field to be located. (§4) | ||
| MUST | 4 | ||
RFC7606-4-2 | For all path attributes other than those specified as having an attribute length that may be zero, it SHALL be considered a syntax error for the attribute to have a length of zero. Of the path attributes considered in this specification, only AS_PATH and ATOMIC_AGGREGATE may validly have an attribute length of zero. (§4) | ||
| MUST | 4 | ||
RFC7606-3.g-2 | If any other attribute (whether recognized or unrecognized) appears more than once in an UPDATE message, then all the occurrences of the attribute other than the first one SHALL be discarded and the UPDATE message will continue to be processed. (§3.g) | ||
| MUST | 3.g | ||
RFC7606-8-1 | A document that specifies a new BGP attribute MUST provide specifics regarding what constitutes an error for that attribute and how that error is to be handled. (§8) | ||
| MUST | 8 | positive
no testno positive testnegative
no testno negative test{not-applicable}: an obligation on the authors of FUTURE attribute specifications, not on an implementation; Ze has no code path that could satisfy or violate it |
|
RFC7606-6-1 | Because of these potential issues, a BGP speaker must provide debugging facilities to permit issues caused by a malformed attribute to be diagnosed. At a minimum, such facilities must include logging an error listing the NLRI involved and containing the entire malformed UPDATE message when such an attribute is detected. (§6) | ||
| MUST | 6 | ||
RFC7606-7.2-2 | A BGP implementation SHOULD also handle routes that violate this check using "treat-as-withdraw" (§7.2) | ||
| SHOULD | 7.2 | positive
no testno positive testnegative
no testno negative test |
|
RFC7606-8-2 | The document SHOULD also provide consideration of what debugging facilities may be required to permit issues caused by a malformed attribute to be diagnosed. (§8) | ||
| SHOULD | 8 | positive
no testno positive testnegative
no testno negative test |
|
RFC7606-7.2-3 | but MAY follow the "session reset" behavior if configured to do so. (§7.2) | ||
| MAY | 7.2 | positive
no testno positive testnegative
no testno negative test |
|
RFC7606-2-4 | AFI/SAFI disable: Section 7 of [RFC4760] allows a BGP speaker that detects an error in a message for a given AFI/SAFI to optionally "ignore all the subsequent routes with that AFI/SAFI received over that session". (§2) | ||
| MAY | 2 | positive
no testno positive testnegative
no testno negative test |
|
RFC7606-3.a-1 | An error detected while processing the UPDATE message for which a session reset is specified MUST be indicated by sending the NOTIFICATION message with the Error Code UPDATE Message Error. (§3.a) | ||
| MUST | 3.a | ||
RFC7606-2-5 | thus causing them to be removed from the Adj-RIB-In according to the procedures of [RFC4271]. (§2) | ||
| MUST | 2 | ||
RFC7606-5.3-1 | The NLRI field or Withdrawn Routes field SHALL be considered "syntactically incorrect" if either of the following are true: o The length of any of the included NLRI is greater than 32. (§5.3) | ||
| MUST | 5.3 | ||
RFC7606-5.3-2 | When parsing NLRI contained in the field, the length of the last NLRI found exceeds the amount of unconsumed data remaining in the field. (§5.3) | ||
| MUST | 5.3 | ||
RFC7606-5.3-3 | Similarly, the MP_REACH_NLRI or MP_UNREACH_NLRI attribute of an update SHALL be considered to be incorrect if any of the following are true: o The length of any of the included NLRI is inconsistent with the given AFI/SAFI (for example, if an IPv4 NLRI has a length greater than 32 or an IPv6 NLRI has a length greater than 128). (§5.3) | ||
| MUST | 5.3 | ||
RFC7606-5.3-4 | When parsing NLRI contained in the attribute, the length of the last NLRI found exceeds the amount of unconsumed data remaining in the attribute. (§5.3) | ||
| MUST | 5.3 | ||
RFC7606-5.3-5 | The attribute flags of the attribute are inconsistent with those specified in [RFC4760]. (§5.3) | ||
| MUST | 5.3 | ||
RFC7606-5.3-6 | The length of the MP_UNREACH_NLRI attribute is less than 3, or the length of the MP_REACH_NLRI attribute is less than 5. (§5.3) | ||
| MUST | 5.3 | ||
RFC7606-2-6 | They are as follows (listed in order, from the one with the "strongest" action to the one with the "weakest" action): (§2, §3.h) | ||
| MUST | 2 | ||
Gaps and untested MUSTs
| Requirement | State | Reason |
|---|---|---|
RFC7606-5.1-1 o The MP_REACH_NLRI or MP_UNREACH_NLRI attribute (if present) SHALL be encoded as the very first path attribute in an UPDATE message. (§5.1) |
{gap}, demonstrated by internal/component/bgp/message/rfc7606_mp_first_test.go::TestRFC7606Section51MPAttributeEncodedFirst | Ze emits MP_UNREACH first and every other attribute by type code, so MP_REACH follows ORIGIN and AS_PATH and is not the very first path attribute; recorded in docs/architecture/wire/mp-nlri-ordering.md and disclosed publicly in the docs/features/rfc-status.md RFC 7606 row |
RFC7606-8-1 A document that specifies a new BGP attribute MUST provide specifics regarding what constitutes an error for that attribute and how that error is to be handled. (§8) |
no test | no test carries this requirement id; annotated {not-applicable}: an obligation on the authors of FUTURE attribute specifications, not on an implementation; Ze has no code path that could satisfy or violate it |
Proof state
A tagged unit reads unproven where no discrimination record exists for it: nothing in this tree has been observed to break it, so the claim its tag makes is unproven.
RFC7606-2-1
o Treat-as-withdraw: In this approach, the UPDATE message containing the path attribute in question MUST be treated as though all contained routes had been withdrawn just as if they had been listed in the WITHDRAWN ROUTES field (or in the MP_UNREACH_NLRI attribute if appropriate) of the UPDATE message, thus causing them to be removed from the Adj-RIB-In according to the procedures of [RFC4271]. (§2)
Audit verdict: enforced (the tests do what the requirement demands), fresh. TestSessionRFC7606TreatAsWithdrawDispatchesWithdrawal (session_test.go) sends a malformed-ORIGIN UPDATE announcing 10.0.0.0/8 and asserts the DISPATCHED body has withdrawn=={0x08,0x0a}, nlri empty, attrLen zero; the message-level TestSynthesizeWithdrawMovesNLRIToWithdrawn and ...ConvertsMPReachToMPUnreach pin the rewrite exactly. Positive TestSessionRFC7606ValidUpdateDispatchesAnnouncement requires a valid UPDATE to stay byte-for-byte an announcement. 2026-07-20 re-audit: coverage STRENGTHENED beyond message synthesis. TestRIBTreatAsWithdrawRemovesInstalledRoute (rib_structured_test.go) now proves the route actually LEAVES the RIB -- Adj-RIB-In Len()==0 and a best-change Withdraw for 10.0.0.0/8 published to the Loc-RIB -- and that a treat-as-withdraw for a never-installed prefix publishes no spurious event. TestRIBTreatAsWithdrawAddPathPreservesPathID proves exactly the re-advertised ADD-PATH path (ID 42) is withdrawn while its sibling (ID 43) for the same prefix survives. Section 2 says the routes MUST be withdrawn; until these, the evidence stopped at 'a withdrawal-shaped message was dispatched'. Assertions are exact and fail on the old suppression bug. 2026-07-22 re-stamp: the verdict went stale only because commit c4038def0 prepended an rfc-test-change-approved header and swapped package qualifiers (bgptypes.RouteAction*->routeaction.*, message.Type*->msgtype.Type*, rib/events->core/bgp/ribevents) in the tagged files; tagged_unit_shas fingerprints the whole enclosing file. Normalising the 254772452..HEAD diff under that rename cancels with ZERO unmatched assertion deletions, and rfc/short/rfc7606.md is byte-identical since the audit, so the judgement above still describes exactly what it judged. 2026-09-24 re-audit: commit 515bbc3b68 changed TestSessionRFC7606TreatAsWithdrawDispatchesWithdrawal and TestSessionRFC7606ValidUpdateDispatchesAnnouncement only in their fixtures (an AS_SEQUENCE carrying the eBGP neighbor AS 65002 so the new first-AS check passes, and a NEXT_HOP of 192.0.2.254) and in the callback signature; every assertion is unchanged, so malformed ORIGIN still has to yield the exact withdrawn-routes body and a valid UPDATE the exact announcement. Enforced. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quote adds two clauses the paraphrase dropped: "(or in the MP_UNREACH_NLRI attribute if appropriate)" is pinned by TestSynthesizeWithdrawConvertsMPReachToMPUnreach, and "thus causing them to be removed from the Adj-RIB-In" by TestRIBTreatAsWithdrawRemovesInstalledRoute (Adj-RIB-In Len()==0). Both units carry this tag. Verdict unchanged. Rejudged 2026-10-16 (independent reader) after the owner-approved multi-label withdrawal change (RFC 8277 Section 2.4, nlrisplit.LabeledWithdrawal): SynthesizeWithdraw gained an addPath bool and SynthesizeWithdrawFamilies/mpReachToUnreach an addPath predicate, which mpReachToUnreach reads only for a family nlrisplit.CarriesCompatibility names (labeled unicast, VPN). Every edit in the tagged units (TestSynthesizeWithdrawMovesNLRIToWithdrawn, TestSynthesizeWithdrawConvertsMPReachToMPUnreach, TestRIBTreatAsWithdrawRemovesInstalledRoute, TestRIBTreatAsWithdrawAddPathPreservesPathID) is the call-site signature change passing false; their inputs are legacy IPv4 NLRI and IPv6 unicast MP_REACH, which never reach the labeled branch, so producer behavior, inputs and assertions are unchanged (the ADD-PATH unit's path IDs ride the legacy Withdrawn Routes field, untouched by the flag). The new labeled branch is covered by the untagged TestSynthesizeWithdrawLabeledOneCompatibilityField and TestSynthesizeWithdrawLabeledMalformedSkipped, outside this verdict's cited set. RFC 7606 Section 2: "the UPDATE message containing the path attribute in question MUST be treated as though all contained routes had been withdrawn just as if they had been listed in the WITHDRAWN ROUTES field (or in the MP_UNREACH_NLRI attribute if appropriate)". Verdict stands.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestSynthesizeWithdrawConvertsMPReachToMPUnreach |
unit/verify | unproven |
| negative | TestSynthesizeWithdrawMovesNLRIToWithdrawn |
unit/verify | unproven |
| negative | TestRIBTreatAsWithdrawAddPathPreservesPathID |
unit/verify | unproven |
| negative | TestRIBTreatAsWithdrawRemovesInstalledRoute |
unit/verify | unproven |
| negative | TestSessionRFC7606TreatAsWithdrawDispatchesWithdrawal |
unit/verify | unproven |
| positive | TestSessionRFC7606ValidUpdateDispatchesAnnouncement |
unit/verify | unproven |
RFC7606-2-2
o Attribute discard: In this approach, the malformed attribute MUST be discarded and the UPDATE message continues to be processed. (§2)
Audit verdict: enforced (the tests do what the requirement demands), fresh. TestApplyAttrDiscardMultipleEntries (attr_discard_test.go:256) asserts findAttrByCode(6)/(7) are nil (discarded) while ORIGIN/AS_PATH/NEXT_HOP survive (continues processing); TestSessionRFC7606AttributeDiscardContinues (session_test.go:1990) drives eBGP LOCAL_PREF and requires callbackCount==1 with session Established (UPDATE still dispatched). Positive TestApplyAttrDiscardEmptyEntries (:337) guards against unconditional discard. Both halves observed exactly. 2026-07-22 re-stamp: the verdict went stale only because commit c4038def0 prepended an rfc-test-change-approved header and swapped package qualifiers (bgptypes.RouteAction*->routeaction.*, message.Type*->msgtype.Type*, rib/events->core/bgp/ribevents) in the tagged files; tagged_unit_shas fingerprints the whole enclosing file. Normalising the 254772452..HEAD diff under that rename cancels with ZERO unmatched assertion deletions, and rfc/short/rfc7606.md is byte-identical since the audit, so the judgement above still describes exactly what it judged. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quoted sentence states the same obligation the paraphrase did, and no clause is new. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestApplyAttrDiscardMultipleEntries |
unit/verify | unproven |
| negative | TestSessionRFC7606AttributeDiscardContinues |
unit/verify | unproven |
| positive | TestApplyAttrDiscardEmptyEntries |
unit/verify | unproven |
RFC7606-2-3
This approach MUST NOT be used except in the case of an attribute that has no effect on route selection or installation. (§2)
Audit verdict: enforced (the tests do what the requirement demands), fresh. TestRFC7606RouteSelectionAttributesNeverDiscarded (rfc7606_structural_test.go:426) enumerates ORIGIN/AS_PATH/NEXT_HOP/MED/LOCAL_PREF(iBGP) and asserts NotEqual(AttributeDiscard) AND NotEqual(None) AND DiscardEntries empty with AttrCode pinned per case — a two-sided exclusion that fails if any route-selection attr is discarded or silently accepted. Positive TestRFC7606NonRouteSelectionAttributesAreDiscarded (:497) shows ATOMIC_AGGREGATE/AGGREGATOR ARE discarded, so the MUST NOT is not trivially satisfied by never discarding. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quote reads "except in the case of an attribute that has no effect on route selection or installation", the paraphrase's scope. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606RouteSelectionAttributesNeverDiscarded |
unit/verify | unproven |
| positive | TestRFC7606NonRouteSelectionAttributesAreDiscarded |
unit/verify | unproven |
RFC7606-3.b-1
If the Withdrawn Routes Length or Total Attribute Length is too large (i.e., if Withdrawn Routes Length + Total Attribute Length + 23 exceeds the message Length), then the Error Subcode MUST be set to Malformed Attribute List. (§3.b)
Audit verdict: enforced (the tests do what the requirement demands), fresh. TestSessionRFC7606SectionLengthConflictNotification (rfc7606_session_structural_test.go:153) declares Total Attr Length=0xFF over a shorter body and reads the NOTIFICATION off the wire: require.Equal notifBody[1]==NotifyUpdateMalformedAttr (subcode 1) plus callbackCount==0. Positive TestEnforceRFC7606_SectionLengthsExactlyFitAccepted (:114) pins the exact-fit boundary with explicit arithmetic asserts so an off-by-one is caught. Exact subcode assertion fails on non-compliance. 2026-07-22 re-stamp: the verdict went stale only because commit c4038def0 prepended an rfc-test-change-approved header and swapped package qualifiers (bgptypes.RouteAction*->routeaction.*, message.Type*->msgtype.Type*, rib/events->core/bgp/ribevents) in the tagged files; tagged_unit_shas fingerprints the whole enclosing file. Normalising the 254772452..HEAD diff under that rename cancels with ZERO unmatched assertion deletions, and rfc/short/rfc7606.md is byte-identical since the audit, so the judgement above still describes exactly what it judged. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quoted sentence states the same obligation the paraphrase did, and no clause is new. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestSessionRFC7606SectionLengthConflictNotification |
unit/verify | unproven |
| positive | TestEnforceRFC7606_SectionLengthsExactlyFitAccepted |
unit/verify | unproven |
RFC7606-3.c-1
If the value of either the Optional or Transitive bits in the Attribute Flags is in conflict with their specified values, then the attribute MUST be treated as malformed and the "treat-as-withdraw" approach used, unless the specification for the attribute mandates different handling for incorrect Attribute Flags. (§3.c)
Audit verdict: enforced (the tests do what the requirement demands), stale-unit: internal/component/bgp/message/rfc7606_attribute_envelope_test.go::TestRFC7606ImplementedAttributeFlagConflicts moved. Current flag controls isolate Optional/Transitive conflicts from attribute-value errors and missing mandatory attributes, assert exact treat-as-withdraw, and retain accepted specified-flag controls for core optional and plugin-owned attributes; the repaired TE/ATTR_SET cases also traverse both session readers, while declared MP and AIGP exceptions retain their attribute-specific handling
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606ImplementedAttributeFlagConflicts |
unit/verify | revert, unit-changed (the tagged unit's behavior changed since the red was observed) |
| negative | TestRFC7606FlagsOptionalAttributeConflict |
unit/verify | revert, verified |
| negative | TestRFC7606FlagsPluginAttributeConflict |
unit/verify | revert, verified |
| negative | TestRFC7606FlagsWellKnownMarkedOptional |
unit/verify | unproven |
| negative | TestRFC7606FlagsWellKnownNotTransitive |
unit/verify | unproven |
| negative | TestRFC7606AttributeEnvelopeSession |
unit/verify | revert, verified |
| positive | TestRFC7606FlagsOptionalAttributeAsSpecifiedAccepted |
unit/verify | revert, verified |
| positive | TestRFC7606FlagsPluginAttributeAsSpecifiedAccepted |
unit/verify | revert, verified |
| positive | TestRFC7606FlagsOptionalAttributeValid |
unit/verify | unproven |
RFC7606-3.d-1
If any of the well-known mandatory attributes are not present in an UPDATE message, then "treat-as-withdraw" MUST be used. (§3.d)
Audit verdict: enforced (the tests do what the requirement demands), fresh. MissingOrigin and MissingASPath assert exact treat-as-withdraw and identify the missing attribute; ValidUpdate accepts the complete mandatory set, and MandatoryAttributesAcrossUpdateForms distinguishes a missing legacy NEXT_HOP from MP-only reception; the corrected empty-MP systematic cases still detect missing ORIGIN/AS_PATH but now require the stronger Section 5.2 session reset rather than an invalid withdrawal of no routes
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606MissingASPath |
unit/verify | unproven |
| negative | TestRFC7606MissingOrigin |
unit/verify | unproven |
| negative | TestRFC7606SystematicLengthCorruption |
unit/verify | revert, producer-changed (the producer's behavior changed since the break was applied to it) |
| positive | TestRFC7606ValidUpdate |
unit/verify | unproven |
RFC7606-3.e-1
"Treat-as-withdraw" MUST be used for the cases that specify a session reset and involve any of the attributes ORIGIN, AS_PATH, NEXT_HOP, MULTI_EXIT_DISC, or LOCAL_PREF. (§3.e)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Malformed ORIGIN length, unrecognized AS_PATH segment, invalid IBGP LOCAL_PREF and isolated NEXT_HOP/MED length cases assert exact treat-as-withdraw rather than accepting session reset as a severity floor; exact accepted EBGP/IBGP baselines guard against indiscriminate rejection, and receive controls exercise withdrawal dispatch rather than a producer halt
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606ASPathUnrecognizedSegmentType |
unit/verify | unproven |
| negative | TestRFC7606LocalPrefIBGPInvalid |
unit/verify | unproven |
| negative | TestRFC7606MalformedOriginLength |
unit/verify | unproven |
| positive | TestRFC7606SystematicLengthCorruption |
unit/verify | revert, producer-changed (the producer's behavior changed since the break was applied to it) |
RFC7606-3.f-1
"Attribute discard" MUST be used for any of the cases that specify a session reset and involve ATOMIC_AGGREGATE or AGGREGATOR. (§3.f)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Malformed ATOMIC_AGGREGATE and wrong-width AGGREGATOR cases assert exact attribute-discard with the offending code and discard reason, while valid baselines remain accepted; extended-session discard controls verify the offending attribute is removed and the surviving announcement continues through both readers
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606DiscardEntryReasonCodes |
unit/verify | unproven |
| negative | TestRFC7606AggregatorLen8NoASN4Invalid |
unit/verify | unproven |
| negative | TestRFC7606MalformedAtomicAggregate |
unit/verify | unproven |
| positive | TestRFC7606SystematicLengthCorruption |
unit/verify | revert, producer-changed (the producer's behavior changed since the break was applied to it) |
RFC7606-3.g-1
If the MP_REACH_NLRI attribute or the MP_UNREACH_NLRI [RFC4760] attribute appears more than once in the UPDATE message, then a NOTIFICATION message MUST be sent with the Error Subcode "Malformed Attribute List". (§3.g)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Duplicate MP_REACH and MP_UNREACH cases select reset, including duplicates encountered before an abandoned attribute walk with reachable NLRI preventing Section 5.2 from supplying a substitute verdict; real-session tests observe NOTIFICATION 3/1 and no route dispatch, including the separate MP_UNREACH wire carrier, while single-MP controls remain accepted
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606MultipleMPReach |
unit/verify | unproven |
| negative | TestRFC7606SystematicLengthCorruption |
unit/verify | revert, producer-changed (the producer's behavior changed since the break was applied to it) |
| negative | TestRFC7606Section3gDuplicateMPBeatsAnAbandonedWalk |
unit/verify | unproven |
| negative | TestRFC7606Section3gDuplicateMPUnreachBeatsAnAbandonedWalk |
unit/verify | unproven |
| negative | TestRFC7606Section3gDuplicateMPUnreachResetsWithRoutesPresent |
unit/verify | unproven |
| negative | TestSessionRFC7606DuplicateMPUnreachNotificationOnTheWire |
unit/verify | unproven |
| negative | TestSessionRFC7606SessionResetNotification |
unit/verify | unproven |
| positive | TestRFC7606MPReachIPv4NextHopValid |
unit/verify | unproven |
RFC7606-3.h-1
When multiple attribute errors exist in an UPDATE message, if the same approach (as described in Section 2) is specified for the handling of these malformed attributes, then the specified approach MUST be used. (§3.h)
Audit verdict: enforced (the tests do what the requirement demands), fresh. TestRFC7606CollectAllErrors (rfc7606_test.go:1393) uses two attribute-discard errors (ATOMIC_AGG + AGGREGATOR) and asserts require.Equal(AttributeDiscard) with DiscardEntries len 2 covering codes 6 and 7 — fails on escalation or on dropping either entry. The {single-polarity,negative} annotation is sound: 'multiple attribute errors exist' has no error-free instance, so no positive can be constructed. Exact assertion, isolated buffer. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quoted sentence states the same obligation the paraphrase did, and no clause is new. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606CollectAllErrors |
unit/verify | unproven |
RFC7606-3.h-2
Otherwise, the approach with the strongest action MUST be used. (§3.h)
Audit verdict: enforced (the tests do what the requirement demands), fresh. TestRFC7606MultipleErrorsStrongest (rfc7606_test.go:1369) combines ATOMIC_AGG discard (weaker) with COMMUNITY TAW (stronger) and asserts require.Equal(TreatAsWithdraw) AttrCode 8 — fails if the weaker action wins or if it over-escalates to SessionReset. The {single-polarity,negative} annotation holds (premise is that errors exist). The stronger boundary (TAW vs SessionReset) is separately pinned by 2-6. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quoted sentence states the same obligation the paraphrase did, and no clause is new. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606MultipleErrorsStrongest |
unit/verify | unproven |
RFC7606-3.i-1
The Withdrawn Routes field MUST be checked for syntactic correctness in the same manner as the NLRI field (§3.i)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Negative TestEnforceRFC7606_InvalidWithdrawnNLRI (session_validate_test.go:87) puts prefix-length 33 in the Withdrawn Routes field (withdrawn-only body, isolated) and asserts require.Equal(SessionReset)+err — proving the withdrawn field IS syntax-checked like NLRI (mirror of TestEnforceRFC7606_InvalidTrailingNLRI for the NLRI field). Positive TestEnforceRFC7606_ValidWithdrawnNLRIAccepted (rfc7606_session_structural_test.go:72) drives a deliberately non-empty valid withdrawn field through enforceRFC7606 => ActionNone, unrewritten. 2026-07-22 re-stamp: the verdict went stale only because commit c4038def0 prepended an rfc-test-change-approved header and swapped package qualifiers (bgptypes.RouteAction*->routeaction.*, message.Type*->msgtype.Type*, rib/events->core/bgp/ribevents) in the tagged files; tagged_unit_shas fingerprints the whole enclosing file. Normalising the 254772452..HEAD diff under that rename cancels with ZERO unmatched assertion deletions, and rfc/short/rfc7606.md is byte-identical since the audit, so the judgement above still describes exactly what it judged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestEnforceRFC7606_InvalidWithdrawnNLRI |
unit/verify | unproven |
| positive | TestEnforceRFC7606_ValidWithdrawnNLRIAccepted |
unit/verify | unproven |
RFC7606-3.j-1
If this is not possible, the procedures of [RFC4271] and/or [RFC4760] continue to apply, meaning that the "session reset" approach (or the "AFI/SAFI disable" approach) MUST be followed. (§3.j)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-audited 2026-08-27. Exact SessionReset negatives cover MP_REACH length 4, MP_UNREACH length 2, IPv4 /33, IPv6 /129, and an NLRI overrun; descriptions or AttrCode isolate the failing parser rule. Parseable IPv4 and IPv6 NLRIs require no error. TestEnforceRFC7606_InvalidTrailingNLRI drives the production Session.enforceRFC7606 path, and test/plugin/rfc7606-reset.ci observes the resulting code-3/subcode-1 NOTIFICATION on the wire. The compiled passivePlugin13 replacement only runs the plugin lifecycle and cannot satisfy that ze-peer expectation. Any downgrade to TreatAsWithdraw, acceptance of an unparseable field, or reset of a parseable field fails an exact assertion. Re-confirmed 2026-09-19 after the fixture gained option=linger. The peer enters its linger loop only with every expectation matched, so the code-3/subcode-1 NOTIFICATION stays a precondition of the pass, and the TCP close this requirement names was asserted by this carrier in neither revision: the exact SessionReset classification is what the message-level and reactor units above pin. 2026-09-25 re-audit: commit 1fb33e8098 renamed the carrier's exec heads (ze-peer to le test peer, ze-test fixture to le test fixture) and changed no assertion, so the judgement above still describes what it judged. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quoted sentence states the same obligation the paraphrase did, and no clause is new. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606MPReachTooShort |
unit/verify | unproven |
| negative | TestRFC7606MPUnreachTooShort |
unit/verify | unproven |
| negative | TestRFC7606NLRIOverrun |
unit/verify | unproven |
| negative | TestRFC7606NLRIPrefixLengthTooLongIPv4 |
unit/verify | unproven |
| negative | TestRFC7606NLRIPrefixLengthTooLongIPv6 |
unit/verify | unproven |
| negative | TestEnforceRFC7606_InvalidTrailingNLRI |
unit/verify | unproven |
| negative | rfc7606-reset.ci |
functional/verify | unproven |
| positive | TestRFC7606NLRIPrefixLengthValidIPv4 |
unit/verify | unproven |
| positive | TestRFC7606NLRIPrefixLengthValidIPv6 |
unit/verify | unproven |
RFC7606-4-1
In either of these cases, an error condition exists and the "treat- as-withdraw" approach MUST be used (unless some other, more severe error is encountered dictating a stronger approach) (§4)
Audit verdict: enforced (the tests do what the requirement demands), fresh. FewerThanThreeOctetsRemainTreatAsWithdraw isolates one- and two-octet tails and an incomplete extended-length header; AttributeLengthConflictTreatAsWithdraw isolates a declared attribute length exceeding the remaining section and requires exact treat-as-withdraw; clean controls require None, and stronger no-reachable-NLRI or unparseable-NLRI outcomes remain separately exercised rather than being counted as equivalent withdrawals
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606FewerThanThreeOctetsRemainTreatAsWithdraw |
unit/verify | revert, producer-changed (the producer's behavior changed since the break was applied to it) |
| negative | TestRFC7606AttributeLengthConflictTreatAsWithdraw |
unit/verify | unproven |
| positive | TestRFC7606FewerThanThreeOctetsRemainTreatAsWithdraw |
unit/verify | revert, producer-changed (the producer's behavior changed since the break was applied to it) |
| positive | TestRFC7606SystematicLengthCorruption |
unit/verify | revert, producer-changed (the producer's behavior changed since the break was applied to it) |
RFC7606-5.1-1
o The MP_REACH_NLRI or MP_UNREACH_NLRI attribute (if present) SHALL be encoded as the very first path attribute in an UPDATE message. (§5.1)
Audit verdict: unimplemented (no code path enforces the requirement), fresh. {gap} is honest and confirmed: buildUpdatePayload (wireu/split.go:440-476) writes the attribute section as baseAttrs, then mpUnreach, then mpReach (:463-470), so MP_REACH is emitted LAST, not 'the very first path attribute' as §5.1 SHALL requires. Disclosed in docs/architecture/wire/mp-nlri-ordering.md ('MP_REACH last: Intentionally non-compliant') and rfc-status.md gap (1). 2026-07-29 (plan/spec-rfcgate-3-audit-teeth.md): the `code` map below transcribes the producing code this note already names, so the claim is falsifiable. Nothing was re-judged: with neither tests nor code fingerprinted this verdict's freshness test was `{} == {}` and it could never go stale, i.e. nobody was ever asked to look again if the gap closed. The fingerprint is the enclosing FUNCTION's span, not the file, because producer files churn far more than test files and a file-level hash here would just manufacture a new false-stale class (spec R-5). 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quoted sentence states the same obligation the paraphrase did, and no clause is new. Verdict unchanged.
No test carries RFC7606-5.1-1, so no unit is bound to it.
RFC7606-5.1-2
o An UPDATE message MUST NOT contain more than one of the following: non-empty Withdrawn Routes field, non-empty Network Layer Reachability Information field, MP_REACH_NLRI attribute, and MP_UNREACH_NLRI attribute. (§5.1)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-judged 2026-10-19 (independent auditor, after 839819ea2d). RFC 7606 Section 5.1: "An UPDATE message MUST NOT contain more than one of the following: non-empty Withdrawn Routes field, non-empty Network Layer Reachability Information field, MP_REACH_NLRI attribute, and MP_UNREACH_NLRI attribute." 839819ea2d changes one comment line in test/plugin/rfc7606-relay-one-field.ci ("./le verify current mode full" becomes "./le verify current", the verify spelling rename); no expectation, config, stimulus or assertion of the carrier changed, and no Go producer or other tagged unit of this row was touched. Mechanical shift; verdict holds. The prior judgment stands: the field-counter table covers all sixteen subsets with the greater-than-one threshold, parsed and raw splitters separate a fitting all-four-field input and keep compliant input identity, both forwarding arms enforce the split, and the native carrier requires the exact withdraw-only frame before the announcement. Recorded breaks are whole-producer halts and prove reach, not selective semantics.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestSplitCompliantPassesThroughCompliantUpdate |
unit/verify | revert, verified |
| negative | TestForwardCompliantShapeKeepsZeroCopy |
unit/verify | unproven |
| negative | TestSplitWireUpdateCompliantShapeUntouched |
unit/verify | unproven |
| positive | TestNLRIBearingFieldCountEveryCombination |
unit/verify | revert, verified |
| positive | TestSplitCompliantSplitsMixedUpdateThatFits |
unit/verify | revert, verified |
| positive | TestForwardSplitsMixedShapeAcrossContextsThatFits |
unit/verify | unproven |
| positive | TestForwardSplitsMixedShapeSameContextThatFits |
unit/verify | unproven |
| positive | TestSplitWireUpdateSplitsMixedShapeThatFits |
unit/verify | unproven |
| positive | TestSplitWireUpdateDoesNotManufactureEoR |
unit/verify | unproven |
| positive | rfc7606-relay-one-field.ci |
functional/verify | revert, verified |
RFC7606-5.1-3
Since older BGP speakers may not implement these restrictions, an implementation MUST still be prepared to receive these fields in any position or combination. (§5.1)
Audit verdict: enforced (the tests do what the requirement demands), shifted: internal/le/interoplab/bgp/check_rfc.go::checkRFC7606MixedUpdate moved. Re-judged 2026-10-19 (independent auditor, after 839819ea2d). RFC 7606 Section 5.1: "Since older BGP speakers may not implement these restrictions, an implementation MUST still be prepared to receive these fields in any position or combination." 839819ea2d changes one comment line in test/plugin/rfc7606-relay-one-field.ci ("./le verify current mode full" becomes "./le verify current", the verify spelling rename); no expectation, config, stimulus or assertion of the carrier changed, and no Go producer or other tagged unit of this row was touched. Mechanical shift; verdict holds. The prior judgment stands: exact ActionNone assertions cover late MP_REACH, late MP_UNREACH, the MP pair and each MP attribute with legacy NLRI, the native combinations carrier requires receipt of every component, and the FRR checker requires session survival and installation of the announced half. Single-polarity positive per the row annotation.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| positive | TestRFC7606Section51AcceptsMPReachWithLegacyNLRI |
unit/verify | unproven |
| positive | TestRFC7606Section51AcceptsMPUnreachAfterOtherAttributes |
unit/verify | unproven |
| positive | TestRFC7606Section51AcceptsMPUnreachWithLegacyNLRI |
unit/verify | unproven |
| positive | TestRFC7606Section51AcceptsReachAndUnreachTogether |
unit/verify | unproven |
| positive | TestRFC7606MPReachIPv4NextHopValid |
unit/verify | unproven |
| positive | checkVPNFRR |
interop/nightly | unproven |
| positive | rfc7606-receive-combinations.ci |
functional/verify | revert, verified |
| positive | rfc7606-relay-one-field.ci |
functional/verify | revert, verified |
RFC7606-5.2-1
Thus, if an UPDATE message is encountered that does contain path attributes other than MP_UNREACH_NLRI and doesn't encode any reachable NLRI, we cannot be confident that the NLRI have been successfully parsed as Section 3 (j) requires. For this reason, if any path attribute errors are encountered in such an UPDATE message and if any encountered error specifies an error-handling approach other than "attribute discard", then the "session reset" approach MUST be used. (§5.2)
Audit verdict: enforced (the tests do what the requirement demands), stale-unit: internal/component/bgp/message/rfc7606_attribute_envelope_test.go::TestRFC7606EmptyMPReachEscalatesToReset moved. Re-judged 2026-10-18 (independent auditor, after e5724325c1). Section 5.2: "Thus, if an UPDATE message is encountered that does contain path attributes other than MP_UNREACH_NLRI and doesn't encode any reachable NLRI, we cannot be confident that the NLRI have been successfully parsed as Section 3 (j) requires." The seam the previous verdict named is closed: TestRFC9830NLRILengthReceiveVerdicts now carries an RFC7606-5.2-1 negative tag and drives both shapes through processMessage on an Established session, with and without ADD-PATH. MP_UNREACH_NLRI alone with a walkable 13-octet SR Policy NLRI is treat-as-withdraw (session Established, nothing written, nothing dispatched), and the same NLRI beside ORIGIN, AS_PATH and LOCAL_PREF with no reachable NLRI resets with exactly NOTIFICATION 3/1 and dispatches nothing. The only error in the second body is the NLRI, so it isolates the population clause: dropping hasAttrOtherThanMPUnreach from the escalation in ValidateUpdateRFC7606AddPath reddens the alone shape, and an always-false condition reddens the reset shape. The message-level units (TestRFC7606Section52MPUnreachOnlyNotEscalated, TestRFC7606NoNLRIEscalation, TestRFC7606NoNLRIAttributeDiscardNoEscalation, TestRFC7606EmptyMPReachEscalatesToReset, the systematic length corruption and the reactor abandoned-walk and NLRI-present units) are unchanged and still cover reachable legacy and MP NLRI, discard-only errors, empty MP_REACH and abandoned walks.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606EmptyMPReachEscalatesToReset |
unit/verify | revert, unit-changed (the tagged unit's behavior changed since the red was observed) |
| negative | TestRFC7606Section52MPUnreachOnlyNotEscalated |
unit/verify | revert, producer-changed (the producer's behavior changed since the break was applied to it) |
| negative | TestRFC7606NoNLRIEscalation |
unit/verify | unproven |
| negative | TestRFC7606SystematicLengthCorruption |
unit/verify | revert, producer-changed (the producer's behavior changed since the break was applied to it) |
| negative | TestRFC7606AttributeEnvelopeSession |
unit/verify | revert, verified |
| negative | TestRFC7606Section52LeavesAnUpdateWithNLRIAlone |
unit/verify | unproven |
| negative | TestRFC9830NLRILengthReceiveVerdicts |
unit/verify | revert, producer-changed (the producer's behavior changed since the break was applied to it) |
| positive | TestRFC7606Section52MPUnreachOnlyNotEscalated |
unit/verify | revert, producer-changed (the producer's behavior changed since the break was applied to it) |
| positive | TestRFC7606MPUnreachMinValid |
unit/verify | unproven |
| positive | TestRFC7606NoNLRIAttributeDiscardNoEscalation |
unit/verify | unproven |
| positive | TestRFC7606SystematicLengthCorruption |
unit/verify | revert, producer-changed (the producer's behavior changed since the break was applied to it) |
| positive | TestRFC7606Section52EscalatesWhenTheAttributeWalkIsAbandoned |
unit/verify | unproven |
RFC7606-5.4-1
A BGP speaker advertising support for such a typed address family MUST handle routes with unrecognized NLRI types within that address family by discarding them, unless the relevant specification for that address family specifies otherwise. (§5.4)
Audit verdict: enforced (the tests do what the requirement demands), shifted: internal/le/interoplab/bgp/check_rfc.go::checkRFC7606TypedNLRIDiscard moved. Re-judged 2026-10-14 (independent auditor) after the Recognizer became a three-way verdict (spec-bgp-update-propagation-rfc-defects N-3). RFC 7606 Section 5.4: "A BGP speaker advertising support for such a typed address family MUST handle routes with unrecognized NLRI types within that address family by discarding them, unless the relevant specification for that address family specifies otherwise." Re-read the four changed units diff by diff: mup and mvpn TestRecognizeNLRIRejectsUnimplementedTypes now compare against nlritype.Keep and nlritype.Discard exactly, which is stricter than the old bool (a recognizer answering Withdraw for an unknown type now goes red); the one moved expectation, a one-octet T1ST body, is now Withdraw (malformed implemented type) and still never Discard. The reactor fake recognizers changed only their return type through keepIf; the survivor-byte, ActionNone and BGP-LS override assertions are unchanged. All other covers and their earlier reading stand. Rejudged 2026-10-16 (independent reader) after the owner-approved multi-label withdrawal change (RFC 8277 Section 2.4, nlrisplit.LabeledWithdrawal): SynthesizeWithdraw gained an addPath bool and SynthesizeWithdrawFamilies/mpReachToUnreach an addPath predicate, which mpReachToUnreach reads only for a family nlrisplit.CarriesCompatibility names (labeled unicast, VPN). The edits in TestRFC7606Section54DropsMPUnreachOnlyUpdateRatherThanForgeEOR, TestRFC7606Section54FiltersTreatAsWithdrawSynthesis and TestRFC7606Section54FiltersWhenTheAttributeWalkIsAbandoned pass a nil addPath predicate to SynthesizeWithdrawFamilies; their families (EVPN, MVPN, MUP) carry no Compatibility field, so the predicate is never read and the discard/filter assertions are unchanged. RFC 7606 Section 5.4: "A BGP speaker advertising support for such a typed address family MUST handle routes with unrecognized NLRI types within that address family by discarding them, unless the relevant specification for that address family specifies otherwise." Verdict stands.
RFC7606-7.1-1
The attribute is considered malformed if its length is not 1 or if it has an undefined value [RFC4271]. An UPDATE message with a malformed ORIGIN attribute SHALL be handled using the approach of "treat-as-withdraw". (§7.1)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-audited 2026-08-27. Isolated exact negatives cover both Section 7.1 clauses: ORIGIN length 2 and defined-length value 3 each require TreatAsWithdraw, AttrCode 1, and a Section 7.1 description. Three positives accept exactly values 0, 1 and 2 with ActionNone. test/plugin/rfc7606-withdraw.ci adds end-to-end session-survival proof: after the malformed ORIGIN, a second valid route must reach the peer. The compiled rfc7606Withdraw13 observer waits for establishment, sends the first route, gates on the post-malformation update counter, then sends the second route; it cannot make the peer expectation pass after a reset. Acceptance, SessionReset over-reaction, the wrong attribute, or rejecting a defined value each fail. 2026-09-25 re-audit: commit 1fb33e8098 renamed the carrier's exec heads (ze-peer to le test peer, ze-test fixture to le test fixture) and changed no assertion, so the judgement above still describes what it judged. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quoted sentence states the same obligation the paraphrase did, and no clause is new. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606MalformedOriginLength |
unit/verify | unproven |
| negative | TestRFC7606OriginValueInvalid |
unit/verify | unproven |
| negative | rfc7606-withdraw.ci |
functional/verify | unproven |
| positive | TestRFC7606OriginValueEGP |
unit/verify | unproven |
| positive | TestRFC7606OriginValueIGP |
unit/verify | unproven |
| positive | TestRFC7606OriginValueIncomplete |
unit/verify | unproven |
RFC7606-7.2-1
An AS_PATH is considered malformed if an unrecognized segment type is encountered or if it contains a malformed segment. A segment is considered malformed if any of the following are true: o There is an overrun where the Path Segment Length field of the last segment encountered would cause the Attribute Length to be exceeded. o There is an underrun where after the last successfully parsed segment there is only a single octet remaining (that is, there is not enough unconsumed data to provide even an empty segment header). o It has a Path Segment Length field of zero. An UPDATE message with a malformed AS_PATH attribute SHALL be handled using the approach of "treat-as-withdraw". (§7.2)
Audit verdict: enforced (the tests do what the requirement demands), stale-unit: internal/component/bgp/message/rfc7606_test.go::TestRFC7606ASPathValidSet moved. All four §7.2 malformation types have isolated exact negatives, each asserting TreatAsWithdraw + AttrCode 2 plus a distinguishing Description substring: unrecognized segment type (rfc7606_test.go:344 'segment type'), overrun (:366 'overrun'), underrun (:389 'underrun'), zero-length segment (:414 'zero'). The substring assertions prevent passing via a neighbouring rule; buffers keep ORIGIN/NEXT_HOP valid so only AS_PATH is malformed. Positives :290/:312 accept valid AS_SEQUENCE/AS_SET (Action None). 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quote spells out all four malformations (unrecognized segment type, overrun, underrun, zero Path Segment Length); each has an isolated tagged negative. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606ASPath4ByteASNOverrun |
unit/verify | unproven |
| negative | TestRFC7606ASPathSegmentOverrun |
unit/verify | unproven |
| negative | TestRFC7606ASPathSegmentUnderrun |
unit/verify | unproven |
| negative | TestRFC7606ASPathUnrecognizedSegmentType |
unit/verify | unproven |
| negative | TestRFC7606ASPathZeroSegmentLength |
unit/verify | unproven |
| positive | TestRFC7606ASPath4ByteASN |
unit/verify | unproven |
| positive | TestRFC7606ASPathValidSequence |
unit/verify | unproven |
| positive | TestRFC7606ASPathValidSet |
unit/verify | unproven |
RFC7606-7.3-1
The attribute is considered malformed if its length is not 4 [RFC4271]. An UPDATE message with a malformed NEXT_HOP attribute SHALL be handled using the approach of "treat-as-withdraw". (§7.3)
Audit verdict: enforced (the tests do what the requirement demands), fresh. SystematicLengthCorruption's isolated padded NEXT_HOP length-eight case uses an exact treat-as-withdraw assertion with AttrCode 3, not the surrounding tables' severity-floor assertion; ValidUpdate accepts length four, while RFC6368 inner NEXT_HOP is correctly ignored in its distinct ATTR_SET context
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606SystematicLengthCorruption |
unit/verify | revert, producer-changed (the producer's behavior changed since the break was applied to it) |
| positive | TestRFC7606ValidUpdate |
unit/verify | unproven |
RFC7606-7.4-1
The attribute is considered malformed if its length is not 4 [RFC4271]. An UPDATE message with a malformed MULTI_EXIT_DISC attribute SHALL be handled using the approach of "treat-as-withdraw". (§7.4)
Audit verdict: enforced (the tests do what the requirement demands), fresh. SystematicLengthCorruption's isolated padded MED length-eight case requires exact treat-as-withdraw and AttrCode 4; FlagsOptionalAttributeValid accepts an otherwise valid four-octet MED, and the zero-length MED case independently exercises malformed length zero without a framing cascade
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606SystematicLengthCorruption |
unit/verify | revert, producer-changed (the producer's behavior changed since the break was applied to it) |
| positive | TestRFC7606FlagsOptionalAttributeValid |
unit/verify | unproven |
RFC7606-7.5-1
o if the LOCAL_PREF attribute is received from an external neighbor, it SHALL be discarded using the approach of "attribute discard"; or (§7.5)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Negative rfc7606_test.go:846 TestRFC7606LocalPrefEBGPDiscard sends a valid-LENGTH (4) LOCAL_PREF over eBGP and asserts require.Equal AttributeDiscard + AttrCode 5 + '7.5', so the discard is attributable purely to eBGP not to length. The positive at :868 sends the identical bytes over iBGP and gets Action None, isolating the eBGP rule. Reinforced by attr_discard_test.go:408 (exact AttributeDiscard + Code 5 + Reason EBGPInvalid) and reactor test session_test.go:1990 which proves the session stays Established (not reset) and the UPDATE is still dispatched. 2026-07-22 re-stamp: the verdict went stale only because commit c4038def0 prepended an rfc-test-change-approved header and swapped package qualifiers (bgptypes.RouteAction*->routeaction.*, message.Type*->msgtype.Type*, rib/events->core/bgp/ribevents) in the tagged files; tagged_unit_shas fingerprints the whole enclosing file. Normalising the 254772452..HEAD diff under that rename cancels with ZERO unmatched assertion deletions, and rfc/short/rfc7606.md is byte-identical since the audit, so the judgement above still describes exactly what it judged. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quoted sentence states the same obligation the paraphrase did, and no clause is new. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606DiscardEntryReasonCodes |
unit/verify | unproven |
| negative | TestRFC7606LocalPrefEBGPDiscard |
unit/verify | unproven |
| negative | TestSessionRFC7606AttributeDiscardContinues |
unit/verify | unproven |
| positive | TestRFC7606LocalPrefIBGPValid |
unit/verify | unproven |
RFC7606-7.5-2
if received from an internal neighbor, it SHALL be considered malformed if its length is not equal to 4. If malformed, the UPDATE message SHALL be handled using the approach of "treat-as- withdraw". (§7.5)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Negative rfc7606_test.go:888 TestRFC7606LocalPrefIBGPInvalid sends iBGP LOCAL_PREF len 3 and asserts require.Equal TreatAsWithdraw + AttrCode 5 + '7.5'; buffer isolates the malformed LOCAL_PREF. Positive :868 accepts iBGP LOCAL_PREF len 4 (Action None). The exact Action equal fails on both non-detection and over-reaction. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quoted sentence states the same obligation the paraphrase did, and no clause is new. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606LocalPrefIBGPInvalid |
unit/verify | unproven |
| positive | TestRFC7606LocalPrefIBGPValid |
unit/verify | unproven |
RFC7606-7.6-1
The attribute SHALL be considered malformed if its length is not 0 [RFC4271]. An UPDATE message with a malformed ATOMIC_AGGREGATE attribute SHALL be handled using the approach of "attribute discard". (§7.6)
Audit verdict: enforced (the tests do what the requirement demands), fresh. MalformedAtomicAggregate and DiscardEntryReasonCodes require exact attribute-discard for nonzero lengths, including the named invalid-length discard entry; the systematic EBGP baseline requires exact acceptance of length zero, and session discard controls distinguish continued announcement processing from withdrawal or reset
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606DiscardEntryReasonCodes |
unit/verify | unproven |
| negative | TestRFC7606MalformedAtomicAggregate |
unit/verify | unproven |
| positive | TestRFC7606SystematicLengthCorruption |
unit/verify | revert, producer-changed (the producer's behavior changed since the break was applied to it) |
RFC7606-7.7-1
The AGGREGATOR attribute SHALL be considered malformed if any of the following applies: o Its length is not 6 (when the 4-octet AS number capability is not advertised to or not received from the peer [RFC6793]). o Its length is not 8 (when the 4-octet AS number capability is both advertised to and received from the peer). An UPDATE message with a malformed AGGREGATOR attribute SHALL be handled using the approach of "attribute discard". (§7.7)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Both length variants are pinned in both directions: positives accept len 6 without asn4 (TestRFC7606AggregatorLen6NoASN4) and len 8 with asn4 (TestRFC7606AggregatorLen8WithASN4), each Action None; negatives assert require.Equal AttributeDiscard + AttrCode 7 + '7.7' for len 8 without asn4 and len 6 with asn4, so the capability-dependent expected length is genuinely exercised. Reinforced by attr_discard_test.go (exact AttributeDiscard + Code 7 + Reason InvalidLength). INDEPENDENT RE-AUDIT 2026-09-20, after a new tagged carrier made the verdict stale: reactor/rfc7606_aggregator_relay_test.go::TestForwardDiscardsMalformedAggregatorAtMatchingWidth. It is genuine added evidence and it reaches a clause the message-level units cannot. Section 7.7 sends a malformed AGGREGATOR to 'attribute discard', and Section 2 defines that as 'the malformed attribute MUST be discarded and the UPDATE message continues to be processed'. Until this unit, every tagged carrier stopped at the CLASSIFICATION ValidateUpdateRFC7606 returns; nothing asserted that the octets are absent from the body ze rebuilds for a peer. This one drives ASPathEdit.Record and buildModifiedPayload over a 7-octet and a 1-octet AGGREGATOR at a four-octet/four-octet width pair, asserts the AGGREGATOR code is gone from the rebuilt payload and that the route is not suppressed, which is the 'continues to be processed' half. Both lengths are malformed under the second bullet of Section 7.7 and neither trips a structural error first, so the buffer isolates the rule. The verdict stays enforced: the message-level pair is exact and unchanged, and this unit only strengthens it. FINDING, recorded rather than fixed because the carrier is another session's uncommitted work. Its tag reads 'RFC7606-7.7-1 positive' and the unit is a NEGATIVE. parseTagRest (internal/le/rfc/tags.go) fixes the meaning: 'a negative-only test passes if the code rejects everything and a positive-only one passes if it accepts everything'. This unit feeds a MALFORMED AGGREGATOR and asserts it is discarded, so an implementation that accepted everything fails it and one that discarded every AGGREGATOR passes it. The sibling tags in rfc7606_test.go use the word the same way the gate does: len 6 without asn4 accepted is 'positive', len 8 without asn4 discarded is 'negative'. The row is not deceived today, because the two genuine positives are still tagged, but the relay path itself now carries a negative and no positive: nothing tagged asserts that a WELL-FORMED AGGREGATOR survives the rebuild, so a recordAggregatorDiscard that fired on every AGGREGATOR would keep this row green. The tag word wants correcting to 'negative', and the relay path wants a positive beside it. RE-AUDIT 2026-09-20 (second pass), by a reader who wrote neither unit. The FINDING the paragraph above recorded is CLOSED IN THE CODE, not waived. The relay-rail carrier now reads 'RFC7606-7.7-1 negative', which is the word parseTagRest gives that polarity and the word its assertions earn, and TestForwardRelaysWellFormedAggregatorAtMatchingWidth is the positive that was missing. It is a genuine opposite input on the same rail rather than the negative's assertion in another hat: an eight-octet AGGREGATOR under SrcASN4 and DstASN4 both true, which RFC 7606 Section 7.7 leaves well-formed ('Its length is not 8 (when the 4-octet AS number capability is both advertised to and received from the peer)'), driven through the same ASPathEdit.Record then buildModifiedPayload path, asserting the value survives byte for byte, the flags stay 0xC0, and no ATTR_TOMBSTONE is written. That closes the hole the previous entry named: a recordAggregatorDiscard firing on EVERY AGGREGATOR now reddens this unit. Both relay units carry a discrimination record in rfc/discrimination/rfc7606.json observed by reverting recordAggregatorDiscard. SCOPE, recorded and not a downgrade: the relay rail's positive covers the four-octet/four-octet width arm alone, which is the arm its name claims, so the two-octet-width and cross-width relay arms still carry a negative and no positive there. Both length arms of the requirement itself stay proven in both polarities by the message-level units above. The verdict stays enforced. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quote carries both length variants (6 without, 8 with the 4-octet AS capability); both are pinned in both directions. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606DiscardEntryReasonCodes |
unit/verify | unproven |
| negative | TestRFC7606AggregatorLen6WithASN4Invalid |
unit/verify | unproven |
| negative | TestRFC7606AggregatorLen8NoASN4Invalid |
unit/verify | unproven |
| negative | TestForwardDiscardsMalformedAggregatorAtMatchingWidth |
unit/verify | revert, verified |
| positive | TestRFC7606AggregatorLen6NoASN4 |
unit/verify | unproven |
| positive | TestRFC7606AggregatorLen8WithASN4 |
unit/verify | unproven |
| positive | TestForwardRelaysWellFormedAggregatorAtMatchingWidth |
unit/verify | revert, verified |
RFC7606-7.8-1
The Community attribute SHALL be considered malformed if its length is not a non-zero multiple of 4. o An UPDATE message with a malformed Community attribute SHALL be handled using the approach of "treat-as-withdraw". (§7.8)
Audit verdict: enforced (the tests do what the requirement demands), fresh. MalformedCommunityLength requires exact treat-as-withdraw for length five and identifies COMMUNITY; the zero-length COMMUNITY case separately checks the nonzero requirement, and the exact valid EBGP baseline accepts a four-octet community; floor-only length-corruption cases are supplementary and are not used to establish the required action
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606CommunityZeroLength |
unit/verify | unproven |
| negative | TestRFC7606MalformedCommunityLength |
unit/verify | unproven |
| positive | TestRFC7606SystematicLengthCorruption |
unit/verify | revert, producer-changed (the producer's behavior changed since the break was applied to it) |
RFC7606-7.9-1
o if the ORIGINATOR_ID attribute is received from an external neighbor, it SHALL be discarded using the approach of "attribute discard"; or (§7.9)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Negative pins the exact eBGP-context discard: attr_discard_test.go:449 (originator_id_ebgp_reason_1, isIBGP=false, code 9) asserts Action==AttributeDiscard, DiscardEntries len 1, Code==9, Reason==DiscardReasonEBGPInvalid; rfc7606_test.go:913 repeats AttributeDiscard+AttrCode 9+"7.9". Positive rfc7606_test.go:935 sends the identical ORIGINATOR_ID from iBGP and asserts ActionNone, pinning the context dependence. Fails if the code stopped discarding eBGP ORIGINATOR_ID. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quoted sentence states the same obligation the paraphrase did, and no clause is new. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606DiscardEntryReasonCodes |
unit/verify | unproven |
| negative | TestRFC7606OriginatorIDEBGPDiscard |
unit/verify | unproven |
| positive | TestRFC7606OriginatorIDIBGPValid |
unit/verify | unproven |
RFC7606-7.9-2
if received from an internal neighbor, it SHALL be considered malformed if its length is not equal to 4. If malformed, the UPDATE message SHALL be handled using the approach of "treat-as- withdraw". (§7.9)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Positive rfc7606_test.go:935 (len-4 iBGP ORIGINATOR_ID) asserts ActionNone; negative rfc7606_test.go:954 (len-5 iBGP) asserts the exact TreatAsWithdraw + AttrCode 9 + Description "7.9", matching validateOriginatorIDAttr (rfc7606.go:517). Fails if a malformed-length iBGP ORIGINATOR_ID were accepted. Minor: only len 5 exercises the !=4 predicate, so a hypothetical >4 bug would slip len 3, but the len-4-accept/len-5-reject boundary is pinned. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quoted sentence states the same obligation the paraphrase did, and no clause is new. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606OriginatorIDIBGPInvalid |
unit/verify | unproven |
| positive | TestRFC7606OriginatorIDIBGPValid |
unit/verify | unproven |
RFC7606-7.10-1
if the CLUSTER_LIST attribute is received from an external neighbor, it SHALL be discarded using the approach of "attribute discard" (§7.10)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Negative attr_discard_test.go:462 (cluster_list_ebgp_reason_1, isIBGP=false) asserts AttributeDiscard + Code 10 + Reason DiscardReasonEBGPInvalid; rfc7606_test.go:979 repeats AttributeDiscard+AttrCode 10+"7.10". Positive rfc7606_test.go:1001 sends the same CLUSTER_LIST from iBGP and asserts ActionNone. Pins the eBGP-context discard of validateClusterListAttr (rfc7606.go:530). 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quoted sentence states the same obligation the paraphrase did, and no clause is new. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606DiscardEntryReasonCodes |
unit/verify | unproven |
| negative | TestRFC7606ClusterListEBGPDiscard |
unit/verify | unproven |
| positive | TestRFC7606ClusterListIBGPValid |
unit/verify | unproven |
RFC7606-7.10-2
if received from an internal neighbor, it SHALL be considered malformed if its length is not a non-zero multiple of 4. If malformed, the UPDATE message SHALL be handled using the approach of "treat-as-withdraw". (§7.10)
Audit verdict: enforced (the tests do what the requirement demands), fresh. TestRFC7606ClusterListIBGPNonMultipleOf4IsMalformed, isIBGP true: lengths 4, 8 None; 0, 3, 5, 6 exact TreatAsWithdraw, AttrCode 10, '7.10', the attribute last so no cascade. Covers the zero and the multiple clauses.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606ClusterListIBGPNonMultipleOf4IsMalformed |
unit/verify | revert, verified |
| negative | TestRFC7606ClusterListIBGPInvalid |
unit/verify | unproven |
| positive | TestRFC7606ClusterListIBGPNonMultipleOf4IsMalformed |
unit/verify | revert, verified |
| positive | TestRFC7606ClusterListIBGPValid |
unit/verify | unproven |
RFC7606-7.11-1
If the Length of Next Hop Network Address field of the MP_REACH attribute is inconsistent with that which was expected, the attribute is considered malformed. Since the next hop precedes the NLRI field in the attribute, in this case it will not be possible to reliably locate the NLRI; thus, the "session reset" or "AFI/SAFI disable" approach MUST be used. (§7.11)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Three negatives assert the exact SessionReset + AttrCode 14 + "7.11" across families for an inconsistent next-hop length: IPv6 NH_LEN=5 (rfc7606_test.go:557), IPv4 NH_LEN=3 (:609), VPNv4 NH_LEN=4 (:660). Four positives assert ActionNone for consistent lengths (IPv6/16 :504, IPv6/32 :529, IPv4/4 :586, VPNv4/12 :634). validateMPReachNextHop (rfc7606.go:695-720) checks nhLen against the per-AFI/SAFI ValidNextHopLens table, so the reset fires for NH inconsistency specifically, not a structural cascade. Would fail if Ze weakened the action below session reset. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quoted sentence states the same obligation the paraphrase did, and no clause is new. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606MPReachIPv4NextHopInvalid |
unit/verify | unproven |
| negative | TestRFC7606MPReachIPv6NextHopInvalid |
unit/verify | unproven |
| negative | TestRFC7606MPReachVPNv4NextHopInvalid |
unit/verify | unproven |
| positive | TestRFC7606MPReachIPv4NextHopValid |
unit/verify | unproven |
| positive | TestRFC7606MPReachIPv6NextHopDualValid |
unit/verify | unproven |
| positive | TestRFC7606MPReachIPv6NextHopValid |
unit/verify | unproven |
| positive | TestRFC7606MPReachVPNv4NextHopValid |
unit/verify | unproven |
RFC7606-7.13-1
In the interim, an implementation that determines (for whatever reason) that an UPDATE message contains a malformed Traffic Engineering path attribute MUST handle it using the approach of "treat-as-withdraw". (§7.13)
Audit verdict: enforced (the tests do what the requirement demands), fresh. IMPLEMENTED 2026-07-20. validateTrafficEngineeringAttr (message/rfc7606_optional_attrs.go) registers code 24 and rejects anything shorter than one RFC 5543 descriptor (36 octets: SwitchingCap+Encoding+Reserved+8x4 bandwidth). Negative TestRFC7606TrafficEngineeringTooShort pins lengths 0/1/35 to TreatAsWithdraw with AttrCode 24; positive TestRFC7606TrafficEngineeringValid pins 36/44/72 to Action None, so a validator that rejected everything would fail. Both drive ValidateUpdateRFC7606, not the validator directly. The check is deliberately minimal because 7.13 states RFC 5543 'does not detail what constitutes malformation'; over-validating would blackhole valid routes. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quoted sentence states the same obligation the paraphrase did, and no clause is new. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606TrafficEngineeringTooShort |
unit/verify | unproven |
| positive | TestRFC7606TrafficEngineeringValid |
unit/verify | unproven |
RFC7606-7.14-1
The Extended Community attribute SHALL be considered malformed if its length is not a non-zero multiple of 8. o An UPDATE message with a malformed Extended Community attribute SHALL be handled using the approach of "treat-as-withdraw". (§7.14)
Audit verdict: enforced (the tests do what the requirement demands), fresh. TestRFC7606ExtendedCommunityNonMultipleOf8IsMalformed: lengths 8, 16 None; 0, 5, 7, 9, 12, 15 exact TreatAsWithdraw, AttrCode 16, '7.14', attribute last. Judge break (drop the length==0 arm in validateExtCommunityAttr): red.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606ExtendedCommunityNonMultipleOf8IsMalformed |
unit/verify | revert, verified |
| negative | TestRFC7606ExtendedCommunityZeroLength |
unit/verify | unproven |
| positive | TestRFC7606ExtendedCommunityNonMultipleOf8IsMalformed |
unit/verify | revert, verified |
| positive | TestRFC7606ExtendedCommunityValid |
unit/verify | unproven |
RFC7606-7.14-2
Note that a BGP speaker MUST NOT treat an unrecognized Extended Community Type or Sub-Type as an error. (§7.14)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Single-polarity (positive-only) argument holds: §7.14 is a MUST NOT prohibition ('MUST NOT treat an unrecognized Extended Community Type or Sub-Type as an error'), so acceptance is the only conforming observation. Positive rfc7606_test.go:247 uses valid length 8 with unrecognized Type 0x3f/Sub-Type 0xee and asserts ActionNone; length kept valid so the 7.14-1 length clause cannot mask the type question. validateExtCommunityAttr (rfc7606.go:550-560) inspects only length, never type, so introducing a type allowlist would flip this test to TreatAsWithdraw and fail it. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quoted sentence states the same obligation the paraphrase did, and no clause is new. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| positive | TestRFC7606ExtendedCommunityUnrecognizedType |
unit/verify | unproven |
RFC7606-7.15-1
The IPv6 Address Specific Extended Community attribute SHALL be considered malformed if its length is not a non-zero multiple of 20. o An UPDATE message with a malformed IPv6 Address Specific Extended Community attribute SHALL be handled using the approach of "treat- as-withdraw". (§7.15)
Audit verdict: enforced (the tests do what the requirement demands), fresh. IMPLEMENTED 2026-07-20. validateIPv6ExtCommunityAttr registers code 25 and applies 7.15's exact rule: malformed unless the length is a non-zero multiple of 20. Negative TestRFC7606IPv6ExtCommunityBadLength pins 0/19/21/30; positive TestRFC7606IPv6ExtCommunityValidLength pins 20/40. Zero is covered explicitly because 0 % 20 == 0 would pass a multiple-of-20 test alone. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quote is the Section 7.15 malformation rule and its handling; the tagged negative pins 0, 19, 21 and 30. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606IPv6ExtCommunityBadLength |
unit/verify | unproven |
| positive | TestRFC7606IPv6ExtCommunityValidLength |
unit/verify | unproven |
RFC7606-7.15-2
Note that a BGP speaker MUST NOT treat an unrecognized IPv6 Address Specific Extended Community Type or Sub-Type as an error. (§7.15)
Audit verdict: enforced (the tests do what the requirement demands), fresh. IMPLEMENTED 2026-07-20, and the basis CHANGED. Previously met by omission (nothing validated code 25, so nothing could error). Now met by design: validateIPv6ExtCommunityAttr takes the attribute value as `_` and tests length alone, so no Type or Sub-Type can reach a rejection. TestRFC7606IPv6ExtCommunityUnrecognizedType pins Type 0x3f / Sub-Type 0xee with a valid length 20 to Action None -- the length clause cannot fire and mask the type question. Positive-only by the same argument as 7.14-2: the rule is a prohibition, so a negative would assert the rejection the RFC forbids. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quoted sentence states the same obligation the paraphrase did, and no clause is new. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| positive | TestRFC7606IPv6ExtCommunityUnrecognizedType |
unit/verify | unproven |
RFC7606-7.16-1
An UPDATE message with a malformed ATTR_SET attribute SHALL be handled using the approach of "treat as withdraw". (§7.16)
Audit verdict: enforced (the tests do what the requirement demands), stale-unit: internal/component/bgp/message/rfc7606_attribute_envelope_test.go::TestRFC7606AttrSetMalformedInnerAttributes moved. ATTR_SET controls cover short Origin AS, forbidden inner MP attributes, malformed inner attributes, nested framing and valid contexts; the corrected InnerDiscardWithdraws test preserves exact standalone AGGREGATOR discard but requires outer ATTR_SET treat-as-withdraw for the malformed inner value and accepts the valid four-octet-AS counterpart, as RFC6368 Section 5 and RFC7606 Section 7.16 require; inner NEXT_HOP is ignored semantically but framing overruns still withdraw, and both session readers assert actual withdrawal versus acceptance
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606AttrSetMalformedInnerAttributes |
unit/verify | revert, unit-changed (the tagged unit's behavior changed since the red was observed) |
| negative | TestRFC7606AttrSetInnerMalformedStillWithdraws |
unit/verify | revert, verified |
| negative | TestRFC7606AttrSetNestingCapBoundary |
unit/verify | revert, verified |
| negative | TestRFC7606AttrSetInnerDiscardWithdraws |
unit/verify | revert, verified |
| negative | TestRFC7606AttrSetMalformed |
unit/verify | unproven |
| negative | TestRFC7606AttributeEnvelopeSession |
unit/verify | revert, verified |
| positive | TestRFC7606AttrSetInnerASPathAlwaysFourOctet |
unit/verify | revert, verified |
| positive | TestRFC7606AttrSetInnerIBGPAttributesOnEBGPSession |
unit/verify | revert, verified |
| positive | TestRFC7606AttrSetValid |
unit/verify | unproven |
RFC7606-4-3
the Total Attribute Length MUST be relied upon to enable the beginning of the NLRI field to be located. (§4)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Forbidden: locating the NLRI from an overrunning attribute's own length instead of the Total Attribute Length. TestRFC7606NLRILocatedByTotalAttributeLength builds an UPDATE whose COMMUNITY declares 8 octets where 4 remain, confirms the validator returns TreatAsWithdraw at code 8, and fails unless SynthesizeWithdraw's Withdrawn Routes equal the two prefixes at the Total Attribute Length offset (assert.Equal nlri); the negative assert.NotEqual rejects the tail the overrun would have pointed at. SynthesizeWithdraw is SynthesizeWithdrawFamilies(body, nil), the same locator the receive path calls in session_read.go. Rejudged 2026-10-16 (independent reader) after the owner-approved multi-label withdrawal change (RFC 8277 Section 2.4, nlrisplit.LabeledWithdrawal): SynthesizeWithdraw gained an addPath bool and SynthesizeWithdrawFamilies/mpReachToUnreach an addPath predicate, which mpReachToUnreach reads only for a family nlrisplit.CarriesCompatibility names (labeled unicast, VPN). The one edit in TestRFC7606NLRILocatedByTotalAttributeLength passes false to SynthesizeWithdraw; the input is legacy IPv4 NLRI after a COMMUNITY that overruns, so the assertion that the NLRI at Total Attribute Length is what is withdrawn is unchanged. RFC 7606 Section 4: "the Total Attribute Length MUST be relied upon to enable the beginning of the NLRI field to be located." Verdict stands.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606NLRILocatedByTotalAttributeLength |
unit/verify | revert, verified |
| positive | TestRFC7606NLRILocatedByTotalAttributeLength |
unit/verify | revert, verified |
RFC7606-4-2
For all path attributes other than those specified as having an attribute length that may be zero, it SHALL be considered a syntax error for the attribute to have a length of zero. Of the path attributes considered in this specification, only AS_PATH and ATOMIC_AGGREGATE may validly have an attribute length of zero. (§4)
Audit verdict: enforced (the tests do what the requirement demands), fresh. ZeroLengthAttributeMalformed supplies otherwise complete valid attributes and isolates zero-length COMMUNITY, MED and LARGE_COMMUNITY, asserting exact treat-as-withdraw and each offending code; ValidUpdate and the exact systematic EBGP baseline accept the specified zero-length AS_PATH and ATOMIC_AGGREGATE exceptions; severity-floor corruption sweeps are not relied upon as semantic proof
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606ZeroLengthAttributeMalformed |
unit/verify | unproven |
| positive | TestRFC7606SystematicLengthCorruption |
unit/verify | revert, producer-changed (the producer's behavior changed since the break was applied to it) |
| positive | TestRFC7606ValidUpdate |
unit/verify | unproven |
RFC7606-3.g-2
If any other attribute (whether recognized or unrecognized) appears more than once in an UPDATE message, then all the occurrences of the attribute other than the first one SHALL be discarded and the UPDATE message will continue to be processed. (§3.g)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Recognized case held by the HEAD first-wins pair; unrecognized case now proven: TestRFC7606UnrecognizedDuplicateAttributeLaterOccurrenceDiscarded (0xF0 twice) requires Action None, DuplicateRanges exactly the later occurrence, and StripAttrRanges leaving mandatory+first byte for byte; the reactor applies StripAttrRanges (session_validation.go).
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606UnrecognizedDuplicateAttributeLaterOccurrenceDiscarded |
unit/verify | revert, producer-changed (the producer's behavior changed since the break was applied to it) |
| negative | TestRFC7606DuplicateAttributeFirstOccurrenceIsValidated |
unit/verify | unproven |
| positive | TestRFC7606UnrecognizedDuplicateAttributeLaterOccurrenceDiscarded |
unit/verify | revert, producer-changed (the producer's behavior changed since the break was applied to it) |
| positive | TestRFC7606DuplicateAttributeFirstOccurrenceWins |
unit/verify | unproven |
RFC7606-8-1
A document that specifies a new BGP attribute MUST provide specifics regarding what constitutes an error for that attribute and how that error is to be handled. (§8)
Audit verdict: not-applicable (the requirement has no reachable code path in Ze), fresh. {not-applicable} annotation holds up: RFC §8 ('A document that specifies a new BGP attribute MUST provide specifics regarding what constitutes an error...') binds AUTHORS of future BGP attribute specifications, not a running implementation. There is genuinely no Ze code path that could satisfy or violate it, so it is not implementable (per the method, an implementable N/A would be 'wrong' -- this one is not). Recorded 'enforced' only in the sense that the N/A classification is honest and correct; there is no test to demand and no finding. 2026-07-29 (plan/spec-rfcgate-3-audit-teeth.md, owner ruling OR-1): the value was `enforced` with an empty `tests` map, which the schema now refuses -- `enforced` means the tests would fail if the code stopped complying, and this note itself says there is no test to demand. That was the honest reading of a vocabulary with no honest state for it, so Thomas added one rather than have the record re-judged: `not-applicable`, which requires an empty `tests` map, a mandatory `no_code_path` reason, and an independently committed {not-applicable} annotation on the checklist line. No judgement changed here; the same finding is now recorded in a word that means it. UNRESOLVED and deliberately left so: ai/rules/rfc-compliance.md voids {not-applicable} as AUTHORITY, so the annotation this verdict agrees with is itself a classification the owner has voided. This ruling makes the VERDICT honest about what the code does; it does not re-affirm the annotation, and re-deriving that is fleet-drain work under the rfcgate umbrella's D4. RFC7606-8-1 needs looking at again when the drain reaches it. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quoted sentence states the same obligation the paraphrase did, and no clause is new. Verdict unchanged.
No test carries RFC7606-8-1, so no unit is bound to it.
RFC7606-6-1
Because of these potential issues, a BGP speaker must provide debugging facilities to permit issues caused by a malformed attribute to be diagnosed. At a minimum, such facilities must include logging an error listing the NLRI involved and containing the entire malformed UPDATE message when such an attribute is detected. (§6)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-judged 2026-10-12 (independent auditor) after owner-approved lint-only edits in 96ec49327b and 4e731b2d96; the row text is unchanged and each changed unit was re-read diff by diff. TestRFC7606DiagnosticWireAndMPNLRI joins consecutive appends of the same attribute bytes, iterates log lines with strings.SplitSeq (same lines as strings.Split), and calls registerTypeLenRecognizer without the constant lo of 1. No input byte or assertion changed. Verdict stands. Diagnostic assertions compare the complete original wire UPDATE and affected NLRI rather than merely checking that logging occurred; controls cover ordinary/coalesced readers, extended/nonextended frames, legacy/MP IPv4/MP IPv6/typed NLRI, NEW/OLD first-AS handling, in-place discard, late empty-MP reset and typed-NLRI early return; next-hop policy diagnostics explicitly identify their processed representation, and disabled-debug allocation controls remain intact
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606DiagnosticWireAndMPNLRI |
unit/verify | revert, verified |
| negative | TestRFC7606DiagnosticsCostsNothingWhenDisabled |
unit/verify | unproven |
| negative | TestRFC7606DiagnosticsSilentWhenDebugDisabled |
unit/verify | revert, verified |
| positive | TestRFC7606DiagnosticWireAndMPNLRI |
unit/verify | revert, verified |
| positive | TestRFC7606DiagnosticsCoverSessionReset |
unit/verify | revert, verified |
| positive | TestRFC7606DiagnosticsListNLRIAndUpdate |
unit/verify | revert, verified |
RFC7606-3.a-1
An error detected while processing the UPDATE message for which a session reset is specified MUST be indicated by sending the NOTIFICATION message with the Error Code UPDATE Message Error. (§3.a)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-audited 2026-08-27 after the fixture observer moved to compiled Go. test/plugin/rfc7606-reset.ci still drives a real session and requires the exact NOTIFICATION frame ...0015030301 (Error Code 3 UPDATE Message Error, subcode 1). Its native passivePlugin13 observer only keeps the required plugin process alive and cannot make the wire expectation pass. TestSessionRFC7606ValidUpdateSendsNoNotification uses a net.Pipe read deadline to prove a valid UPDATE sends no NOTIFICATION and stays Established. TestEnforceRFC7606_ShortBody reinforces the exact SessionReset classification. Under-reaction, a wrong Error Code, and notifying a conforming UPDATE each fail a distinct assertion. Re-confirmed 2026-09-19 after the fixture gained option=linger. Holding the session open runs only after checker.Completed(), so the exact NOTIFICATION frame is still required before the peer can pass, and no Error Code or subcode assertion depends on which side closes. 2026-09-24 re-audit: commit 515bbc3b68 changed TestSessionRFC7606ValidUpdateSendsNoNotification only in its fixture, which now carries an AS_SEQUENCE starting with the eBGP neighbor AS 65002 so the new first-AS check does not withdraw it; the read-deadline assertion that no NOTIFICATION is sent and the Established assertion are unchanged. Enforced. 2026-09-25 re-audit: commit 1fb33e8098 renamed the carrier's exec heads (ze-peer to le test peer, ze-test fixture to le test fixture) and changed no assertion, so the judgement above still describes what it judged. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quote drops the subcode sentence and keeps the Error Code obligation the units pin. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestEnforceRFC7606_ShortBody |
unit/verify | unproven |
| negative | rfc7606-reset.ci |
functional/verify | unproven |
| positive | TestSessionRFC7606ValidUpdateSendsNoNotification |
unit/verify | unproven |
RFC7606-2-5
thus causing them to be removed from the Adj-RIB-In according to the procedures of [RFC4271]. (§2)
Audit verdict: enforced (the tests do what the requirement demands), fresh. now proven at the Adj-RIB-In itself, not by a dispatch-shape proxy shared with 2-1. TestAdjRIBInRFC7606TreatAsWithdrawRemovesRoute (adj_rib_in/rib_test.go): a valid announce leaves 10.0.0.0/8 in ribIn (Len 1, the positive); feeding the message.SynthesizeWithdraw output for a malformed re-announce of the same prefix removes it (Len 0, the negative), observed directly on r.ribIn via removeStructuredNLRIs. The proxy 2-5 tags on the reactor session tests were removed; the reactor still proves 2-1 (dispatched as a withdrawal). 2026-07-22 re-stamp: the verdict went stale only because commit c4038def0 prepended an rfc-test-change-approved header and swapped package qualifiers (bgptypes.RouteAction*->routeaction.*, message.Type*->msgtype.Type*, rib/events->core/bgp/ribevents) in the tagged files; tagged_unit_shas fingerprints the whole enclosing file. Normalising the 254772452..HEAD diff under that rename cancels with ZERO unmatched assertion deletions, and rfc/short/rfc7606.md is byte-identical since the audit, so the judgement above still describes exactly what it judged. 2026-07-25 re-stamp: stale again for the same structural reason -- spec-fixit-bgp-egress-rail-divergence edited OTHER tests in rib_test.go (replay now carries the source peer; the update-hex command form is gone), and tagged_unit_shas fingerprints the whole enclosing file. TestAdjRIBInRFC7606TreatAsWithdrawRemovesRoute is byte-identical, and so is every line of the path it exercises: git diff on rib.go shows handleReceivedStructured, installStructuredNLRIs and removeStructuredNLRIs unmodified (the only diff match is a context line). rfc/short/rfc7606.md is byte-identical, so requirement_sha is unchanged. Re-judged against rfc/full/rfc7606.txt:243-254 rather than the summary: the obligation is that contained routes be removed from the Adj-RIB-In, and the test asserts ribIn Len 1->0 -- RIB state, not a dispatch-shape proxy -- with exact equality on both halves, and the two buffers differ only in the ORIGIN length octet with identical NLRI, so the removal is measured against the key that was installed. Narrowness noted and unchanged: it covers the IPv4-unicast body-NLRI encoding, not the MP_UNREACH_NLRI parenthetical, which is a separate branch. Verdict stands. 2026-07-25 re-stamp (2): stale again for the SAME structural reason -- tagged_unit_shas fingerprints the whole enclosing FILE, and this change added three subtests to TestReplayOwnerDedupe in rib_test.go for the declarative stage-2 replay claim. The sole diff hunk is @@ -958,6 +958,58 @@ inside TestReplayOwnerDedupe; TestAdjRIBInRFC7606TreatAsWithdrawRemovesRoute (line 269) is byte-identical and did not even shift line number, and requirement_sha is unchanged (verified: stored == recomputed). The judgement above still describes exactly what it judged. 2026-07-26 re-stamp: stale again, and again only from the file-level fingerprint. requirement_sha is byte-identical (a8534d7b2f2b4ae6) and git shows ZERO changed lines in TestAdjRIBInRFC7606TreatAsWithdrawRemovesRoute or its two tags; b8f64e345 added a maxMsgID argument to buildReplayRoutes call sites elsewhere in rib_test.go, and tagged_unit_shas hashes the whole enclosing file. Re-read both halves before re-stamping: the positive feeds a valid announce and asserts ribIn[peer].Len()==1, the negative feeds message.SynthesizeWithdraw of a malformed-ORIGIN re-announce and asserts Len()==0, so an implementation that stopped removing on treat-as-withdraw leaves Len()==1 and fails the negative. Verdict unchanged: enforced. This is the SECOND such false-stale for this requirement (see the 2026-07-22 entry above); both cost a re-audit of an untouched test, which is the documented over-trigger bias working as designed. 2026-07-26 re-stamp (3): stale again for the SAME structural reason -- tagged_unit_shas fingerprints the whole enclosing FILE, and the peer-up replay-cut fix (adj-rib-in: max-msg-id bounded by PRESENCE, not by the value 0) changed six buildReplayRoutes CALL SITES in rib_test.go at lines 351, 397, 428, 433, 442, 462 and 584 -- every one of them a `0` -> `unboundedReplay()` argument-form change, with no assertion added, removed, reworded or weakened anywhere in the file. TestAdjRIBInRFC7606TreatAsWithdrawRemovesRoute (rib_test.go:269-317) is byte-identical: every diff hunk is at line 351 or below. So is the path it exercises -- the rib.go diff touches handleStructuredState, handleState and buildReplayRoutes only, none of which that test calls; handleReceivedStructured, installStructuredNLRIs and removeStructuredNLRIs are unmodified. rfc/short/rfc7606.md is untouched and requirement_sha is unchanged (a8534d7b2f2b4ae6), and both tag lines are still 267/268. Verdict stands, with the same narrowness noted before: IPv4-unicast body NLRI, not the MP_UNREACH_NLRI parenthetical. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quote narrows to the removal clause of the Section 2 treat-as-withdraw bullet ("thus causing them to be removed from the Adj-RIB-In according to the procedures of [RFC4271]"), which is what TestAdjRIBInRFC7606TreatAsWithdrawRemovesRoute asserts. Verdict unchanged. Rejudged 2026-10-16 (independent reader) after the owner-approved multi-label withdrawal change (RFC 8277 Section 2.4, nlrisplit.LabeledWithdrawal): SynthesizeWithdraw gained an addPath bool and SynthesizeWithdrawFamilies/mpReachToUnreach an addPath predicate, which mpReachToUnreach reads only for a family nlrisplit.CarriesCompatibility names (labeled unicast, VPN). The one edit in TestAdjRIBInRFC7606TreatAsWithdrawRemovesRoute passes false to SynthesizeWithdraw; the input is legacy IPv4 10.0.0.0/8, which does not reach the labeled branch, so the synthesized withdrawal and the Adj-RIB-In removal assertion are unchanged. RFC 7606 Section 2: "thus causing them to be removed from the Adj-RIB-In according to the procedures of [RFC4271]." Verdict stands.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestAdjRIBInRFC7606TreatAsWithdrawRemovesRoute |
unit/verify | unproven |
| positive | TestAdjRIBInRFC7606TreatAsWithdrawRemovesRoute |
unit/verify | unproven |
RFC7606-5.3-1
The NLRI field or Withdrawn Routes field SHALL be considered "syntactically incorrect" if either of the following are true: o The length of any of the included NLRI is greater than 32. (§5.3)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Negative session_validate_test.go:107 (TestEnforceRFC7606_InvalidTrailingNLRI) runs the production path enforceRFC7606 -> ValidateNLRISyntax(nlri,false) on a trailing /33; asserts require.Error + require.Equal(SessionReset). Valid ORIGIN/AS_PATH/NEXT_HOP filler isolates it, and removing the >32 check (rfc7606.go:857) would let the /33 parse cleanly (5 bytes fit) -> ActionNone -> test fails. Positive structural_test.go:55 accepts /32; direct unit negative rfc7606_test.go:736 reinforces. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quote is the lead-in plus its first bullet ("The length of any of the included NLRI is greater than 32."). Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606NLRIPrefixLengthTooLongIPv4 |
unit/verify | unproven |
| negative | TestEnforceRFC7606_InvalidTrailingNLRI |
unit/verify | unproven |
| positive | TestRFC7606NLRIMaxPrefixLengthAccepted |
unit/verify | unproven |
RFC7606-5.3-2
When parsing NLRI contained in the field, the length of the last NLRI found exceeds the amount of unconsumed data remaining in the field. (§5.3)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-judged 2026-10-12 (independent auditor). SHIFTED by 4e731b2d96 (owner-approved lint-only reactor test edits). The tagged unit is byte-identical (gate-verified); the file-level changes are comment punctuation, slices.Concat over the same bytes, joined appends, strings.SplitSeq, newDefaultOriginatePeer taking its fixed addresses from constants with the same values, and registerTypeLenRecognizer losing a lo every caller passed as 1, none of which changes what a helper this unit calls produces. Verdict stands. NLRILastPrefixExactlyFitsAccepted provides the exact-boundary positive; InvalidWithdrawnNLRI uses a legal /24 with only two prefix octets, isolating the last-prefix overrun from the greater-than-32 rule and requiring session reset; CoalescedMalformedNLRIBoundaries additionally checks original physical-message boundaries with and without ADD-PATH and Extended Message so adjoining messages cannot repair a truncated NLRI
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606CoalescedMalformedNLRIBoundaries |
unit/verify | revert, verified |
| negative | TestEnforceRFC7606_InvalidWithdrawnNLRI |
unit/verify | unproven |
| positive | TestRFC7606NLRILastPrefixExactlyFitsAccepted |
unit/verify | unproven |
| positive | TestRFC7606CoalescedMalformedNLRIBoundaries |
unit/verify | revert, verified |
RFC7606-5.3-3
Similarly, the MP_REACH_NLRI or MP_UNREACH_NLRI attribute of an update SHALL be considered to be incorrect if any of the following are true: o The length of any of the included NLRI is inconsistent with the given AFI/SAFI (for example, if an IPv4 NLRI has a length greater than 32 or an IPv6 NLRI has a length greater than 128). (§5.3)
Audit verdict: enforced (the tests do what the requirement demands), fresh. MP NLRI length-vs-AFI/SAFI is enforced by validateMPNLRIField in the ValidateUpdateRFC7606 main loop (ADD-PATH aware: skips the RFC 7911 4-byte path id) -> ValidateNLRISyntaxAddPath. Negative (TestRFC7606NLRIPrefixLengthTooLongIPv6) drives ValidateUpdateRFC7606 with an IPv6 MP_REACH NLRI prefixLen 129>128 (17 octets follow, so a length not overrun violation); asserts '129 > 128'. Positive is a well-formed IPv6 MP_REACH. Isolated. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quote is the MP lead-in plus its first bullet, including the example "(for example, if an IPv4 NLRI has a length greater than 32 or an IPv6 NLRI has a length greater than 128)". The operative condition is "inconsistent with the given AFI/SAFI"; the tagged negative drives the IPv6 instance. The example is illustrative, not a second obligation. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606NLRIPrefixLengthTooLongIPv6 |
unit/verify | unproven |
| positive | TestRFC7606MPNLRILengthConsistentWithAFIAccepted |
unit/verify | unproven |
| positive | TestRFC7606MPReachWellFormedAccepted |
unit/verify | unproven |
RFC7606-5.3-4
When parsing NLRI contained in the attribute, the length of the last NLRI found exceeds the amount of unconsumed data remaining in the attribute. (§5.3)
Audit verdict: enforced (the tests do what the requirement demands), fresh. MP last-NLRI overrun is enforced by validateMPNLRIField in the main loop (ADD-PATH aware). Negative (TestRFC7606MPReachNLRIOverrunsAttribute) uses a valid 16-octet next hop so 7.11 passes and the only defect is a /128 NLRI with 2 octets; asserts 'overrun'. Positive fits exactly. Isolated from the 7.11 NHLen=0 path the old test passed through. TestRFC7606MPReachAddPathValidNotReset guards that a valid ADD-PATH UPDATE is not misread. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quote is the attribute overrun bullet alone. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606MPReachNLRIOverrunsAttribute |
unit/verify | unproven |
| positive | TestRFC7606MPReachWellFormedAccepted |
unit/verify | unproven |
RFC7606-5.3-5
The attribute flags of the attribute are inconsistent with those specified in [RFC4760]. (§5.3)
Audit verdict: enforced (the tests do what the requirement demands), fresh. validateAttributeFlags now checks MP_REACH/MP_UNREACH flags per RFC 4760 (optional, non-transitive) and returns SessionReset. Negative (TestRFC7606MPReachFlagsInconsistentWithRFC4760) uses a valid next hop and NLRI with flags 0x40; flipping to 0x80 yields None, proving isolation; asserts 'RFC 4760'. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quote is the flags bullet alone; the paraphrase's NOTE about Section 3.j routing is gone from the line, and the units still assert SessionReset. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606MPReachFlagsInconsistentWithRFC4760 |
unit/verify | unproven |
| positive | TestRFC7606MPReachWellFormedAccepted |
unit/verify | unproven |
RFC7606-5.3-6
The length of the MP_UNREACH_NLRI attribute is less than 3, or the length of the MP_REACH_NLRI attribute is less than 5. (§5.3)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-audited and strengthened 2026-08-27. TestRFC7606MPReachLengthFourIsIncorrect is the isolated negative at the exact boundary: AFI, SAFI and zero NextHopLen occupy four octets for FlowSpec, and it requires SessionReset, AttrCode 14, and description 'length 4 < 5'. TestRFC7606MPMinimumLengthsAccepted pins MP_REACH length 5 and MP_UNREACH length 3 to ActionNone, so both lower boundaries and off-by-one over-reaction are covered. test/plugin/rfc7606-reset.ci supplies end-to-end reinforcement with a shorter MP_REACH and exact code-3/subcode-1 NOTIFICATION; its compiled passive observer cannot satisfy the wire check. This closes the prior audit's admitted hole where a len=2 negative could pass through a neighbouring framing error while length 4 was wrongly accepted. Re-confirmed 2026-09-19 after the fixture gained option=linger. The option delays only the peer's own close. The MP_REACH length-2 UPDATE, its 12-octet attribute framing and the NOTIFICATION expectation are byte-identical to the sealed revision, and the boundary negative and both minimum-length positives are Go units that did not move. 2026-09-25 re-audit: commit 1fb33e8098 renamed the carrier's exec heads (ze-peer to le test peer, ze-test fixture to le test fixture) and changed no assertion, so the judgement above still describes what it judged. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The quote names both minima. MP_REACH < 5 has the isolated tagged negative; MP_UNREACH < 3 is pinned by TestRFC7606MPUnreachTooShort (tagged RFC7606-3.j-1, exact SessionReset), and both boundaries are pinned by the tagged positive. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606MPReachLengthFourIsIncorrect |
unit/verify | unproven |
| negative | rfc7606-reset.ci |
functional/verify | unproven |
| positive | TestRFC7606MPMinimumLengthsAccepted |
unit/verify | unproven |
RFC7606-2-6
They are as follows (listed in order, from the one with the "strongest" action to the one with the "weakest" action): (§2, §3.h)
Audit verdict: enforced (the tests do what the requirement demands), fresh. TestRFC7606StrongestActionWins (rfc7606_structural_test.go:324) has two require.Equal cases: ATOMIC_AGG(discard)+COMMUNITY(TAW) => TreatAsWithdraw AttrCode 8, and COMMUNITY(TAW)+MP_REACH IPv6 5-byte-NH => SessionReset AttrCode 14. Positive TestRFC7606EqualStrengthErrorsDoNotEscalate (:378) requires two discard errors to yield EXACTLY AttributeDiscard (both DiscardEntries present), which only require.Equal can enforce against escalation. Buffers isolate the intended errors with controlled ordering. 2026-09-26 RE-AUDIT, stale-requirement: the checklist line was rewritten to a verbatim span of rfc/full/rfc7606.txt (spec-rfc-requirement-verbatim-quote). The tagged units are byte-identical to the last judgement, so its reading of them stands; this pass compared the new sentence clause by clause against what those units assert. The row now quotes the Section 2 ordering sentence ("listed in order, from the one with the strongest action to the one with the weakest"). Ze implements three of the four approaches (RFC7606Action: AttributeDiscard, TreatAsWithdraw, SessionReset; AFI/SAFI disable is the MAY of RFC7606-2-4 that ze does not take), and the tagged units pin discard < treat-as-withdraw < session reset with require.Equal, so the order among the approaches ze can select is proven. Verdict unchanged.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC7606StrongestActionWins |
unit/verify | unproven |
| positive | TestRFC7606EqualStrengthErrorsDoNotEscalate |
unit/verify | unproven |
Extraction sign-off
| Field | Value |
|---|---|
| Reviewer | claude |
| Signed off | 2026-09-21 |
| Register | rfc2119 |
| Source | rfc/full/rfc7606.txt |
| Source fingerprint | a291720189c2ff63 |
| Record | rfc/extraction/rfc7606.json |
| Mapped sentences | 52 |
| Declined as scope | 5 |
| Relocated to a spec, which Ze OWES | 0 |
| Unclassified | 0 |
Sections
| Section | Name | Sites | Disposition | Reason |
|---|---|---|---|---|
front |
not stated | 0 | walked | not stated |
1 |
not stated | 0 | walked | not stated |
1.1 |
not stated | 0 | walked | not stated |
2 |
not stated | 3 | walked | not stated |
3 |
not stated | 15 | walked | not stated |
4 |
not stated | 2 | walked | not stated |
5 |
not stated | 0 | walked | not stated |
5.1 |
not stated | 3 | walked | not stated |
5.2 |
not stated | 1 | walked | not stated |
5.3 |
not stated | 2 | walked | not stated |
5.4 |
not stated | 1 | walked | not stated |
6 |
not stated | 0 | walked | not stated |
7 |
not stated | 0 | walked | not stated |
7.1 |
not stated | 1 | walked | not stated |
7.2 |
not stated | 1 | walked | not stated |
7.3 |
not stated | 1 | walked | not stated |
7.4 |
not stated | 1 | walked | not stated |
7.5 |
not stated | 3 | walked | not stated |
7.6 |
not stated | 2 | walked | not stated |
7.7 |
not stated | 2 | walked | not stated |
7.8 |
not stated | 2 | walked | not stated |
7.9 |
not stated | 3 | walked | not stated |
7.10 |
not stated | 2 | walked | not stated |
7.11 |
not stated | 1 | walked | not stated |
7.12 |
not stated | 0 | walked | not stated |
7.13 |
not stated | 1 | walked | not stated |
7.14 |
not stated | 3 | walked | not stated |
7.15 |
not stated | 3 | walked | not stated |
7.16 |
not stated | 3 | walked | not stated |
8 |
not stated | 1 | walked | not stated |
9 |
not stated | 0 | walked | not stated |
10 |
not stated | 0 | walked | not stated |
10.1 |
not stated | 0 | walked | not stated |
10.2 |
not stated | 0 | walked | not stated |
Excluded sentences
| Site | Excluded kind | Reason | Quote |
|---|---|---|---|
3:1 |
not-a-requirementnever bound Ze the sentence states a fact or describes another document, and directs no implementation |
Old Text quotation: RFC 7606 Section 3.a prints the superseded RFC 4271 Section 6.3 wording under an explicit 'Old Text:' heading in order to REPLACE it. The obligation in force is the 'New Text' block, mapped at site 3:2. | All errors detected while processing the UPDATE message MUST be indicated by sending the NOTIFICATION message with the Error Code UPDATE Message Error. |
3:4 |
not-a-requirementnever bound Ze the sentence states a fact or describes another document, and directs no implementation |
Old Text quotation: RFC 7606 Section 3.c prints the superseded RFC 4271 Section 6.3 wording under an explicit 'Old Text:' heading in order to REPLACE it. The obligation in force is the 'New Text' block, mapped at site 3:6. | If any recognized attribute has Attribute Flags that conflict with the Attribute Type Code, then the Error Subcode MUST be set to Attribute Flags Error. |
3:5 |
not-a-requirementnever bound Ze the sentence states a fact or describes another document, and directs no implementation |
Old Text quotation: RFC 7606 Section 3.c prints the superseded RFC 4271 Section 6.3 wording under an explicit 'Old Text:' heading in order to REPLACE it. The obligation in force is the 'New Text' block, mapped at site 3:6. This sentence is the second half of the same Old Text block; the New Text drops the Data-field mandate along with the Attribute Flags Error subcode. | The Data field MUST contain the erroneous attribute (type, length, and value). |
7.16:1 |
not-a-requirementnever bound Ze the sentence states a fact or describes another document, and directs no implementation |
Old Text quotation: RFC 7606 Section 7.16 prints the superseded RFC 6368 Section 5 wording under an explicit 'Old Text:' heading in order to REPLACE it. The obligation in force is the 'New Text' block, mapped at site 7.16:3. | An UPDATE message with a malformed ATTR_SET attribute SHALL be handled as follows. |
7.16:2 |
not-a-requirementnever bound Ze the sentence states a fact or describes another document, and directs no implementation |
Old Text quotation: RFC 7606 Section 7.16 prints the superseded RFC 6368 Section 5 wording under an explicit 'Old Text:' heading in order to REPLACE it. The obligation in force is the 'New Text' block, mapped at site 7.16:3. The New Text replaces the Partial/Neighbor-Complete split with one unconditional treat-as-withdraw, so the RFC 4271 Optional Attribute Error branch no longer exists. | Otherwise (i.e., Partial flag is clear or Neighbor-Complete is set), the procedures of the BGP-4 base specification [RFC4271] MUST be followed with respect to an Optional Attribute Error. |
Superseded
No document obsoletes RFC 7606, so its obligations are stated where they were written.