diff --git a/docs/source/simulation.rst b/docs/source/simulation.rst index 0af6c89f..81476998 100644 --- a/docs/source/simulation.rst +++ b/docs/source/simulation.rst @@ -17,3 +17,4 @@ Contents simulation_structure simulation_components/network/physical_layer + simulation_components/network/transport_to_data_link_layer diff --git a/docs/source/simulation_components/network/transport_to_data_link_layer.rst b/docs/source/simulation_components/network/transport_to_data_link_layer.rst new file mode 100644 index 00000000..8273339c --- /dev/null +++ b/docs/source/simulation_components/network/transport_to_data_link_layer.rst @@ -0,0 +1,135 @@ +.. only:: comment + + © Crown-owned copyright 2023, Defence Science and Technology Laboratory UK + +Transport Layer to Data Link Layer +================================== + +From the `OSI Model <[OSI Model](https://en.wikipedia.org/wiki/OSI_model,>`_, the transport layer (layer 4) through to +the data link layer (layer 2) have been loosely modelled to provide somewhat realistic network Frame generation. + +Transport Layer (Layer 4) +######################### + +**UDPHeader:** Represents a UDP header for the transport layer of a Network Frame. It includes source and destination +ports. UDP (User Datagram Protocol) is a connectionless and unreliable transport protocol used for data transmission. + +**TCPFlags:** Enum representing TCP control flags used in a TCP connection, such as SYN, ACK, FIN, and RST. TCP +(Transmission Control Protocol) is a connection-oriented and reliable transport protocol used for establishing and +maintaining data streams. + +**TCPHeader:** Represents a TCP header for the transport layer of a Network Frame. It includes source and destination +ports and TCP flags. This header is used for establishing and managing TCP connections. + +Network Layer (Layer 3) +####################### + + +**IPProtocol:** Enum representing transport layer protocols in the IP header, such as TCP, UDP, and ICMP. It is used to +indicate the type of transport layer protocol being used in the IP header. + +**Precedence:** Enum representing the Precedence levels in Quality of Service (QoS) for IP packets. It is used to +specify the priority of IP packets for Quality of Service handling. + +**ICMPType:** Enumeration of common ICMP (Internet Control Message Protocol) types. It defines various types of ICMP +messages used for network troubleshooting and error reporting. + +**ICMPHeader:** Models an ICMP header and includes ICMP type, code, identifier, and sequence number. It is used to +create ICMP packets for network control and error reporting. + +**IPPacket:** Represents the IP layer of a network frame. It includes source and destination IP addresses, protocol +(TCP/UDP/ICMP), Time to Live (TTL), and Precedence for QoS. This header is used to route data packets across the +network based on IP addresses. + + +PrimAITE Layer (Custom Layer) +############################# + +The PrimAITE layer has a custom header represented by the ``PrimaiteHeader`` class. It is designed to carry +PrimAITE-specific metadata required for reinforcement learning (RL) purposes. + +**PrimaiteHeader:** This is a custom header for carrying PrimAITE-specific metadata. It contains the following fields: + - **agent_source:** Enum representing the agent source of the transmission, such as RED, GREEN, or BLUE. This field helps identify the source or category of the data transmission. + - **data_status:** Enum representing the status of the data in transmission, such as GOOD, COMPROMISED, or CORRUPT. This field indicates the integrity of the data being transmitted. + +Data Link Layer (Layer 2) +######################### + +**EthernetHeader:** Represents the Ethernet layer of a network frame. It includes source and destination MAC addresses. +This header is used to identify the physical hardware addresses of devices on a local network. + +**Frame:** Represents a complete network frame with all layers. It includes an ``EthernetHeader``, an ``IPPacket``, an +optional ``TCPHeader``, ``UDPHeader``, or ``ICMPHeader``, a ``PrimaiteHeader`` and an optional payload. This class +combines all the headers and data to create a complete network frame that can be sent over the network and used in the +PrimAITE simulation. + +Basic Usage +########### + +TCP SYN Frame +------------- + +Here we will model a TCP synchronize request from a port 80 on the host 192.168.0.100 which has a NIC with a MAC +address of 'aa:bb:cc:dd:ee:ff' to port 8080 on the host 10.0.0.10 which has a NIC with a MAC address of +'11:22:33:44:55:66'. + + +.. code-block:: python + + from primaite.simulator.network.transmission.data_link_layer import EthernetHeader, Frame + from primaite.simulator.network.transmission.network_layer import IPPacket, IPProtocol + from primaite.simulator.network.transmission.transport_layer import TCPFlags, TCPHeader + + # Transport Layer + tcp_header = TCPHeader( + src_port=80, + dst_port=8080, + flags=[TCPFlags.SYN] + ) + + # Network Layer + ip_packet = IPPacket( + src_ip="192.168.0.100", + dst_ip="10.0.0.10", + protocol=IPProtocol.TCP + ) + # Data Link Layer + ethernet_header = EthernetHeader( + src_mac_addr="aa:bb:cc:dd:ee:ff", + dst_mac_addr="11:22:33:44:55:66" + ) + + frame = Frame( + ethernet=ethernet_header, + ip=ip_packet, + tcp=tcp_header, + ) + +This produces the following ``Frame`` (displayed in json format) + +.. code-block:: json + + { + "ethernet": { + "src_mac_addr": "aa:bb:cc:dd:ee:ff", + "dst_mac_addr": "11:22:33:44:55:66" + }, + "ip": { + "src_ip": "192.168.0.100", + "dst_ip": "10.0.0.10", + "protocol": "tcp", + "ttl": 64, + "precedence": 0 + }, + "tcp": { + "src_port": 80, + "dst_port": 8080, + "flags": [ + 1 + ] + }, + "primaite": { + "agent_source": 2, + "data_status": 1 + } + } diff --git a/src/primaite/simulator/network/transmission/data_link_layer.py b/src/primaite/simulator/network/transmission/data_link_layer.py index e8133f86..b9d969bd 100644 --- a/src/primaite/simulator/network/transmission/data_link_layer.py +++ b/src/primaite/simulator/network/transmission/data_link_layer.py @@ -89,12 +89,12 @@ class Frame(BaseModel): "UDP header." icmp: Optional[ICMPHeader] = None "ICMP header." - primaite_header: PrimaiteHeader = PrimaiteHeader() + primaite: PrimaiteHeader = PrimaiteHeader() "PrimAITE header." payload: Optional[Any] = None "Raw data payload." @property def size(self) -> int: - """The size of the Frame in Bytes.""" + """The size in Bytes.""" return len(self.model_dump_json().encode("utf-8")) diff --git a/src/primaite/simulator/network/transmission/physical_layer.py b/src/primaite/simulator/network/transmission/physical_layer.py index 2fbfbc6b..ee2297b6 100644 --- a/src/primaite/simulator/network/transmission/physical_layer.py +++ b/src/primaite/simulator/network/transmission/physical_layer.py @@ -3,7 +3,7 @@ from __future__ import annotations import re import secrets from ipaddress import IPv4Address, IPv4Network -from typing import Any, Dict, List, Optional +from typing import Dict, List, Optional from primaite import getLogger from primaite.exceptions import NetworkError @@ -221,16 +221,19 @@ class Link(SimComponent): current_load: int = 0 "The current load on the link in Mbps." - def model_post_init(self, __context: Any) -> None: + def __init__(self, **kwargs): """ Ensure that endpoint_a and endpoint_b are not the same NIC. + Connect the link to the NICs after creation. + :raises ValueError: If endpoint_a and endpoint_b are the same NIC. """ - if self.endpoint_a == self.endpoint_b: + if kwargs["endpoint_a"] == kwargs["endpoint_b"]: msg = "endpoint_a and endpoint_b cannot be the same NIC" _LOGGER.error(msg) raise ValueError(msg) + super().__init__(**kwargs) self.endpoint_a.connect_link(self) self.endpoint_b.connect_link(self) diff --git a/tests/unit_tests/_primaite/_simulator/_network/_transmission/test_data_link_layer.py b/tests/unit_tests/_primaite/_simulator/_network/_transmission/test_data_link_layer.py index e8e3fa57..83b215ca 100644 --- a/tests/unit_tests/_primaite/_simulator/_network/_transmission/test_data_link_layer.py +++ b/tests/unit_tests/_primaite/_simulator/_network/_transmission/test_data_link_layer.py @@ -26,8 +26,8 @@ def test_frame_minimal_instantiation(): assert frame.tcp.flags == [TCPFlags.SYN] # Check primaite custom header default values - assert frame.primaite_header.agent_source == AgentSource.GREEN - assert frame.primaite_header.data_status == DataStatus.GOOD + assert frame.primaite.agent_source == AgentSource.GREEN + assert frame.primaite.data_status == DataStatus.GOOD # Check that model can be dumped down to json and returned as size in Bytes assert frame.size