L2TP - Layer Two Tunnelling Protocol

pcapkit.protocols.link.l2tp contains L2TP only, an abstract base class for the Layer Two Tunnelling Protocol family [*]. The concrete versions live in modules of their own:

Version

Class

Specification

L2TPv2

L2TPv2

RFC 2661

Only L2TPv2 is implemented.

The base deliberately parses no header, as IP does for its family, because the versions do not share one. Only the first 16-bit word, with a version nibble at bits 12-15, is common to all of them; a base that parsed further would assume one version’s layout for every version.

class pcapkit.protocols.link.l2tp.L2TP[_PT: Data, _ST: Schema](file=None, length=None, **kwargs)[source]

Bases: Link[_PT, _ST], Generic[_PT, _ST]

This class implements all protocols in L2TP family.

It is abstract for the same mechanical reason IP is: name and read() are both declared abstract by ProtocolBase and neither is defined here, so the class cannot be instantiated. Bind a version, never this class.

Changed in version 1.5.0: Became an abstract base for the family; its version-2 parsing moved to L2TPv2.

property info_name: Literal['l2tp']

Key name of the info dict.

classmethod id()[source]

Index ID of the protocol.

Return type:

tuple[Literal['L2TP'], Literal['L2TPv2']]

Returns:

Index ID of the protocol – the family name, then every version in it, as HTTP.id does for its own family. L2F and L2TPv3 join this tuple when they are implemented.

classmethod __index__()[source]

Numeral registry index of the protocol.

Raises:

UnsupportedCall – This protocol has no registry entry.

Return type:

NoReturn

Note

An abstract base is reached by nothing, so it has no index of its own. That is also the project’s rule for module layout – a distinct __index__ means a distinct module, and a base carrying none claims no module of its own beyond holding the family together.

Versions Not Implemented

L2TPv3 [RFC 3931] has a different session header and control message header from v2, and is reachable two ways: over UDP port 1701 like v2, and directly over IP as protocol number 115. That second route is why Internet.__proto__ leaves 115 unbound: the binding waits on an L2TPv3 class. It would also be the first member of this family with a real __index__(), and so the first that needs a module of its own under the project’s one-module-per-index rule.

L2F [RFC 2341] is reached when the version nibble reads 1. It is not an earlier version of L2TP: RFC 2661 §3.1 requires Ver to be 2 and reserves the value 1 “to permit detection of L2F packets should they arrive intermixed with L2TP packets”. L2F is a separate protocol with its own header, so it is to be implemented as L2F, the canonical name, with L2TPv1 only as an alias in its id() – the relationship HTTP/3 has to QUIC. See HTTPv1.id for how a version-flavoured alias is spelled: canonical name first, alias second, since callers take element zero as canonical.

Version Dispatch

Nothing dispatches on the version nibble, because L2TPv2 is the only version implemented. Another would follow the precedent of HTTP, which reads a version and delegates to a per-version class. L2TP is the easier case: HTTP._guess_version has to infer the version – recognising the HTTP/2 preface or an HTTP/1 start line, else trial-parsing each candidate – because HTTP’s wire format carries no version field, whereas L2TP states its version explicitly in those four bits. A deterministic switch on Ver is enough, and no new registry is needed.

Footnotes