Esempio n. 1
0
	def _keepalive (self, proto):
		need_ka   = False
		generator = None

		while True:
			# SEND KEEPALIVES
			need_ka |= self.send_timer.need_ka()

			if need_ka:
				if not generator:
					generator = proto.new_keepalive()
					need_ka = False

			if not generator:
				yield False
				continue

			try:
				# try to close the generator and raise a StopIteration in one call
				six.next(generator)
				six.next(generator)
				# still running
				yield True
			except NetworkError:
				raise Notify(4,0,'problem with network while trying to send keepalive')
			except StopIteration:
				generator = None
				yield False
Esempio n. 2
0
	def schedule (self):
		try:
			# read at least on message per process if there is some and parse it
			for service,command in self.processes.received():
				self.api.text(self,service,command)

			# if we have nothing to do, return or save the work
			if not self._running:
				if not self._pending:
					return False
				self._running,name = self._pending.popleft()
				self.logger.reactor('callback | installing %s' % name)

			if self._running:
				# run it
				try:
					self.logger.reactor('callback | running')
					six.next(self._running)  # run
					# should raise StopIteration in most case
					# and prevent us to have to run twice to run one command
					six.next(self._running)  # run
				except StopIteration:
					self._running = None
					self.logger.reactor('callback | removing')
				return True

		except StopIteration:
			pass
		except KeyboardInterrupt:
			self._shutdown = True
			self.logger.reactor('^C received','error')
Esempio n. 3
0
    def schedule(self):
        try:
            # read at least on message per process if there is some and parse it
            for service, command in self.processes.received():
                self.api.text(self, service, command)

            # if we have nothing to do, return or save the work
            if not self._running:
                if not self._pending:
                    return False
                self._running, name = self._pending.popleft()
                self.logger.reactor('callback | installing %s' % name)

            if self._running:
                # run it
                try:
                    self.logger.reactor('callback | running')
                    six.next(self._running)  # run
                    # should raise StopIteration in most case
                    # and prevent us to have to run twice to run one command
                    six.next(self._running)  # run
                except StopIteration:
                    self._running = None
                    self.logger.reactor('callback | removing')
                return True

        except StopIteration:
            pass
        except KeyboardInterrupt:
            self._shutdown = True
            self.logger.reactor('^C received', 'error')
Esempio n. 4
0
    def run(self):
        if not self.ready():
            return False

        length = range(len(self._async))
        uid, generator = self._async.popleft()

        for _ in length:
            try:
                six.next(generator)
                six.next(generator)
            except StopIteration:
                if not self._async:
                    return False
                uid, generator = self._async.popleft()
            except KeyboardInterrupt:
                raise
            except Exception as exc:
                self.logger.error('async | %s | problem with function' % uid,
                                  'reactor')
                for line in str(exc).split('\n'):
                    self.logger.error('async | %s | %s' % (uid, line),
                                      'reactor')

        self._async.appendleft((uid, generator))
        return True
Esempio n. 5
0
def test ():
	OPEN = ''.join([chr(int(_,16)) for _ in "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 1D 01 04 78 14 00 5A 52 DB 00 45 00".split()])
	KEEP = ''.join([chr(int(_,16)) for _ in "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 04".split()])

	from exabgp.reactor.network.outgoing import Outgoing
	connection = Outgoing(1,'82.219.0.69','82.219.212.34')
	writer = connection.writer(OPEN)
	while six.next(writer) is False:
		pass
	writer = connection.writer(KEEP)
	while six.next(writer) is False:
		pass

	reader = connection.reader()

	for size,msg,header,body,notification in reader:
		if size:
			print(od(header+body))
		else:
			sys.stdout.write('-')

	reader = connection.reader()

	for size,msg,header,body,notification in reader:
		if size:
			print(od(header+body))
		else:
			sys.stdout.write('+')

	connection.close()
Esempio n. 6
0
 def formated():
     while True:
         line = six.next(fileobject).rstrip()
         self.index_line += 1
         while line.endswith('\\'):
             line = line[:-1] + six.next(fileobject).rstrip()
             self.index_line += 1
         yield line
Esempio n. 7
0
 def _scheduled_listener(self, flipflop=[]):
     try:
         for generator in self._async:
             try:
                 six.next(generator)
                 six.next(generator)
                 flipflop.append(generator)
             except StopIteration:
                 pass
         self._async, flipflop = flipflop, self._async
         return len(self._async) > 0
     except KeyboardInterrupt:
         self._termination('^C received')
         return False
Esempio n. 8
0
	def connect (self):
		# allows to test the protocol code using modified StringIO with a extra 'pending' function
		if not self.connection:
			local = self.neighbor.md5_ip
			peer = self.neighbor.peer_address
			md5 = self.neighbor.md5_password
			ttl_out = self.neighbor.ttl_out
			self.connection = Outgoing(peer.afi,peer.top(),local.top(),self.port,md5,ttl_out)

			try:
				generator = self.connection.establish()
				while True:
					connected = six.next(generator)
					if not connected:
						yield False
						continue
					if self.peer.neighbor.api['neighbor-changes']:
						self.peer.reactor.processes.connected(self.peer.neighbor)
					yield True
					return
			except StopIteration:
				# close called by the caller
				# self.close('could not connect to remote end')
				yield False
				return
