Ejemplo n.º 1
0
    def __init__(self, router_name, router_link_manager, service_transmitter,
            **kwargs):
        self._update_period  = kwargs.pop('update_period',
            config.rip_update_period)
        self._inf_timeout    = kwargs.pop('inf_timeout',
            config.rip_inf_timeout)
        self._remove_timeout = kwargs.pop('remove_timeout',
            config.rip_remove_timeout)
        super(RIPService, self).__init__()

        self._router_name = router_name
        self._link_manager = router_link_manager
        self._service_transmitter = service_transmitter

        self._logger = logging.getLogger("RIPService.router={0}".format(
            self._router_name))

        self._dynamic_routing_table = DynamicRoutingTable(
            {router_name: RIPService.RIPRouteToDestination(router_name, 0)})

        # If working thread will be able to acquire the lock, then it should
        # terminate himself.
        self._exit_lock = threading.RLock()
        self._exit_lock.acquire()

        self._working_thread = threading.Thread(target=self._work)
        self._working_thread.start()
Ejemplo n.º 2
0
class RIPService(object):
    protocol = 520
    inf_distance = 16

    @total_ordering
    class RIPRouteToDestination(RouteToDestination):
        def __init__(self, next_router=None, distance=None):
            super(RIPService.RIPRouteToDestination, self).\
                __init__(next_router=next_router)

            self.distance = distance

        def __eq__(self, other):
            return (super(RIPService.RIPRouteToDestination, self).
                    __eq__(other) and
                self.distance == other.distance)

        def __lt__(self, other):
            if (super(RIPService.RIPRouteToDestination, self).
                    __eq__(other)):
                return self.distance < other.distance
            else:
                return super(RIPService.RIPRouteToDestination, self).\
                    __lt__(other)

        def __repr__(self):
            return "RIPRouteToDestination(next_router={0}, dist={1})".\
                format(self.next_router, self.distance)

    def __init__(self, router_name, router_link_manager, service_transmitter,
            **kwargs):
        self._update_period  = kwargs.pop('update_period',
            config.rip_update_period)
        self._inf_timeout    = kwargs.pop('inf_timeout',
            config.rip_inf_timeout)
        self._remove_timeout = kwargs.pop('remove_timeout',
            config.rip_remove_timeout)
        super(RIPService, self).__init__()

        self._router_name = router_name
        self._link_manager = router_link_manager
        self._service_transmitter = service_transmitter

        self._logger = logging.getLogger("RIPService.router={0}".format(
            self._router_name))

        self._dynamic_routing_table = DynamicRoutingTable(
            {router_name: RIPService.RIPRouteToDestination(router_name, 0)})

        # If working thread will be able to acquire the lock, then it should
        # terminate himself.
        self._exit_lock = threading.RLock()
        self._exit_lock.acquire()

        self._working_thread = threading.Thread(target=self._work)
        self._working_thread.start()

    # TODO
    def terminate(self):
        # Release exit lock and wait until working thread will not terminate.
        self._exit_lock.release()
        self._working_thread.join()

    def dynamic_routing_table(self):
        return self._dynamic_routing_table

    def _work(self):
        def update_connected_routers_info():
            new_connected_routers = \
                connected_routers - prev_connected_routers
            new_disconnected_routers = \
                prev_connected_routers - connected_routers

            if new_connected_routers:
                self._logger.debug("Connected to routers: {0}".format(
                    list(new_connected_routers)))
            if new_disconnected_routers:
                self._logger.debug("Disconnected from routers: {0}".format(
                    list(new_disconnected_routers)))

            # Remove disconnected routers from list.
            for router_name in new_disconnected_routers:
                assert router_name in connected_rrs_info
                del connected_rrs_info[router_name]

            # Set distance to infinity for destination routers route to which
            # leaded through disconnected routers.
            for to_router, dest_router_info in dest_routers_info.iteritems():
                if dest_router_info.next_router in new_disconnected_routers:
                    dest_router_info.dist = RIPService.inf_distance
                    self._logger.debug(
                        "Remove route: Due to disconnection: "
                        "{dest}:(d={dist}, n={next})".format(
                            dest=to_router, dist=dest_router_info.dist,
                            next=dest_router_info.next_router))

            # Add connected hosts to according list.
            for router_name in new_connected_routers:
                router_name not in connected_rrs_info
                connected_rrs_info[router_name] = \
                    ConnectedRouterInfo(Timer(self._update_period,
                        start_time=time.time() - self._update_period - 1.0))
                assert connected_rrs_info[router_name].timer.is_expired()

            # Update routing information for directly connected destination
            # routers.
            for router_name in new_connected_routers:
                dest_routers_info[router_name] = DestRouterInfo(
                    dist=1, next_router=router_name,
                    timer=DummyTimer())
                self._logger.debug(
                    "Add route: Directly connected: "
                    "{dest}:(d={dist}, n={next})".format(
                        dest=router_name,
                        dist=dest_routers_info[router_name].dist,
                        next=dest_routers_info[router_name].next_router))

            table_changed = new_connected_routers or new_disconnected_routers
            if table_changed:
                update_routing_table()

        def connected_routers_with_timeout():
            for rr_name, connected_rr_info in connected_rrs_info.iteritems():
                if connected_rr_info.timer.is_expired():
                    yield rr_name

        def set_infinite_timeout_distances():
            routing_table_updated = False
            for dest, dest_router_info in dest_routers_info.iteritems():
                if (dest_router_info.dist < RIPService.inf_distance and
                        dest_router_info.timer.is_expired()):
                    dest_router_info.dist = RIPService.inf_distance
                    dest_router_info.timer = Timer(self._remove_timeout)
                    routing_table_updated = True

                    self._logger.debug(
                        "Remove route: Due to timeout: "
                        "{dest}:(d={dist}, n={next})".format(
                            dest=dest, dist=dest_router_info.dist,
                            next=dest_router_info.next_router))

            if routing_table_updated:
                update_routing_table()

        def remove_timeout_distances():
            routing_table_updated = False
            for dest, dest_router_info in dest_routers_info.items():
                if (dest_router_info.dist == RIPService.inf_distance and
                        dest_router_info.timer.is_expired()):
                    del dest_routers_info[dest]

                    routing_table_updated = True

                    self._logger.debug(
                        "Due to big timeout removing target: {dest}".format(
                            dest=dest))

            # TODO: Not actually needed.
            if routing_table_updated:
                update_routing_table()

        def distances_for_sending(to_router):
            distances = []
            for dest, dest_router_info in dest_routers_info.iteritems():
                if dest_router_info.next_router == to_router:
                    # Rule 1A from [vasilev04netsoft]:
                    # For router R: if packets to destination router X are sent
                    # through router G, then distance to router X that being
                    # sent to router G is infinity.
                    #
                    #   R ----------------- G - ... - X
                    #       X:(inf, G) ->
                    #
                    d = (RIPService.inf_distance, dest)
                    distances.append(d)
                else:
                    d = (dest_router_info.dist, dest)
                    distances.append(d)
            return distances

        def send_distances(routers):
            for to_router in routers:
                # Prepare data to send.
                distances = distances_for_sending(to_router)
                raw_data = RIPData(distances).serialize()

                # Send data.
                self._service_transmitter.send_data(to_router, raw_data)

                # Mark time data was sent.
                connected_rrs_info[to_router].timer.restart()

                # TODO: Assume that computer is not slow.
                assert not connected_rrs_info[to_router].timer.is_expired()

        def handle_receive():
            while True:
                result = self._service_transmitter.receive_data(block=False)
                if result is None:
                    break
                src, raw_data = result

                rip_data = RIPData.deserialize(raw_data)

                self._logger.debug(
                    "Received vector from {0}:\n  {1}".format(
                        src,
                        pprint.pformat(rip_data.distances)))

                routing_table_updated = False
                for dist, dest in rip_data.distances:
                    dist = min(dist + 1, RIPService.inf_distance)

                    if dest not in dest_routers_info:
                        # Route to new router.

                        if dist < RIPService.inf_distance:
                            dest_routers_info[dest] = DestRouterInfo(
                                dist=dist, next_router=src,
                                timer=Timer(self._inf_timeout))

                            routing_table_updated = True

                            self._logger.debug(
                                "Received route to new router: "
                                "{dest}:(d={dist}, n={next})".format(
                                    dest=dest, dist=dist,
                                    next=src))
                        else:
                            # Ignore.
                            pass

                    elif dist < dest_routers_info[dest].dist:
                        # Received shorter then all known path to router.
                        
                        dest_routers_info[dest].dist = dist
                        dest_routers_info[dest].next_router = src
                        dest_routers_info[dest].timer = \
                            Timer(self._inf_timeout)

                        routing_table_updated = True

                        self._logger.debug(
                            "Found shorter path: "
                            "{dest}:(d={dist}, n={next})".format(
                                dest=dest, dist=dist,
                                next=src))

                    elif (dest_routers_info[dest].next_router == src and
                            dest_routers_info[dest].dist != dist):
                        # Received route update from source.

                        dest_routers_info[dest].dist = dist

                        timer = Timer(self._inf_timeout) \
                            if dist < RIPService.inf_distance \
                            else Timer(self._remove_timeout)
                        dest_routers_info[dest].timer = timer

                        routing_table_updated = True

                        self._logger.debug(
                            "Received route update from source: "
                            "{dest}:(d={dist}, n={next})".format(
                                dest=dest, dist=dist,
                                next=src))
                    else:
                        if dist < RIPService.inf_distance:
                            # Update timer.
                            dest_routers_info[dest].timer.restart()
                        else:
                            # Don't update timer for infinite paths.
                            pass

                if routing_table_updated:
                    update_routing_table()

        def update_routing_table():
            old_routing_table = self._dynamic_routing_table.table().copy()

            new_routing_table = {}

            for dest, dest_rr_info in dest_routers_info.iteritems():
                if dest_rr_info.dist < RIPService.inf_distance:
                    assert dest not in new_routing_table
                    new_routing_table[dest] = \
                        RIPService.RIPRouteToDestination(
                            dest_rr_info.next_router, dest_rr_info.dist)

            if old_routing_table != new_routing_table:
                self._logger.debug("New routing table:\n  {0}".format(
                    pprint.pformat(new_routing_table)))

                self._dynamic_routing_table.update(new_routing_table)

        self._logger.info("Working thread started")

        DestRouterInfo = recordtype(
            'DestRouterInfo', 'dist next_router timer')
        ConnectedRouterInfo = recordtype('ConnectedRouterInfo', 'timer')

        # {destination router name: DestRouterInfo()}
        # `timer' member is for last time information about destination router
        # was updated,
        dest_routers_info = {self._router_name:
            DestRouterInfo(dist=0, next_router=self._router_name,
                timer=DummyTimer())}

        # {connected router: ConnectedRouterInfo()}
        # `timer' member if for last time information packet was sent to
        # router}
        connected_rrs_info = {}

        connected_routers = frozenset()
        while True:
            if self._exit_lock.acquire(False):
                # Obtained exit lock. Terminate.

                self._exit_lock.release()
                self._logger.info("Exit working thread")
                return

            # Prepare data for detecting changes in current router connectivity.
            prev_connected_routers = connected_routers
            connected_routers = \
                frozenset(self._link_manager.connected_routers())
                
            # Remove information about disconnected routers and add information
            # about newly connected routers.
            update_connected_routers_info()

            # Send distances information to all who needs it.
            send_distances(list(connected_routers_with_timeout()))

            # Update distances according to received packets.
            handle_receive()

            set_infinite_timeout_distances()
            remove_timeout_distances()

            time.sleep(config.thread_sleep_time)