示例#1
0
def check_nlri(neighbor, routes):
    option.enabled['parser'] = True

    announced = _hexa(routes)
    negotiated = _negotiated(neighbor)

    afi, safi = neighbor.families()[0]

    # Is the peer going to send us some Path Information with the route (AddPath)
    addpath = negotiated.addpath.send(afi, safi)

    nlris = []
    try:
        while announced:
            log.debug('parsing NLRI %s' % announced, 'parser')
            nlri, announced = NLRI.unpack_nlri(afi, safi, announced, IN.ANNOUNCED, addpath)
            nlris.append(nlri)
    except Exception as exc:
        log.error('could not parse the nlri', 'parser')
        from exabgp.debug import string_exception
        log.error(string_exception(exc), 'parser')
        if getenv().debug.pdb:
            raise
        return False

    log.debug('', 'parser')  # new line
    for nlri in nlris:
        log.info('nlri json %s' % nlri.json(), 'parser')
    return True
示例#2
0
    def run(self):
        if self.reactor.processes.broken(self.neighbor):
            # XXX: we should perhaps try to restart the process ??
            log.error('ExaBGP lost the helper process for this peer - stopping', 'process')
            if self.reactor.processes.terminate_on_error:
                self.reactor.api_shutdown()
            else:
                self.stop()
            return True

        if self.generator:
            try:
                # This generator only stops when it raises
                # otherwise return one of the ACTION
                return next(self.generator)
            except StopIteration:
                # Trying to run a closed loop, no point continuing
                self.generator = None
                if self._restart:
                    return ACTION.LATER
                return ACTION.CLOSE

        elif self.generator is None:
            if self.fsm in [FSM.OPENCONFIRM, FSM.ESTABLISHED]:
                log.debug('stopping, other connection is established', self.id())
                self.generator = False
                return ACTION.LATER
            if self._delay.backoff():
                return ACTION.LATER
            if self._restart:
                log.debug('initialising connection to %s' % self.id(), 'reactor')
                self.generator = self._run()
                return ACTION.LATER  # make sure we go through a clean loop
            return ACTION.CLOSE
示例#3
0
    def savepid(self):
        if not self.pid:
            return True

        ownid = os.getpid()

        flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY
        mode = ((os.R_OK | os.W_OK) << 6) | (os.R_OK << 3) | os.R_OK

        try:
            fd = os.open(self.pid, flags, mode)
        except OSError:
            try:
                pid = open(self.pid, 'r').readline().strip()
                if self.check_pid(int(pid)):
                    log.debug("PIDfile already exists and program still running %s" % self.pid, 'daemon')
                    return False
                else:
                    # If pid is not running, reopen file without O_EXCL
                    fd = os.open(self.pid, flags ^ os.O_EXCL, mode)
            except (OSError, IOError, ValueError):
                log.debug("issue accessing PID file %s (most likely permission or ownership)" % self.pid, 'daemon')
                return False

        try:
            f = os.fdopen(fd, 'w')
            line = "%d\n" % ownid
            f.write(line)
            f.close()
            self._saved_pid = True
        except IOError:
            log.warning("Can not create PIDfile %s" % self.pid, 'daemon')
            return False
        log.warning("Created PIDfile %s with value %d" % (self.pid, ownid), 'daemon')
        return True
示例#4
0
    def close(self, reason='protocol closed, reason unspecified'):
        if self.connection:
            log.debug(reason, self.connection.session())
            self.peer.stats['down'] = self.peer.stats.get('down', 0) + 1

            self.connection.close()
            self.connection = None
示例#5
0
    def _enter(self, name):
        location = self.tokeniser.iterate()
        log.debug("> %-16s | %s" % (location, self.tokeniser.params()),
                  'configuration')

        if location not in self._structure[name]['sections']:
            return self.error.set('section %s is invalid in %s, %s' %
                                  (location, name, self.scope.location()))

        self.scope.enter(location)
        self.scope.to_context()

        class_name = self._structure[name]['sections'][location]
        instance = self._structure[class_name].get('class', None)
        if not instance:
            raise RuntimeError('This should not be happening, debug time !')

        if not instance.pre():
            return False

        if not self.dispatch(self._structure[name]['sections'][location]):
            return False

        if not instance.post():
            return False

        left = self.scope.leave()
        if not left:
            return self.error.set('closing too many parenthesis')
        self.scope.to_context()

        log.debug("< %-16s | %s" % (left, self.tokeniser.params()),
                  'configuration')
        return True