Esempio n. 9
0
    def connect(self):
        # allows to test the protocol code using modified StringIO with a extra 'pending' function
        if not self.connection:
            local = self.neighbor.md5_ip
            peer = self.neighbor.peer_address
            md5 = self.neighbor.md5_password
            ttl_out = self.neighbor.ttl_out
            self.connection = Outgoing(peer.afi, peer.top(), local.top(),
                                       self.port, md5, ttl_out)

            try:
                generator = self.connection.establish()
                while True:
                    connected = six.next(generator)
                    if not connected:
                        yield False
                        continue
                    if self.peer.neighbor.api['neighbor-changes']:
                        self.peer.reactor.processes.connected(
                            self.peer.neighbor)
                    yield True
                    return
            except StopIteration:
                # close called by the caller
                # self.close('could not connect to remote end')
                yield False
                return
Esempio n. 10
0
 def peek(self):
     try:
         peaked = six.next(self.generator)
         self.next.append(peaked)
         return peaked
     except StopIteration:
         return ''
Esempio n. 11
0
	def run (self):
		if self.reactor.processes.broken(self.neighbor):
			# XXX: we should perhaps try to restart the process ??
			self.logger.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 six.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]:
				self.logger.debug('stopping, other connection is established',self.id())
				self.generator = False
				return ACTION.LATER
			if self._delay.backoff():
				return ACTION.LATER
			if self._restart:
				self.logger.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
Esempio n. 12
0
	def connect (self):
		# allows to test the protocol code using modified StringIO with a extra 'pending' function
		if not self.connection:
			local = self.neighbor.md5_ip.top() if not self.neighbor.auto_discovery else None
			peer = self.neighbor.peer_address.top()
			afi = self.neighbor.peer_address.afi
			md5 = self.neighbor.md5_password
			md5_base64 = self.neighbor.md5_base64
			ttl_out = self.neighbor.ttl_out
			self.connection = Outgoing(afi,peer,local,self.port,md5,md5_base64,ttl_out)
			if not self.connection.init:
				yield False
				return
			if not local:
				self.neighbor.local_address = IP.create(self.connection.local)
				if self.neighbor.router_id is None and self.neighbor.local_address.afi == AFI.ipv4:
					self.neighbor.router_id = self.neighbor.local_address

			try:
				generator = self.connection.establish()
				while True:
					connected = six.next(generator)
					if not connected:
						yield False
						continue
					if self.peer.neighbor.api['neighbor-changes']:
						self.peer.reactor.processes.connected(self.peer.neighbor)
					yield True
					return
			except StopIteration:
				# close called by the caller
				# self.close('could not connect to remote end')
				yield False
				return
Esempio n. 13
0
    def run(self):
        if not self._async:
            return False
        running = []

        for (uid, generator) in self._async[0]:
            try:
                six.next(generator)
                six.next(generator)
                running.append((uid, generator))
            except StopIteration:
                pass
        self._async.pop()
        if running:
            self._async.append(running)
        return True
Esempio n. 14
0
	def run (self):
		if self.reactor.processes.broken(self.neighbor):
			# XXX: we should perhaps try to restart the process ??
			self.logger.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 six.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]:
				self.logger.debug('stopping, other connection is established',self.id())
				self.generator = False
				return ACTION.LATER
			if self._delay.backoff():
				return ACTION.LATER
			if self._restart:
				self.logger.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
Esempio n. 15
0
	def __call__ (self):
		#  True  if we need or are trying
		#  False if we do not need to send one
		try:
			return six.next(self._generator)
		except StopIteration:
			raise Notify(4,0,'could not send keepalive')
Esempio n. 16
0
	def connect (self):
		# allows to test the protocol code using modified StringIO with a extra 'pending' function
		if not self.connection:
			local = self.neighbor.md5_ip.top() if not self.neighbor.auto_discovery else None
			peer = self.neighbor.peer_address.top()
			afi = self.neighbor.peer_address.afi
			md5 = self.neighbor.md5_password
			md5_base64 = self.neighbor.md5_base64
			ttl_out = self.neighbor.ttl_out
			self.connection = Outgoing(afi,peer,local,self.port,md5,md5_base64,ttl_out)
			if not local and self.connection.init:
				self.neighbor.local_address = IP.create(self.connection.local)
				if self.neighbor.router_id is None and self.neighbor.local_address.afi == AFI.ipv4:
					self.neighbor.router_id = self.neighbor.local_address

			try:
				generator = self.connection.establish()
				while True:
					connected = six.next(generator)
					if not connected:
						yield False
						continue
					if self.peer.neighbor.api['neighbor-changes']:
						self.peer.reactor.processes.connected(self.peer.neighbor)
					yield True
					return
			except StopIteration:
				# close called by the caller
				# self.close('could not connect to remote end')
				yield False
				return
