Пример #1
0
    def __init__(self, common_conf, neighbors_conf, vrfs_conf):
        self._common_config = common_conf
        self._neighbors_conf = neighbors_conf
        self._vrfs_conf = vrfs_conf

        Activity.__init__(self, name='core_service')

        self._signal_bus = BgpSignalBus()
        self._init_signal_listeners()

        self._rt_mgr = RouteTargetManager(self, neighbors_conf, vrfs_conf)

        self._table_manager = core_managers.TableCoreManager(
            self, common_conf
        )

        self._importmap_manager = core_managers.ImportMapManager()

        # Autonomous system number of this BGP speaker.
        self._asn = self._common_config.local_as

        self._peer_manager = core_managers.PeerManager(
            self,
            self._neighbors_conf,
        )

        # Initialize sink for flexinet-peers
        self._sinks = set()

        self._conf_manager = core_managers.ConfigurationManager(
            self, common_conf, vrfs_conf, neighbors_conf
        )

        # Register Flexinet peer sink
        from ryu.services.protocols.bgp.net_ctrl import NET_CONTROLLER

        self.register_flexinet_sink(NET_CONTROLLER)

        # State per route family
        # Key: RouteFamily
        # Value: BgpInstanceRf
        self.rf_state = {}

        # Protocol factories for pro-active and re-active bgp-sessions.
        self.client_factory = None
        self.server_factory = None

        # Key: RD:Next_Hop
        # Value: label
        self._next_hop_label = {}

        # BgpProcessor instance (initialized during start)
        self._bgp_processor = None

        # BMP clients key: (host, port) value: BMPClient instance
        self.bmpclients = {}
