Skip to content

Commit

Permalink
Refactored ARP related code.
Browse files Browse the repository at this point in the history
  • Loading branch information
ccie18643 committed Nov 12, 2023
1 parent 7e106ef commit a0ff053
Show file tree
Hide file tree
Showing 45 changed files with 1,862 additions and 1,244 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@

__pycache__/
venv/
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@
"python.analysis.extraPaths": [
"./pytcp"
],
"python.formatting.provider": "black"
"python.formatting.provider": "none",
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
}
}
19 changes: 3 additions & 16 deletions pytcp/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,6 @@
}
LOG_DEBUG = False

# Packet integrity sanity check, if enabled it protects the protocol parsers
# from being exposed to malformed or malicious packets that could cause them
# to crash during packet parsing. It progessively check appropriate length
# fields and ensure they are set within sane boundaries. It also checks
# packet's actual header/options/data lengths against above values and default
# minimum/maximum lengths for given protocol. Also packet options (if any) are
# checked in similar fashion to ensure they will not exploit or crash parser.
PACKET_INTEGRITY_CHECK = True

# Packet sanity check, if enabled it validates packet's fields to detect invalid
# values or invalid combinations of values. For example in TCP/UDP it drops
# packets with port set to 0, in TCP it drop packet with SYN and FIN flags set
# simultaneously, for ICMPv6 it provides very detailed check of messages
# integrity.
PACKET_SANITY_CHECK = True

# Drop IPv4 packets containing options - this seems to be widely adopted
# security feature. Stack parses but doesn't support IPv4 options as they are
# mostly useless anyway.
Expand Down Expand Up @@ -144,3 +128,6 @@
# Native support for UDP Echo (used for packet flow unit testing only and should
# always be disabled).
UDP_ECHO_NATIVE_DISABLE = True

# LRU cache size, used by packet parsers to cache parsed field values.
LRU_CACHE_SIZE = 16
79 changes: 79 additions & 0 deletions pytcp/lib/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/usr/bin/env python3

############################################################################
# #
# PyTCP - Python TCP/IP stack #
# Copyright (C) 2020-present Sebastian Majewski #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
# #
# Author's email: [email protected] #
# Github repository: https://github.com/ccie18643/PyTCP #
# #
############################################################################


"""
Module contains methods supporting errors.
pytcp/lib/errors.py
ver 2.7
"""


from __future__ import annotations


class PyTcpError(Exception):
"""
Base class for all PyTCP exceptions.
"""

...


class UnsupportedCaseError(PyTcpError):
"""
Exception raised when the not supposed to be reached
'match' case is being reached for whatever reason.
"""

...


class PacketValidationError(PyTcpError):
"""
Exception raised when packet validation fails.
"""

...


class PacketIntegrityError(PacketValidationError):
"""
Exception raised when integrity check fails.
"""

def __init__(self, message: str):
super().__init__("[INTEGRITY]" + message)


class PacketSanityError(PacketValidationError):
"""
Exception raised when sanity check fails.
"""

def __init__(self, message: str):
super().__init__("[SANITY]" + message)
59 changes: 59 additions & 0 deletions pytcp/lib/protocol_enum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/env python3

############################################################################
# #
# PyTCP - Python TCP/IP stack #
# Copyright (C) 2020-present Sebastian Majewski #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
# #
# Author's email: [email protected] #
# Github repository: https://github.com/ccie18643/PyTCP #
# #
############################################################################


"""
Module contains the ProtocolEnum class.
pytcp/lib/protocol_enum.py
ver 2.7
"""


from __future__ import annotations

from enum import Enum
from typing import Self


class ProtocolEnum(Enum):
def __int__(self) -> int:
return int(self.value)

def __str__(self) -> str:
return str(self.value)

@staticmethod
def _extract(frame: bytes) -> int:
raise NotImplementedError

@classmethod
def from_frame(cls, /, frame: bytes) -> Self:
return cls(cls._extract(frame))

@classmethod
def sanity_check(cls, /, frame: bytes) -> bool:
return cls._extract(frame) in cls
72 changes: 31 additions & 41 deletions pytcp/protocols/arp/fpa.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,87 +41,77 @@
from pytcp.lib.ip4_address import Ip4Address
from pytcp.lib.mac_address import MacAddress
from pytcp.lib.tracker import Tracker
from pytcp.protocols.arp.ps import ARP_HEADER_LEN, ARP_OP_REPLY, ARP_OP_REQUEST
from pytcp.protocols.ether.ps import ETHER_TYPE_ARP


class ArpAssembler:
from pytcp.protocols.arp.ps import (
ARP_HEADER_LEN,
Arp,
ArpHardwareLength,
ArpHardwareType,
ArpOperation,
ArpProtocolLength,
ArpProtocolType,
)


class ArpAssembler(Arp):
"""
ARP packet assembler support class.
"""

ether_type = ETHER_TYPE_ARP

def __init__(
self,
*,
sha: MacAddress = MacAddress(0),
spa: Ip4Address = Ip4Address(0),
tha: MacAddress = MacAddress(0),
tpa: Ip4Address = Ip4Address(0),
oper: int = ARP_OP_REQUEST,
oper: ArpOperation = ArpOperation.REQUEST,
echo_tracker: Tracker | None = None,
) -> None:
"""
Class constructor.
"""

assert oper in (ARP_OP_REQUEST, ARP_OP_REPLY), f"{oper=}"

self._tracker = Tracker(prefix="TX", echo_tracker=echo_tracker)

self._hrtype: int = 1
self._prtype: int = 0x0800
self._hrlen: int = 6
self._prlen: int = 4
self._oper: int = oper
self._sha: MacAddress = sha
self._spa: Ip4Address = spa
self._tha: MacAddress = tha
self._tpa: Ip4Address = tpa
self._hrtype = ArpHardwareType.ETHERNET
self._prtype = ArpProtocolType.IP4
self._hrlen = ArpHardwareLength.ETHERNET
self._prlen = ArpProtocolLength.IP4
self._oper = oper
self._sha = sha
self._spa = spa
self._tha = tha
self._tpa = tpa

def __len__(self) -> int:
"""
Length of the packet.
"""
return ARP_HEADER_LEN

def __str__(self) -> str:
"""
Packet log string.
"""
if self._oper == ARP_OP_REQUEST:
return (
f"ARP request {self._spa} / {self._sha}"
f" > {self._tpa} / {self._tha}"
)
if self._oper == ARP_OP_REPLY:
return (
f"ARP reply {self._spa} / {self._sha}"
f" > {self._tpa} / {self._tha}"
)
return f"ARP request unknown operation {self._oper}"
return ARP_HEADER_LEN

@property
def tracker(self) -> Tracker:
"""
Getter for the '_tracker' property.
"""

return self._tracker

def assemble(self, frame: memoryview) -> None:
def assemble(self, /, frame: memoryview) -> None:
"""
Assemble packet into the raw form.
"""

struct.pack_into(
"!HH BBH 6s 4s 6s 4s",
frame,
0,
self._hrtype,
self._prtype,
self._hrlen,
self._prlen,
self._oper,
int(self._hrtype),
int(self._prtype),
int(self._hrlen),
int(self._prlen),
int(self._oper),
bytes(self._sha),
bytes(self._spa),
bytes(self._tha),
Expand Down
Loading

0 comments on commit a0ff053

Please sign in to comment.