Esempio n. 17
0
        def __call__(self):
            if self.next:
                return self.next.popleft()

            try:
                return six.next(self.generator)
            except StopIteration:
                return ''
Esempio n. 18
0
	def __init__ (self, rd, endpoint, base, offset, size):
		NLRI.__init__(self,AFI.l2vpn,SAFI.vpls)
		self.action = OUT.ANNOUNCE
		self.nexthop = None
		self.rd = rd
		self.base = base
		self.offset = offset
		self.size = size
		self.endpoint = endpoint
		self.unique = six.next(unique)
Esempio n. 19
0
	def __init__ (self, rd, endpoint, base, offset, size):
		NLRI.__init__(self,AFI.l2vpn,SAFI.vpls)
		self.action = OUT.ANNOUNCE
		self.nexthop = None
		self.rd = rd
		self.base = base
		self.offset = offset
		self.size = size
		self.endpoint = endpoint
		self.unique = six.next(unique)
Esempio n. 20
0
    def run(self):
        if self.reactor.processes.broken(self.neighbor):
            # XXX: we should perhaps try to restart the process ??
            self.logger.processes(
                'ExaBGP lost the helper process for this peer - stopping',
                'error')
            self.stop()
            return True

        back = ACTION.LATER if self._restart else ACTION.CLOSE

        for direction in (self._incoming, self._outgoing):
            if direction.generator:
                try:
                    # This generator only stops when it raises
                    r = six.next(direction.generator)

                    # if r is ACTION.NOW: status = 'immediately'
                    # elif r is ACTION.LATER:   status = 'next second'
                    # elif r is ACTION.CLOSE:   status = 'stop'
                    # else: status = 'buggy'
                    # self.logger.network('%s loop %11s, state is %s' % (direction.name,status,direction.fsm),'debug')

                    if r == ACTION.NOW:
                        back = ACTION.NOW
                    elif r == ACTION.LATER:
                        back = ACTION.LATER if back != ACTION.NOW else ACTION.NOW
                except StopIteration:
                    # Trying to run a closed loop, no point continuing
                    direction.generator = direction.enabled

            elif direction.generator is None:
                if direction.opposite.fsm in [
                        FSM.OPENCONFIRM, FSM.ESTABLISHED
                ]:
                    self.logger.network(
                        '%s loop, stopping, other one is established' %
                        direction.name, 'debug')
                    direction.generator = False
                    continue
                if direction.name == 'out' and self._delay.backoff():
                    self.logger.network(
                        '%s loop, skipping, not time yet' % direction.name,
                        'debug')
                    back = ACTION.LATER
                    continue
                if self._restart:
                    self.logger.network(
                        '%s loop, intialising' % direction.name, 'debug')
                    direction.generator = self._run(direction)
                    back = ACTION.LATER  # make sure we go through a clean loop

        return back
Esempio n. 21
0
	def run (self):
		if not self._async:
			return False
		running = []

		for (uid,generator) in self._async[0]:
			try:
				six.next(generator)
				six.next(generator)
				running.append((uid,generator))
			except StopIteration:
				pass
			except KeyboardInterrupt:
				raise
			except Exception as exc:
				self.logger.error('async | %s | problem with function' % uid,'reactor')
				for line in str(exc).split('\n'):
					self.logger.error('async | %s | %s' % (uid,line),'reactor')
		self._async.pop()
		if running:
			self._async.append(running)
		return True
Esempio n. 22
0
def test():
    OPEN = ''.join([
        chr(int(_, 16)) for _ in
        "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 1D 01 04 78 14 00 5A 52 DB 00 45 00"
        .split()
    ])
    KEEP = ''.join([
        chr(int(_, 16)) for _ in
        "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 04".split()
    ])

    from exabgp.reactor.network.outgoing import Outgoing
    connection = Outgoing(1, '82.219.0.69', '82.219.212.34')
    writer = connection.writer(OPEN)
    while six.next(writer) is False:
        pass
    writer = connection.writer(KEEP)
    while six.next(writer) is False:
        pass

    reader = connection.reader()

    for size, msg, header, body, notification in reader:
        if size:
            print(od(header + body))
        else:
            sys.stdout.write('-')

    reader = connection.reader()

    for size, msg, header, body, notification in reader:
        if size:
            print(od(header + body))
        else:
            sys.stdout.write('+')

    connection.close()
Esempio n. 23
0
	def run (self):
		if not self.ready():
			return False

		length = range(min(len(self._async),self.LIMIT))
		uid, generator = self._async.popleft()

		for _ in length:
			try:
				six.next(generator)
				six.next(generator)
			except StopIteration:
				if not self._async:
					return False
				uid, generator = self._async.popleft()
			except KeyboardInterrupt:
				raise
			except Exception as exc:
				self.logger.error('async | %s | problem with function' % uid,'reactor')
				for line in str(exc).split('\n'):
					self.logger.error('async | %s | %s' % (uid,line),'reactor')

		self._async.appendleft((uid, generator))
		return True