示例#6
0
 def listen_on(self, local_addr, remote_addr, port, md5_password,
               md5_base64, ttl_in):
     try:
         if not remote_addr:
             remote_addr = IP.create(
                 '0.0.0.0') if local_addr.ipv4() else IP.create('::')
         self._listen(local_addr, remote_addr, port, md5_password,
                      md5_base64, ttl_in)
         log.debug(
             'listening for BGP session(s) on %s:%d%s' %
             (local_addr, port, ' with MD5' if md5_password else ''),
             'network',
         )
         return True
     except NetworkError as exc:
         if os.geteuid() != 0 and port <= 1024:
             log.critical(
                 'can not bind to %s:%d, you may need to run ExaBGP as root'
                 % (local_addr, port), 'network')
         else:
             log.critical(
                 'can not bind to %s:%d (%s)' %
                 (local_addr, port, str(exc)), 'network')
         log.critical(
             'unset exabgp.tcp.bind if you do not want listen for incoming connections',
             'network')
         log.critical(
             'and check that no other daemon is already binding to port %d'
             % port, 'network')
         return False
示例#7
0
文件: nlri.py 项目: nnvai47/exabgp
    def unpack_nlri(cls, afi, safi, data, action, addpath):
        a, s = AFI.create(afi), SAFI.create(safi)
        log.debug(LazyNLRI(a, s, addpath, data), 'parser')

        key = '%s/%s' % (a, s)
        if key in cls.registered_nlri:
            return cls.registered_nlri[key].unpack_nlri(a, s, data, action, addpath)
        raise Notify(3, 0, 'trying to decode unknown family %s/%s' % (a, s))
示例#8
0
 def _terminate(self, process_name):
     log.debug('terminating process %s' % process_name, 'process')
     process = self._process[process_name]
     del self._process[process_name]
     self._update_fds()
     thread = Thread(target=self._terminate_run, args=(process, ))
     thread.start()
     return thread
示例#9
0
    def _run(self, name):
        command = self.tokeniser.iterate()
        log.debug(". %-16s | %s" % (command, self.tokeniser.params()),
                  'configuration')

        if not self.run(name, command):
            return False
        return True
示例#10
0
 def new_notification(self, notification):
     for _ in self.write(notification):
         yield _NOP
     log.debug(
         '>> NOTIFICATION (%d,%d,"%s")'
         % (notification.code, notification.subcode, notification.data.decode('utf-8')),
         self.connection.session(),
     )
     yield notification
示例#11
0
    def writer(self, data):
        if not self.io:
            # XXX: FIXME: Make sure it does not hold the cleanup during the closing of the peering session
            yield True
            return
        while not self.writing():
            yield False
        logfunc.debug(lazyformat('sending TCP payload', data), self.session())
        # The first while is here to setup the try/catch block once as it is very expensive
        while True:
            try:
                while True:
                    if self.defensive and random.randint(0, 2):
                        raise socket.error(errno.EAGAIN,
                                           'raising network error on purpose')

                    # we can not use sendall as in case of network buffer filling
                    # it does raise and does not let you know how much was sent
                    number = self.io.send(data)
                    if not number:
                        self.close()
                        log.warning(
                            '%s %s lost TCP connection with peer' %
                            (self.name(), self.peer), self.session())
                        raise LostConnection('lost the TCP connection')

                    data = data[number:]
                    if not data:
                        yield True
                        return
                    yield False
            except socket.error as exc:
                if exc.args[0] in error.block:
                    log.debug(
                        '%s %s blocking io problem mid-way through writing a message %s, trying to complete'
                        % (self.name(), self.peer, errstr(exc)),
                        self.session(),
                    )
                    yield False
                elif exc.errno == errno.EPIPE:
                    # The TCP connection is gone.
                    self.close()
                    raise NetworkError('Broken TCP connection')
                elif exc.args[0] in error.fatal:
                    self.close()
                    log.critical(
                        '%s %s problem sending message (%s)' %
                        (self.name(), self.peer, errstr(exc)), self.session())
                    raise NetworkError(
                        'Problem while writing data to the network (%s)' %
                        errstr(exc))
                # what error could it be !
                else:
                    log.critical(
                        '%s %s undefined error writing on socket' %
                        (self.name(), self.peer), self.session())
                    yield False
示例#12
0
    def new_keepalive(self, comment=''):
        keepalive = KeepAlive()

        for _ in self.write(keepalive):
            yield _NOP

        log.debug('>> KEEPALIVE%s' % (' (%s)' % comment if comment else ''), self.connection.session())

        yield keepalive
示例#13
0
 def _handle_problem(self, process):
     if process not in self._process:
         return
     if self.respawn_number and self._restart[process]:
         log.debug('process %s ended, restarting it' % process, 'process')
         self._terminate(process)
         self._start(process)
     else:
         log.debug('process %s ended' % process, 'process')
         self._terminate(process)
