def __init__(self): """ Called when the instance is initialized. DO NOT remove any existing code from this method. However, feel free to add to it for memory purposes in the final stage! """ assert not (self.SPLIT_HORIZON and self.POISON_REVERSE), \ "Split horizon and poison reverse can't both be on" self.start_timer() # Starts signaling the timer at correct rate. # Contains all current ports and their latencies. # See the write-up for documentation. self.ports = Ports() # This is the table that contains all current routes self.table = Table() self.table.owner = self self.history = { } # {(host, port) : RoutePacket} or RoutePackets contain [destination, latency]
class DVRouter(DVRouterBase): # A route should time out after this interval ROUTE_TTL = 15 # Dead entries should time out after this interval GARBAGE_TTL = 10 # ----------------------------------------------- # At most one of these should ever be on at once SPLIT_HORIZON = False POISON_REVERSE = False # ----------------------------------------------- # Determines if you send poison for expired routes POISON_EXPIRED = False # Determines if you send updates when a link comes up SEND_ON_LINK_UP = False # Determines if you send poison when a link goes down POISON_ON_LINK_DOWN = False def __init__(self): """ Called when the instance is initialized. DO NOT remove any existing code from this method. However, feel free to add to it for memory purposes in the final stage! """ assert not (self.SPLIT_HORIZON and self.POISON_REVERSE), \ "Split horizon and poison reverse can't both be on" self.start_timer() # Starts signaling the timer at correct rate. # Contains all current ports and their latencies. # See the write-up for documentation. self.ports = Ports() # This is the table that contains all current routes self.table = Table() self.table.owner = self def add_static_route(self, host, port): """ Adds a static route to this router's table. Called automatically by the framework whenever a host is connected to this router. :param host: the host. :param port: the port that the host is attached to. :returns: nothing. """ # `port` should have been added to `peer_tables` by `handle_link_up` # when the link came up. assert port in self.ports.get_all_ports( ), "Link should be up, but is not." # TODO: fill this in! self.table[host] = TableEntry(host, port, self.ports.get_latency(port), FOREVER) def handle_data_packet(self, packet, in_port): """ Called when a data packet arrives at this router. You may want to forward the packet, drop the packet, etc. here. :param packet: the packet that arrived. :param in_port: the port from which the packet arrived. :return: nothing. """ # TODO: fill this in! if self.table.get(packet.dst): if self.table.get(packet.dst).latency < INFINITY: self.send(packet, self.table.get(packet.dst).port) def send_routes(self, force=False, single_port=None): """ Send route advertisements for all routes in the table. :param force: if True, advertises ALL routes in the table; otherwise, advertises only those routes that have changed since the last advertisement. single_port: if not None, sends updates only to that port; to be used in conjunction with handle_link_up. :return: nothing. """ # TODO: fill this in! # print("these are the table items: ") # print(self.table.items()) if force: for host, entry in self.table.items(): ad = RoutePacket(host, entry.latency) for port in self.ports.get_all_ports(): # print(ad) if (entry.port != port) and (self.SPLIT_HORIZON): self.send(ad, port) elif self.POISON_REVERSE: if (entry.port != port): self.send(ad, port) else: self.send(RoutePacket(host, INFINITY), port) else: self.send(ad, port) # else: # for host, entry in self.table.items(): # ad = RoutePacket(host, entry.latency) # for port in self.ports.get_all_ports(): # if entry.port != port: # self.send(ad, port) def expire_routes(self): """ Clears out expired routes from table. accordingly. """ # TODO: fill this in! keys = self.table.items() delete = [] for dst, entry in keys: if entry.has_expired: delete.append(dst) if not self.POISON_EXPIRED: for dest in delete: self.table.pop(dest) else: for dest in delete: self.table[dest] = TableEntry( dest, self.table[dest].port, INFINITY, api.current_time() + self.ROUTE_TTL) # ad = RoutePacket(dest, INFINITY) # for port in self.ports.get_all_ports(): # self.send(ad, port) def handle_route_advertisement(self, route_dst, route_latency, port): """ Called when the router receives a route advertisement from a neighbor. :param route_dst: the destination of the advertised route. :param route_latency: latency from the neighbor to the destination. :param port: the port that the advertisement arrived on. :return: nothing. """ # TODO: fill this in! if route_dst in self.table: current_route = self.table.get(route_dst) if (route_latency + self.ports.get_latency(port) < current_route.latency) or (port == self.table[route_dst].port): if route_latency >= INFINITY: self.table[route_dst] = TableEntry(route_dst, port, route_latency, api.current_time()) else: self.table[route_dst] = TableEntry( route_dst, port, route_latency + self.ports.get_latency(port), api.current_time() + self.ROUTE_TTL) else: if (route_latency < INFINITY): self.table[route_dst] = TableEntry( route_dst, port, route_latency + self.ports.get_latency(port), api.current_time() + self.ROUTE_TTL) def handle_link_up(self, port, latency): """ Called by the framework when a link attached to this router goes up. :param port: the port that the link is attached to. :param latency: the link latency. :returns: nothing. """ self.ports.add_port(port, latency) # TODO: fill in the rest! def handle_link_down(self, port): """ Called by the framework when a link attached to this router does down. :param port: the port number used by the link. :returns: nothing. """ self.ports.remove_port(port)
class DVRouter(DVRouterBase): # A route should time out after this interval ROUTE_TTL = 15 # Dead entries should time out after this interval GARBAGE_TTL = 10 # ----------------------------------------------- # At most one of these should ever be on at once SPLIT_HORIZON = False POISON_REVERSE = False # ----------------------------------------------- # Determines if you send poison for expired routes POISON_EXPIRED = False # Determines if you send updates when a link comes up SEND_ON_LINK_UP = False # Determines if you send poison when a link goes down POISON_ON_LINK_DOWN = False def __init__(self): """ Called when the instance is initialized. DO NOT remove any existing code from this method. However, feel free to add to it for memory purposes in the final stage! """ assert not (self.SPLIT_HORIZON and self.POISON_REVERSE), \ "Split horizon and poison reverse can't both be on" self.start_timer() # Starts signaling the timer at correct rate. # Contains all current ports and their latencies. # See the write-up for documentation. self.ports = Ports() # This is the table that contains all current routes self.table = Table() self.table.owner = self self.history = {} def add_static_route(self, host, port): """ Adds a static route to this router's table. Called automatically by the framework whenever a host is connected to this router. :param host: the host. :param port: the port that the host is attached to. :returns: nothing. """ # `port` should have been added to `peer_tables` by `handle_link_up` # when the link came up. self.table[host] = TableEntry(dst=host, port=port, latency=self.ports.get_latency(port), expire_time=FOREVER) self.send_routes() assert port in self.ports.get_all_ports( ), "Link should be up, but is not." # TODO: fill this in! def handle_data_packet(self, packet, in_port): """ Called when a data packet arrives at this router. You may want to forward the packet, drop the packet, etc. here. :param packet: the packet that arrived. :param in_port: the port from which the packet arrived. :return: nothing. """ if packet.dst in self.table and self.table[ packet.dst].latency < INFINITY: self.send(packet, self.table[packet.dst].port) # TODO: fill this in! def send_routes(self, force=False, single_port=None): """ Send route advertisements for all routes in the table. :param force: if True, advertises ALL routes in the table; otherwise, advertises only those routes that have changed since the last advertisement. single_port: if not None, sends updates only to that port; to be used in conjunction with handle_link_up. :return: nothing. """ for host, link in self.table.items(): for each_port in self.ports.get_all_ports(): if self.SPLIT_HORIZON and each_port == link.port: continue if self.POISON_REVERSE and link.port == each_port: latency = INFINITY else: latency = link.latency if (force == True) or ( link.dst not in self.history[each_port]) or ( self.history[each_port][link.dst] != latency): if (single_port == None) or (single_port == each_port): self.send_route(each_port, link.dst, latency) self.history[each_port][link.dst] = latency # TODO: fill this in! def expire_routes(self): """ Clears out expired routes from table. accordingly. """ l = [] for host, link in self.table.items(): if api.current_time() > link.expire_time: self.s_log("Route from {} to {} is expired.", self, host) l.append(host) for to_delete in l: if self.POISON_EXPIRED: self.table[to_delete] = TableEntry( dst=to_delete, port=self.table[to_delete].port, latency=INFINITY, expire_time=api.current_time() + self.ROUTE_TTL) #self.send_routes() else: del self.table[to_delete] # TODO: fill this in! def handle_route_advertisement(self, route_dst, route_latency, port): """ Called when the router receives a route advertisement from a neighbor. :param route_dst: the destination of the advertised route. :param route_latency: latency from the neighbor to the destination. :param port: the port that the advertisement arrived on. :return: nothing. """ if route_dst not in self.table or route_latency + self.ports.get_latency( port) < self.table[route_dst].latency or ( port == self.table[route_dst].port and self.table[route_dst].latency < INFINITY): self.table[route_dst] = TableEntry( dst=route_dst, port=port, latency=min(INFINITY, route_latency + self.ports.get_latency(port)), expire_time=api.current_time() + self.ROUTE_TTL) self.send_routes() elif route_dst in self.table and self.table[ route_dst].latency != INFINITY and route_latency == INFINITY: self.send_route(port, route_dst, self.table[route_dst].latency) def handle_link_up(self, port, latency): """ Called by the framework when a link attached to this router goes up. :param port: the port that the link is attached to. :param latency: the link latency. :returns: nothing. """ self.history[port] = {} self.ports.add_port(port, latency) if self.SEND_ON_LINK_UP: self.send_routes(single_port=port) # TODO: fill in the rest! def handle_link_down(self, port): """ Called by the framework when a link attached to this router does down. :param port: the port number used by the link. :returns: nothing. """ del self.history[port] self.ports.remove_port(port) if not self.POISON_ON_LINK_DOWN: return l = [] for host, link in self.table.items(): if link.port == port: self.s_log("Route from {} to {} is down.", self, host) l.append(host) for to_delete in l: self.table[to_delete] = TableEntry(dst=to_delete, port=self.table[to_delete].port, latency=INFINITY, expire_time=api.current_time() + self.ROUTE_TTL) self.send_routes()
class DVRouter(DVRouterBase): # A route should time out after this interval ROUTE_TTL = 15 # Dead entries should time out after this interval GARBAGE_TTL = 10 # ----------------------------------------------- # At most one of these should ever be on at once SPLIT_HORIZON = False POISON_REVERSE = False # ----------------------------------------------- # Determines if you send poison for expired routes POISON_EXPIRED = False # Determines if you send updates when a link comes up SEND_ON_LINK_UP = False # Determines if you send poison when a link goes down POISON_ON_LINK_DOWN = False def __init__(self): """ Called when the instance is initialized. DO NOT remove any existing code from this method. However, feel free to add to it for memory purposes in the final stage! """ assert not (self.SPLIT_HORIZON and self.POISON_REVERSE), \ "Split horizon and poison reverse can't both be on" self.start_timer() # Starts signaling the timer at correct rate. # Contains all current ports and their latencies. # See the write-up for documentation. self.ports = Ports() # This is the table that contains all current routes self.table = Table() self.table.owner = self self.history = {} def add_static_route(self, host, port): """ Adds a static route to this router's table. Called automatically by the framework whenever a host is connected to this router. :param host: the host. :param port: the port that the host is attached to. :returns: nothing. """ # `port` should have been added to `peer_tables` by `handle_link_up` # when the link came up. assert port in self.ports.get_all_ports( ), "Link should be up, but is not." # TODO: fill this in! self.table[host] = TableEntry(host, port, self.ports.get_latency(port), FOREVER) def handle_data_packet(self, packet, in_port): """ Called when a data packet arrives at this router. You may want to forward the packet, drop the packet, etc. here. :param packet: the packet that arrived. :param in_port: the port from which the packet arrived. :return: nothing. """ # TODO: fill this in! for host, entry in self.table.items(): # iterate through my table if packet.dst in entry: if entry.latency < INFINITY: self.send(packet, entry.port, False) """ Helper functions that will decide what to advertise. It will either not advertise or advertise a latency of infinity. If the algorithm decides to advertise, the sendAd will send the advertisement """ def skipOrSend(self, advertisedPort, currPort, sh, pr): if sh is True and pr is False: return self.SPLIT_HORIZON is True and currPort == advertisedPort else: return self.POISON_REVERSE is True and currPort == advertisedPort def isSame(self, force, entry, port, latency): return force is False and entry.dst in self.history and port in self.history[ entry.dst] and self.history[entry.dst][port] == latency def sendAd(self, port, dst, latency): self.history.setdefault(dst, {})[port] = latency self.send_route(port, dst, latency) def send_routes(self, force=False, single_port=None): """ Send route advertisements for all routes in the table. :param force: if True, advertises ALL routes in the table; otherwise, advertises only those routes that have changed since the last advertisement. single_port: if not None, sends updates only to that port; to be used in conjunction with handle_link_up. :return: nothing. """ # TODO: fill this in! if single_port is not None: for entry in self.table.values(): latency = entry.latency self.sendAd(single_port, host, latency) else: for host, entry in self.table.items(): for port in self.ports.get_all_ports(): latency = entry.latency switcher = { 1: self.skipOrSend(port, entry.port, True, False), 2: self.skipOrSend(port, entry.port, False, True), 3: self.isSame(force, entry, port, latency) } splitHorizon = switcher.get(1, lambda: "Invalid Choice") positionReverse = switcher.get(2, lambda: "Invalid Choice") checkHistory = switcher.get(3, lambda: "Invalid Choice") if splitHorizon: continue if positionReverse: latency = INFINITY if self.isSame(force, entry, port, latency) or checkHistory: continue self.sendAd(port, host, latency) # Will delete the routes that I appended to toDelete def deleteRoutes(self, toDelete): for host in toDelete: del self.table[host] def expire_routes(self): """ Clears out expired routes from table. accordingly. """ # TODO: fill this in! toDelete = [] if self.POISON_EXPIRED is True: for host, entry in self.table.items(): if entry.has_expired: self.table[host] = TableEntry(host, entry.port, INFINITY, api.current_time()) else: for host, entry in self.table.items(): if entry.has_expired: toDelete.append(host) self.deleteRoutes(toDelete) def handle_route_advertisement(self, route_dst, route_latency, port): """ Called when the router receives a route advertisement from a neighbor. :param route_dst: the destination of the advertised route. :param route_latency: latency from the neighbor to the destination. :param port: the port that the advertisement arrived on. :return: nothing. """ # TODO: fill this in! # if its not in the table entry then I need to add it since it is a new destination if route_dst not in self.table.keys(): self.table[route_dst] = TableEntry( route_dst, port, self.ports.get_latency(port) + route_latency, api.current_time() + self.ROUTE_TTL) else: for host, entry in self.table.items(): if route_dst == host: # if my destination is in my table entry then maybe I have found a better path and must update my existing path if port == entry.port and route_latency >= INFINITY: self.table[host] = TableEntry(route_dst, port, INFINITY, api.current_time()) self.send_routes(False) elif port == entry.port or entry.latency > route_latency + self.ports.get_latency( port): self.table[host] = TableEntry( route_dst, port, route_latency + self.ports.get_latency(port), api.current_time() + self.ROUTE_TTL) self.send_routes(False) def handle_link_up(self, port, latency): """ Called by the framework when a link attached to this router goes up. :param port: the port that the link is attached to. :param latency: the link latency. :returns: nothing. """ self.ports.add_port(port, latency) # TODO: fill in the rest! if self.SEND_ON_LINK_UP: self.send_routes(False, port) def handle_link_down(self, port): """ Called by the framework when a link attached to this router does down. :param port: the port number used by the link. :returns: nothing. """ self.ports.remove_port(port) # TODO: fill this in! toDelete = [] for host, entry in self.table.items(): if entry.port == port: if self.POISON_ON_LINK_DOWN: self.table[host] = TableEntry( host, port, INFINITY, api.current_time() + self.ROUTE_TTL) self.send_routes(False) else: toDelete.append(host) self.deleteRoutes(toDelete)
class DVRouter(DVRouterBase): # A route should time out after this interval ROUTE_TTL = 15 # Dead entries should time out after this interval GARBAGE_TTL = 10 # ----------------------------------------------- # At most one of these should ever be on at once SPLIT_HORIZON = False POISON_REVERSE = False # ----------------------------------------------- # Determines if you send poison for expired routes POISON_EXPIRED = False # Determines if you send updates when a link comes up SEND_ON_LINK_UP = False # Determines if you send poison when a link goes down POISON_ON_LINK_DOWN = False def __init__(self): """ Called when the instance is initialized. DO NOT remove any existing code from this method. However, feel free to add to it for memory purposes in the final stage! """ assert not (self.SPLIT_HORIZON and self.POISON_REVERSE), \ "Split horizon and poison reverse can't both be on" self.start_timer() # Starts signaling the timer at correct rate. # Contains all current ports and their latencies. # See the write-up for documentation. self.ports = Ports() # This is the table that contains all current routes self.table = Table() self.table.owner = self self.history = { } # {(host, port) : RoutePacket} or RoutePackets contain [destination, latency] def add_static_route(self, host, port): """ Adds a static route to this router's table. Called automatically by the framework whenever a host is connected to this router. :param host: the host. :param port: the port that the host is attached to. :returns: nothing. """ # `port` should have been added to `peer_tables` by `handle_link_up` # when the link came up. assert port in self.ports.get_all_ports( ), "Link should be up, but is not." # TODO: fill this in! latency = self.ports.get_latency(port) self.table[host] = TableEntry(host, port, latency, expire_time=FOREVER) # add to table def handle_data_packet(self, packet, in_port): """ Called when a data packet arrives at this router. You may want to forward the packet, drop the packet, etc. here. :param packet: the packet that arrived. :param in_port: the port from which the packet arrived. :return: nothing. """ # TODO: fill this in! # If no route exists for a packet’s destination, your router should drop the packet (do nothing). pack_dest = packet.dst if not self.table.get(pack_dest): return # if the latency is greater than or equal to INFINITY you should also drop the packet table_entry = self.table.get(pack_dest) latency = table_entry.latency if latency >= INFINITY: return out_port = table_entry.port if out_port == in_port: return self.send(packet, out_port, flood=False) def send_routes(self, force=False, single_port=None): """Send route advertisements for all routes in the table. :param force: if True, advertises ALL routes in the table; otherwise, advertises only those routes that have changed since the last advertisement. single_port: if not None, sends updates only to that port; to be used in conjunction with handle_link_up. :return: nothing.""" # TODO: fill this in! ports_dict = self.ports.get_underlying_dict() ports_keys_list = list(ports_dict.keys()) # send new RoutePacket to all ports for port in ports_keys_list: for host, entry in self.table.items(): port_packet_came_from = entry.port no_table_entry = (host, port) not in self.history.keys() if not no_table_entry: old_ad = self.history[(host, port)] if single_port is not None: print("Single Port not None! ") if self.SPLIT_HORIZON: if port != port_packet_came_from: new_ad = RoutePacket(host, entry.latency) self.send(new_ad, port, flood=False) elif self.POISON_REVERSE: if port == port_packet_came_from: if not force: if no_table_entry or old_ad.destination != host or old_ad.latency != INFINITY: new_ad = RoutePacket( host, INFINITY) # advertise as unreachable self.send(new_ad, port, flood=False) self.history[(host, port)] = new_ad else: new_ad = RoutePacket( host, INFINITY) # advertise as unreachable self.send(new_ad, port, flood=False) self.history[(host, port)] = new_ad else: if not force: if no_table_entry or old_ad.destination != host or old_ad.latency != entry.latency: new_ad = RoutePacket(host, entry.latency) self.send(new_ad, port, flood=False) self.history[(host, port)] = new_ad else: new_ad = RoutePacket(host, entry.latency) self.send(new_ad, port, flood=False) self.history[(host, port)] = new_ad # business as usual else: new_ad = RoutePacket(host, entry.latency) self.send(new_ad, port) def expire_routes(self): """ Clears out expired routes from table. accordingly. """ # TODO: fill this in! hosts = list(self.table.keys()) for host in hosts: table_entry = self.table[host] if table_entry.expire_time == FOREVER: return # route is poison and expired if self.POISON_EXPIRED and api.current_time( ) > table_entry.expire_time: current_route = self.table[host] poison_route = TableEntry(host, current_route.port, latency=INFINITY, expire_time=self.ROUTE_TTL) self.table[host] = poison_route # route is expired elif api.current_time() > table_entry.expire_time: del self.table[host] def handle_route_advertisement(self, route_dst, route_latency, port): """Called when the router receives a route advertisement from a neighbor. :param route_dst: the destination of the advertised route. :param route_latency: latency from the neighbor to the destination. :param port: the port that the advertisement arrived on. :return: nothing.""" # TODO: fill this in! port_latency = self.ports.get_latency(port) new_latency = port_latency + route_latency new_expire_time = api.current_time() + self.ROUTE_TTL current_route = self.table.get(route_dst) new_route = TableEntry(dst=route_dst, port=port, latency=new_latency, expire_time=new_expire_time) # poison advertisement if route_latency >= INFINITY and current_route.port == port: current_expire_time = current_route.expire_time poison_route = TableEntry(dst=route_dst, port=port, latency=INFINITY, expire_time=current_expire_time) self.table[route_dst] = poison_route self.send_routes(force=False) return # if NO route currently exists, add/update the new route if not current_route: self.table[route_dst] = new_route self.send_routes(force=False) return # found a better route if new_latency < current_route.latency: self.table[route_dst] = new_route self.send_routes(force=False) return if current_route.port == port: self.table[route_dst] = new_route self.send_routes(force=False) return def handle_link_up(self, port, latency): """ Called by the framework when a link attached to this router goes up. :param port: the port that the link is attached to. :param latency: the link latency. :returns: nothing. """ self.ports.add_port(port, latency) # TODO: fill in the rest! for host in self.table.keys(): if self.SEND_ON_LINK_UP: new_ad = RoutePacket(host, self.table[host].latency) self.send(new_ad, port, flood=False, flooding=False) def handle_link_down(self, port): """ Called by the framework when a link attached to this router does down. :param port: the port number used by the link. :returns: nothing. """ self.ports.remove_port(port) # TODO: fill this in! for host in list(self.table.keys()): if self.table[host].port == port: if self.POISON_ON_LINK_DOWN: # poison and immediately send any routes that need to be updated poison_route = TableEntry( host, port, latency=INFINITY, expire_time=self.table[host].expire_time) self.table[host] = poison_route self.send_routes(force=False) # remove any routes that go through that link del self.table[host]