Esempio n. 24
0
	def _set (self, function):
		try:
			self._tokens = function
			self._next = six.next(self._tokens)
		except IOError as exc:
			error = str(exc)
			if error.count(']'):
				self.error.set(error.split(']')[1].strip())
			else:
				self.error.set(error)
			self._tokens = Tokeniser._off
			self._next = []
			return self.error.set('issue setting the configuration parser')
		except StopIteration:
			self._tokens = Tokeniser._off
			self._next = []
			return self.error.set('issue setting the configuration parser, no data')
		return True
Esempio n. 25
0
    def __call__(self):
        self.number += 1
        try:
            self.line, self._next = self._next, six.next(self._tokens)
            self.end = self.line[-1]
        except StopIteration:
            if not self.finished:
                self.finished = True
                self.line, self._next = self._next, []
                self.end = self.line[-1]
            else:
                self.line = []
                self.end = ''

        # should we raise a Location if called with no more data ?
        self.iterate.replenish(self.line[:-1])

        return self.line
Esempio n. 26
0
	def establish (self):
		if not self.init:
			yield False
			return

		try:
			generator = ready(self.io)
			while True:
				connected = six.next(generator)
				if not connected:
					yield False
					continue
				yield True
				return
		except StopIteration:
			# self.io MUST NOT be closed here, it is closed by the caller
			yield False
			return

		nagle(self.io,self.peer)
		# Not working after connect() at least on FreeBSD TTL(self.io,self.peer,self.ttl)
		yield True
Esempio n. 27
0
    def establish(self):
        if not self.init:
            yield False
            return

        try:
            generator = ready(self.io)
            while True:
                connected = six.next(generator)
                if not connected:
                    yield False
                    continue
                yield True
                return
        except StopIteration:
            # self.io MUST NOT be closed here, it is closed by the caller
            yield False
            return

        nagle(self.io, self.peer)
        # Not working after connect() at least on FreeBSD TTL(self.io,self.peer,self.ttl)
        yield True
Esempio n. 28
0
	def _connect (self):
		proto = Protocol(self)
		generator = proto.connect()

		connected = False
		try:
			while not connected:
				if self._teardown:
					raise StopIteration()
				connected = six.next(generator)
				# we want to come back as soon as possible
				yield ACTION.LATER
			self.proto = proto
		except StopIteration:
			# Connection failed
			if not connected and self.proto:
				self.proto.close('connection to %s:%d failed' % (self.neighbor.peer_address,self.neighbor.connect))

			# A connection arrived before we could establish !
			if not connected or self.proto:
				yield ACTION.NOW
				raise Interrupted()
Esempio n. 29
0
	def _connect (self):
		proto = Protocol(self)
		generator = proto.connect()

		connected = False
		try:
			while not connected:
				if self._teardown:
					raise StopIteration()
				connected = six.next(generator)
				# we want to come back as soon as possible
				yield ACTION.LATER
			self.proto = proto
		except StopIteration:
			# Connection failed
			if not connected and self.proto:
				self.proto.close('connection to %s:%d failed' % (self.neighbor.peer_address,self.neighbor.connect))

			# A connection arrived before we could establish !
			if not connected or self.proto:
				yield ACTION.NOW
				raise Interrupted()
Esempio n. 30
0
    def _run(self, direction):
        """yield True if we want the reactor to give us back the hand with the same peer loop, None if we do not have any more work to do"""
        try:
            for action in direction.code():
                yield action

            for action in self._main(direction):
                yield action

        # CONNECTION FAILURE
        except NetworkError as network:
            # we tried to connect once, it failed and it was not a manual request, we stop
            if self.once and not self._teardown:
                self.logger.network(
                    'only one attempt to connect is allowed, stopping the peer'
                )
                self.stop()

            self._reset(direction, 'closing connection', network)
            return

        # NOTIFY THE PEER OF AN ERROR
        except Notify as notify:
            if direction.proto:
                try:
                    generator = direction.proto.new_notification(notify)
                    try:
                        maximum = 20
                        while maximum:
                            six.next(generator)
                            maximum -= 1
                            yield ACTION.NOW if maximum > 10 else ACTION.LATER
                    except StopIteration:
                        pass
                except (NetworkError, ProcessError):
                    self.logger.network(self.me('NOTIFICATION NOT SENT'),
                                        'error')
                self._reset(
                    direction, 'notification sent (%d,%d)' %
                    (notify.code, notify.subcode), notify)
            else:
                self._reset(direction)
            return

        # THE PEER NOTIFIED US OF AN ERROR
        except Notification as notification:
            # we tried to connect once, it failed and it was not a manual request, we stop
            if self.once and not self._teardown:
                self.logger.network(
                    'only one attempt to connect is allowed, stopping the peer'
                )
                self.stop()

            self._reset(direction,'notification received (%d,%d)' \
             % (notification.code, notification.subcode), notification)
            return

        # RECEIVED a Message TYPE we did not expect
        except Message as message:
            self._reset(direction, 'unexpected message received', message)
            return

        # PROBLEM WRITING TO OUR FORKED PROCESSES
        except ProcessError as process:
            self._reset(direction, 'process problem', process)
            return

        # ....
        except Interrupted as interruption:
            self._reset(interruption.direction)
            return

        # UNHANDLED PROBLEMS
        except Exception as exc:
            # Those messages can not be filtered in purpose
            self.logger.raw('\n'.join([
                no_panic,
                self.me(''), '',
                str(type(exc)),
                str(exc),
                trace(), footer
            ]))
            self._reset(direction)
            return