示例#14
0
    def _reader(self, number):
        # The function must not be called if it does not return with no data with a smaller size as parameter
        if not self.io:
            self.close()
            raise NotConnected('Trying to read on a closed TCP connection')
        if number == 0:
            yield b''
            return

        while not self.reading():
            yield b''
        data = b''
        reported = ''
        while True:
            try:
                while True:
                    if self.defensive and random.randint(0, 2):
                        raise socket.error(errno.EAGAIN, 'raising network error on purpose')

                    read = self.io.recv(number)
                    if not read:
                        self.close()
                        log.warning('%s %s lost TCP session with peer' % (self.name(), self.peer), self.session())
                        raise LostConnection('the TCP connection was closed by the remote end')
                    data += read

                    number -= len(read)
                    if not number:
                        log.debug(LazyFormat('received TCP payload', data), self.session())
                        yield data
                        return

                    yield b''
            except socket.timeout as exc:
                self.close()
                log.warning('%s %s peer is too slow' % (self.name(), self.peer), self.session())
                raise TooSlowError('Timeout while reading data from the network (%s)' % errstr(exc))
            except socket.error as exc:
                if exc.args[0] in error.block:
                    message = '%s %s blocking io problem mid-way through reading a message %s, trying to complete' % (
                        self.name(),
                        self.peer,
                        errstr(exc),
                    )
                    if message != reported:
                        reported = message
                        log.debug(message, self.session())
                    yield b''
                elif exc.args[0] in error.fatal:
                    self.close()
                    raise LostConnection('issue reading on the socket: %s' % errstr(exc))
                # what error could it be !
                else:
                    log.critical('%s %s undefined error reading on socket' % (self.name(), self.peer), self.session())
                    raise NetworkError('Problem while reading data from the network (%s)' % errstr(exc))
示例#15
0
 def new_update(self, include_withdraw):
     updates = self.neighbor.rib.outgoing.updates(self.neighbor['group-updates'])
     number = 0
     for update in updates:
         for message in update.messages(self.negotiated, include_withdraw):
             number += 1
             for boolean in self.send(message):
                 # boolean is a transient network error we already announced
                 yield _NOP
     if number:
         log.debug('>> %d UPDATE(s)' % number, self.connection.session())
     yield _UPDATE
示例#16
0
    def read_open(self, ip):
        for received_open in self.read_message():
            if received_open.TYPE == NOP.TYPE:
                yield received_open
            else:
                break

        if received_open.TYPE != Open.TYPE:
            raise Notify(5, 1, 'The first packet received is not an open message (%s)' % received_open)

        log.debug('<< %s' % received_open, self.connection.session())
        yield received_open
示例#17
0
 def removepid(self):
     if not self.pid or not self._saved_pid:
         return
     try:
         os.remove(self.pid)
     except OSError as exc:
         if exc.errno == errno.ENOENT:
             pass
         else:
             log.error("Can not remove PIDfile %s" % self.pid, 'daemon')
             return
     log.debug("Removed PIDfile %s" % self.pid, 'daemon')
示例#18
0
    def __init__(self, afi, peer, local, io):
        Connection.__init__(self, afi, peer, local)

        log.debug('connection from %s' % self.peer, 'network')

        try:
            self.io = io
            asynchronous(self.io, self.peer)
            nagle(self.io, self.peer)
            self.success()
        except NetworkError as exc:
            self.close()
            raise NotConnected(errstr(exc))
示例#19
0
 def _terminate_run(self, process, process_name):
     try:
         process.terminate()
         try:
             process.wait(timeout=2)
         except subprocess.TimeoutExpired:
             log.debug('force kill unresponsive %s' % process_name,
                       'process')
             process.kill()
             process.wait(timeout=1)
     except (OSError, KeyError, subprocess.TimeoutExpired):
         # the process is most likely already dead
         pass
示例#20
0
    def close(self, reason='protocol closed, reason unspecified'):
        if self.connection:
            log.debug(reason, self.connection.session())

            # must be first otherwise we could have a loop caused by the raise in the below
            self.connection.close()
            self.connection = None

            self.peer.stats['down'] = self.peer.stats.get('down', 0) + 1
            try:
                if self.peer.neighbor.api['neighbor-changes']:
                    self.peer.reactor.processes.down(self.peer.neighbor, reason)
            except ProcessError:
                log.debug('could not send notification of neighbor close to API', self.connection.session())
