Skip to content

Commit

Permalink
Working on RAW socket
Browse files Browse the repository at this point in the history
  • Loading branch information
ccie18643 committed Sep 16, 2024
1 parent 1ff534f commit f2a7f7b
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 11 deletions.
2 changes: 1 addition & 1 deletion pytcp/protocols/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,6 @@ def from_proto(proto: Proto) -> IpProto:
return IpProto.ICMP6

if isinstance(proto, Raw):
return IpProto.RAW
return proto.ip_proto

raise ValueError(f"Unknown protocol: {type(proto)}")
3 changes: 3 additions & 0 deletions pytcp/protocols/raw/raw__assembler.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

from pytcp.lib.proto_assembler import ProtoAssembler
from pytcp.lib.tracker import Tracker
from pytcp.protocols.enums import IpProto
from pytcp.protocols.raw.raw__base import Raw


Expand All @@ -51,6 +52,7 @@ def __init__(
self,
*,
raw__payload: bytes = bytes(),
ip_proto: IpProto = IpProto.RAW,
echo_tracker: Tracker | None = None,
) -> None:
"""
Expand All @@ -60,3 +62,4 @@ def __init__(
self._tracker: Tracker = Tracker(prefix="TX", echo_tracker=echo_tracker)

self._payload: bytes = raw__payload
self._ip_proto: IpProto = ip_proto
10 changes: 10 additions & 0 deletions pytcp/protocols/raw/raw__base.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from typing import override

from pytcp.lib.proto import Proto
from pytcp.protocols.enums import IpProto


class Raw(Proto):
Expand All @@ -46,6 +47,7 @@ class Raw(Proto):
"""

_payload: bytes
_ip_proto: IpProto

