diff --git a/DVrouter.py b/DVrouter.py index a462a09..59ba3ed 100644 --- a/DVrouter.py +++ b/DVrouter.py @@ -5,7 +5,8 @@ ##################################################### from router import Router - +import json +from packet import Packet class DVrouter(Router): """Distance vector routing protocol implementation. @@ -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.""" @@ -30,7 +34,8 @@ 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 @@ -38,7 +43,15 @@ def handle_packet(self, port, packet): # 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.""" @@ -46,7 +59,12 @@ def handle_new_link(self, port, endpoint, cost): # 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.""" @@ -54,7 +72,13 @@ def handle_remove_link(self, port): # 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.""" @@ -62,7 +86,54 @@ def handle_time(self, time_ms): 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.""" diff --git a/LSrouter.py b/LSrouter.py index c375573..ae4aeb1 100644 --- a/LSrouter.py +++ b/LSrouter.py @@ -5,6 +5,8 @@ ##################################################### from router import Router +import json +from packet import Packet class LSrouter(Router): @@ -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.""" @@ -30,28 +36,53 @@ 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.""" @@ -59,10 +90,72 @@ def handle_time(self, time_ms): 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