Esempio n. 31
0
	def _run (self):
		"""yield True if we want the reactor to give us back the hand with the same peer loop, None if we do not have any more work to do"""
		try:
			for action in self._establish():
				yield action

			for action in self._main():
				yield action

		# CONNECTION FAILURE
		except NetworkError as network:
			# we tried to connect once, it failed and it was not a manual request, we stop
			if self.once and not self._teardown:
				self.logger.debug('only one attempt to connect is allowed, stopping the peer',self.id())
				self.stop()

			self._reset('closing connection',network)
			return

		# NOTIFY THE PEER OF AN ERROR
		except Notify as notify:
			if self.proto:
				try:
					generator = self.proto.new_notification(notify)
					try:
						while True:
							six.next(generator)
							yield ACTION.NOW
					except StopIteration:
						pass
				except (NetworkError,ProcessError):
					self.logger.error('Notification not sent',self.id())
				self._reset('notification sent (%d,%d)' % (notify.code,notify.subcode),notify)
			else:
				self._reset()
			return

		# THE PEER NOTIFIED US OF AN ERROR
		except Notification as notification:
			# we tried to connect once, it failed and it was not a manual request, we stop
			if self.once and not self._teardown:
				self.logger.debug('only one attempt to connect is allowed, stopping the peer',self.id())
				self.stop()

			self._reset(
				'notification received (%d,%d)' % (
					notification.code,
					notification.subcode),
				notification
			)
			return

		# RECEIVED a Message TYPE we did not expect
		except Message as message:
			self._reset('unexpected message received',message)
			return

		# PROBLEM WRITING TO OUR FORKED PROCESSES
		except ProcessError as process:
			self._reset('process problem',process)
			return

		# ....
		except Interrupted as interruption:
			self._reset('connection received before we could fully establish one')
			return

		# UNHANDLED PROBLEMS
		except Exception as exc:
			# Those messages can not be filtered in purpose
			self.logger.debug('\n'.join([
				NO_PANIC,
				'',
				'',
				str(type(exc)),
				str(exc),
				trace(),
				FOOTER
			]),'reactor')
			self._reset()
			return