示例#21
0
文件: timer.py 项目: waders909/exabgp
 def check_ka_timer(self, message=_NOP, ignore=_NOP.TYPE):
     if self.holdtime == 0:
         return message.TYPE != KeepAlive.TYPE
     now = int(time.time())
     if message.TYPE != ignore:
         self.last_read = now
     elapsed = now - self.last_read
     if elapsed > self.holdtime:
         raise Notify(self.code, self.subcode, self.message)
     if self.last_print != now:
         left = self.holdtime - elapsed
         log.debug('receive-timer %d second(s) left' % left,
                   source='ka-' + self.session())
         self.last_print = now
     return True
示例#22
0
文件: loop.py 项目: pierky/exabgp
    def restart(self):
        """Kill the BGP session and restart it"""
        log.info('performing restart of exabgp %s' % version, 'reactor')

        # XXX: FIXME: Could return False, in case there is interference with old config...
        reloaded = self.configuration.reload()

        for key in self._peers.keys():
            if key not in self.configuration.neighbors.keys():
                peer = self._peers[key]
                log.debug('removing peer %s' % peer.neighbor.name(), 'reactor')
                self._peers[key].remove()
            else:
                self._peers[key].reestablish()
        self.processes.start(self.configuration.processes, True)
示例#23
0
def cmdline(cmdarg):
    env = getenv()

    # Must be done before setting the logger as it modify its behaviour
    if cmdarg.verbose:
        env.log.all = True
        env.log.level = syslog.LOG_DEBUG

    log.init()

    if cmdarg.pdb:
        env.debug.pdb = True

    if cmdarg.verbose:
        env.log.parser = True

    for configuration in cmdarg.configuration:
        log.notice(f'loading {configuration}', 'configuration')
        location = getconf(configuration)
        if not location:
            log.critical(f'{configuration} is not an exabgp config file',
                         'configuration')
            sys.exit(1)

        config = Reactor([location]).configuration

        if not config.reload():
            log.critical(f'{configuration} is not a valid config file',
                         'configuration')
            sys.exit(1)
        log.info(f'\u2713 loading', 'configuration')

        if cmdarg.neighbor:
            log.notice(f'checking neighbors', 'configuration')
            for name, neighbor in config.neighbors.items():
                reparsed = neighbor.string()
                for line in reparsed.split('\n'):
                    log.debug(line, configuration)
                log.info(f'\u2713 neighbor  {name.split()[1]}',
                         'configuration')

        if cmdarg.route:
            log.notice(f'checking routes', 'configuration')
            if not check_generation(config.neighbors):
                log.critical(f'{configuration} has an invalid route',
                             'configuration')
                sys.exit(1)
            log.info(f'\u2713 routes', 'configuration')
示例#24
0
文件: timer.py 项目: waders909/exabgp
    def need_ka(self):
        if not self.keepalive:
            return False

        now = int(time.time())
        left = self.last_sent + self.keepalive - now

        if now != self.last_print:
            log.debug('send-timer %d second(s) left' % left,
                      source='ka-' + self.session())
            self.last_print = now

        if left <= 0:
            self.last_sent = now
            return True
        return False
示例#25
0
def check_update(neighbor, raw):
    option.enabled['parser'] = True
    negotiated = _negotiated(neighbor)

    while raw:
        if raw.startswith(b'\xff' * 16):
            kind = raw[18]
            size = (raw[16] << 16) + raw[17]

            injected, raw = raw[19:size], raw[size:]

            if kind == 2:
                log.debug('the message is an update', 'parser')
                decoding = 'update'
            else:
                log.debug(
                    'the message is not an update (%d) - aborting' % kind,
                    'parser')
                return False
        else:
            log.debug('header missing, assuming this message is ONE update',
                      'parser')
            decoding = 'update'
            injected, raw = raw, ''

        try:
            # This does not take the BGP header - let's assume we will not break that :)
            update = Update.unpack_message(injected, Direction.IN, negotiated)
        except Notify:
            import traceback

            log.error('could not parse the message', 'parser')
            log.error(traceback.format_exc(), 'parser')
            if getenv().debug.pdb:
                raise
            return False
        except Exception:
            import traceback

            log.error('could not parse the message', 'parser')
            log.error(traceback.format_exc(), 'parser')
            if getenv().debug.pdb:
                raise
            return False

        log.debug('', 'parser')  # new line
        for number in range(len(update.nlris)):
            change = Change(update.nlris[number], update.attributes)
            log.info(
                'decoded %s %s %s' %
                (decoding, change.nlri.action, change.extensive()), 'parser')
        log.info(
            'update json %s' % Response.JSON(json_version).update(
                neighbor, 'in', update, None, '', ''), 'parser')

    return True
