#1800 - Fixed routing and processing of ICMP packets in the Router class
This commit is contained in:
@@ -744,14 +744,12 @@ class ICMP:
|
|||||||
:param frame: The Frame containing the ICMP packet to process.
|
:param frame: The Frame containing the ICMP packet to process.
|
||||||
"""
|
"""
|
||||||
if frame.icmp.icmp_type == ICMPType.ECHO_REQUEST:
|
if frame.icmp.icmp_type == ICMPType.ECHO_REQUEST:
|
||||||
self.sys_log.info(f"Received echo request from {frame.ip.src_ip}")
|
if not is_reattempt:
|
||||||
|
self.sys_log.info(f"Received echo request from {frame.ip.src_ip}")
|
||||||
target_mac_address = self.arp.get_arp_cache_mac_address(frame.ip.src_ip)
|
target_mac_address = self.arp.get_arp_cache_mac_address(frame.ip.src_ip)
|
||||||
|
|
||||||
src_nic = self.arp.get_arp_cache_nic(frame.ip.src_ip)
|
src_nic = self.arp.get_arp_cache_nic(frame.ip.src_ip)
|
||||||
if not src_nic:
|
if not src_nic:
|
||||||
print(self.sys_log.hostname)
|
|
||||||
print(frame.ip.src_ip)
|
|
||||||
self.arp.show()
|
|
||||||
self.arp.send_arp_request(frame.ip.src_ip)
|
self.arp.send_arp_request(frame.ip.src_ip)
|
||||||
self.process_icmp(frame=frame, from_nic=from_nic, is_reattempt=True)
|
self.process_icmp(frame=frame, from_nic=from_nic, is_reattempt=True)
|
||||||
return
|
return
|
||||||
@@ -932,14 +930,13 @@ class Node(SimComponent):
|
|||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
"""Prints a table of the NICs on the Node."""
|
"""Prints a table of the NICs on the Node."""
|
||||||
table = PrettyTable(["MAC Address", "Address", "Default Gateway", "Speed", "Status"])
|
table = PrettyTable(["MAC Address", "Address", "Speed", "Status"])
|
||||||
table.title = f"{self.hostname} Network Interface Cards"
|
table.title = f"{self.hostname} Network Interface Cards"
|
||||||
for nic in self.nics.values():
|
for nic in self.nics.values():
|
||||||
table.add_row(
|
table.add_row(
|
||||||
[
|
[
|
||||||
nic.mac_address,
|
nic.mac_address,
|
||||||
f"{nic.ip_address}/{nic.ip_network.prefixlen}",
|
f"{nic.ip_address}/{nic.ip_network.prefixlen}",
|
||||||
nic.gateway,
|
|
||||||
nic.speed,
|
nic.speed,
|
||||||
"Enabled" if nic.enabled else "Disabled",
|
"Enabled" if nic.enabled else "Disabled",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -69,14 +69,14 @@ class AccessControlList(SimComponent):
|
|||||||
return self._acl
|
return self._acl
|
||||||
|
|
||||||
def add_rule(
|
def add_rule(
|
||||||
self,
|
self,
|
||||||
action: ACLAction,
|
action: ACLAction,
|
||||||
protocol: Optional[IPProtocol] = None,
|
protocol: Optional[IPProtocol] = None,
|
||||||
src_ip: Optional[Union[str, IPv4Address]] = None,
|
src_ip: Optional[Union[str, IPv4Address]] = None,
|
||||||
src_port: Optional[Port] = None,
|
src_port: Optional[Port] = None,
|
||||||
dst_ip: Optional[Union[str, IPv4Address]] = None,
|
dst_ip: Optional[Union[str, IPv4Address]] = None,
|
||||||
dst_port: Optional[Port] = None,
|
dst_port: Optional[Port] = None,
|
||||||
position: int = 0,
|
position: int = 0,
|
||||||
) -> None:
|
) -> None:
|
||||||
if isinstance(src_ip, str):
|
if isinstance(src_ip, str):
|
||||||
src_ip = IPv4Address(src_ip)
|
src_ip = IPv4Address(src_ip)
|
||||||
@@ -96,12 +96,12 @@ class AccessControlList(SimComponent):
|
|||||||
raise ValueError(f"Position {position} is out of bounds.")
|
raise ValueError(f"Position {position} is out of bounds.")
|
||||||
|
|
||||||
def is_permitted(
|
def is_permitted(
|
||||||
self,
|
self,
|
||||||
protocol: IPProtocol,
|
protocol: IPProtocol,
|
||||||
src_ip: Union[str, IPv4Address],
|
src_ip: Union[str, IPv4Address],
|
||||||
src_port: Optional[Port],
|
src_port: Optional[Port],
|
||||||
dst_ip: Union[str, IPv4Address],
|
dst_ip: Union[str, IPv4Address],
|
||||||
dst_port: Optional[Port],
|
dst_port: Optional[Port],
|
||||||
) -> Tuple[bool, Optional[Union[str, ACLRule]]]:
|
) -> Tuple[bool, Optional[Union[str, ACLRule]]]:
|
||||||
if not isinstance(src_ip, IPv4Address):
|
if not isinstance(src_ip, IPv4Address):
|
||||||
src_ip = IPv4Address(src_ip)
|
src_ip = IPv4Address(src_ip)
|
||||||
@@ -112,23 +112,23 @@ class AccessControlList(SimComponent):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(rule.src_ip == src_ip or rule.src_ip is None)
|
(rule.src_ip == src_ip or rule.src_ip is None)
|
||||||
and (rule.dst_ip == dst_ip or rule.dst_ip is None)
|
and (rule.dst_ip == dst_ip or rule.dst_ip is None)
|
||||||
and (rule.protocol == protocol or rule.protocol is None)
|
and (rule.protocol == protocol or rule.protocol is None)
|
||||||
and (rule.src_port == src_port or rule.src_port is None)
|
and (rule.src_port == src_port or rule.src_port is None)
|
||||||
and (rule.dst_port == dst_port or rule.dst_port is None)
|
and (rule.dst_port == dst_port or rule.dst_port is None)
|
||||||
):
|
):
|
||||||
return rule.action == ACLAction.PERMIT, rule
|
return rule.action == ACLAction.PERMIT, rule
|
||||||
|
|
||||||
return self.implicit_action == ACLAction.PERMIT, f"Implicit {self.implicit_action.name}"
|
return self.implicit_action == ACLAction.PERMIT, f"Implicit {self.implicit_action.name}"
|
||||||
|
|
||||||
def get_relevant_rules(
|
def get_relevant_rules(
|
||||||
self,
|
self,
|
||||||
protocol: IPProtocol,
|
protocol: IPProtocol,
|
||||||
src_ip: Union[str, IPv4Address],
|
src_ip: Union[str, IPv4Address],
|
||||||
src_port: Port,
|
src_port: Port,
|
||||||
dst_ip: Union[str, IPv4Address],
|
dst_ip: Union[str, IPv4Address],
|
||||||
dst_port: Port,
|
dst_port: Port,
|
||||||
) -> List[ACLRule]:
|
) -> List[ACLRule]:
|
||||||
if not isinstance(src_ip, IPv4Address):
|
if not isinstance(src_ip, IPv4Address):
|
||||||
src_ip = IPv4Address(src_ip)
|
src_ip = IPv4Address(src_ip)
|
||||||
@@ -140,11 +140,11 @@ class AccessControlList(SimComponent):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(rule.src_ip == src_ip or rule.src_ip is None)
|
(rule.src_ip == src_ip or rule.src_ip is None)
|
||||||
or (rule.dst_ip == dst_ip or rule.dst_ip is None)
|
or (rule.dst_ip == dst_ip or rule.dst_ip is None)
|
||||||
or (rule.protocol == protocol or rule.protocol is None)
|
or (rule.protocol == protocol or rule.protocol is None)
|
||||||
or (rule.src_port == src_port or rule.src_port is None)
|
or (rule.src_port == src_port or rule.src_port is None)
|
||||||
or (rule.dst_port == dst_port or rule.dst_port is None)
|
or (rule.dst_port == dst_port or rule.dst_port is None)
|
||||||
):
|
):
|
||||||
relevant_rules.append(rule)
|
relevant_rules.append(rule)
|
||||||
|
|
||||||
@@ -247,11 +247,11 @@ class RouteTable(SimComponent):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def add_route(
|
def add_route(
|
||||||
self,
|
self,
|
||||||
address: Union[IPv4Address, str],
|
address: Union[IPv4Address, str],
|
||||||
subnet_mask: Union[IPv4Address, str],
|
subnet_mask: Union[IPv4Address, str],
|
||||||
next_hop: Union[IPv4Address, str],
|
next_hop: Union[IPv4Address, str],
|
||||||
metric: float = 0.0,
|
metric: float = 0.0,
|
||||||
):
|
):
|
||||||
"""Add a route to the routing table.
|
"""Add a route to the routing table.
|
||||||
|
|
||||||
@@ -367,36 +367,46 @@ class RouterICMP(ICMP):
|
|||||||
# determine if request is for router interface or whether it needs to be routed
|
# determine if request is for router interface or whether it needs to be routed
|
||||||
|
|
||||||
for nic in self.router.nics.values():
|
for nic in self.router.nics.values():
|
||||||
if nic.ip_address == frame.ip.dst_ip and nic.enabled:
|
if nic.ip_address == frame.ip.dst_ip:
|
||||||
# reply to the request
|
if nic.enabled:
|
||||||
self.sys_log.info(f"Received echo request from {frame.ip.src_ip}")
|
# reply to the request
|
||||||
target_mac_address = self.arp.get_arp_cache_mac_address(frame.ip.src_ip)
|
if not is_reattempt:
|
||||||
src_nic = self.arp.get_arp_cache_nic(frame.ip.src_ip)
|
self.sys_log.info(f"Received echo request from {frame.ip.src_ip}")
|
||||||
tcp_header = TCPHeader(src_port=Port.ARP, dst_port=Port.ARP)
|
target_mac_address = self.arp.get_arp_cache_mac_address(frame.ip.src_ip)
|
||||||
|
src_nic = self.arp.get_arp_cache_nic(frame.ip.src_ip)
|
||||||
|
tcp_header = TCPHeader(src_port=Port.ARP, dst_port=Port.ARP)
|
||||||
|
|
||||||
# Network Layer
|
# Network Layer
|
||||||
ip_packet = IPPacket(src_ip=nic.ip_address, dst_ip=frame.ip.src_ip, protocol=IPProtocol.ICMP)
|
ip_packet = IPPacket(src_ip=nic.ip_address, dst_ip=frame.ip.src_ip, protocol=IPProtocol.ICMP)
|
||||||
# Data Link Layer
|
# Data Link Layer
|
||||||
ethernet_header = EthernetHeader(src_mac_addr=src_nic.mac_address, dst_mac_addr=target_mac_address)
|
ethernet_header = EthernetHeader(src_mac_addr=src_nic.mac_address, dst_mac_addr=target_mac_address)
|
||||||
icmp_reply_packet = ICMPPacket(
|
icmp_reply_packet = ICMPPacket(
|
||||||
icmp_type=ICMPType.ECHO_REPLY,
|
icmp_type=ICMPType.ECHO_REPLY,
|
||||||
icmp_code=0,
|
icmp_code=0,
|
||||||
identifier=frame.icmp.identifier,
|
identifier=frame.icmp.identifier,
|
||||||
sequence=frame.icmp.sequence + 1,
|
sequence=frame.icmp.sequence + 1,
|
||||||
)
|
)
|
||||||
frame = Frame(ethernet=ethernet_header, ip=ip_packet, tcp=tcp_header, icmp=icmp_reply_packet)
|
frame = Frame(ethernet=ethernet_header, ip=ip_packet, tcp=tcp_header, icmp=icmp_reply_packet)
|
||||||
self.sys_log.info(f"Sending echo reply to {frame.ip.dst_ip}")
|
self.sys_log.info(f"Sending echo reply to {frame.ip.dst_ip}")
|
||||||
|
|
||||||
src_nic.send_frame(frame)
|
src_nic.send_frame(frame)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Route the frame
|
# Route the frame
|
||||||
self.router.route_frame(frame, from_nic)
|
self.router.route_frame(frame, from_nic)
|
||||||
|
|
||||||
elif frame.icmp.icmp_type == ICMPType.ECHO_REPLY:
|
elif frame.icmp.icmp_type == ICMPType.ECHO_REPLY:
|
||||||
self.sys_log.info(f"Received echo reply from {frame.ip.src_ip}")
|
for nic in self.router.nics.values():
|
||||||
if not self.request_replies.get(frame.icmp.identifier):
|
if nic.ip_address == frame.ip.dst_ip:
|
||||||
self.request_replies[frame.icmp.identifier] = 0
|
if nic.enabled:
|
||||||
self.request_replies[frame.icmp.identifier] += 1
|
self.sys_log.info(f"Received echo reply from {frame.ip.src_ip}")
|
||||||
|
if not self.request_replies.get(frame.icmp.identifier):
|
||||||
|
self.request_replies[frame.icmp.identifier] = 0
|
||||||
|
self.request_replies[frame.icmp.identifier] += 1
|
||||||
|
|
||||||
|
return
|
||||||
|
# Route the frame
|
||||||
|
self.router.route_frame(frame, from_nic)
|
||||||
|
|
||||||
|
|
||||||
class Router(Node):
|
class Router(Node):
|
||||||
@@ -436,25 +446,34 @@ class Router(Node):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def route_frame(self, frame: Frame, from_nic: NIC, re_attempt: bool = False) -> None:
|
def route_frame(self, frame: Frame, from_nic: NIC, re_attempt: bool = False) -> None:
|
||||||
if not re_attempt:
|
# Check if src ip is on network of one of the NICs
|
||||||
# Check if src ip is on network of one of the NICs
|
nic = self.arp.get_arp_cache_nic(frame.ip.dst_ip)
|
||||||
nic = self.arp.get_arp_cache_nic(frame.ip.dst_ip)
|
target_mac = self.arp.get_arp_cache_mac_address(frame.ip.dst_ip)
|
||||||
target_mac = self.arp.get_arp_cache_mac_address(frame.ip.dst_ip)
|
|
||||||
if not nic:
|
if re_attempt and not nic:
|
||||||
self.arp.send_arp_request(frame.ip.dst_ip)
|
|
||||||
return self.route_frame(frame=frame, from_nic=from_nic, re_attempt=True)
|
|
||||||
for nic in self.nics.values():
|
|
||||||
if nic.enabled and frame.ip.dst_ip in nic.ip_network:
|
|
||||||
from_port = self._get_port_of_nic(from_nic)
|
|
||||||
to_port = self._get_port_of_nic(nic)
|
|
||||||
self.sys_log.info(f"Routing frame to internally from port {from_port} to port {to_port}")
|
|
||||||
frame.decrement_ttl()
|
|
||||||
frame.ethernet.src_mac_addr = nic.mac_address
|
|
||||||
frame.ethernet.dst_mac_addr = target_mac
|
|
||||||
nic.send_frame(frame)
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
self.sys_log.info(f"Destination {frame.ip.dst_ip} is unreachable")
|
self.sys_log.info(f"Destination {frame.ip.dst_ip} is unreachable")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not nic:
|
||||||
|
self.arp.send_arp_request(frame.ip.dst_ip)
|
||||||
|
return self.route_frame(frame=frame, from_nic=from_nic, re_attempt=True)
|
||||||
|
|
||||||
|
if not nic.enabled:
|
||||||
|
# TODO: Add sys_log here
|
||||||
|
return
|
||||||
|
|
||||||
|
if frame.ip.dst_ip in nic.ip_network:
|
||||||
|
from_port = self._get_port_of_nic(from_nic)
|
||||||
|
to_port = self._get_port_of_nic(nic)
|
||||||
|
self.sys_log.info(f"Routing frame to internally from port {from_port} to port {to_port}")
|
||||||
|
frame.decrement_ttl()
|
||||||
|
frame.ethernet.src_mac_addr = nic.mac_address
|
||||||
|
frame.ethernet.dst_mac_addr = target_mac
|
||||||
|
nic.send_frame(frame)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
# TODO: Deal with routing from route tables
|
||||||
|
|
||||||
def receive_frame(self, frame: Frame, from_nic: NIC):
|
def receive_frame(self, frame: Frame, from_nic: NIC):
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user