Esempio n. 32
0
	def _main (self):
		"""yield True if we want to come back to it asap, None if nothing urgent, and False if stopped"""
		if self._teardown:
			raise Notify(6,3)

		self.neighbor.rib.incoming.clear()

		include_withdraw = False

		# Announce to the process BGP is up
		self.logger.notice('connected to %s with %s' % (self.id(),self.proto.connection.name()),'reactor')
		self.stats['up'] = self.stats.get('up',0) + 1
		if self.neighbor.api['neighbor-changes']:
			try:
				self.reactor.processes.up(self.neighbor)
			except ProcessError:
				# Can not find any better error code than 6,0 !
				# XXX: We can not restart the program so this will come back again and again - FIX
				# XXX: In the main loop we do exit on this kind of error
				raise Notify(6,0,'ExaBGP Internal error, sorry.')

		send_eor = not self.neighbor.manual_eor
		new_routes = None
		self._resend_routes = SEND.NORMAL
		send_families = []

		# Every last asm message should be re-announced on restart
		for family in self.neighbor.asm:
			if family in self.neighbor.families():
				self.neighbor.messages.appendleft(self.neighbor.asm[family])

		operational = None
		refresh = None
		command_eor = None
		number = 0
		refresh_enhanced = True if self.proto.negotiated.refresh == REFRESH.ENHANCED else False

		send_ka = KA(self.proto.connection.session,self.proto)

		while not self._teardown:
			for message in self.proto.read_message():
				self.recv_timer.check_ka(message)

				if send_ka() is not False:
					# we need and will send a keepalive
					while send_ka() is None:
						yield ACTION.NOW

				# Received update
				if message.TYPE == Update.TYPE:
					number += 1
					self.logger.debug('<< UPDATE #%d' % number,self.id())

					for nlri in message.nlris:
						self.neighbor.rib.incoming.update_cache(Change(nlri,message.attributes))
						self.logger.debug(LazyFormat('   UPDATE #%d nlri ' % number,nlri,str),self.id())

				elif message.TYPE == RouteRefresh.TYPE:
					if message.reserved == RouteRefresh.request:
						self._resend_routes = SEND.REFRESH
						send_families.append((message.afi,message.safi))

				# SEND OPERATIONAL
				if self.neighbor.operational:
					if not operational:
						new_operational = self.neighbor.messages.popleft() if self.neighbor.messages else None
						if new_operational:
							operational = self.proto.new_operational(new_operational,self.proto.negotiated)

					if operational:
						try:
							six.next(operational)
						except StopIteration:
							operational = None
				# make sure that if some operational message are received via the API
				# that we do not eat memory for nothing
				elif self.neighbor.messages:
					self.neighbor.messages.popleft()

				# SEND REFRESH
				if self.neighbor.route_refresh:
					if not refresh:
						new_refresh = self.neighbor.refresh.popleft() if self.neighbor.refresh else None
						if new_refresh:
							refresh = self.proto.new_refresh(new_refresh)

					if refresh:
						try:
							six.next(refresh)
						except StopIteration:
							refresh = None

				# Take the routes already sent to that peer and resend them
				if self._reconfigure:
					self._reconfigure = False

					# we are here following a configuration change
					if self._neighbor:
						# see what changed in the configuration
						self.neighbor.rib.outgoing.replace(self._neighbor.backup_changes,self._neighbor.changes)
						# do not keep the previous routes in memory as they are not useful anymore
						self._neighbor.backup_changes = []

				# Take the routes already sent to that peer and resend them
				if self._resend_routes != SEND.DONE:
					enhanced = True if refresh_enhanced and self._resend_routes == SEND.REFRESH else False
					self._resend_routes = SEND.DONE
					self.neighbor.rib.outgoing.resend(send_families,enhanced)
					send_families = []

				# Need to send update
				if not new_routes and self.neighbor.rib.outgoing.pending():
					# XXX: in proto really. hum to think about ?
					new_routes = self.proto.new_update(include_withdraw)

				if new_routes:
					try:
						for _ in range(25):
							# This can raise a NetworkError
							six.next(new_routes)
					except StopIteration:
						new_routes = None
						include_withdraw = True

				elif send_eor:
					send_eor = False
					for _ in self.proto.new_eors():
						yield ACTION.NOW
					self.logger.debug('>> EOR(s)',self.id())

				# SEND MANUAL KEEPALIVE (only if we have no more routes to send)
				elif not command_eor and self.neighbor.eor:
					new_eor = self.neighbor.eor.popleft()
					command_eor = self.proto.new_eors(new_eor.afi,new_eor.safi)

				if command_eor:
					try:
						six.next(command_eor)
					except StopIteration:
						command_eor = None

				if new_routes or message.TYPE != NOP.TYPE:
					yield ACTION.NOW
				elif self.neighbor.messages or operational:
					yield ACTION.NOW
				elif self.neighbor.eor or command_eor:
					yield ACTION.NOW
				else:
					yield ACTION.LATER

				# read_message will loop until new message arrives with NOP
				if self._teardown:
					break

		# If graceful restart, silent shutdown
		if self.neighbor.graceful_restart and self.proto.negotiated.sent_open.capabilities.announced(Capability.CODE.GRACEFUL_RESTART):
			self.logger.error('closing the session without notification',self.id())
			self.proto.close('graceful restarted negotiated, closing without sending any notification')
			raise NetworkError('closing')

		# notify our peer of the shutdown
		raise Notify(6,self._teardown)
Esempio n. 33
0
    def _run(self):
        """yield True if we want the reactor to give us back the hand with the same peer loop, None if we do not have any more work to do"""
        try:
            for action in self._establish():
                yield action

            for action in self._main():
                yield action

        # CONNECTION FAILURE
        except NetworkError as network:
            # we tried to connect once, it failed and it was not a manual request, we stop
            if self.once and not self._teardown:
                self.logger.debug(
                    'only one attempt to connect is allowed, stopping the peer',
                    self.id())
                self.stop()

            self._reset('closing connection', network)
            return

        # NOTIFY THE PEER OF AN ERROR
        except Notify as notify:
            if self.proto:
                try:
                    generator = self.proto.new_notification(notify)
                    try:
                        while True:
                            six.next(generator)
                            yield ACTION.NOW
                    except StopIteration:
                        pass
                except (NetworkError, ProcessError):
                    self.logger.error('Notification not sent', self.id())
                self._reset(
                    'notification sent (%d,%d)' %
                    (notify.code, notify.subcode), notify)
            else:
                self._reset()
            return

        # THE PEER NOTIFIED US OF AN ERROR
        except Notification as notification:
            # we tried to connect once, it failed and it was not a manual request, we stop
            if self.once and not self._teardown:
                self.logger.debug(
                    'only one attempt to connect is allowed, stopping the peer',
                    self.id())
                self.stop()

            self._reset(
                'notification received (%d,%d)' %
                (notification.code, notification.subcode), notification)
            return

        # RECEIVED a Message TYPE we did not expect
        except Message as message:
            self._reset('unexpected message received', message)
            return

        # PROBLEM WRITING TO OUR FORKED PROCESSES
        except ProcessError as process:
            self._reset('process problem', process)
            return

        # ....
        except Interrupted as interruption:
            self._reset(
                'connection received before we could fully establish one')
            return

        # UNHANDLED PROBLEMS
        except Exception as exc:
            # Those messages can not be filtered in purpose
            self.logger.debug(
                '\n'.join([
                    NO_PANIC, '', '',
                    str(type(exc)),
                    str(exc),
                    trace(), FOOTER
                ]), 'reactor')
            self._reset()
            return
