L2TP - Layer Two Tunnelling Protocol

pcapkit.protocols.link.l2tp.protocol 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

The base parses no header, as IP does for its family: only the first 16-bit word, with the version nibble at bits 12-15, is common to every version, so parsing further would impose one version’s layout on all.

class pcapkit.protocols.link.l2tp.protocol.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 no instance of it exists. Bind a version, never this class.

It dispatches on both paths, never giving an instance of this class (#2574). The version field names the member; without it, octets give the one their version nibble names, and fields are refused. from_data() reads the version of its data. See __new__().

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

Changed in version 1.6.0: Constructing it and from_data() give L2TPv2, where they raised TypeError. The version field names the member; without it, octets give the one their version nibble names, and fields are refused (#2574).

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 VLAN.id does for its own family. L2F and L2TPv3 join this tuple when they are implemented.

classmethod from_data(data, **kwargs)[source]

Create protocol instance from data.

Parameters:
Return type:

Self

Returns:

Protocol instance; on this class, an instance of the member the version of data names.

Raises:

ProtocolError – If no member reads that version, on this class.

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.

static __new__(cls, file=None, length=None, **kwargs)[source]

Create the member a construction of this class describes.

The version field names the member, on either path. Without it, parsing octets gives the member the version nibble, bits 12 to 15 of their first word, names, and building from fields is refused, since no field picks a member. That member’s __init__ then runs with the same arguments. A subclass of this class is created as itself.

Parameters:
  • file (IO[bytes] | bytes | None) – Source packet stream, read here without being consumed.

  • length (int | None) – Length of packet data.

  • **kwargs (Any) – Arbitrary keyword arguments.

Return type:

L2TP

Returns:

An uninitialised instance of the member.

Raises:

ProtocolError – If no member reads version; or, without it, if building from fields, or if the octets are shorter than the first word or no member reads their version.

static _member_of_version(version)[source]

The member that reads a version of the version nibble.

Parameters:

version (Any) – Version of L2TP.

Return type:

Type[L2TP]

Returns:

L2TPv2 for 2.

Raises:

ProtocolError – For any other version, or one that is no int, as a bool or a float is even where it equals 2: 1 is L2F and 3 is L2TPv3, neither implemented.

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. Protocol 115 names v3 alone: v2’s __index__() is its UDP port.

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(). See L2TPv2.id for how a version-flavoured alias is spelled: canonical name first, alias second, since callers take element zero as canonical.

Version Dispatch

Constructing the base gives the member its version field names, from octets or from fields; without it, octets give the one their version nibble names, and fields are refused, since no field picks a member (#2574). _member_of_version() maps 2 to L2TPv2 and refuses any other version, for that field, for the nibble and for the data from_data() is handed. UDP port 1701 binds L2TPv2 itself. A further member goes in that switch, and binding the port to the base through a hook making the same switch, as IGMP._dispatch does for IP protocol 2, needs no new registry. Unlike HTTP, whose _guess_version() has to infer the version, L2TP states it in the version nibble.

Footnotes