@override
def __len__(self) -> int:
Expand Down Expand Up @@ -86,3 +88,11 @@ def payload(self) -> bytes:
"""

return self._payload

@property
def ip_proto(self) -> IpProto:
"""
Get the IP protocol number.
"""

return self._ip_proto
12 changes: 3 additions & 9 deletions pytcp/socket/raw__metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any

from pytcp.protocols.enums import IpProto
from pytcp.socket.socket import AddressFamily, SocketType

if TYPE_CHECKING:
Expand All @@ -54,6 +55,7 @@ class RawMetadata:
ip__ver: int
ip__local_address: IpAddress
ip__remote_address: IpAddress
ip__proto: IpProto

raw__data: bytes = bytes()

Expand All @@ -70,15 +72,7 @@ def socket_ids(self) -> list[tuple[Any, ...]]:
AddressFamily.from_ver(self.ip__ver),
SocketType.RAW,
self.ip__local_address.unspecified,
0,
self.ip__remote_address.unspecified,
0,
),
(
AddressFamily.from_ver(self.ip__ver),
SocketType.RAW,
self.ip__local_address.unspecified,
0,
int(self.ip__proto),
self.ip__remote_address.unspecified,
0,
),
Expand Down
139 changes: 138 additions & 1 deletion pytcp/socket/raw__socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import threading
from typing import TYPE_CHECKING, Any, override
from pytcp import stack
from net_addr import (
Ip4Address,
Expand Down Expand Up @@ -127,7 +128,117 @@ def _get_ip_addresses(
return local_ip_address, remote_ip_address
def sendto(self, data: bytes, address: tuple[str, int]) -> int:
def bind(self, address: tuple[str, int]) -> None:
"""
Bind the socket to local address.
"""
# The 'bind' call will bind socket to specific / unspecified local IP
# address.
local_ip_address: IpAddress
match self._address_family:
case AddressFamily.INET6:
try:
if (local_ip_address := Ip6Address(address[0])) not in set(
stack.packet_handler.ip6_unicast
) | {Ip6Address()}:
raise OSError(
"[Errno 99] Cannot assign requested address - "
"[Local IP address not owned by stack]"
)
except Ip6AddressFormatError as error:
raise gaierror(
"[Errno -2] Name or service not known - "
"[Malformed local IP address]"
) from error
case AddressFamily.INET4:
try:
if (local_ip_address := Ip4Address(address[0])) not in set(
stack.packet_handler.ip4_unicast
) | {Ip4Address()}:
raise OSError(
"[Errno 99] Cannot assign requested address - "
"[Local IP address not owned by stack]"
)
except Ip4AddressFormatError as error:
raise gaierror(
"[Errno -2] Name or service not known - "
"[Malformed local IP address]"
) from error
stack.sockets.pop(self.socket_id, None)
self._local_ip_address = local_ip_address
stack.sockets[self.socket_id] = self
__debug__ and log("socket", f"<g>[{self}]</> - Bound")
def connect(self, address: tuple[str, int]) -> None:
"""
Connect local socket to remote socket.
"""
# The 'connect' call will bind socket to specific local IP address (will
# rebind if necessary) and specific remote IP address.
# Sanity check on remote port number (0 is a valid remote port in
# BSD socket implementation).
if (remote_port := address[1]) not in range(0, 65536):
raise OverflowError(
"connect(): port must be 0-65535. - [Port out of range]"
)
# Set local and remote ip addresses aproprietely
local_ip_address, remote_ip_address = self._get_ip_addresses(
remote_address=address,
)
# Re-register socket with new socket id
stack.sockets.pop(self.socket_id, None)
self._local_ip_address = local_ip_address
self._remote_ip_address = remote_ip_address
self._remote_port = remote_port
stack.sockets[self.socket_id] = self
__debug__ and log("socket", f"<g>[{self}]</> - Connected socket")
def send(self, data: bytes) -> int:
"""
Send the data to connected remote host.
"""
# The 'send' call requires 'connect' call to be run prior to it.
if self._remote_ip_address.is_unspecified:
raise OSError(
"[Errno 89] Destination address require - "
"[Socket has no destination address set]"
)
# TODO: Implement support for IP6 and IP4 instead UDP
tx_status = stack.packet_handler.send_udp_packet(
ip__local_address=self._local_ip_address,
ip__remote_address=self._remote_ip_address,
udp__local_port=self._local_port,
udp__remote_port=self._remote_port,
udp__payload=data,
)
sent_data_len = (
len(data)
if tx_status is TxStatus.PASSED__ETHERNET__TO_TX_RING
else 0
)
__debug__ and log(
"socket",
f"<g>[{self}]</> - <lr>Sent</> {sent_data_len} bytes of data",
)
return sent_data_len
def sendto(self, data: bytes, address: tuple[str, int]) -> int:
"""
Send the data to remote host.
"""
Expand Down Expand Up @@ -158,6 +269,32 @@ def sendto(self, data: bytes, address: tuple[str, int]) -> int:
return sent_data_len
def recv(
self, bufsize: int | None = None, timeout: float | None = None
) -> bytes:
"""
Read data from socket.
"""
# TODO - Implement support for buffsize
if self._unreachable:
self._unreachable = False
raise ConnectionRefusedError(
"[Errno 111] Connection refused - "
"[Remote host sent ICMP Unreachable]"
)
if self._packet_rx_md_ready.acquire(timeout=timeout):
data_rx = self._packet_rx_md.pop(0).udp__data
__debug__ and log(
"socket",
f"<g>[{self}]</> - <lg>Received</> {len(data_rx)} "
"bytes of data",
)
return data_rx
raise ReceiveTimeout
def recvfrom(
self, bufsize: int | None = None, timeout: float | None = None
) -> tuple[bytes, tuple[str, int]]:
Expand Down
22 changes: 22 additions & 0 deletions pytcp/stack/packet_handler/packet_handler__ip4__tx.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from pytcp.lib.logger import log
from pytcp.lib.tx_status import TxStatus
from pytcp.protocols.defaults import IP4__DEFAULT_TTL
from pytcp.protocols.enums import IpProto
from pytcp.protocols.ip4.ip4__assembler import Ip4Assembler, Ip4FragAssembler
from pytcp.protocols.raw.raw__assembler import RawAssembler
from pytcp.protocols.tcp.tcp__assembler import TcpAssembler
Expand Down Expand Up @@ -376,3 +377,24 @@ def __validate_dst_ip4_address(
return TxStatus.DROPED__IP4__DST_UNSPECIFIED

return ip4__dst

def send_ip4_packet(
self,
*,
ip4__local_address: Ip4Address,
ip4__remote_address: Ip4Address,
ip4__proto: IpProto,
ip4__payload: bytes = bytes(),
) -> TxStatus:
"""
Interface method for RAW Socket -> Packet Assembler communication.
"""

return self._phtx_ip4(
ip4__src=ip4__local_address,
ip4__dst=ip4__remote_address,
ip4__payload=RawAssembler(
raw__payload=ip4__payload,
ip_proto=ip4__proto,
),
)
22 changes: 22 additions & 0 deletions pytcp/stack/packet_handler/packet_handler__ip6__tx.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from pytcp.lib.logger import log
from pytcp.lib.tx_status import TxStatus
from pytcp.protocols.defaults import IP6__DEFAULT_HOP_LIMIT
from pytcp.protocols.enums import IpProto
from pytcp.protocols.icmp6.icmp6__base import Icmp6
from pytcp.protocols.icmp6.message.mld2.icmp6_mld2_message__report import (
Icmp6Mld2ReportMessage,
Expand Down Expand Up @@ -307,3 +308,24 @@ def __validate_dst_ip6_address(
return TxStatus.DROPED__IP6__DST_UNSPECIFIED

return ip6__dst

def send_ip6_packet(
self,
*,
ip6__local_address: Ip6Address,
ip6__remote_address: Ip6Address,
ip6__next: IpProto,
ip6__payload: bytes = bytes(),
) -> TxStatus:
"""
Interface method for RAW Socket -> Packet Assembler communication.
"""

return self._phtx_ip6(
ip6__src=ip6__local_address,
ip6__dst=ip6__remote_address,
ip6__payload=RawAssembler(
raw__payload=ip6__payload,
ip_proto=ip6__next,
),
)

0 comments on commit f2a7f7b

Please sign in to comment.