Esempio n. 34
0
    def _main(self):
        """yield True if we want to come back to it asap, None if nothing urgent, and False if stopped"""
        if self._teardown:
            raise Notify(6, 3)

        self.neighbor.rib.incoming.clear()

        include_withdraw = False

        # Announce to the process BGP is up
        self.logger.notice(
            'connected to %s with %s' %
            (self.id(), self.proto.connection.name()), 'reactor')
        self.stats['up'] = self.stats.get('up', 0) + 1
        if self.neighbor.api['neighbor-changes']:
            try:
                self.reactor.processes.up(self.neighbor)
            except ProcessError:
                # Can not find any better error code than 6,0 !
                # XXX: We can not restart the program so this will come back again and again - FIX
                # XXX: In the main loop we do exit on this kind of error
                raise Notify(6, 0, 'ExaBGP Internal error, sorry.')

        send_eor = not self.neighbor.manual_eor
        new_routes = None
        self._resend_routes = SEND.NORMAL
        send_families = []

        # Every last asm message should be re-announced on restart
        for family in self.neighbor.asm:
            if family in self.neighbor.families():
                self.neighbor.messages.appendleft(self.neighbor.asm[family])

        operational = None
        refresh = None
        command_eor = None
        number = 0
        refresh_enhanced = True if self.proto.negotiated.refresh == REFRESH.ENHANCED else False

        send_ka = KA(self.proto.connection.session, self.proto)

        while not self._teardown:
            for message in self.proto.read_message():
                self.recv_timer.check_ka(message)

                if send_ka() is not False:
                    # we need and will send a keepalive
                    while send_ka() is None:
                        yield ACTION.NOW

                # Received update
                if message.TYPE == Update.TYPE:
                    number += 1
                    self.logger.debug('<< UPDATE #%d' % number, self.id())

                    for nlri in message.nlris:
                        self.neighbor.rib.incoming.update_cache(
                            Change(nlri, message.attributes))
                        self.logger.debug(
                            LazyFormat('   UPDATE #%d nlri ' % number, nlri,
                                       str), self.id())

                elif message.TYPE == RouteRefresh.TYPE:
                    if message.reserved == RouteRefresh.request:
                        self._resend_routes = SEND.REFRESH
                        send_families.append((message.afi, message.safi))

                # SEND OPERATIONAL
                if self.neighbor.operational:
                    if not operational:
                        new_operational = self.neighbor.messages.popleft(
                        ) if self.neighbor.messages else None
                        if new_operational:
                            operational = self.proto.new_operational(
                                new_operational, self.proto.negotiated)

                    if operational:
                        try:
                            six.next(operational)
                        except StopIteration:
                            operational = None
                # make sure that if some operational message are received via the API
                # that we do not eat memory for nothing
                elif self.neighbor.messages:
                    self.neighbor.messages.popleft()

                # SEND REFRESH
                if self.neighbor.route_refresh:
                    if not refresh:
                        new_refresh = self.neighbor.refresh.popleft(
                        ) if self.neighbor.refresh else None
                        if new_refresh:
                            refresh = self.proto.new_refresh(new_refresh)

                    if refresh:
                        try:
                            six.next(refresh)
                        except StopIteration:
                            refresh = None

                # Take the routes already sent to that peer and resend them
                if self._reconfigure:
                    self._reconfigure = False

                    # we are here following a configuration change
                    if self._neighbor:
                        # see what changed in the configuration
                        self.neighbor.rib.outgoing.replace(
                            self._neighbor.backup_changes,
                            self._neighbor.changes)
                        # do not keep the previous routes in memory as they are not useful anymore
                        self._neighbor.backup_changes = []

                # Take the routes already sent to that peer and resend them
                if self._resend_routes != SEND.DONE:
                    enhanced = True if refresh_enhanced and self._resend_routes == SEND.REFRESH else False
                    self._resend_routes = SEND.DONE
                    self.neighbor.rib.outgoing.resend(send_families, enhanced)
                    send_families = []

                # Need to send update
                if not new_routes and self.neighbor.rib.outgoing.pending():
                    # XXX: in proto really. hum to think about ?
                    new_routes = self.proto.new_update(include_withdraw)

                if new_routes:
                    try:
                        for _ in range(25):
                            # This can raise a NetworkError
                            six.next(new_routes)
                    except StopIteration:
                        new_routes = None
                        include_withdraw = True

                elif send_eor:
                    send_eor = False
                    for _ in self.proto.new_eors():
                        yield ACTION.NOW
                    self.logger.debug('>> EOR(s)', self.id())

                # SEND MANUAL KEEPALIVE (only if we have no more routes to send)
                elif not command_eor and self.neighbor.eor:
                    new_eor = self.neighbor.eor.popleft()
                    command_eor = self.proto.new_eors(new_eor.afi,
                                                      new_eor.safi)

                if command_eor:
                    try:
                        six.next(command_eor)
                    except StopIteration:
                        command_eor = None

                if new_routes or message.TYPE != NOP.TYPE:
                    yield ACTION.NOW
                elif self.neighbor.messages or operational:
                    yield ACTION.NOW
                elif self.neighbor.eor or command_eor:
                    yield ACTION.NOW
                else:
                    yield ACTION.LATER

                # read_message will loop until new message arrives with NOP
                if self._teardown:
                    break

        # If graceful restart, silent shutdown
        if self.neighbor.graceful_restart and self.proto.negotiated.sent_open.capabilities.announced(
                Capability.CODE.GRACEFUL_RESTART):
            self.logger.error('closing the session without notification',
                              self.id())
            self.proto.close(
                'graceful restarted negotiated, closing without sending any notification'
            )
            raise NetworkError('closing')

        # notify our peer of the shutdown
        raise Notify(6, self._teardown)
