#2248 - Big refactor of base with all Network Interface subclasses created to allow for proper management of ports on devices as it was starting to get messy with the Router. Some routing tests still need fixing as ARP doesn't seem to be working properly
This commit is contained in:
@@ -21,7 +21,7 @@ class PacketCapture:
|
||||
The PCAPs are logged to: <simulation output directory>/<hostname>/<hostname>_<ip address>_pcap.log
|
||||
"""
|
||||
|
||||
def __init__(self, hostname: str, ip_address: Optional[str] = None, switch_port_number: Optional[int] = None):
|
||||
def __init__(self, hostname: str, ip_address: Optional[str] = None, interface_num: Optional[int] = None):
|
||||
"""
|
||||
Initialize the PacketCapture process.
|
||||
|
||||
@@ -32,8 +32,8 @@ class PacketCapture:
|
||||
"The hostname for which PCAP logs are being recorded."
|
||||
self.ip_address: str = ip_address
|
||||
"The IP address associated with the PCAP logs."
|
||||
self.switch_port_number = switch_port_number
|
||||
"The SwitchPort number."
|
||||
self.interface_num = interface_num
|
||||
"The interface num on the Node."
|
||||
|
||||
self.inbound_logger = None
|
||||
self.outbound_logger = None
|
||||
@@ -81,8 +81,8 @@ class PacketCapture:
|
||||
"""Get PCAP the logger name."""
|
||||
if self.ip_address:
|
||||
return f"{self.hostname}_{self.ip_address}_{'outbound' if outbound else 'inbound'}_pcap"
|
||||
if self.switch_port_number:
|
||||
return f"{self.hostname}_port-{self.switch_port_number}_{'outbound' if outbound else 'inbound'}_pcap"
|
||||
if self.interface_num:
|
||||
return f"{self.hostname}_port-{self.interface_num}_{'outbound' if outbound else 'inbound'}_pcap"
|
||||
return f"{self.hostname}_{'outbound' if outbound else 'inbound'}_pcap"
|
||||
|
||||
def _get_log_path(self, outbound: bool = False) -> Path:
|
||||
|
||||
@@ -13,7 +13,7 @@ from primaite.simulator.network.transmission.network_layer import IPPacket, IPPr
|
||||
from primaite.simulator.network.transmission.transport_layer import Port, TCPHeader, UDPHeader
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from primaite.simulator.network.hardware.base import ARPCache, NIC
|
||||
from primaite.simulator.network.hardware.base import NetworkInterface
|
||||
from primaite.simulator.system.core.software_manager import SoftwareManager
|
||||
from primaite.simulator.system.core.sys_log import SysLog
|
||||
|
||||
@@ -84,8 +84,6 @@ class SessionManager:
|
||||
self.software_manager: SoftwareManager = None # Noqa
|
||||
self.node: Node = None # noqa
|
||||
|
||||
|
||||
|
||||
def describe_state(self) -> Dict:
|
||||
"""
|
||||
Produce a dictionary describing the current state of this object.
|
||||
@@ -104,7 +102,7 @@ class SessionManager:
|
||||
|
||||
@staticmethod
|
||||
def _get_session_key(
|
||||
frame: Frame, inbound_frame: bool = True
|
||||
frame: Frame, inbound_frame: bool = True
|
||||
) -> Tuple[IPProtocol, IPv4Address, Optional[Port], Optional[Port]]:
|
||||
"""
|
||||
Extracts the session key from the given frame.
|
||||
@@ -142,19 +140,19 @@ class SessionManager:
|
||||
dst_port = None
|
||||
return protocol, with_ip_address, src_port, dst_port
|
||||
|
||||
def resolve_outbound_nic(self, dst_ip_address: IPv4Address) -> Optional[NIC]:
|
||||
for nic in self.node.nics.values():
|
||||
if dst_ip_address in nic.ip_network and nic.enabled:
|
||||
return nic
|
||||
return self.software_manager.arp.get_default_gateway_nic()
|
||||
def resolve_outbound_network_interface(self, dst_ip_address: IPv4Address) -> Optional['NetworkInterface']:
|
||||
for network_interface in self.node.network_interfaces.values():
|
||||
if dst_ip_address in network_interface.ip_network and network_interface.enabled:
|
||||
return network_interface
|
||||
return self.software_manager.arp.get_default_gateway_network_interface()
|
||||
|
||||
def resolve_outbound_transmission_details(
|
||||
self, dst_ip_address: Optional[Union[IPv4Address, IPv4Network]] = None, session_id: Optional[str] = None
|
||||
) -> Tuple[Optional["NIC"], Optional[str], IPv4Address, Optional[IPProtocol], bool]:
|
||||
self, dst_ip_address: Optional[Union[IPv4Address, IPv4Network]] = None, session_id: Optional[str] = None
|
||||
) -> Tuple[Optional['NetworkInterface'], Optional[str], IPv4Address, Optional[IPProtocol], bool]:
|
||||
if not isinstance(dst_ip_address, (IPv4Address, IPv4Network)):
|
||||
dst_ip_address = IPv4Address(dst_ip_address)
|
||||
is_broadcast = False
|
||||
outbound_nic = None
|
||||
outbound_network_interface = None
|
||||
dst_mac_address = None
|
||||
protocol = None
|
||||
|
||||
@@ -172,36 +170,36 @@ class SessionManager:
|
||||
dst_ip_address = dst_ip_address.broadcast_address
|
||||
if dst_ip_address:
|
||||
# Find a suitable NIC for the broadcast
|
||||
for nic in self.node.nics.values():
|
||||
if dst_ip_address in nic.ip_network and nic.enabled:
|
||||
for network_interface in self.node.network_interfaces.values():
|
||||
if dst_ip_address in network_interface.ip_network and network_interface.enabled:
|
||||
dst_mac_address = "ff:ff:ff:ff:ff:ff"
|
||||
outbound_nic = nic
|
||||
outbound_network_interface = network_interface
|
||||
break
|
||||
else:
|
||||
# Resolve MAC address for unicast transmission
|
||||
use_default_gateway = True
|
||||
for nic in self.node.nics.values():
|
||||
if dst_ip_address in nic.ip_network and nic.enabled:
|
||||
for network_interface in self.node.network_interfaces.values():
|
||||
if dst_ip_address in network_interface.ip_network and network_interface.enabled:
|
||||
dst_mac_address = self.software_manager.arp.get_arp_cache_mac_address(dst_ip_address)
|
||||
break
|
||||
|
||||
if dst_ip_address:
|
||||
use_default_gateway = False
|
||||
outbound_nic = self.software_manager.arp.get_arp_cache_nic(dst_ip_address)
|
||||
outbound_network_interface = self.software_manager.arp.get_arp_cache_network_interface(dst_ip_address)
|
||||
|
||||
if use_default_gateway:
|
||||
dst_mac_address = self.software_manager.arp.get_default_gateway_mac_address()
|
||||
outbound_nic = self.software_manager.arp.get_default_gateway_nic()
|
||||
return outbound_nic, dst_mac_address, dst_ip_address, protocol, is_broadcast
|
||||
outbound_network_interface = self.software_manager.arp.get_default_gateway_network_interface()
|
||||
return outbound_network_interface, dst_mac_address, dst_ip_address, protocol, is_broadcast
|
||||
|
||||
def receive_payload_from_software_manager(
|
||||
self,
|
||||
payload: Any,
|
||||
dst_ip_address: Optional[Union[IPv4Address, IPv4Network]] = None,
|
||||
dst_port: Optional[Port] = None,
|
||||
session_id: Optional[str] = None,
|
||||
ip_protocol: IPProtocol = IPProtocol.TCP,
|
||||
icmp_packet: Optional[ICMPPacket] = None
|
||||
self,
|
||||
payload: Any,
|
||||
dst_ip_address: Optional[Union[IPv4Address, IPv4Network]] = None,
|
||||
dst_port: Optional[Port] = None,
|
||||
session_id: Optional[str] = None,
|
||||
ip_protocol: IPProtocol = IPProtocol.TCP,
|
||||
icmp_packet: Optional[ICMPPacket] = None
|
||||
) -> Union[Any, None]:
|
||||
"""
|
||||
Receive a payload from the SoftwareManager and send it to the appropriate NIC for transmission.
|
||||
@@ -222,19 +220,19 @@ class SessionManager:
|
||||
dst_mac_address = "ff:ff:ff:ff:ff:ff"
|
||||
else:
|
||||
dst_mac_address = payload.target_mac_addr
|
||||
outbound_nic = self.resolve_outbound_nic(payload.target_ip_address)
|
||||
outbound_network_interface = self.resolve_outbound_network_interface(payload.target_ip_address)
|
||||
is_broadcast = payload.request
|
||||
ip_protocol = IPProtocol.UDP
|
||||
else:
|
||||
vals = self.resolve_outbound_transmission_details(
|
||||
dst_ip_address=dst_ip_address, session_id=session_id
|
||||
)
|
||||
outbound_nic, dst_mac_address, dst_ip_address, protocol, is_broadcast = vals
|
||||
outbound_network_interface, dst_mac_address, dst_ip_address, protocol, is_broadcast = vals
|
||||
if protocol:
|
||||
ip_protocol = protocol
|
||||
|
||||
# Check if outbound NIC and destination MAC address are resolved
|
||||
if not outbound_nic or not dst_mac_address:
|
||||
if not outbound_network_interface or not dst_mac_address:
|
||||
return False
|
||||
|
||||
tcp_header = None
|
||||
@@ -249,10 +247,18 @@ class SessionManager:
|
||||
src_port=dst_port,
|
||||
dst_port=dst_port,
|
||||
)
|
||||
# TODO: Only create IP packet if not ARP
|
||||
# ip_packet = None
|
||||
# if dst_port != Port.ARP:
|
||||
# IPPacket(
|
||||
# src_ip_address=outbound_network_interface.ip_address,
|
||||
# dst_ip_address=dst_ip_address,
|
||||
# protocol=ip_protocol
|
||||
# )
|
||||
# Construct the frame for transmission
|
||||
frame = Frame(
|
||||
ethernet=EthernetHeader(src_mac_addr=outbound_nic.mac_address, dst_mac_addr=dst_mac_address),
|
||||
ip=IPPacket(src_ip_address=outbound_nic.ip_address, dst_ip_address=dst_ip_address, protocol=ip_protocol),
|
||||
ethernet=EthernetHeader(src_mac_addr=outbound_network_interface.mac_address, dst_mac_addr=dst_mac_address),
|
||||
ip=IPPacket(src_ip_address=outbound_network_interface.ip_address, dst_ip_address=dst_ip_address, protocol=ip_protocol),
|
||||
tcp=tcp_header,
|
||||
udp=udp_header,
|
||||
icmp=icmp_packet,
|
||||
@@ -271,9 +277,9 @@ class SessionManager:
|
||||
self.sessions_by_uuid[session.uuid] = session
|
||||
|
||||
# Send the frame through the NIC
|
||||
return outbound_nic.send_frame(frame)
|
||||
return outbound_network_interface.send_frame(frame)
|
||||
|
||||
def receive_frame(self, frame: Frame, from_nic: NIC):
|
||||
def receive_frame(self, frame: Frame, from_network_interface: 'NetworkInterface'):
|
||||
"""
|
||||
Receive a Frame.
|
||||
|
||||
@@ -302,7 +308,7 @@ class SessionManager:
|
||||
port=dst_port,
|
||||
protocol=frame.ip.protocol,
|
||||
session_id=session.uuid,
|
||||
from_nic=from_nic,
|
||||
from_network_interface=from_network_interface,
|
||||
frame=frame
|
||||
)
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ class SoftwareManager:
|
||||
)
|
||||
|
||||
def receive_payload_from_session_manager(
|
||||
self, payload: Any, port: Port, protocol: IPProtocol, session_id: str, from_nic: "NIC", frame: Frame
|
||||
self, payload: Any, port: Port, protocol: IPProtocol, session_id: str, from_network_interface: "NIC", frame: Frame
|
||||
):
|
||||
"""
|
||||
Receive a payload from the SessionManager and forward it to the corresponding service or application.
|
||||
@@ -177,7 +177,7 @@ class SoftwareManager:
|
||||
"""
|
||||
receiver: Optional[Union[Service, Application]] = self.port_protocol_mapping.get((port, protocol), None)
|
||||
if receiver:
|
||||
receiver.receive(payload=payload, session_id=session_id, from_nic=from_nic, frame=frame)
|
||||
receiver.receive(payload=payload, session_id=session_id, from_network_interface=from_network_interface, frame=frame)
|
||||
else:
|
||||
self.sys_log.error(f"No service or application found for port {port} and protocol {protocol}")
|
||||
pass
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import abstractmethod
|
||||
from ipaddress import IPv4Address
|
||||
from typing import Any, Dict, Optional, Union
|
||||
|
||||
from prettytable import MARKDOWN, PrettyTable
|
||||
|
||||
from primaite.simulator.network.hardware.base import NIC
|
||||
from primaite.simulator.network.hardware.base import NetworkInterface
|
||||
from primaite.simulator.network.protocols.arp import ARPEntry, ARPPacket
|
||||
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 Port, UDPHeader
|
||||
from primaite.simulator.network.transmission.network_layer import IPProtocol
|
||||
from primaite.simulator.network.transmission.transport_layer import Port
|
||||
from primaite.simulator.system.services.service import Service
|
||||
from primaite.utils.validators import IPV4Address
|
||||
|
||||
|
||||
class ARP(Service):
|
||||
@@ -21,7 +20,7 @@ class ARP(Service):
|
||||
Manages ARP for resolving network layer addresses into link layer addresses. It maintains an ARP cache,
|
||||
sends ARP requests and replies, and processes incoming ARP packets.
|
||||
"""
|
||||
arp: Dict[IPv4Address, ARPEntry] = {}
|
||||
arp: Dict[IPV4Address, ARPEntry] = {}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
kwargs["name"] = "ARP"
|
||||
@@ -30,7 +29,7 @@ class ARP(Service):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def describe_state(self) -> Dict:
|
||||
pass
|
||||
return super().describe_state()
|
||||
|
||||
def show(self, markdown: bool = False):
|
||||
"""
|
||||
@@ -48,7 +47,7 @@ class ARP(Service):
|
||||
[
|
||||
str(ip),
|
||||
arp.mac_address,
|
||||
self.software_manager.node.nics[arp.nic_uuid].mac_address,
|
||||
self.software_manager.node.network_interfaces[arp.network_interface_uuid].mac_address,
|
||||
]
|
||||
)
|
||||
print(table)
|
||||
@@ -57,7 +56,13 @@ class ARP(Service):
|
||||
"""Clears the arp cache."""
|
||||
self.arp.clear()
|
||||
|
||||
def add_arp_cache_entry(self, ip_address: IPv4Address, mac_address: str, nic: NIC, override: bool = False):
|
||||
def add_arp_cache_entry(
|
||||
self,
|
||||
ip_address: IPV4Address,
|
||||
mac_address: str,
|
||||
network_interface: NetworkInterface,
|
||||
override: bool = False
|
||||
):
|
||||
"""
|
||||
Add an ARP entry to the cache.
|
||||
|
||||
@@ -66,20 +71,20 @@ class ARP(Service):
|
||||
|
||||
:param ip_address: The IP address to be added to the cache.
|
||||
:param mac_address: The MAC address associated with the IP address.
|
||||
:param nic: The NIC through which the NIC with the IP address is reachable.
|
||||
:param network_interface: The NIC through which the NIC with the IP address is reachable.
|
||||
:param override: If True, an existing entry for the IP address will be overridden. Default is False.
|
||||
"""
|
||||
for _nic in self.software_manager.node.nics.values():
|
||||
if _nic.ip_address == ip_address:
|
||||
for _network_interface in self.software_manager.node.network_interfaces.values():
|
||||
if _network_interface.ip_address == ip_address:
|
||||
return
|
||||
if override or not self.arp.get(ip_address):
|
||||
self.sys_log.info(f"Adding ARP cache entry for {mac_address}/{ip_address} via NIC {nic}")
|
||||
arp_entry = ARPEntry(mac_address=mac_address, nic_uuid=nic.uuid)
|
||||
self.sys_log.info(f"Adding ARP cache entry for {mac_address}/{ip_address} via NIC {network_interface}")
|
||||
arp_entry = ARPEntry(mac_address=mac_address, network_interface_uuid=network_interface.uuid)
|
||||
|
||||
self.arp[ip_address] = arp_entry
|
||||
|
||||
@abstractmethod
|
||||
def get_arp_cache_mac_address(self, ip_address: IPv4Address) -> Optional[str]:
|
||||
def get_arp_cache_mac_address(self, ip_address: IPV4Address) -> Optional[str]:
|
||||
"""
|
||||
Retrieves the MAC address associated with a given IP address from the ARP cache.
|
||||
|
||||
@@ -89,7 +94,7 @@ class ARP(Service):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_arp_cache_nic(self, ip_address: IPv4Address) -> Optional[NIC]:
|
||||
def get_arp_cache_network_interface(self, ip_address: IPV4Address) -> Optional[NetworkInterface]:
|
||||
"""
|
||||
Retrieves the NIC associated with a given IP address from the ARP cache.
|
||||
|
||||
@@ -98,18 +103,20 @@ class ARP(Service):
|
||||
"""
|
||||
pass
|
||||
|
||||
def send_arp_request(self, target_ip_address: Union[IPv4Address, str]):
|
||||
def send_arp_request(self, target_ip_address: Union[IPV4Address, str]):
|
||||
"""
|
||||
Sends an ARP request to resolve the MAC address of a target IP address.
|
||||
|
||||
:param target_ip_address: The target IP address for which the MAC address is being requested.
|
||||
"""
|
||||
outbound_nic = self.software_manager.session_manager.resolve_outbound_nic(target_ip_address)
|
||||
if outbound_nic:
|
||||
self.sys_log.info(f"Sending ARP request from NIC {outbound_nic} for ip {target_ip_address}")
|
||||
outbound_network_interface = self.software_manager.session_manager.resolve_outbound_network_interface(
|
||||
target_ip_address
|
||||
)
|
||||
if outbound_network_interface:
|
||||
self.sys_log.info(f"Sending ARP request from NIC {outbound_network_interface} for ip {target_ip_address}")
|
||||
arp_packet = ARPPacket(
|
||||
sender_ip_address=outbound_nic.ip_address,
|
||||
sender_mac_addr=outbound_nic.mac_address,
|
||||
sender_ip_address=outbound_network_interface.ip_address,
|
||||
sender_mac_addr=outbound_network_interface.mac_address,
|
||||
target_ip_address=target_ip_address,
|
||||
)
|
||||
self.software_manager.session_manager.receive_payload_from_software_manager(
|
||||
@@ -125,11 +132,13 @@ class ARP(Service):
|
||||
Sends an ARP reply in response to an ARP request.
|
||||
|
||||
:param arp_reply: The ARP packet containing the reply.
|
||||
:param from_nic: The NIC from which the ARP reply is sent.
|
||||
:param from_network_interface: The NIC from which the ARP reply is sent.
|
||||
"""
|
||||
|
||||
outbound_nic = self.software_manager.session_manager.resolve_outbound_nic(arp_reply.target_ip_address)
|
||||
if outbound_nic:
|
||||
outbound_network_interface = self.software_manager.session_manager.resolve_outbound_network_interface(
|
||||
arp_reply.target_ip_address
|
||||
)
|
||||
if outbound_network_interface:
|
||||
self.sys_log.info(
|
||||
f"Sending ARP reply from {arp_reply.sender_mac_addr}/{arp_reply.sender_ip_address} "
|
||||
f"to {arp_reply.target_ip_address}/{arp_reply.target_mac_addr} "
|
||||
@@ -147,31 +156,33 @@ class ARP(Service):
|
||||
|
||||
|
||||
@abstractmethod
|
||||
def _process_arp_request(self, arp_packet: ARPPacket, from_nic: NIC):
|
||||
def _process_arp_request(self, arp_packet: ARPPacket, from_network_interface: NIC):
|
||||
"""
|
||||
Processes an incoming ARP request.
|
||||
|
||||
:param arp_packet: The ARP packet containing the request.
|
||||
:param from_nic: The NIC that received the ARP request.
|
||||
:param from_network_interface: The NIC that received the ARP request.
|
||||
"""
|
||||
self.sys_log.info(
|
||||
f"Received ARP request for {arp_packet.target_ip_address} from "
|
||||
f"{arp_packet.sender_mac_addr}/{arp_packet.sender_ip_address} "
|
||||
)
|
||||
|
||||
def _process_arp_reply(self, arp_packet: ARPPacket, from_nic: NIC):
|
||||
def _process_arp_reply(self, arp_packet: ARPPacket, from_network_interface: NIC):
|
||||
"""
|
||||
Processes an incoming ARP reply.
|
||||
|
||||
:param arp_packet: The ARP packet containing the reply.
|
||||
:param from_nic: The NIC that received the ARP reply.
|
||||
:param from_network_interface: The NIC that received the ARP reply.
|
||||
"""
|
||||
self.sys_log.info(
|
||||
f"Received ARP response for {arp_packet.sender_ip_address} "
|
||||
f"from {arp_packet.sender_mac_addr} via NIC {from_nic}"
|
||||
f"from {arp_packet.sender_mac_addr} via Network Interface {from_network_interface}"
|
||||
)
|
||||
self.add_arp_cache_entry(
|
||||
ip_address=arp_packet.sender_ip_address, mac_address=arp_packet.sender_mac_addr, nic=from_nic
|
||||
ip_address=arp_packet.sender_ip_address,
|
||||
mac_address=arp_packet.sender_mac_addr,
|
||||
network_interface=from_network_interface
|
||||
)
|
||||
|
||||
def receive(self, payload: Any, session_id: str, **kwargs) -> bool:
|
||||
@@ -183,15 +194,15 @@ class ARP(Service):
|
||||
:param kwargs: Additional keyword arguments.
|
||||
:return: True if the payload was processed successfully, otherwise False.
|
||||
"""
|
||||
if not isinstance(payload, ARPPacket):
|
||||
print("failied on payload check", type(payload))
|
||||
if not super().receive(payload, session_id, **kwargs):
|
||||
return False
|
||||
|
||||
from_nic = kwargs.get("from_nic")
|
||||
from_network_interface = kwargs.get("from_network_interface")
|
||||
if payload.request:
|
||||
self._process_arp_request(arp_packet=payload, from_nic=from_nic)
|
||||
self._process_arp_request(arp_packet=payload, from_network_interface=from_network_interface)
|
||||
else:
|
||||
self._process_arp_reply(arp_packet=payload, from_nic=from_nic)
|
||||
self._process_arp_reply(arp_packet=payload, from_network_interface=from_network_interface)
|
||||
return True
|
||||
|
||||
def __contains__(self, item: Any) -> bool:
|
||||
"""
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from ipaddress import IPv4Address
|
||||
from typing import Optional
|
||||
|
||||
from primaite.simulator.network.hardware.base import NIC
|
||||
from primaite.simulator.system.services.arp.arp import ARP, ARPPacket
|
||||
|
||||
|
||||
class HostARP(ARP):
|
||||
def get_default_gateway_mac_address(self) -> Optional[str]:
|
||||
if self.software_manager.node.default_gateway:
|
||||
return self.get_arp_cache_mac_address(self.software_manager.node.default_gateway)
|
||||
|
||||
def get_default_gateway_nic(self) -> Optional[NIC]:
|
||||
if self.software_manager.node.default_gateway:
|
||||
return self.get_arp_cache_nic(self.software_manager.node.default_gateway)
|
||||
|
||||
def _get_arp_cache_mac_address(
|
||||
self, ip_address: IPv4Address, is_reattempt: bool = False, is_default_gateway_attempt: bool = False
|
||||
) -> Optional[str]:
|
||||
arp_entry = self.arp.get(ip_address)
|
||||
|
||||
if arp_entry:
|
||||
return arp_entry.mac_address
|
||||
else:
|
||||
if not is_reattempt:
|
||||
self.send_arp_request(ip_address)
|
||||
return self._get_arp_cache_mac_address(
|
||||
ip_address=ip_address, is_reattempt=True, is_default_gateway_attempt=is_default_gateway_attempt
|
||||
)
|
||||
else:
|
||||
if self.software_manager.node.default_gateway:
|
||||
if not is_default_gateway_attempt:
|
||||
self.send_arp_request(self.software_manager.node.default_gateway)
|
||||
return self._get_arp_cache_mac_address(
|
||||
ip_address=self.software_manager.node.default_gateway, is_reattempt=True, is_default_gateway_attempt=True
|
||||
)
|
||||
return None
|
||||
|
||||
def get_arp_cache_mac_address(self, ip_address: IPv4Address) -> Optional[str]:
|
||||
"""
|
||||
Get the MAC address associated with an IP address.
|
||||
|
||||
:param ip_address: The IP address to look up in the cache.
|
||||
:return: The MAC address associated with the IP address, or None if not found.
|
||||
"""
|
||||
return self._get_arp_cache_mac_address(ip_address)
|
||||
|
||||
def _get_arp_cache_nic(
|
||||
self, ip_address: IPv4Address, is_reattempt: bool = False, is_default_gateway_attempt: bool = False
|
||||
) -> Optional[NIC]:
|
||||
arp_entry = self.arp.get(ip_address)
|
||||
|
||||
if arp_entry:
|
||||
return self.software_manager.node.nics[arp_entry.nic_uuid]
|
||||
else:
|
||||
if not is_reattempt:
|
||||
self.send_arp_request(ip_address)
|
||||
return self._get_arp_cache_nic(
|
||||
ip_address=ip_address, is_reattempt=True, is_default_gateway_attempt=is_default_gateway_attempt
|
||||
)
|
||||
else:
|
||||
if self.software_manager.node.default_gateway:
|
||||
if not is_default_gateway_attempt:
|
||||
self.send_arp_request(self.software_manager.node.default_gateway)
|
||||
return self._get_arp_cache_nic(
|
||||
ip_address=self.software_manager.node.default_gateway, is_reattempt=True, is_default_gateway_attempt=True
|
||||
)
|
||||
return None
|
||||
|
||||
def get_arp_cache_nic(self, ip_address: IPv4Address) -> Optional[NIC]:
|
||||
"""
|
||||
Get the NIC associated with an IP address.
|
||||
|
||||
:param ip_address: The IP address to look up in the cache.
|
||||
:return: The NIC associated with the IP address, or None if not found.
|
||||
"""
|
||||
return self._get_arp_cache_nic(ip_address)
|
||||
|
||||
def _process_arp_request(self, arp_packet: ARPPacket, from_nic: NIC):
|
||||
super()._process_arp_request(arp_packet, from_nic)
|
||||
# Unmatched ARP Request
|
||||
if arp_packet.target_ip_address != from_nic.ip_address:
|
||||
self.sys_log.info(
|
||||
f"Ignoring ARP request for {arp_packet.target_ip_address}. Current IP address is {from_nic.ip_address}"
|
||||
)
|
||||
return
|
||||
|
||||
# Matched ARP request
|
||||
self.add_arp_cache_entry(
|
||||
ip_address=arp_packet.sender_ip_address, mac_address=arp_packet.sender_mac_addr, nic=from_nic
|
||||
)
|
||||
arp_packet = arp_packet.generate_reply(from_nic.mac_address)
|
||||
self.send_arp_reply(arp_packet)
|
||||
@@ -1,98 +1,78 @@
|
||||
# class RouterARPCache(ARPCache):
|
||||
# from ipaddress import IPv4Address
|
||||
# from typing import Optional, Any
|
||||
#
|
||||
# from primaite.simulator.network.hardware.nodes.network.router import RouterInterface, Router
|
||||
# from primaite.simulator.network.protocols.arp import ARPPacket
|
||||
# from primaite.simulator.network.transmission.data_link_layer import Frame
|
||||
# from primaite.simulator.system.services.arp.arp import ARP
|
||||
#
|
||||
#
|
||||
# class RouterARP(ARP):
|
||||
# """
|
||||
# Inherits from ARPCache and adds router-specific ARP packet processing.
|
||||
#
|
||||
# :ivar SysLog sys_log: A system log for logging messages.
|
||||
# :ivar Router router: The router to which this ARP cache belongs.
|
||||
# """
|
||||
# router: Router
|
||||
#
|
||||
# def __init__(self, sys_log: SysLog, router: Router):
|
||||
# super().__init__(sys_log)
|
||||
# self.router: Router = router
|
||||
# def get_arp_cache_mac_address(self, ip_address: IPv4Address) -> Optional[str]:
|
||||
# arp_entry = self.arp.get(ip_address)
|
||||
#
|
||||
# def process_arp_packet(
|
||||
# self, from_nic: NIC, frame: Frame, route_table: RouteTable, is_reattempt: bool = False
|
||||
# ) -> None:
|
||||
# """
|
||||
# Processes a received ARP (Address Resolution Protocol) packet in a router-specific way.
|
||||
# if arp_entry:
|
||||
# return arp_entry.mac_address
|
||||
# return None
|
||||
#
|
||||
# This method is responsible for handling both ARP requests and responses. It processes ARP packets received on a
|
||||
# Network Interface Card (NIC) and performs actions based on whether the packet is a request or a reply. This
|
||||
# includes updating the ARP cache, forwarding ARP replies, sending ARP requests for unknown destinations, and
|
||||
# handling packet TTL (Time To Live).
|
||||
# def get_arp_cache_network_interface(self, ip_address: IPv4Address) -> Optional[RouterInterface]:
|
||||
# arp_entry = self.arp.get(ip_address)
|
||||
# if arp_entry:
|
||||
# return self.software_manager.node.network_interfaces[arp_entry.network_interface_uuid]
|
||||
# return None
|
||||
#
|
||||
# The method first checks if the ARP packet is a request or a reply. For ARP replies, it updates the ARP cache
|
||||
# and forwards the reply if necessary. For ARP requests, it checks if the target IP matches one of the router's
|
||||
# NICs and sends an ARP reply if so. If the destination is not directly connected, it consults the routing table
|
||||
# to find the best route and reattempts ARP request processing if needed.
|
||||
#
|
||||
# :param from_nic: The NIC that received the ARP packet.
|
||||
# :param frame: The frame containing the ARP packet.
|
||||
# :param route_table: The routing table of the router.
|
||||
# :param is_reattempt: Flag to indicate if this is a reattempt of processing the ARP packet, defaults to False.
|
||||
# """
|
||||
# arp_packet = frame.arp
|
||||
#
|
||||
# # ARP Reply
|
||||
# if not arp_packet.request:
|
||||
# if arp_packet.target_ip_address == from_nic.ip_address:
|
||||
# # reply to the Router specifically
|
||||
# self.sys_log.info(
|
||||
# f"Received ARP response for {arp_packet.sender_ip_address} "
|
||||
# f"from {arp_packet.sender_mac_addr} via NIC {from_nic}"
|
||||
# )
|
||||
# self.add_arp_cache_entry(
|
||||
# ip_address=arp_packet.sender_ip_address,
|
||||
# mac_address=arp_packet.sender_mac_addr,
|
||||
# nic=from_nic,
|
||||
# )
|
||||
# return
|
||||
#
|
||||
# # # Reply for a connected requested
|
||||
# # nic = self.get_arp_cache_nic(arp_packet.target_ip_address)
|
||||
# # if nic:
|
||||
# # self.sys_log.info(
|
||||
# # f"Forwarding arp reply for {arp_packet.target_ip_address}, from {arp_packet.sender_ip_address}"
|
||||
# # )
|
||||
# # arp_packet.sender_mac_addr = nic.mac_address
|
||||
# # frame.decrement_ttl()
|
||||
# # if frame.ip and frame.ip.ttl < 1:
|
||||
# # self.sys_log.info("Frame discarded as TTL limit reached")
|
||||
# # return
|
||||
# # nic.send_frame(frame)
|
||||
# # return
|
||||
#
|
||||
# # ARP Request
|
||||
# self.sys_log.info(
|
||||
# f"Received ARP request for {arp_packet.target_ip_address} from "
|
||||
# f"{arp_packet.sender_mac_addr}/{arp_packet.sender_ip_address} "
|
||||
# )
|
||||
# # Matched ARP request
|
||||
# def _process_arp_request(self, arp_packet: ARPPacket, from_network_interface: RouterInterface):
|
||||
# super()._process_arp_request(arp_packet, from_network_interface)
|
||||
# self.add_arp_cache_entry(
|
||||
# ip_address=arp_packet.sender_ip_address, mac_address=arp_packet.sender_mac_addr, nic=from_nic
|
||||
# ip_address=arp_packet.sender_ip_address, mac_address=arp_packet.sender_mac_addr,
|
||||
# network_interface=from_network_interface
|
||||
# )
|
||||
#
|
||||
# # If the target IP matches one of the router's NICs
|
||||
# for nic in self.nics.values():
|
||||
# if nic.enabled and nic.ip_address == arp_packet.target_ip_address:
|
||||
# arp_reply = arp_packet.generate_reply(from_nic.mac_address)
|
||||
# self.send_arp_reply(arp_reply, from_nic)
|
||||
# for network_interface in self.network_interfaces.values():
|
||||
# if network_interface.enabled and network_interface.ip_address == arp_packet.target_ip_address:
|
||||
# arp_reply = arp_packet.generate_reply(from_network_interface.mac_address)
|
||||
# self.send_arp_reply(arp_reply)
|
||||
# return
|
||||
#
|
||||
# # # Check Route Table
|
||||
# # route = route_table.find_best_route(arp_packet.target_ip_address)
|
||||
# # if route and route != self.router.route_table.default_route:
|
||||
# # nic = self.get_arp_cache_nic(route.next_hop_ip_address)
|
||||
# #
|
||||
# # if not nic:
|
||||
# # if not is_reattempt:
|
||||
# # self.send_arp_request(route.next_hop_ip_address, ignore_networks=[frame.ip.src_ip_address])
|
||||
# # return self.process_arp_packet(from_nic, frame, route_table, is_reattempt=True)
|
||||
# # else:
|
||||
# # self.sys_log.info("Ignoring ARP request as destination unavailable/No ARP entry found")
|
||||
# # return
|
||||
# # else:
|
||||
# # arp_reply = arp_packet.generate_reply(from_nic.mac_address)
|
||||
# # self.send_arp_reply(arp_reply, from_nic)
|
||||
# # return
|
||||
# def _process_arp_reply(self, arp_packet: ARPPacket, from_network_interface: RouterInterface):
|
||||
# if arp_packet.target_ip_address == from_network_interface.ip_address:
|
||||
# super()._process_arp_reply(arp_packet, from_network_interface)
|
||||
#
|
||||
# def receive(self, payload: Any, session_id: str, **kwargs) -> bool:
|
||||
# """
|
||||
# Processes received data, handling ARP packets.
|
||||
#
|
||||
# :param payload: The payload received.
|
||||
# :param session_id: The session ID associated with the received data.
|
||||
# :param kwargs: Additional keyword arguments.
|
||||
# :return: True if the payload was processed successfully, otherwise False.
|
||||
# """
|
||||
# if not super().receive(payload, session_id, **kwargs):
|
||||
# return False
|
||||
#
|
||||
# arp_packet: ARPPacket = payload
|
||||
# from_network_interface: RouterInterface = kwargs["from_network_interface"]
|
||||
#
|
||||
# for network_interface in self.network_interfaces.values():
|
||||
# # ARP frame is for this Router
|
||||
# if network_interface.ip_address == arp_packet.target_ip_address:
|
||||
# if payload.request:
|
||||
# self._process_arp_request(arp_packet=arp_packet, from_network_interface=from_network_interface)
|
||||
# else:
|
||||
# self._process_arp_reply(arp_packet=arp_packet, from_network_interface=from_network_interface)
|
||||
# return True
|
||||
#
|
||||
# # ARP frame is not for this router, pass back down to Router to continue routing
|
||||
# frame: Frame = kwargs["frame"]
|
||||
# self.router.process_frame(frame=frame, from_network_interface=from_network_interface)
|
||||
#
|
||||
# return True
|
||||
|
||||
@@ -3,7 +3,6 @@ from ipaddress import IPv4Address
|
||||
from typing import Dict, Any, Union, Optional, Tuple
|
||||
|
||||
from primaite import getLogger
|
||||
from primaite.simulator.network.hardware.base import NIC
|
||||
from primaite.simulator.network.protocols.icmp import ICMPPacket, ICMPType
|
||||
from primaite.simulator.network.transmission.data_link_layer import Frame
|
||||
from primaite.simulator.network.transmission.network_layer import IPProtocol
|
||||
@@ -53,7 +52,7 @@ class ICMP(Service):
|
||||
return False
|
||||
if target_ip_address.is_loopback:
|
||||
self.sys_log.info("Pinging loopback address")
|
||||
return any(nic.enabled for nic in self.nics.values())
|
||||
return any(network_interface.enabled for network_interface in self.network_interfaces.values())
|
||||
self.sys_log.info(f"Pinging {target_ip_address}:", to_terminal=True)
|
||||
sequence, identifier = 0, None
|
||||
while sequence < pings:
|
||||
@@ -88,9 +87,9 @@ class ICMP(Service):
|
||||
:param pings: The number of pings to send. Defaults to 4.
|
||||
:return: A tuple containing the next sequence number and the identifier.
|
||||
"""
|
||||
nic = self.software_manager.session_manager.resolve_outbound_nic(target_ip_address)
|
||||
network_interface = self.software_manager.session_manager.resolve_outbound_network_interface(target_ip_address)
|
||||
|
||||
if not nic:
|
||||
if not network_interface:
|
||||
self.sys_log.error(
|
||||
"Cannot send ICMP echo request as there is no outbound NIC to use. Try configuring the default gateway."
|
||||
)
|
||||
@@ -118,9 +117,11 @@ class ICMP(Service):
|
||||
"""
|
||||
self.sys_log.info(f"Received echo request from {frame.ip.src_ip_address}")
|
||||
|
||||
nic = self.software_manager.session_manager.resolve_outbound_nic(frame.ip.src_ip_address)
|
||||
network_interface = self.software_manager.session_manager.resolve_outbound_network_interface(
|
||||
frame.ip.src_ip_address
|
||||
)
|
||||
|
||||
if not nic:
|
||||
if not network_interface:
|
||||
self.sys_log.error(
|
||||
"Cannot send ICMP echo reply as there is no outbound NIC to use. Try configuring the default gateway."
|
||||
)
|
||||
|
||||
@@ -16,30 +16,30 @@
|
||||
# super().__init__(sys_log, arp_cache)
|
||||
# self.router = router
|
||||
#
|
||||
# def process_icmp(self, frame: Frame, from_nic: NIC, is_reattempt: bool = False):
|
||||
# def process_icmp(self, frame: Frame, from_network_interface: NIC, is_reattempt: bool = False):
|
||||
# """
|
||||
# Process incoming ICMP frames based on ICMP type.
|
||||
#
|
||||
# :param frame: The incoming frame to process.
|
||||
# :param from_nic: The network interface where the frame is coming from.
|
||||
# :param from_network_interface: The network interface where the frame is coming from.
|
||||
# :param is_reattempt: Flag to indicate if the process is a reattempt.
|
||||
# """
|
||||
# if frame.icmp.icmp_type == ICMPType.ECHO_REQUEST:
|
||||
# # determine if request is for router interface or whether it needs to be routed
|
||||
#
|
||||
# for nic in self.router.nics.values():
|
||||
# if nic.ip_address == frame.ip.dst_ip_address:
|
||||
# if nic.enabled:
|
||||
# for network_interface in self.router.network_interfaces.values():
|
||||
# if network_interface.ip_address == frame.ip.dst_ip_address:
|
||||
# if network_interface.enabled:
|
||||
# # reply to the request
|
||||
# if not is_reattempt:
|
||||
# self.sys_log.info(f"Received echo request from {frame.ip.src_ip_address}")
|
||||
# target_mac_address = self.arp.get_arp_cache_mac_address(frame.ip.src_ip_address)
|
||||
# src_nic = self.arp.get_arp_cache_nic(frame.ip.src_ip_address)
|
||||
# src_nic = self.arp.get_arp_cache_network_interface(frame.ip.src_ip_address)
|
||||
# tcp_header = TCPHeader(src_port=Port.ARP, dst_port=Port.ARP)
|
||||
#
|
||||
# # Network Layer
|
||||
# ip_packet = IPPacket(
|
||||
# src_ip_address=nic.ip_address,
|
||||
# src_ip_address=network_interface.ip_address,
|
||||
# dst_ip_address=frame.ip.src_ip_address,
|
||||
# protocol=IPProtocol.ICMP,
|
||||
# )
|
||||
@@ -67,12 +67,12 @@
|
||||
# return
|
||||
#
|
||||
# # Route the frame
|
||||
# self.router.process_frame(frame, from_nic)
|
||||
# self.router.process_frame(frame, from_network_interface)
|
||||
#
|
||||
# elif frame.icmp.icmp_type == ICMPType.ECHO_REPLY:
|
||||
# for nic in self.router.nics.values():
|
||||
# if nic.ip_address == frame.ip.dst_ip_address:
|
||||
# if nic.enabled:
|
||||
# for network_interface in self.router.network_interfaces.values():
|
||||
# if network_interface.ip_address == frame.ip.dst_ip_address:
|
||||
# if network_interface.enabled:
|
||||
# time = frame.transmission_duration()
|
||||
# time_str = f"{time}ms" if time > 0 else "<1ms"
|
||||
# self.sys_log.info(
|
||||
@@ -87,4 +87,4 @@
|
||||
#
|
||||
# return
|
||||
# # Route the frame
|
||||
# self.router.process_frame(frame, from_nic)
|
||||
# self.router.process_frame(frame, from_network_interface)
|
||||
|
||||
Reference in New Issue
Block a user