Source code for pcapkit.protocols.link.l2tp
# -*- coding: utf-8 -*-
"""L2TP - Layer Two Tunnelling Protocol
==========================================
:mod:`pcapkit.protocols.link.l2tp` contains
:class:`~pcapkit.protocols.link.l2tp.L2TP` only,
which implements extractor for Layer Two Tunnelling
Protocol (L2TP) [*]_, whose structure is described
as below:
+========+=======+=======================+============================================+
| Octets | Bits | Name | Description |
+========+=======+=======================+============================================+
| 0 | 0 | ``l2tp.flags`` | Flags and Version Info |
+--------+-------+-----------------------+--------------------------------------------+
| 0 | 0 | ``l2tp.flags.type`` | Type (control / data) |
+--------+-------+-----------------------+--------------------------------------------+
| 0 | 1 | ``l2tp.flags.len`` | Length |
+--------+-------+-----------------------+--------------------------------------------+
| 0 | 2 | | Reserved (must be zero ``\\x00``) |
+--------+-------+-----------------------+--------------------------------------------+
| 0 | 4 | ``l2tp.flags.seq`` | Sequence |
+--------+-------+-----------------------+--------------------------------------------+
| 0 | 5 | | Reserved (must be zero ``\\x00``) |
+--------+-------+-----------------------+--------------------------------------------+
| 0 | 6 | ``l2tp.flags.offset`` | Offset |
+--------+-------+-----------------------+--------------------------------------------+
| 0 | 7 | ``l2tp.flags.prio`` | Priority |
+--------+-------+-----------------------+--------------------------------------------+
| 1 | 8 | | Reserved (must be zero ``\\x00``) |
+--------+-------+-----------------------+--------------------------------------------+
| 1 | 12 | ``l2tp.version`` | Version (``2``) |
+--------+-------+-----------------------+--------------------------------------------+
| 2 | 16 | ``l2tp.length`` | Length (optional by ``len``) |
+--------+-------+-----------------------+--------------------------------------------+
| 4 | 32 | ``l2tp.tunnelid`` | Tunnel ID |
+--------+-------+-----------------------+--------------------------------------------+
| 6 | 48 | ``l2tp.sessionid`` | Session ID |
+--------+-------+-----------------------+--------------------------------------------+
| 8 | 64 | ``l2tp.ns`` | Sequence Number (optional by ``seq``) |
+--------+-------+-----------------------+--------------------------------------------+
| 10 | 80 | ``l2tp.nr`` | Next Sequence Number (optional by ``seq``) |
+--------+-------+-----------------------+--------------------------------------------+
| 12 | 96 | ``l2tp.offset`` | Offset Size (optional by ``offset``) |
+========+=======+=======================+============================================+
.. [*] https://en.wikipedia.org/wiki/Layer_2_Tunneling_Protocol
"""
from typing import TYPE_CHECKING
from pcapkit.const.l2tp.type import Type as RegType_Type
from pcapkit.protocols.data.link.l2tp import L2TP as DataType_L2TP
from pcapkit.protocols.data.link.l2tp import Flags as DataType_Flags
from pcapkit.protocols.link.link import Link
from pcapkit.utilities.exceptions import UnsupportedCall
if TYPE_CHECKING:
from typing import Any, NoReturn, Optional
from typing_extensions import Literal
__all__ = ['L2TP']
[docs]class L2TP(Link[DataType_L2TP]):
"""This class implements Layer Two Tunnelling Protocol."""
##########################################################################
# Properties.
##########################################################################
@property
def name(self) -> 'Literal["Layer 2 Tunnelling Protocol"]':
"""Name of current protocol."""
return 'Layer 2 Tunnelling Protocol'
@property
def length(self) -> 'int':
"""Header length of current protocol."""
return self._info.hdr_len
@property
def type(self) -> 'Literal["control", "data"]':
"""L2TP type."""
return self._info.flags.type
##########################################################################
# Methods.
##########################################################################
[docs] def read(self, length: 'Optional[int]' = None, **kwargs: 'Any') -> 'DataType_L2TP': # pylint: disable=unused-argument
"""Read Layer Two Tunnelling Protocol.
Structure of L2TP header [:rfc:`2661`]:
.. code-block:: text
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|T|L|x|x|S|x|O|P|x|x|x|x| Ver | Length (opt) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Tunnel ID | Session ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Ns (opt) | Nr (opt) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Offset Size (opt) | Offset pad... (opt)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Args:
length: Length of packet data.
**kwargs: Arbitrary keyword arguments.
Returns:
Parsed packet data.
"""
if length is None:
length = len(self)
_flag = self._read_binary(1)
flags = DataType_Flags(
type=RegType_Type(int(_flag[0])),
len=bool(int(_flag[1])),
seq=bool(int(_flag[4])),
offset=bool(int(_flag[6])),
prio=bool(int(_flag[7])),
)
_vers = self._read_fileng(1).hex()[1]
_hlen = self._read_unpack(2) if flags.len else None
_tnnl = self._read_unpack(2)
_sssn = self._read_unpack(2)
_nseq = self._read_unpack(2) if flags.seq else None
_nrec = self._read_unpack(2) if flags.seq else None
_size = self._read_unpack(2) if flags.offset else 0
l2tp = DataType_L2TP(
flags=flags,
version=int(_vers, base=16),
length=_hlen,
tunnelid=_tnnl,
sessionid=_sssn,
ns=_nseq,
nr=_nrec,
offset=8*_size or None,
)
hdr_len = _hlen or (6 + 2*(int(_flag[1]) + 2*int(_flag[4]) + int(_flag[6])))
l2tp.__update__([
('hdr_len', hdr_len + _size * 8),
])
# if _size:
# l2tp['padding'] = self._read_fileng(_size * 8)
return self._decode_next_layer(l2tp, length - l2tp.hdr_len)
[docs] def make(self, **kwargs: 'Any') -> 'NoReturn': # pylint: disable=unused-argument
"""Make (construct) packet data.
Args:
**kwargs: Arbitrary keyword arguments.
Returns:
bytes: Constructed packet data.
"""
raise NotImplementedError
##########################################################################
# Data models.
##########################################################################
def __length_hint__(self) -> 'Literal[16]':
"""Return an estimated length for the object."""
return 16
[docs] @classmethod
def __index__(cls) -> 'NoReturn': # pylint: disable=invalid-index-returned
"""Numeral registry index of the protocol.
Raises:
UnsupportedCall: This protocol has no registry entry.
"""
raise UnsupportedCall(f'{cls.__name__!r} object cannot be interpreted as an integer')