Пример #2
0
class CoreService(Factory, Activity):
    """A service that maintains eBGP/iBGP sessions with BGP peers.

    Two instances of this class don't share any BGP state with each
    other. Manages peers, tables for various address-families, etc.
    """

    protocol = BgpProtocol

    def __init__(self, common_conf, neighbors_conf, vrfs_conf):
        self._common_config = common_conf
        self._neighbors_conf = neighbors_conf
        self._vrfs_conf = vrfs_conf

        Activity.__init__(self, name='core_service')

        self._signal_bus = BgpSignalBus()
        self._init_signal_listeners()

        self._rt_mgr = RouteTargetManager(self, neighbors_conf, vrfs_conf)

        self._table_manager = core_managers.TableCoreManager(
            self, common_conf
        )

        self._importmap_manager = core_managers.ImportMapManager()

        # Autonomous system number of this BGP speaker.
        self._asn = self._common_config.local_as

        self._peer_manager = core_managers.PeerManager(
            self,
            self._neighbors_conf,
        )

        # Initialize sink for flexinet-peers
        self._sinks = set()

        self._conf_manager = core_managers.ConfigurationManager(
            self, common_conf, vrfs_conf, neighbors_conf
        )

        # Register Flexinet peer sink
        from ryu.services.protocols.bgp.net_ctrl import NET_CONTROLLER

        self.register_flexinet_sink(NET_CONTROLLER)

        # State per route family
        # Key: RouteFamily
        # Value: BgpInstanceRf
        self.rf_state = {}

        # Protocol factories for pro-active and re-active bgp-sessions.
        self.client_factory = None
        self.server_factory = None

        # Key: RD:Next_Hop
        # Value: label
        self._next_hop_label = {}

        # BgpProcessor instance (initialized during start)
        self._bgp_processor = None

        # BMP clients key: (host, port) value: BMPClient instance
        self.bmpclients = {}

    def _init_signal_listeners(self):
        self._signal_bus.register_listener(
            BgpSignalBus.BGP_DEST_CHANGED,
            lambda _, dest: self.enqueue_for_bgp_processing(dest)
        )
        self._signal_bus.register_listener(
            BgpSignalBus.BGP_VRF_REMOVED,
            lambda _, route_dist: self.on_vrf_removed(route_dist)
        )
        self._signal_bus.register_listener(
            BgpSignalBus.BGP_VRF_ADDED,
            lambda _, vrf_conf: self.on_vrf_added(vrf_conf)
        )
        self._signal_bus.register_listener(
            BgpSignalBus.BGP_VRF_STATS_CONFIG_CHANGED,
            lambda _, vrf_conf: self.on_stats_config_change(vrf_conf)
        )

    @property
    def router_id(self):
        return self._common_config.router_id

    @property
    def global_interested_rts(self):
        return self._rt_mgr.global_interested_rts

    @property
    def asn(self):
        return self._asn

    @property
    def table_manager(self):
        return self._table_manager

    @property
    def importmap_manager(self):
        return self._importmap_manager

    @property
    def peer_manager(self):
        return self._peer_manager

    @property
    def rt_manager(self):
        return self._rt_mgr

    @property
    def signal_bus(self):
        return self._signal_bus

    def enqueue_for_bgp_processing(self, dest):
        return self._bgp_processor.enqueue(dest)

    def on_vrf_removed(self, route_dist):
        # Remove stats timer linked with this vrf.
        vrf_stats_timer = self._timers.get(route_dist)
        if vrf_stats_timer:
            vrf_stats_timer.stop()
            del self._timers[route_dist]

    def on_vrf_added(self, vrf_conf):
        # Setup statistics timer.
        rd = vrf_conf.route_dist
        rf = vrf_conf.route_family
        vrf_table = self._table_manager.get_vrf_table(rd, rf)
        vrf_stats_timer = self._create_timer(
            rd,
            stats.log,
            stats_source=vrf_table.get_stats_summary_dict
        )

        # Start statistics timer if applicable.
        if vrf_conf.stats_log_enabled:
            vrf_stats_timer.start(vrf_conf.stats_time)

    def on_stats_config_change(self, vrf_conf):
        vrf_stats_timer = self._timers.get(
            vrf_conf.route_dist
        )
        vrf_stats_timer.stop()
        vrf_stats_timer.start(vrf_conf.stats_time)

    def _run(self, *args, **kwargs):
        from ryu.services.protocols.bgp.processor import BgpProcessor
        # Initialize bgp processor.
        self._bgp_processor = BgpProcessor(self)
        # Start BgpProcessor in a separate thread.
        processor_thread = self._spawn_activity(self._bgp_processor)

        # Pro-actively try to establish bgp-session with peers.
        for peer in self._peer_manager.iterpeers:
            self._spawn_activity(peer, self.start_protocol)

        # Reactively establish bgp-session with peer by listening on
        # server port for connection requests.
        server_addr = (CORE_IP, self._common_config.bgp_server_port)
        waiter = kwargs.pop('waiter')
        waiter.set()
        server_thread, sockets = self._listen_tcp(server_addr,
                                                  self.start_protocol)
        self.listen_sockets = sockets

        server_thread.wait()
        processor_thread.wait()

    # ========================================================================
    # RTC address family related utilities
    # ========================================================================

    def update_rtfilters(self):
        """Updates RT filters for each peer.

        Should be called if a new RT Nlri's have changed based on the setting.
        Currently only used by `Processor` to update the RT filters after it
        has processed a RT destination. If RT filter has changed for a peer we
        call RT filter change handler.
        """
        # Update RT filter for all peers
        # TODO(PH): Check if getting this map can be optimized (if expensive)
        new_peer_to_rtfilter_map = self._compute_rtfilter_map()

        # If we have new best path for RT NLRI, we have to update peer RT
        # filters and take appropriate action of sending them NLRIs for other
        # address-families as per new RT filter if necessary.
        for peer in self._peer_manager.iterpeers:
            pre_rt_filter = self._rt_mgr.peer_to_rtfilter_map.get(peer, set())
            curr_rt_filter = new_peer_to_rtfilter_map.get(peer, set())

            old_rts = pre_rt_filter - curr_rt_filter
            new_rts = curr_rt_filter - pre_rt_filter
            # If interested RTs for a peer changes
            if new_rts or old_rts:
                LOG.debug('RT Filter for peer %s updated: '
                          'Added RTs %s, Removed Rts %s' %
                          (peer.ip_address, new_rts, old_rts))
                self._on_update_rt_filter(peer, new_rts, old_rts)
                # Update to new RT filters
        self._peer_manager.set_peer_to_rtfilter_map(new_peer_to_rtfilter_map)
        self._rt_mgr.peer_to_rtfilter_map = new_peer_to_rtfilter_map
        LOG.debug('Updated RT filters: %s' %
                  (str(self._rt_mgr.peer_to_rtfilter_map)))
        # Update interested RTs i.e. RTs on the path that will be installed
        # into global tables
        self._rt_mgr.update_interested_rts()

    def _on_update_rt_filter(self, peer, new_rts, old_rts):
        """Handles update of peer RT filter.

        Parameters:
            - `peer`: (Peer) whose RT filter has changed.
            - `new_rts`: (set) of new RTs that peer is interested in.
            - `old_rts`: (set) of RTs that peers is no longer interested in.
        """
        for table in self._table_manager._global_tables.itervalues():
            if table.route_family == RF_RTC_UC:
                continue
            self._spawn('rt_filter_chg_%s' % peer,
                        self._rt_mgr.on_rt_filter_chg_sync_peer,
                        peer, new_rts, old_rts, table)
            LOG.debug('RT Filter change handler launched for route_family %s'
                      % table.route_family)

    def _compute_rtfilter_map(self):
        """Returns neighbor's RT filter (permit/allow filter based on RT).

        Walks RT filter tree and computes current RT filters for each peer that
        have advertised RT NLRIs.
        Returns:
            dict of peer, and `set` of rts that a particular neighbor is
            interested in.
        """
        rtfilter_map = {}

        def get_neigh_filter(neigh):
            neigh_filter = rtfilter_map.get(neigh)
            # Lazy creation of neighbor RT filter
            if neigh_filter is None:
                neigh_filter = set()
                rtfilter_map[neigh] = neigh_filter
            return neigh_filter

        # Check if we have to use all paths or just best path
        if self._common_config.max_path_ext_rtfilter_all:
            # We have to look at all paths for a RtDest
            for rtcdest in self._table_manager.get_rtc_table().itervalues():
                known_path_list = rtcdest.known_path_list
                for path in known_path_list:
                    neigh = path.source

                    # We ignore NC
                    if neigh is None:
                        continue

                    neigh_filter = get_neigh_filter(neigh)
                    neigh_filter.add(path.nlri.route_target)
        else:
            # We iterate over all destination of the RTC table and for iBGP
            # peers we use all known paths' RTs for RT filter and for eBGP
            # peers we only consider best-paths' RTs for RT filter
            for rtcdest in self._table_manager.get_rtc_table().itervalues():
                path = rtcdest.best_path
                # If this destination does not have any path, we continue
                if not path:
                    continue

                neigh = path.source
                # Consider only eBGP peers and ignore NC
                if neigh and neigh.is_ebgp_peer():
                    # For eBGP peers we use only best-path to learn RT filter
                    neigh_filter = get_neigh_filter(neigh)
                    neigh_filter.add(path.nlri.route_target)
                else:
                    # For iBGP peers we use all known paths to learn RT filter
                    known_path_list = rtcdest.known_path_list
                    for path in known_path_list:
                        neigh = path.source
                        # We ignore NC, and eBGP peers
                        if neigh and not neigh.is_ebgp_peer():
                            neigh_filter = get_neigh_filter(neigh)
                            neigh_filter.add(path.nlri.route_target)

        return rtfilter_map

    # ========================================================================
    # Peer or Neighbor related handles/utilities.
    # ========================================================================
    def register_flexinet_sink(self, sink):
        self._sinks.add(sink)

    def unregister_flexinet_sink(self, sink):
        self._sinks.remove(sink)

    def update_flexinet_peers(self, path, route_dist):
        for sink in self._sinks:
            out_route = FlexinetOutgoingRoute(path, route_dist)
            sink.enque_outgoing_msg(out_route)

    def _set_password(self, address, password):
        if netaddr.valid_ipv4(address):
            family = socket.AF_INET
        else:
            family = socket.AF_INET6

        for sock in self.listen_sockets.values():
            if sock.family == family:
                sockopt.set_tcp_md5sig(sock, address, password)

    def on_peer_added(self, peer):
        if peer._neigh_conf.password:
            self._set_password(peer._neigh_conf.ip_address,
                               peer._neigh_conf.password)

        if self.started:
            self._spawn_activity(
                peer, self.start_protocol
            )

        # We need to handle new RTC_AS
        if peer.rtc_as != self.asn:
            self._spawn(
                'NEW_RTC_AS_HANDLER %s' % peer.rtc_as,
                self._rt_mgr.update_rtc_as_set
            )

    def on_peer_removed(self, peer):
        if peer._neigh_conf.password:
            # seting zero length key means deleting the key
            self._set_password(peer._neigh_conf.ip_address, '')

        if peer.rtc_as != self.asn:
            self._spawn(
                'OLD_RTC_AS_HANDLER %s' % peer.rtc_as,
                self._rt_mgr.update_rtc_as_set
            )

    def build_protocol(self, socket):
        assert socket
        # Check if its a reactive connection or pro-active connection
        _, remote_port = self.get_remotename(socket)
        is_reactive_conn = True
        if remote_port == STD_BGP_SERVER_PORT_NUM:
            is_reactive_conn = False

        bgp_protocol = self.protocol(
            socket,
            self._signal_bus,
            is_reactive_conn=is_reactive_conn
        )
        return bgp_protocol

    def start_protocol(self, socket):
        """Handler of new connection requests on bgp server port.

        Checks if new connection request is valid and starts new instance of
        protocol.
        """
        assert socket
        peer_addr, peer_port = self.get_remotename(socket)
        peer = self._peer_manager.get_by_addr(peer_addr)
        bgp_proto = self.build_protocol(socket)

        # We reject this connection request from peer:
        # 1) If we have connection initiated by a peer that is not in our
        #     configuration.
        # 2) If this neighbor is not enabled according to configuration.
        if not peer or not peer.enabled:
            LOG.debug('Closed connection to %s:%s as it is not a recognized'
                      ' peer.' % (peer_addr, peer_port))
            # Send connection rejected notification as per RFC
            code = BGP_ERROR_CEASE
            subcode = BGP_ERROR_SUB_CONNECTION_RESET
            bgp_proto.send_notification(code, subcode)
        elif not (peer.in_idle() or peer.in_active() or peer.in_connect()):
            LOG.debug('Closing connection to %s:%s as we have connection'
                      ' in state other than IDLE or ACTIVE,'
                      ' i.e. connection resolution' %
                      (peer_addr, peer_port))
            # Send Connection Collision Resolution notification as per RFC.
            code = BGP_ERROR_CEASE
            subcode = BGP_ERROR_SUB_CONNECTION_COLLISION_RESOLUTION
            bgp_proto.send_notification(code, subcode)
        else:
            bind_ip, bind_port = self.get_localname(socket)
            peer._host_bind_ip = bind_ip
            peer._host_bind_port = bind_port
            self._spawn_activity(bgp_proto, peer)

    def start_bmp(self, host, port):
        if (host, port) in self.bmpclients:
            bmpclient = self.bmpclients[(host, port)]
            if bmpclient.started:
                LOG.warn("bmpclient is already running for %s:%s" % (host,
                                                                     port))
                return False
        bmpclient = BMPClient(self, host, port)
        self.bmpclients[(host, port)] = bmpclient
        self._spawn_activity(bmpclient)
        return True

    def stop_bmp(self, host, port):
        if (host, port) not in self.bmpclients:
            LOG.warn("no bmpclient is running for %s:%s" % (host, port))
            return False

        bmpclient = self.bmpclients[(host, port)]
        bmpclient.stop()