Example #1
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
        self.logger.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()
                        self.logger.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:
                    self.logger.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()
                    self.logger.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:
                    self.logger.critical(
                        '%s %s undefined error writing on socket' %
                        (self.name(), self.peer), self.session())
                    yield False
Example #2
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
		if not self.writing():
			yield False
			return
		self.logger.wire(LazyFormat("%s %-32s SENDING " % (self.name(),'%s / %s' % (self.local,self.peer)),od,data))
		# The first while is here to setup the try/catch block once as it is very expensive
		while True:
			try:
				while True:
					if self._writing is None:
						self._writing = time.time()
					elif time.time() > self._writing + self.read_timeout:
						self.close()
						self.logger.wire("%s %s peer is too slow" % (self.name(),self.peer))
						raise TooSlowError('Waited to write for data on a socket for more than %d second(s)' % self.read_timeout)

					if self.defensive and random.randint(0,2):
						raise socket.error(errno.EAGAIN,'raising network error in 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
					nb = self.io.send(data)
					if not nb:
						self.close()
						self.logger.wire("%s %s lost TCP connection with peer" % (self.name(),self.peer))
						raise LostConnection('lost the TCP connection')

					data = data[nb:]
					if not data:
						self._writing = None
						yield True
						return
					yield False
			except socket.error,e:
				if e.args[0] in error.block:
					self.logger.wire("%s %s blocking io problem mid-way through writing a message %s, trying to complete" % (self.name(),self.peer,errstr(e)),'debug')
				elif e.errno == errno.EPIPE:
					# The TCP connection is gone.
					self.close()
					raise NetworkError('Broken TCP connection')
				elif e.args[0] in error.fatal:
					self.close()
					self.logger.wire("%s %s problem sending message (%s)" % (self.name(),self.peer,errstr(e)))
					raise NetworkError('Problem while writing data to the network (%s)' % errstr(e))
				# what error could it be !
				else:
					self.logger.wire("%s %s undefined error writing on socket" % (self.name(),self.peer))
					yield False
Example #3
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()
						self.logger.wire("%s %s lost TCP session with peer" % (self.name(),self.peer),source=self.session())
						raise LostConnection('the TCP connection was closed by the remote end')
					data += read

					number -= len(read)
					if not number:
						self.logger.wire(
							LazyFormat(
								"%s %-32s RECEIVED " % (
									self.name(),
									'%s / %s' % (self.local,self.peer)
								),
								read
							),
							source=self.session()
						)
						yield data
						return

					yield b''
			except socket.timeout as exc:
				self.close()
				self.logger.wire("%s %s peer is too slow" % (self.name(),self.peer),source=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
						self.logger.wire(message,'debug',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:
					self.logger.wire("%s %s undefined error reading on socket" % (self.name(),self.peer),source=self.session())
					raise NetworkError('Problem while reading data from the network (%s)' % errstr(exc))
Example #4
0
	def listen (self, local_ip, peer_ip, local_port, md5):
		self.serving = True

		for sock,(local,port,peer,md) in self._sockets.items():
			if local_ip.top() != local:
				continue
			if local_port != port:
				continue
			if md5:
				MD5(sock,peer_ip.top(),0,md5)
			return

		try:
			sock = self._new_socket(local_ip)
			if md5:
				# MD5 must match the peer side of the TCP, not the local one
				MD5(sock,peer_ip.top(),0,md5)
			try:
				sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
			except (socket.error,AttributeError):
				pass
			sock.setblocking(0)
			# s.settimeout(0.0)
			sock.bind((local_ip.top(),local_port))
			sock.listen(self._backlog)
			self._sockets[sock] = (local_ip.top(),local_port,peer_ip.top(),md5)
		except socket.error,exc:
			if exc.args[0] == errno.EADDRINUSE:
				raise BindingError('could not listen on %s:%d, the port already in use by another application' % (local_ip,local_port))
			elif exc.args[0] == errno.EADDRNOTAVAIL:
				raise BindingError('could not listen on %s:%d, this is an invalid address' % (local_ip,local_port))
			raise NetworkError(str(exc))
 def _new_socket(self, ip):
     if ip.afi == AFI.ipv6:
         return socket.socket(socket.AF_INET6, socket.SOCK_STREAM,
                              socket.IPPROTO_TCP)
     if ip.afi == AFI.ipv4:
         return socket.socket(socket.AF_INET, socket.SOCK_STREAM,
                              socket.IPPROTO_TCP)
     raise NetworkError(
         'Can not create socket for listening, family of IP %s is unknown' %
         ip)
Example #6
0
	def writing (self):
		while True:
			try:
				_,w,_ = select.select([],[self.io,],[],0)
			except select.error,exc:
				if exc.args[0] not in error.block:
					self.close()
					self.logger.wire("%s %s errno %s on socket" % (self.name(),self.peer,errno.errorcode[exc.args[0]]))
					raise NetworkError('errno %s on socket' % errno.errorcode[exc.args[0]])
				return False
			return w != []
Example #7
0
	def reading (self):
		while True:
			try:
				r,_,_ = select.select([self.io,],[],[],0)
			except select.error as exc:
				if exc.args[0] not in error.block:
					self.close()
					self.logger.wire("%s %s errno %s on socket" % (self.name(),self.peer,errno.errorcode[exc.args[0]]),source=self.session())
					raise NetworkError('errno %s on socket' % errno.errorcode[exc.args[0]])
				return False
			return r != []
Example #8
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 close TCP conncetion')
		if number == 0:
			yield ''
			return
		# XXX: one of the socket option is to recover the size of the buffer
		# XXX: we could use it to not have to put together the string with multiple reads
		# XXX: and get rid of the self.read_timeout option
		while not self.reading():
			yield ''
		data = ''
		while True:
			try:
				while True:
					if self._reading is None:
						self._reading = time.time()
					elif time.time() > self._reading + self.read_timeout:
						self.close()
						self.logger.wire("%s %s peer is too slow (we were told there was data on the socket but we can not read up to what should be there)" % (self.name(),self.peer))
						raise TooSlowError('Waited to read for data on a socket for more than %d second(s)' % self.read_timeout)

					if self.defensive and random.randint(0,2):
						raise socket.error(errno.EAGAIN,'raising network error in purpose')

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

					data += read
					number -= len(read)
					if not number:
						self.logger.wire(LazyFormat("%s %-32s RECEIVED " % (self.name(),'%s / %s' % (self.local,self.peer)),od,read))
						self._reading = None
						yield data
						return
			except socket.timeout,e:
				self.close()
				self.logger.wire("%s %s peer is too slow" % (self.name(),self.peer))
				raise TooSlowError('Timeout while reading data from the network (%s)' % errstr(e))
			except socket.error,e:
				if e.args[0] in error.block:
					self.logger.wire("%s %s blocking io problem mid-way through reading a message %s, trying to complete" % (self.name(),self.peer,errstr(e)),'debug')
				elif e.args[0] in error.fatal:
					self.close()
					raise LostConnection('issue reading on the socket: %s' % errstr(e))
				# what error could it be !
				else:
					self.logger.wire("%s %s undefined error reading on socket" % (self.name(),self.peer))
					raise NetworkError('Problem while reading data from the network (%s)' % errstr(e))
Example #9
0
	def reading (self):
		while True:
			try:
				r,_,_ = select.select([self.io,],[],[],0)
			except select.error,e:
				if e.args[0] not in error.block:
					self.close()
					self.logger.wire("%s %s errno %s on socket" % (self.name(),self.peer,errno.errorcode[e.args[0]]))
					raise NetworkError('errno %s on socket' % errno.errorcode[e.args[0]])
				return False

			if r:
				self._reading = time.time()
			return r != []
Example #10
0
    def _listen(self, local_ip, peer_ip, local_port, md5, md5_base64, ttl_in):
        self.serving = True

        for sock, (local, port, peer, md) in self._sockets.items():
            if local_ip.top() != local:
                continue
            if local_port != port:
                continue
            MD5(sock, peer_ip.top(), 0, md5, md5_base64)
            if ttl_in:
                MIN_TTL(sock, peer_ip, ttl_in)
            return

        try:
            sock = self._new_socket(local_ip)
            # MD5 must match the peer side of the TCP, not the local one
            MD5(sock, peer_ip.top(), 0, md5, md5_base64)
            if ttl_in:
                MIN_TTL(sock, peer_ip, ttl_in)
            try:
                sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                if local_ip.ipv6():
                    sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
            except (socket.error, AttributeError):
                pass
            sock.setblocking(0)
            # s.settimeout(0.0)
            sock.bind((local_ip.top(), local_port))
            sock.listen(self._backlog)
            self._sockets[sock] = (local_ip.top(), local_port, peer_ip.top(),
                                   md5)
        except socket.error as exc:
            if exc.args[0] == errno.EADDRINUSE:
                raise BindingError(
                    'could not listen on %s:%d, the port may already be in use by another application'
                    % (local_ip, local_port))
            elif exc.args[0] == errno.EADDRNOTAVAIL:
                raise BindingError(
                    'could not listen on %s:%d, this is an invalid address' %
                    (local_ip, local_port))
            raise NetworkError(str(exc))
        except NetworkError as exc:
            self.logger.critical(str(exc), 'network')
            raise exc
Example #11
0
	def _main (self, direction):
		"""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)

		proto = self._[direction]['proto']

		# Announce to the process BGP is up
		self.logger.network('Connected to peer %s (%s)' % (self.neighbor.name(),direction))
		if self.neighbor.api['neighbor-changes']:
			try:
				self.reactor.processes.up(self)
			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 proto.negotiated.refresh == REFRESH.ENHANCED else False

		self.send_ka = KA(self.me,proto)

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

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

				# Received update
				if message.TYPE == Update.TYPE:
					number += 1

					self.logger.routes(LazyFormat(self.me('<< UPDATE (%d)' % number),message.attributes,lambda _: "%s%s" % (' attributes' if _ else '',_)))

					for nlri in message.nlris:
						self.neighbor.rib.incoming.insert_received(Change(nlri,message.attributes))
						self.logger.routes(LazyFormat(self.me('<< UPDATE (%d) nlri ' % number),nlri,str))

				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 = proto.new_operational(new_operational,proto.negotiated)

					if operational:
						try:
							operational.next()
						except StopIteration:
							operational = None

				# 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 = proto.new_refresh(new_refresh)

					if refresh:
						try:
							refresh.next()
						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 = []

					self._have_routes = True

				# 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)
					self._have_routes = True
					send_families = []

				# Need to send update
				if self._have_routes and not new_routes:
					self._have_routes = False
					# XXX: in proto really. hum to think about ?
					new_routes = proto.new_update()

				if new_routes:
					try:
						count = 20
						while count:
							# This can raise a NetworkError
							new_routes.next()
							count -= 1
					except StopIteration:
						new_routes = None

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

				# 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 = proto.new_eors(new_eor.afi,new_eor.safi)

				if command_eor:
					try:
						command_eor.next()
					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 proto.negotiated.sent_open.capabilities.announced(Capability.CODE.GRACEFUL_RESTART):
			self.logger.network('Closing the session without notification','error')
			proto.close('graceful restarted negotiated, closing without sending any notification')
			raise NetworkError('closing')

		# notify our peer of the shutdown
		raise Notify(6,self._teardown)
Example #12
0
	def _main (self,direction):
		"""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)

		proto = self._[direction]['proto']

		# Announce to the process BGP is up
		self.logger.network('Connected to peer %s (%s)' % (self.neighbor.name(),direction))
		if self.neighbor.api['neighbor-changes']:
			try:
				self.reactor.processes.up(self)
			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 = True
		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])

		counter = Counter(self.logger,self.me)
		operational = None
		refresh = None
		number = 0

		self.send_ka = KA(self.me,proto)

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

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

				# Give information on the number of routes seen
				counter.display()

				# Received update
				if message.TYPE == Update.TYPE:
					counter.increment(len(message.nlris))
					number += 1

					self.logger.routes(LazyFormat(self.me('<< UPDATE (%d)' % number),lambda _: "%s%s" % (' attributes' if _ else '',_),message.attributes))

					for nlri in message.nlris:
						self.neighbor.rib.incoming.insert_received(Change(nlri,message.attributes))
						self.logger.routes(LazyFormat(self.me('<< UPDATE (%d) nlri ' % number),str,nlri))

				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 = proto.new_operational(new_operational,proto.negotiated)

					if operational:
						try:
							operational.next()
						except StopIteration:
							operational = None

				# 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:
							enhanced_negotiated = True if proto.negotiated.refresh == REFRESH.enhanced else False
							refresh = proto.new_refresh(new_refresh,enhanced_negotiated)

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

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

				# Need to send update
				if self._have_routes and not new_routes:
					self._have_routes = False
					# XXX: in proto really. hum to think about ?
					new_routes = proto.new_update()

				if new_routes:
					try:
						count = 20
						while count:
							# This can raise a NetworkError
							new_routes.next()
							count -= 1
					except StopIteration:
						new_routes = None

				elif send_eor:
					send_eor = False
					for eor in proto.new_eors():
						yield ACTION.immediate
					self.logger.message(self.me('>> EOR(s)'))

				# Go to other Peers
				yield ACTION.immediate if new_routes or message.TYPE != NOP.TYPE or self.neighbor.messages else 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 proto.negotiated.sent_open.capabilities.announced(Capability.ID.GRACEFUL_RESTART):
			self.logger.network('Closing the session without notification','error')
			proto.close('graceful restarted negotiated, closing without sending any notification')
			raise NetworkError('closing')

		# notify our peer of the shutdown
		raise Notify(6,self._teardown)