示例#26
0
 def terminate(self):
     for process in list(self._process):
         if not self.silence:
             try:
                 self.write(process, self._encoder[process].shutdown())
             except ProcessError:
                 pass
     self.silence = True
     # waiting a little to make sure IO is flushed to the pipes
     # we are using unbuffered IO but still ..
     time.sleep(0.1)
     for process in list(self._process):
         try:
             t = self._terminate(process)
             t.join()
         except OSError:
             # we most likely received a SIGTERM signal and our child is already dead
             log.debug('child process %s was already dead' % process,
                       'process')
     self.clean()
示例#27
0
    def partial(self, section, text, action='announce'):
        self._cleanup(
        )  # this perform a big cleanup (may be able to be smarter)
        self._clear()
        self.tokeniser.set_api(
            text if text.endswith(';') or text.endswith('}') else text + ' ;')
        self.tokeniser.set_action(action)

        if self.parseSection(section) is not True:
            self._rollback_reload()
            log.debug(
                "\n"
                "syntax error in api command %s\n"
                "line %d: %s\n"
                "\n%s" % (self.scope.location(), self.tokeniser.number,
                          ' '.join(self.tokeniser.line), str(self.error)),
                'configuration',
            )
            return False
        return True
示例#28
0
    def handle_connection(self, connection):
        log.debug("state machine for the peer is %s" % self.fsm.name(),
                  self.id())

        # if the other side fails, we go back to idle
        if self.fsm == FSM.ESTABLISHED:
            log.debug(
                'we already have a peer in state established for %s' %
                connection.name(), self.id())
            return connection.notification(
                6, 7, 'could not accept the connection, already established')

        # 6.8 The convention is to compare the BGP Identifiers of the peers
        # involved in the collision and to retain only the connection initiated
        # by the BGP speaker with the higher-valued BGP Identifier.
        # FSM.IDLE , FSM.ACTIVE , FSM.CONNECT , FSM.OPENSENT , FSM.OPENCONFIRM , FSM.ESTABLISHED

        if self.fsm == FSM.OPENCONFIRM:
            # We cheat: we are not really reading the OPEN, we use the data we have instead
            # it does not matter as the open message will be the same anyway
            local_id = self.neighbor.router_id.pack()
            remote_id = self.proto.negotiated.received_open.router_id.pack()

            if remote_id < local_id:
                log.debug(
                    'closing incoming connection as we have an outgoing connection with higher router-id for %s'
                    % connection.name(),
                    self.id(),
                )
                return connection.notification(
                    6,
                    7,
                    'could not accept the connection, as another connection is already in open-confirm and will go through',
                )

        # accept the connection
        if self.proto:
            log.debug(
                'closing outgoing connection as we have another incoming on with higher router-id for %s'
                % connection.name(),
                self.id(),
            )
            self.proto.close(
                'closing outgoing connection as we have another incoming on with higher router-id'
            )

        self.proto = Protocol(self).accept(connection)
        self.generator = None
        # Let's make sure we do some work with this connection
        self._delay.reset()
        return None
示例#29
0
    def write(self, process, string, neighbor=None):
        if string is None:
            return True

        # XXX: FIXME: This is potentially blocking
        while True:
            try:
                self._process[process].stdin.write(
                    bytes('%s\n' % string, 'ascii'))
            except IOError as exc:
                self._broken.append(process)
                if exc.errno == errno.EPIPE:
                    self._broken.append(process)
                    log.debug('issue while sending data to our helper program',
                              'process')
                    raise ProcessError()
                else:
                    # Could it have been caused by a signal ? What to do.
                    log.debug(
                        'error received while sending data to helper program, retrying (%s)'
                        % errstr(exc), 'process')
                    continue
            break

        try:
            self._process[process].stdin.flush()
        except IOError as exc:
            # AFAIK, the buffer should be flushed at the next attempt.
            log.debug(
                'error received while FLUSHING data to helper program, retrying (%s)'
                % errstr(exc), 'process')

        return True
示例#30
0
    def establish(self):
        last = time.time() - 2.0

        while True:
            notify = time.time() - last > 1.0
            if notify:
                last = time.time()

            if notify:
                log.debug('attempting connection to %s:%d' % (self.peer, self.port), self.session())

            connect_issue = self._connect()
            if connect_issue:
                if notify:
                    log.debug('connection to %s:%d failed' % (self.peer, self.port), self.session())
                    log.debug(str(connect_issue), self.session())
                yield False
                continue

            connected = False
            for r, message in ready(self.io):
                if not r:
                    yield False
                    continue
                connected = True

            if connected:
                self.success()
                if not self.local:
                    self.local = self.io.getsockname()[0]
                yield True
                return

            self._setup()