Esempio n. 35
0
    def _connect(self):
        # try to establish the outgoing connection

        self._outgoing.fsm.change(FSM.CONNECT)

        proto = Protocol(self)
        generator = proto.connect()

        connected = False
        try:
            while not connected:
                if self._teardown:
                    raise StopIteration()
                connected = six.next(generator)
                # we want to come back as soon as possible
                yield ACTION.LATER
        except StopIteration:
            # Connection failed
            if not connected:
                proto.close('connection to %s:%d failed' %
                            (self.neighbor.peer_address, proto.port))
            # A connection arrived before we could establish !
            if not connected or self._incoming.proto:
                yield ACTION.NOW
                raise Interrupted(self._outgoing)

        self._outgoing.proto = proto

        # send OPEN
        # Only yield if we have not the open, otherwise the reactor can run the other connection
        # which would be bad as we need to set the state without going to the other peer
        message = Message.CODE.NOP
        for message in proto.new_open(self._restarted):
            if ord(message.TYPE) == Message.CODE.NOP:
                yield ACTION.NOW

        proto.negotiated.sent(message)

        self._outgoing.fsm.change(FSM.OPENSENT)

        # Read OPEN
        wait = environment.settings().bgp.openwait
        opentimer = ReceiveTimer(
            self.me, wait, 1, 1,
            'waited for open too long, we do not like stuck in active')
        for message in self._outgoing.proto.read_open(
                self.neighbor.peer_address.top()):
            opentimer.check_ka(message)
            # XXX: FIXME: change the whole code to use the ord and not the chr version
            # Only yield if we have not the open, otherwise the reactor can run the other connection
            # which would be bad as we need to do the collission check
            if ord(message.TYPE) == Message.CODE.NOP:
                yield ACTION.LATER

        self._outgoing.fsm.change(FSM.OPENCONFIRM)
        proto.negotiated.received(message)
        proto.validate_open()

        if self._incoming.fsm == FSM.OPENCONFIRM:
            self.logger.network(
                'outgoing connection finds the incoming connection is in openconfirm'
            )
            local_id = self.neighbor.router_id.pack()
            remote_id = proto.negotiated.received_open.router_id.pack()

            if local_id < remote_id:
                self.logger.network('aborting the outgoing connection')
                raise Interrupted(self._outgoing)
            else:
                self.logger.network('closing the incoming connection')
                self._stop(self._incoming, 'collision local id < remote id')
                yield ACTION.LATER

        # Send KEEPALIVE
        for message in proto.new_keepalive('OPENCONFIRM'):
            yield ACTION.NOW

        # Start keeping keepalive timer
        self.recv_timer = ReceiveTimer(self.me, proto.negotiated.holdtime, 4,
                                       0)
        # Read KEEPALIVE
        for message in self._outgoing.proto.read_keepalive():
            self.recv_timer.check_ka(message)
            yield ACTION.NOW

        self._outgoing.fsm.change(FSM.ESTABLISHED)
        # let the caller know that we were sucesfull
        yield ACTION.NOW
Esempio n. 36
0
 def start(*args, **kwargs):
     generator = function(*args, **kwargs)
     return lambda: six.next(generator)  # noqa
Esempio n. 37
0
	def start (*args, **kwargs):
		generator = function(*args, **kwargs)
		return lambda: six.next(generator)  # noqa