Example #13
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
        log.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
                    log.debug('<< UPDATE #%d' % number, self.id())

                    for nlri in message.nlris:
                        self.neighbor.rib.incoming.update_cache(
                            Change(nlri, message.attributes))
                        log.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:
                            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:
                            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:
                    count = 1 if self.neighbor.rate_limit > 0 else 25
                    try:
                        for _ in range(count):
                            # This can raise a NetworkError
                            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
                    log.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:
                        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):
            log.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)
Example #14
0
    def _main(self, direction):
        "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)

        proto = self._[direction]['proto']

        # Announce to the process BGP is up
        self.logger.network('Connected to peer %s (%s)' %
                            (self.neighbor.name(), direction))
        if self.neighbor.api.neighbor_changes:
            try:
                self.reactor.processes.up(self.neighbor.peer_address)
            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 = True
        new_routes = None
        self._resend_routes = True

        # 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])

        counter = Counter(self.logger, self._log(direction))
        need_keepalive = False
        keepalive = None
        operational = None

        while not self._teardown:
            for message in proto.read_message():
                # Update timer
                self.timer.tick(message)

                # Give information on the number of routes seen
                counter.display()

                # Received update
                if message.TYPE == Update.TYPE:
                    counter.increment(len(message.nlris))

                    for nlri in message.nlris:
                        self.neighbor.rib.incoming.insert_received(
                            Change(nlri, message.attributes))
                        self.logger.routes(LazyFormat(self.me(''), str, nlri))

                # SEND KEEPALIVES
                need_keepalive |= self.timer.keepalive()

                if need_keepalive and not keepalive:
                    keepalive = proto.new_keepalive()
                    need_keepalive = False

                if keepalive:
                    try:
                        keepalive.next()
                    except StopIteration:
                        keepalive = None

                # 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 = proto.new_operational(
                                new_operational)

                    if operational:
                        try:
                            operational.next()
                        except StopIteration:
                            operational = None

                # Take the routes already sent to that peer and resend them
                if self._resend_routes:
                    self._resend_routes = False
                    self.neighbor.rib.outgoing.resend_known()
                    self._have_routes = True

                # Need to send update
                if self._have_routes and not new_routes:
                    self._have_routes = False
                    # XXX: in proto really. hum to think about ?
                    new_routes = proto.new_update()

                if new_routes:
                    try:
                        count = 20
                        while count:
                            # This can raise a NetworkError
                            new_routes.next()
                            count -= 1
                    except StopIteration:
                        new_routes = None

                elif send_eor:
                    send_eor = False
                    for eor in proto.new_eors():
                        yield ACTION.immediate
                    self.logger.message(self.me('>> EOR(s)'))

                # Go to other Peers
                yield ACTION.immediate if new_routes or message.TYPE != NOP.TYPE else 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 proto.negotiated.sent_open.capabilities.announced(
                CapabilityID.GRACEFUL_RESTART):
            self.logger.network('Closing the session without notification',
                                'error')
            proto.close(
                'graceful restarted negotiated, closing without sending any notification'
            )
            raise NetworkError('closing')

        # notify our peer of the shutdown
        raise Notify(6, self._teardown)