Which bases an IPv6 extension header names

Every IPv6 extension header in this package subclasses IPv6_Ext. Some name a second base as well, and which ones do is a ruling rather than an accident. The owner’s words, on #924:

I think on subclassing, we might wanna keep this convention: if the IPv6 extension header is only usable as an extension header, then it only inherit from IPv6_Ext, like IPv6_Frag; but if it is useable as a standalone protocol itself, then it herit from both IPv6_Ext and Internet (or IPsec), like ESP.

The family as it stands:

Header

Bases

Classification

HOPOPT, IPv6_Route, IPv6_Frag, IPv6_Opts, MH

IPv6_Ext

extension-header only

AH, ESP

IPsec, IPv6_Ext

also standalone

HIP

IPv6_Ext, Internet

also standalone

IPsec is itself an Internet subclass, which is the parenthetical “(or IPsec)” in the ruling: naming it satisfies the convention, and it is the right second base for a header whose standalone form is an IPsec one.

The code cannot be used as evidence

This is the part a future reader will get wrong, so it is stated before the criterion itself. The obvious way to decide whether a header is “usable as a standalone protocol” is to ask what the library’s own dispatch already allows. That answer is useless, and measurably so:

>>> from pcapkit.protocols.internet.ipv4 import IPv4
>>> from pcapkit.protocols.internet.internet import Internet
>>> IPv4.__proto__ is Internet.__proto__
True

The protocol-number registry is one shared object, so every extension header is reachable as an IPv4 payload in this library – HOPOPT and IPv6_Frag included. Registry membership therefore says nothing at all about standalone-ness, and a classification derived from it would make all eight headers standalone.

Nor does IANA’s IPv6 Extension Header Types registry discriminate: it lists all eight of the implemented headers, plus Shim6 (140) and 253/254. Being in that registry is what makes something an extension header; it is not evidence about whether the same header is also a protocol in its own right.

The operative test is what the RFCs say

So the census is read out of the specifications, on the owner’s instruction:

So my suggestion is to read through the RFCs to figure out any of the defined IPv6 extension headers are extension header only or standalone protocol as well. Then we can decide if they should inherit only IPv6_Ext or additional bases.

And the limb that decides is whether a primary source shows the header carried directly as an IPv4 payload:

  • AH – RFC 4302 Section 3.1.1, “In the context of IPv4, this calls for placing AH after the IP header”, with a before-and-after IPv4 diagram.

  • ESP – RFC 4303 Section 3.1.1, the same sentence and the same diagram for ESP.

  • HIP – RFC 7401 Appendix C.2, “IPv4 HIP Packet (I1 Packet)”, whose worked checksum is over an IPv4 header carrying Next Header: 139. RFC 7401 Section 5.1 also calls the HIP header “logically an IPv6 extension header”, so HIP is genuinely both.

MH is the instructive failure, because it is a protocol in its own right and still does not qualify: RFC 6275 Section 6.1.1 defines its checksum over a pseudo-header of “IPv6 header fields” whose addresses are “the addresses that appear in the Source and Destination Address fields in the IPv6 packet carrying the Mobility Header” – there is no IPv4 variant of that computation – and the IPv4 equivalent function is not protocol 135 at all, since RFC 5944 carries Mobile IPv4 over UDP port 434. Shim6 (140) has the same shape and the same verdict; this package has never had a parser class for it, so nothing implements the classification, but a future one inherits IPv6_Ext alone.

Important

Own-protocolhood on its own is not sufficient, and MH is the case that settles it: the alternative reading – that a protocol in its own right qualifies whether or not it can appear under IPv4 – was put to the owner explicitly on #924 and not taken, so MH and Shim6 stay extension-only. A header that is a protocol in its own right but structurally cannot be an IPv4 payload names IPv6_Ext alone.

The declaration is what carries the classification

Name the second base explicitly, even though it is already in the MRO. IPv6_Ext derives Internet, so every one of the eight reaches Internet transitively and an __mro__ check cannot tell the two groups apart. The declaration is the only place the classification exists, which is why tests/protocols/internet/test_ipv6_ext_unit.py pins it against __bases__:

STANDALONE_MEMBERS = frozenset({'AH', 'ESP', 'HIP'})

Add a header to that set in the same change that adds its second base, and keep the RFC ground in the #: comment beside it. The test walks IPv6_Ext.__subclasses__() rather than a hard-coded list, so a ninth header is held to the convention whether or not anyone remembers this page.

The base is named IPv6_Ext, and nothing else

The class arrived as IPv6_GenericExt, a fallback parser for an unrecognised extension header (#891), and #917 merged that role with the shared-base role into one class under the shorter name. No compatibility alias was left behind, and that was deliberate. The owner’s ruling:

No more IPv6_GenericExt name. Its an intermediate state and never released.

The reasoning is what makes it safe rather than merely decided: the old name existed on main from b3551cb63 to 93cf940b3 – under four hours on one day, and after the most recent release tag – so it appears in no release, and the break has no callers to inconvenience. Do not reintroduce it as an alias, and do not cite it in prose as a former public name; it was never one.

ESP is an extension header, and still cannot short-circuit

Two facts about ESP coexist, and each is routinely mistaken for a refutation of the other.

It is an extension header. IANA’s IPv6 Extension Header Types registry lists protocol number 50, and this package’s ExtensionHeader agrees (ESP = 50). RFC 8200 Section 4.5 appears to say otherwise – “the Encapsulating Security Payload (ESP) is not considered an extension header” – but that sentence opens “For this purpose,”, scoping it to the fragmentation discussion it sits in, and the sentence after it lists ESP among “examples of upper-layer headers”. The library follows the registry, on the owner’s ruling for #895, which is why ESP carries the same extension-mode contract as its siblings.

And it terminates the chain walk. RFC 4303 places ESP’s Next Header byte inside the encrypted trailer, so with no key material there is nothing to continue on: ESP’s own data model reports next as None, and IPv6._decode_next_layer ends the ordinary way one iteration later. So ESP is absent from IPv6.__generic_ext_codes__ – not because it lacks a parser, which it has, but because it cannot hand the walk a successor.

The full reasoning, including how ESP’s terminal case differs from 253/254’s, is written where the code is and is deliberately not restated at length here: see the #: comment on IPv6.__generic_ext_codes__ (pcapkit/protocols/internet/ipv6.py) and the module docstring of pcapkit.protocols.internet.ipv6_ext.

Caution

The two facts have to be kept apart when reading any of this. “ESP is not an extension header” (wrong, and RFC 8200 Section 4.5 quoted out of scope) is a different claim from “ESP cannot be walked past” (right, and about RFC 4303’s wire format). Collapsing them is how ESP ends up either dropped from the extension-header contract or wrongly added to the generic-dispatch set.