diff --git a/docs/source/configuration/simulation/nodes/network_examples.rst b/docs/source/configuration/simulation/nodes/network_examples.rst
index 572d8bb5..f5084d4c 100644
--- a/docs/source/configuration/simulation/nodes/network_examples.rst
+++ b/docs/source/configuration/simulation/nodes/network_examples.rst
@@ -29,7 +29,75 @@ and a Server on the same subnet with a single Link connecting the two.
:width: 800
:align: center
+The yaml file contains two nodes in the ``simulation.network.nodes`` array, one with the `pc_1` reference and another
+with the `server_1` reference. both nodes are given a node type, `pc_1` being a `computer` and `server_1` being a
+`server`. Both nodes are then given an ip address and subnet mask.
+The link between the two nodes is configured in the ``simulation.network.links`` array, with the hostname and network
+interface for each being configured under ``endpoint__hostname`` and ``endpoint__port`` respectively.
+
+
+
+.. code-block:: yaml
+ :linenos:
+ :emphasive-lines:
+
+ simulation:
+ network:
+ nodes:
+ - hostname: pc_1
+ type: computer
+ ip_address: 192.168.1.11
+ subnet_mask: 255.255.255.0
+
+ - hostname: server_1
+ type: server
+ ip_address: 192.168.1.13
+ subnet_mask: 255.255.255.0
+
+ links:
+ - endpoint_a_hostname: pc_1
+ endpoint_a_port: 1
+ endpoint_b_hostname: server_1
+ endpoint_b_port: 1
+
+The following codeblock demonstrates how to access this network and all ``.show()`` to output the network details:
+
+.. code-block:: python
+
+ from primaite.simulator.network.networks import client_server_p2p_network
+
+ network = client_server_p2p_network()
+
+ network.show()
+
+Which gives the output:
+
+.. code-block:: text
+
+ +---------------------------------------+
+ | Nodes |
+ +----------+----------+-----------------+
+ | Node | Type | Operating State |
+ +----------+----------+-----------------+
+ | server_1 | Server | ON |
+ | pc_1 | Computer | ON |
+ +----------+----------+-----------------+
+ +------------------------------------------------------------------+
+ | IP Addresses |
+ +----------+------+--------------+---------------+-----------------+
+ | Node | Port | IP Address | Subnet Mask | Default Gateway |
+ +----------+------+--------------+---------------+-----------------+
+ | server_1 | 1 | 192.168.1.13 | 255.255.255.0 | None |
+ | pc_1 | 1 | 192.168.1.11 | 255.255.255.0 | None |
+ +----------+------+--------------+---------------+-----------------+
+ +------------------------------------------------------------------------------------------------------------------------------------------------------+
+ | Links |
+ +------------+----------------------------------------+------------+----------------------------------------+-------+-------------------+--------------+
+ | Endpoint A | A Port | Endpoint B | B Port | is Up | Bandwidth (MBits) | Current Load |
+ +------------+----------------------------------------+------------+----------------------------------------+-------+-------------------+--------------+
+ | pc_1 | Port 1: dd:70:be:52:b1:a9/192.168.1.11 | server_1 | Port 1: 17:3a:11:af:9b:b1/192.168.1.13 | True | 100.0 | 0.00000% |
+ +------------+----------------------------------------+------------+----------------------------------------+-------+-------------------+--------------+
#2. Basic Switched Network
--------------------------
diff --git a/src/primaite/config/_package_data/client-server-p2p-network-example.yaml b/src/primaite/config/_package_data/client-server-p2p-network-example.yaml
index edaeb6f4..798dd318 100644
--- a/src/primaite/config/_package_data/client-server-p2p-network-example.yaml
+++ b/src/primaite/config/_package_data/client-server-p2p-network-example.yaml
@@ -1,3 +1,11 @@
+game:
+ ports:
+ - ARP
+ protocols:
+ - ICMP
+ - TCP
+ - UDP
+
simulation:
network:
nodes:
@@ -8,7 +16,7 @@ simulation:
- hostname: server_1
type: server
- ip_address: 192.168.1.11
+ ip_address: 192.168.1.13
subnet_mask: 255.255.255.0
links:
diff --git a/src/primaite/game/agent/observations/observations.py b/src/primaite/game/agent/observations/observations.py
index 0d6ff2a3..518fdf9f 100644
--- a/src/primaite/game/agent/observations/observations.py
+++ b/src/primaite/game/agent/observations/observations.py
@@ -1,6 +1,6 @@
"""Manages the observation space for the agent."""
from abc import ABC, abstractmethod
-from typing import Any, Dict, Iterable, Type
+from typing import Any, Dict, Iterable, Type, Optional, Union
from gymnasium import spaces
from gymnasium.core import ObsType
@@ -9,7 +9,7 @@ from pydantic import BaseModel, ConfigDict
from primaite import getLogger
_LOGGER = getLogger(__name__)
-WhereType = Iterable[str | int] | None
+WhereType = Optional[Iterable[Union[str, int]]]
class AbstractObservation(ABC):
diff --git a/src/primaite/game/agent/rewards.py b/src/primaite/game/agent/rewards.py
index 726afaa4..0222bfcc 100644
--- a/src/primaite/game/agent/rewards.py
+++ b/src/primaite/game/agent/rewards.py
@@ -26,7 +26,7 @@ the structure:
```
"""
from abc import abstractmethod
-from typing import Callable, Dict, Iterable, List, Optional, Tuple, Type, TYPE_CHECKING
+from typing import Callable, Dict, Iterable, List, Optional, Tuple, Type, TYPE_CHECKING, Union
from typing_extensions import Never
@@ -37,7 +37,7 @@ if TYPE_CHECKING:
from primaite.game.agent.interface import AgentActionHistoryItem
_LOGGER = getLogger(__name__)
-WhereType = Iterable[str | int] | None
+WhereType = Optional[Iterable[Union[str, int]]]
class AbstractReward:
diff --git a/src/primaite/game/game.py b/src/primaite/game/game.py
index 908b5148..336c27df 100644
--- a/src/primaite/game/game.py
+++ b/src/primaite/game/game.py
@@ -244,7 +244,7 @@ class PrimaiteGame:
hostname=node_cfg["hostname"],
ip_address=node_cfg["ip_address"],
subnet_mask=IPv4Address(node_cfg.get("subnet_mask", "255.255.255.0")),
- default_gateway=node_cfg["default_gateway"],
+ default_gateway=node_cfg.get("default_gateway"),
dns_server=node_cfg.get("dns_server", None),
operating_state=NodeOperatingState.ON
if not (p := node_cfg.get("operating_state"))
@@ -255,7 +255,7 @@ class PrimaiteGame:
hostname=node_cfg["hostname"],
ip_address=node_cfg["ip_address"],
subnet_mask=IPv4Address(node_cfg.get("subnet_mask", "255.255.255.0")),
- default_gateway=node_cfg["default_gateway"],
+ default_gateway=node_cfg.get("default_gateway"),
dns_server=node_cfg.get("dns_server", None),
operating_state=NodeOperatingState.ON
if not (p := node_cfg.get("operating_state"))
diff --git a/src/primaite/simulator/network/networks.py b/src/primaite/simulator/network/networks.py
index c1eef224..36d34bc3 100644
--- a/src/primaite/simulator/network/networks.py
+++ b/src/primaite/simulator/network/networks.py
@@ -1,5 +1,9 @@
from ipaddress import IPv4Address
+import yaml
+
+from primaite import PRIMAITE_PATHS
+from primaite.game.game import PrimaiteGame
from primaite.simulator.network.container import Network
from primaite.simulator.network.hardware.nodes.host.computer import Computer
from primaite.simulator.network.hardware.nodes.host.host_node import NIC
@@ -279,3 +283,13 @@ def arcd_uc2_network() -> Network:
router_1.acl.add_rule(action=ACLAction.PERMIT, src_port=Port.HTTP, dst_port=Port.HTTP, position=3)
return network
+
+
+def client_server_p2p_network_example() -> Network:
+ path = PRIMAITE_PATHS.user_config_path / "example_config" / "client-server-p2p-network-example.yaml"
+ with open(path, "r") as file:
+ cfg = yaml.safe_load(file)
+
+ game = PrimaiteGame.from_config(cfg)
+
+ return game.simulation.network