IPv6-Ext - IPv6 Extension Header¶
pcapkit.protocols.internet.ipv6_ext.protocol contains
IPv6_Ext
only, in a double role:
the base class of every IPv6 extension header this package implements, supplying the
extension-mode guards that makepayloadandprotochainunavailable on a header parsed as part of an IPv6 chain. Each subclass guards its ownprotocolthe same way; this class’sprotocoldeliberately does not.a generic extractor [*] for a header with no dedicated parser. RFC 6564 Section 4 requires, with an RFC 2119 MUST, that any IPv6 extension header defined since April 2012 carry the same first two octets, so those are parseable without knowing anything else about the header:
Octets |
Bits |
Name |
Description |
|---|---|---|---|
0 |
0 |
|
Next Header |
1 |
8 |
|
Hdr Ext Len (8-octet units, excluding the first 8 octets) |
2 |
16 |
|
Header-specific content |
- class pcapkit.protocols.internet.ipv6_ext.protocol.IPv6_Ext[_PT: Data, _ST: Schema](file=None, length=None, **kwargs)[source]¶
Bases:
Internet[_PT,_ST],Generic[_PT,_ST]This class implements a generic IPv6 extension header parser, and is the shared base of every IPv6 extension header in this package.
The module documentation has the RFC citations behind the length rules, the ways this class is dispatched to, and why an overrun stops the walk rather than clipping.
Base and Fallback Roles
One class plays both, as ruled on #917:
The concrete fallback parser for an RFC 6564-conforming header with no dedicated class, which is Shim6.
read(),make(),name,aliasand theschema=/data=class keywords implement it.The base class of the implemented extension headers (
HOPOPT,IPv6_Route,IPv6_Frag,IPv6_Opts,HIP,MH,AHandESP), which is what the_extfguards onpayloadandprotochain, and each subclass’s own onprotocol, are for.
It is generic in its data and schema types, like
IPsec, another base in this package, so that a subclass keeps its own_PT/_STinstead of inheriting this class’s.AHandESPtherefore double-inherit two identically-parameterised generic bases,IPsec[…]andIPv6_Ext[…].Warning
A subclass must define
name,alias,protocol,length,__index__()andid()itself. Each carries this class’s fallback-role answer, which is wrong for a header with an identity of its own: it would report itself asIPv6 Extension Header/IPv6-Ext, read its length off a data model that is not its own,__index__()would raise rather than return its IANA number, andid()would match every member’s name rather than narrow to its own class name. The language cannot enforce the override, sotests/protocols/internet/test_ipv6_ext_unit.pydoes, over every subclass discovered at runtime.Added in version 1.5.0.
Changed in version 1.6.0:
The library does not pass
errortoread(), soData_IPv6_Ext.errorisNoneunless a caller passes one. A header whose dedicated parser raises isRaw, whoseinfo.errorholds the error (#2477).nextofmake()takes a member or anintcode, resolved with the enumeration’sget; a name, abool, or a code that does not fit in one octet raisesProtocolError, rather than being handed to the schema unchecked (#2594).from_data()keepsprotocol, handingread()thealiasits parse took, rather than rebuilding it asNone(#2768).
- property name: str¶
Name of current protocol.
Annotated
strrather than theLiteralevery leaf protocol in this package uses, because this one is also a base: aLiteralhere makes each subclass’s ownLiteralan incompatible override (mypy[override]). The value is still the single fallback-role constant.
- property alias: str¶
Acronym of corresponding protocol.
Annotated
strrather thanLiteral, for the reason given onname.Hyphenated, like
IPv6_Frag.aliasandIPv6_Opts.alias, for the same reason:IPv6._decode_next_layerkeys the packet dict throughIPv6._exthdr_key, which isalias.lstrip('IPv6-').lower(), andstr.lstrip()strips a character set, not a prefix. The default (class-name) alias'IPv6_Ext'would strip to'_Ext'and key the dict as_ext; the hyphen makes every leading character (I,P,v,6,-) part of the stripped set, givingext.
- property length: int¶
Header length of current protocol.
Fallback-role member: it reads this module’s own data model, so a subclass whose data model records its length elsewhere, or not at all (as
IPv6_Frag), must override it. Every implemented header does, andtests/protocols/internet/test_ipv6_ext_unit.pyholds them to it.
- property protocol: ExtensionHeader | None | str¶
The extension header this instance stands in for.
In the fallback role (this instance parsed this module’s own schema) this is not the base
Protocol.protocolmeaning (“name of next layer protocol”). As ruled on #891, it is repointed at this instance’s own identity, i.e. which header’s format it parsed, which for this class is onlyShim6. The identity is resolved per instance from the numeric code handed to the constructor (alias), whichpcapkit.protocols.internet.ipv6.protocol.IPv6._decode_next_layer()already holds before it dispatches. See__index__()for why the class-level identity cannot work the same way. It is deliberately not_extf-guarded in this role: that same call passesextension=Truefor every extension header in a chain, so guarding it would make the identity unreadable in exactly the case it exists for.In the base role it falls through to
super(), restoring the ordinaryProtocolBasemeaning. That is load-bearing: every subclass implements its own_extf-guardedprotocolasreturn super().protocol, and this class sits between them andProtocolBasein the MRO, so without the discriminator below each would readself._info.protocoloff a data model with no such field (AttributeError).The discriminator is the class-level
__data__rather than anisinstance()test onself._info, because amake-only instance has no_infoat all. Reading it would turnProtocol.protocol, which only needsself._protos, into anAttributeError;tests/protocols/internet/test_ipv6_extension_unit.pyconstructs exactly that instance.
- property next: TransType | None¶
Next header, as parsed off the wire.
Shared by every subclass rather than fallback-role-only, since each of their data models records the Next Header value under this name.
Nonewhen the declared length would have overrun what remained of the chain and the walk stopped instead of trusting it; see The Overrun Guard.
- property payload: ProtocolBase | NoReturn¶
Payload of current instance.
- Raises:
UnsupportedCall – if the protocol is used as an IPv6 extension header
- property protochain: ProtoChain | NoReturn¶
Protocol chain of current instance.
- Raises:
UnsupportedCall – if the protocol is used as an IPv6 extension header
- classmethod id()[source]¶
Index ID of the protocol.
- Return type:
tuple[Literal['HOPOPT'],Literal['IPv6_Route'],Literal['IPv6_Frag'],Literal['ESP'],Literal['AH'],Literal['IPv6_Opts'],Literal['MH'],Literal['HIP']]- Returns:
Index ID of every direct concrete subclass, each of which narrows its own
id()to itself. A Shim6 header, which this class parses, therefore matches every one of these names.
- read(length=None, *, extension=False, **kwargs)[source]¶
Read a generically-parsed IPv6 extension header.
- Parameters:
extension (
bool) – If the protocol is used as an IPv6 extension header.**kwargs (
Any) –Arbitrary keyword arguments, including these, which are this class’s own:
alias– the numeric code of the extension header this instance parses,140forShim6(the only code registered to this class), supplied byIPv6._import_next_layer.error– a parsing error to record in the result, if any, which the library does not pass.
- Return type:
_PT- Returns:
Parsed packet data.
Note
These are read out of
**kwargsrather than declared, andversionis not declared either, because this class is also the base of subclasses whose ownreadaccepts none of them. Declaring them would assert of the whole family an interface only the fallback has, which mypy (Signature of "read" incompatible with supertype,[override]) and pylint (arguments-differ) both reject. This method never readsversion; the version gate lives in__post_init__(), which still takes it explicitly.
- make(*, next=TransType.UDP, len=0, data=b'', payload=b'', **kwargs)[source]¶
Make (construct) packet data.
- Parameters:
len (
int) – RawHdr Ext Lenoctet to emit. The caller must size it, since this class cannot know at construction time which per-protocol rule (see Length Rules) it must satisfy.data (
bytes) – Header-specific content after the two fixed octets, emitted verbatim ahead ofpayload.payload (
bytes|ProtocolBase|Any) – Payload of current instance.**kwargs (
Any) – Arbitrary keyword arguments.
- Return type:
_ST- Returns:
Constructed packet data.
- Raises:
ProtocolError – If
nextis a name, abool, or a member or code that does not fit in one octet.
Note
Declared, and keyword-only, each for its own reason.
These keywords stay declared, unlike
read()’s own, because_check_construction_keywords()builds its allowlist from theinspect.signature()ofmakeand the other construction methods of every class in the MRO, plus any__keywords__. Hiding them in**kwargsbreaks construction: a plainIPv6_Ext(next=..., len=..., data=..., payload=...)raisesUnsupportedCall: IPv6_Ext: unexpected keyword(s). The__keywords__escape hatch would cover that too, but a set there is unioned down the MRO, so every subclass would accept these names and drop them. Amakesignature can be kept from subclasses instead: this class setsProtocol.__own_make_keywords__, so a subclass that overridesmakeacceptslenordataonly where its ownmakedeclares it (#2286).They are keyword-only because this class is also a base and each subclass’s
makeputs different names in the same positions. As positional parameters they draw pylintarguments-renamedwarnings (ESP.makehasspi,seqandnextwhere these havenext,lenanddata) and mypy[override]errors. With no positional parameters there is no position to disagree about, and the allowlist is unaffected because_declared_keywords()collectsKEYWORD_ONLYparameters too. Nothing calls a protocol’smakepositionally;__init__spreads**kwargsinto it.readneeds none of this forerror: it arrives on the parse path alone, which__init__exempts from the construction check.aliasarrives on a rebuild too, from_make_data(), so this class lists it in__keywords__(#2768).
- __post_init__(file=None, length=None, *, version=6, extension=False, **kwargs)[source]¶
Post initialisation hook.
- Overloads:
- Parameters:
- Raises:
ProtocolError – If this is the fallback parser (see the class docstring’s “Base and Fallback Roles”) and
versionis not6.
See also
For construction arguments, please refer to
make().Note
The version gate is scoped to the fallback role by the same
__data__discriminatorprotocoluses. Applying it to subclasses would break those that are not IPv6-only:AHandESPboth default toversion=4and are valid under IPv4, so an unconditional gate would reject them (ProtocolError: ESP: invalid version: must be 6, not 4).The gate exists because this class is registered into
Internet.__proto__, which everyInternetsubclass shares,IPv4included, so thatShim6resolves to a working parser instead ofRaw. Without it, an IPv4 packet whose protocol byte is 140 would reach this class and walk an IPv6-style extension-header chain out of an IPv4 payload, readingIPv4:IPv6-Ext:...instead of theIPv4:Shim6that a plain, non-continuingRawgives. Rejecting here sends construction back throughbeholder()at the caller’s layer, which substitutes that sameRaw: the version-agnostic behaviour, with no IPv4-specific code.
- classmethod __index__()[source]¶
Numeral registry index of the protocol.
- Raises:
UnsupportedCall – This protocol has no class-level registry entry. Unlike
Raw.__index__, which raises becauseRawhas no identity to report at all, this class does have one – seeprotocol– but it is resolved per instance, not per class: oneIPv6_ExtparsesShim6(and any other RFC 6564-format code registered to it), while__index__()is a@classmethodwith nowhere to put a value that differs per instance.- Return type:
- classmethod _make_data(data)[source]¶
Create key-value pairs from
datafor protocol construction.Emits the
Hdr Ext Lenoctet and the body as they were read, so the rebuild is byte-exact whichever per-protocol length ruleread()applied, and also for an overrun, wheredata.nextisNoneanddata.declared_nextkeeps the octet. Handsread()theprotocolit resolved asalias, which picks that rule and the identity again (#2768).
Length Rules¶
The generic layout is not retroactive (RFC 6564 Section 5), so the headers that predate it get the closed exception table below rather than being assumed to conform. RFC 8200 Section 4.8 restates the layout without a MUST and mislabels its length field as the Destination Options header’s; cite RFC 6564 Section 4 instead.
The exception table, verified against IANA’s protocol-numbers-1.csv
IPv6 Extension Header column and each cited RFC:
Header(s) |
Length rule |
|---|---|
|
|
|
constant |
|
|
|
terminal – has a dedicated, registered parser
( |
|
terminal – no dedicated parser exists, so no next header field is ever read at all, either |
This table classifies by wire format alone, over every code
ExtensionHeader enumerates,
which match IANA’s IPv6 Extension Header Types registry exactly
(#925).
Shim6 conforms to the generic rule but has no dedicated parser; it reaches
this class as Entry Paths describes.
RFC 8200 Section 4.5 counts ESP among upper-layer headers, not extension
headers, and 253 and 254 are reserved for experimentation (RFC 3692), with no
wire format at all. None of them reaches this class, by
construction (IPv6._import_next_layer). Each is still
an ExtensionHeader member, so it enters
the walk below, where they part ways:
ESPresolves to its own parser, which decodes any plaintext it recovers itself and reports anextofNonewhile the payload stays opaque, soExtensionHeader(None)fails at the next membership check and the walk ends.253and254resolve to plainRaw, whose info has nonextattribute. For these, and any future IANA code without an implementation, the walk stops on that structural check rather than on a list of codes.
The Extension Header Walk¶
The walk IPv6._decode_next_layer runs over the
header chain, and where it stops:
flowchart TD
S["proto = the IPv6 Next Header"] --> Q1
Q1{"proto an ExtensionHeader member?"}
Q1 -->|yes| Q2{"EXTENSION\_HEADER\_LIMIT headers walked?"}
Q2 -->|no| Q3{"the frame at FRAME\_LAYER\_LIMIT layers?"}
Q3 -->|no| IMP["\_import\_next\_layer(proto, extension=True)<br/>its dedicated class; Raw for 253, 254,<br/>or a parser that raises"]
IMP --> Q4{"its info has a next attribute?"}
Q4 -->|no| STOP["walk ends<br/>a plain Raw is reused as the upper layer"]
Q4 -->|yes| REC["recorded among the extension headers<br/>payload moves past it; proto = its next"]
REC --> Q5{"an IPv6-Frag header?"}
Q5 -->|no| Q1
Q5 -->|yes| J{"the first Fragment header,<br/>in a jumbogram?"}
J -->|yes| ERR["ProtocolError"]
J -->|no| Q6{"Fragment Offset zero?"}
Q6 -->|yes| Q1
Q6 -->|no| OPQ["walk ends opaque<br/>the upper layer gets no code, so Raw"]
Q1 -->|no| EXIT
Q2 -->|"yes: the rest kept Raw, one warning"| EXIT
Q3 -->|"yes: the rest kept Raw, one warning"| EXIT
STOP --> EXIT
OPQ --> EXIT
EXIT["Internet.\_decode\_next\_layer<br/>the upper layer, with the extension header chain"]
click S "ipv6.html#pcapkit.protocols.internet.ipv6.protocol.IPv6._decode_next_layer"
click IMP "ipv6.html#pcapkit.protocols.internet.ipv6.protocol.IPv6._import_next_layer"
click EXIT "../protocol.html#protocol-next-layer-dispatch"
Entry Paths¶
This class is reached by direct dispatch only.
Shim6 (RFC 5533)
has no dedicated parser, so
Internet._lookup_next_layer
would default it to plain Raw, whose
missing next would end the walk (#891); this module registers this
class for that code instead. The registration is global, shared by every
Internet subclass, so
__post_init__() gates on
version == 6 to keep an IPv4 payload of protocol number 140 out.
A header with a dedicated class whose parser raises does not reach this class.
beholder() makes it plain
Raw, and the walk stops there, as
RFC 8200 Section 4 has a node discard such a packet (#2477).
The Overrun Guard¶
The house convention for a declared length that does not fit what remains
is warn-and-clip: warn, naming what was declared against what is left, then read
only what is left – as pcapkit.protocols.misc.pcapng.schema.bounded_area()
and pcapkit.protocols.misc.pcapng.protocol.PCAPNG.read() do.
This class keeps the warning – a
SchemaWarning, in
bounded_area()’s form – and
deliberately diverges on the action. Clipping is right where the declared
length governs only how much of the current object to read. Here it also
decides where the next header starts, and a clipped skip points at whatever
bytes end the buffer, which are not a header: continuing would fabricate a layer
and record a false ProtoChain entry, which
reads as a parsed fact. So read()
warns, then stops the walk: this instance absorbs every remaining octet and
reports a next of None, which the walk already reads as no more
extension headers.
Header Schemas¶
Removed in version 1.6.0: pcapkit.protocols.schema.internet.ipv6_ext and
pcapkit.protocols.schema.internet.ipv6_option, with no alias: the modules merged
here (#2534).
- class pcapkit.protocols.internet.ipv6_ext.schema.IPv6_Ext(next=NO_VALUE, len=NO_VALUE, payload=NO_VALUE, *, __packet__=None, **kwargs)[source]¶
Bases:
SchemaHeader schema for a generically-parsed IPv6 extension header.
Only the two octets RFC 6564 Section 4 guarantees are parsed at this layer –
nextand the rawHdr Ext Lenoctet. Combining them into an actual skip distance is per protocol (a constant forIPv6-Frag, 4-octet units forAH, 8-octet units for the rest), so that part is done inpcapkit.protocols.internet.ipv6_ext.protocol.IPv6_Ext.read(), which knows which protocol this instance stands in for; this schema does not.
Data Models¶
Removed in version 1.6.0: pcapkit.protocols.data.internet.ipv6_ext, with no alias: the module moved here
(#2534).
- class pcapkit.protocols.internet.ipv6_ext.data.IPv6_Ext(protocol, next, declared_next, len, length, data, error)[source]¶
Bases:
ProtocolData model for a generically-parsed IPv6 extension header.
See
pcapkit.protocols.internet.ipv6_ext.protocol.IPv6_Extfor how each field below is derived.- protocol: ExtensionHeader | None¶
The extension header this instance stands in for – the numeric code the caller dispatched on, resolved to its
ExtensionHeadermember.Nonewhenaliaswas not given or named no such member (readsets it so in both cases, and the class property atprotocolis typed to match).
- next: TransType | None¶
Next header, parsed off the wire.
Nonewhen the declared length would have overrun what remained and the walk stopped instead of trusting it.
Footnotes