예제 #1
0
    def _run(self, max_wait_open=10.0):
        try:
            if self.supervisor.processes.broken(self.neighbor.peer_address):
                # XXX: we should perhaps try to restart the process ??
                raise Failure(
                    'ExaBGP lost the helper process for this peer - peer down')

            self.bgp = Protocol(self)
            self.bgp.connect()

            self._reset_skip()

            _open = self.bgp.new_open(self._restarted, self._asn4)
            logger.message(self.me('>> %s' % _open))
            yield None

            start = time.time()
            while True:
                self.open = self.bgp.read_open(_open,
                                               self.neighbor.peer_address.ip)
                if time.time() - start > max_wait_open:
                    logger.message(
                        self.
                        me('Waited for an OPEN for too long - killing the session'
                           ))
                    raise Notify(
                        1, 1,
                        'The client took over %s seconds to send the OPEN, closing'
                        % str(max_wait_open))
                # OPEN or NOP
                if self.open.TYPE == NOP.TYPE:
                    yield None
                    continue
                # This test is already done in read_open
                #if self.open.TYPE != Open.TYPE:
                #	raise Notify(5,1,'We are expecting an OPEN message')
                logger.message(self.me('<< %s' % self.open))
                if not self.open.capabilities.announced(
                        Capabilities.FOUR_BYTES_ASN) and _open.asn.asn4():
                    self._asn4 = False
                    raise Notify(
                        2, 0,
                        'peer does not speak ASN4 - restarting in compatibility mode'
                    )
                if _open.capabilities.announced(Capabilities.MULTISESSION_BGP):
                    if not self.open.capabilities.announced(
                            Capabilities.MULTISESSION_BGP):
                        raise Notify(2, 7,
                                     'peer does not support MULTISESSION')
                    local_sessionid = set(
                        _open.capabilities[Capabilities.MULTISESSION_BGP])
                    remote_sessionid = self.open.capabilities[
                        Capabilities.MULTISESSION_BGP]
                    # Empty capability is the same as MultiProtocol (which is what we send)
                    if not remote_sessionid:
                        remote_sessionid.append(
                            Capabilities.MULTIPROTOCOL_EXTENSIONS)
                    remote_sessionid = set(remote_sessionid)
                    # As we only send one MP per session, if the matching fails, we have nothing in common
                    if local_sessionid.intersection(
                            remote_sessionid) != local_sessionid:
                        raise Notify(
                            2, 8,
                            'peer did not reply with the sessionid we sent')
                    # We can not collide due to the way we generate the configuration
                yield None
                break

            message = self.bgp.new_keepalive(force=True)
            logger.message(self.me('>> KEEPALIVE (OPENCONFIRM)'))
            yield True

            while True:
                message = self.bgp.read_keepalive()
                # KEEPALIVE or NOP
                if message.TYPE == KeepAlive.TYPE:
                    logger.message(self.me('<< KEEPALIVE (ESTABLISHED)'))
                    break
                yield None

            try:
                for name in self.supervisor.processes.notify(
                        self.neighbor.peer_address):
                    self.supervisor.processes.write(
                        name, 'neighbor %s up\n' % self.neighbor.peer_address)
            except ProcessError:
                # Can not find any better error code that 6,0 !
                raise Notify(6, 0, 'ExaBGP Internal error, sorry.')

            count = 0
            for count in self.bgp.new_announce():
                yield True
            self._updates = self.bgp.buffered()
            if count:
                logger.message(self.me('>> %d UPDATE(s)' % count))

            eor = False
            if self.neighbor.graceful_restart and \
             self.open.capabilities.announced(Capabilities.MULTIPROTOCOL_EXTENSIONS) and \
             self.open.capabilities.announced(Capabilities.GRACEFUL_RESTART):

                families = []
                for family in self.open.capabilities[
                        Capabilities.GRACEFUL_RESTART].families():
                    if family in self.neighbor.families():
                        families.append(family)
                self.bgp.new_eors(families)
                if families:
                    eor = True
                    logger.message(
                        self.me('>> EOR %s' % ', '.join([
                            '%s %s' % (str(afi), str(safi))
                            for (afi, safi) in families
                        ])))

            if not eor:
                # If we are not sending an EOR, send a keepalive as soon as when finished
                # So the other routers knows that we have no (more) routes to send ...
                # (is that behaviour documented somewhere ??)
                c, k = self.bgp.new_keepalive(True)
                if k:
                    logger.message(
                        self.me('>> KEEPALIVE (no more UPDATE and no EOR)'))

            seen_update = False
            while self._running:
                self._now = time.time()
                if self._now > self._next_info:
                    self._next_info = self._now + self.update_time
                    display_update = True
                else:
                    display_update = False

                c, k = self.bgp.new_keepalive()
                if k: logger.message(self.me('>> KEEPALIVE'))

                if display_update:
                    logger.timers(
                        self.me('Sending Timer %d second(s) left' % c))

                message = self.bgp.read_message()
                # let's read if we have keepalive before doing the timer check
                c = self.bgp.check_keepalive()

                if display_update:
                    logger.timers(
                        self.me('Receive Timer %d second(s) left' % c))

                if message.TYPE == KeepAlive.TYPE:
                    logger.message(self.me('<< KEEPALIVE'))
                elif message.TYPE == Update.TYPE:
                    seen_update = True
                    self._received_routes.extend(message.routes)
                    if message.routes:
                        logger.message(self.me('<< UPDATE'))
                        self._route_parsed += len(message.routes)
                        if self._route_parsed:
                            for route in message.routes:
                                logger.routes(
                                    LazyFormat(self.me(''), str, route))
                    else:
                        logger.message(self.me('<< UPDATE (not parsed)'))
                elif message.TYPE not in (NOP.TYPE, ):
                    logger.message(self.me('<< %d' % ord(message.TYPE)))

                if seen_update and display_update:
                    logger.supervisor(
                        self.me('processed %d routes' % self._route_parsed))
                    seen_update = False

                if self._updates:
                    count = 0
                    for count in self.bgp.new_update():
                        yield True
                    logger.message(self.me('>> UPDATE (%d)' % count))
                    self._updates = self.bgp.buffered()

                yield None

            if self.neighbor.graceful_restart and self.open.capabilities.announced(
                    Capabilities.GRACEFUL_RESTART):
                logger.warning('Closing the connection without notification')
                self.bgp.close()
                return

            # User closing the connection
            raise Notify(6, 3)
        except NotConnected, e:
            logger.warning('we can not connect to the peer %s' % str(e))
            self._more_skip()
            try:
                self.bgp.close()
            except Failure:
                pass
            return