Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
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 = {
        }  # {(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]