Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 78 additions & 7 deletions DVrouter.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
#####################################################

from router import Router

import json
from packet import Packet

class DVrouter(Router):
"""Distance vector routing protocol implementation.
Expand All @@ -21,7 +22,10 @@ def __init__(self, addr, heartbeat_time):
self.last_time = 0
# TODO
# add your own class fields and initialization code here
pass
self.local_links = {}
self.distance_vector = {self.addr: 0}
self.forwarding_table = {}
self.neighbors_dv = {}

def handle_packet(self, port, packet):
"""Process incoming packet."""
Expand All @@ -30,39 +34,106 @@ def handle_packet(self, port, packet):
# Hint: this is a normal data packet
# If the forwarding table contains packet.dst_addr
# send packet based on forwarding table, e.g., self.send(port, packet)
pass
if packet.dst_addr in self.forwarding_table:
self.send(self.forwarding_table[packet.dst_addr], packet)
else:
# Hint: this is a routing packet generated by your routing protocol
# If the received distance vector is different
# update the local copy of the distance vector
# update the distance vector of this router
# update the forwarding table
# broadcast the distance vector of this router to neighbors
pass

# Áp dụng thuật toán Bellman-Ford:
received_dv = json.loads(packet.content)
if port in self.local_links:
neighbor = self.local_links[port][0]
if neighbor not in self.neighbors_dv or self.neighbors_dv[neighbor] != received_dv:
self.neighbors_dv[neighbor] = received_dv
if self._recompute_dv():
self._broadcast_dv()

def handle_new_link(self, port, endpoint, cost):
"""Handle new link."""
# TODO
# update the distance vector of this router
# update the forwarding table
# broadcast the distance vector of this router to neighbors
pass
self.local_links[port] = (endpoint, cost)
if self._recompute_dv():
self._broadcast_dv()
else:
# Gửi DV của mình cho neighbor mới ngay cả khi DV không thay đổi
self._broadcast_dv()

def handle_remove_link(self, port):
"""Handle removed link."""
# TODO
# update the distance vector of this router
# update the forwarding table
# broadcast the distance vector of this router to neighbors
pass
if port in self.local_links:
endpoint = self.local_links[port][0]
if endpoint in self.neighbors_dv:
del self.neighbors_dv[endpoint]
del self.local_links[port]
if self._recompute_dv():
self._broadcast_dv()

def handle_time(self, time_ms):
"""Handle current time."""
if time_ms - self.last_time >= self.heartbeat_time:
self.last_time = time_ms
# TODO
# broadcast the distance vector of this router to neighbors
pass
self._broadcast_dv()

def _recompute_dv(self):
# Thuật toán Bellman-Ford
new_dv = {self.addr: 0}
new_fw = {}
all_dests = set([self.addr])
for _, (endpoint, _) in self.local_links.items():
all_dests.add(endpoint)
for ndv in self.neighbors_dv.values():
all_dests.update(ndv.keys())

for dest in all_dests:
if dest == self.addr:
continue
min_cost = 16
best_port = None
for port, (neighbor, cost) in self.local_links.items():
if dest == neighbor:
if cost < min_cost:
min_cost = cost
best_port = port
if neighbor in self.neighbors_dv and dest in self.neighbors_dv[neighbor]:
total_cost = cost + self.neighbors_dv[neighbor][dest]
if total_cost < min_cost:
min_cost = total_cost
best_port = port
if min_cost < 16:
new_dv[dest] = min_cost
new_fw[dest] = best_port

changed = (new_dv != self.distance_vector)
self.distance_vector = new_dv
self.forwarding_table = new_fw
return changed

def _broadcast_dv(self):
for port, (neighbor, cost) in self.local_links.items():
poisoned_dv = {}
for dest, dist in self.distance_vector.items():
# Split Horizon with Poison Reverse
if dest != neighbor and self.forwarding_table.get(dest) == port:
poisoned_dv[dest] = 16
else:
poisoned_dv[dest] = dist
content = json.dumps(poisoned_dv)
packet = Packet(Packet.ROUTING, self.addr, "BROADCAST", content)
self.send(port, packet)

def __repr__(self):
"""Representation for debugging in the network visualizer."""
Expand Down
107 changes: 100 additions & 7 deletions LSrouter.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#####################################################

from router import Router
import json
from packet import Packet


class LSrouter(Router):
Expand All @@ -21,7 +23,11 @@ def __init__(self, addr, heartbeat_time):
self.last_time = 0
# TODO
# add your own class fields and initialization code here
pass
self.local_links = {}
self.link_states = {}
self.sequence_numbers = {}
self.sequence_number = 0
self.forwarding_table = {}

def handle_packet(self, port, packet):
"""Process incoming packet."""
Expand All @@ -30,39 +36,126 @@ def handle_packet(self, port, packet):
# Hint: this is a normal data packet
# If the forwarding table contains packet.dst_addr
# send packet based on forwarding table, e.g., self.send(port, packet)
pass
if packet.dst_addr in self.forwarding_table:
self.send(self.forwarding_table[packet.dst_addr], packet)
else:
# Hint: this is a routing packet generated by your routing protocol
# If the sequence number is higher and the received link state is different
# update the local copy of the link state
# update the forwarding table
# broadcast the packet to other neighbors
pass
try:
data = json.loads(packet.content)
source = data["source"]
seq_num = data["sequence_number"]
neighbors = data["neighbors"]

if source == self.addr:
return

if seq_num > self.sequence_numbers.get(source, -1):
self.sequence_numbers[source] = seq_num
self.link_states[source] = neighbors
self._recompute_forwarding_table()
self._flood_lsp(port, packet)
except Exception:
pass

def handle_new_link(self, port, endpoint, cost):
"""Handle new link."""
# TODO
# update local data structures and forwarding table
# broadcast the new link state of this router to all neighbors
pass
self.local_links[port] = (endpoint, cost)
self.link_states[self.addr] = {end: c for p, (end, c) in self.local_links.items()}
self.sequence_number += 1
self._recompute_forwarding_table()
self._broadcast_link_state()

def handle_remove_link(self, port):
"""Handle removed link."""
# TODO
# update local data structures and forwarding table
# broadcast the new link state of this router to all neighbors
pass
if port in self.local_links:
del self.local_links[port]
self.link_states[self.addr] = {end: c for p, (end, c) in self.local_links.items()}
self.sequence_number += 1
self._recompute_forwarding_table()
self._broadcast_link_state()

def handle_time(self, time_ms):
"""Handle current time."""
if time_ms - self.last_time >= self.heartbeat_time:
self.last_time = time_ms
# TODO
# broadcast the link state of this router to all neighbors
pass
self._broadcast_link_state()

def __repr__(self):
"""Representation for debugging in the network visualizer."""
# TODO
# NOTE This method is for your own convenience and will not be graded
return f"LSrouter(addr={self.addr})"
return f"LSrouter(addr={self.addr}, fw={self.forwarding_table})"

def _broadcast_link_state(self):
lsp_data = {
"source": self.addr,
"sequence_number": self.sequence_number,
"neighbors": self.link_states.get(self.addr, {})
}
content = json.dumps(lsp_data)
for port in self.local_links:
packet = Packet(Packet.ROUTING, self.addr, "BROADCAST", content)
self.send(port, packet)

def _flood_lsp(self, incoming_port, packet):
for port in self.local_links:
if port != incoming_port:
self.send(port, packet)

def _recompute_forwarding_table(self):
nodes = set()
nodes.add(self.addr)
for u, neighbors in self.link_states.items():
nodes.add(u)
for v in neighbors:
nodes.add(v)

dist = {node: float('inf') for node in nodes}
prev = {node: None for node in nodes}
dist[self.addr] = 0

Q = set(nodes)

while Q:
u = min(Q, key=lambda node: dist[node])
if dist[u] == float('inf'):
break
Q.remove(u)

for v, cost in self.link_states.get(u, {}).items():
if v in Q:
alt = dist[u] + cost
if alt < dist[v]:
dist[v] = alt
prev[v] = u

new_forwarding_table = {}
for dest in nodes:
if dest == self.addr:
continue
curr = dest
path = []
while curr in prev and prev[curr] is not None:
path.append(curr)
curr = prev[curr]

if curr == self.addr and path:
next_hop = path[-1]
for port, (endpoint, cost) in self.local_links.items():
if endpoint == next_hop:
new_forwarding_table[dest] = port
break

self.forwarding_table = new_forwarding_table