Пример #1
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()
Пример #2
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.5', '82.219.212.34')
    writer = connection._writer(OPEN)
    while writer() == False:
        pass
    writer = connection._writer(KEEP)
    while writer() == False:
        pass

    reader = connection.reader()

    for size, kind, header, body in reader:
        if size: print od(header + body)
        else: sys.stdout.write('-')
Пример #3
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 next(writer) is False:
        pass
    writer = connection.writer(KEEP)
    while 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()
Пример #4
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.5','82.219.212.34')
	writer=connection._writer(OPEN)
	while writer() == False:
		pass
	writer=connection._writer(KEEP)
	while writer() == False:
		pass

	reader=connection.reader()

	for size,kind,header,body in reader:
		if size: print od(header+body)
		else: sys.stdout.write('-')
Пример #5
0
class Protocol (object):
	decode = True

	def __init__ (self,peer):
		try:
			self.logger = Logger()
		except RuntimeError:
			self.logger = FakeLogger()
		self.peer = peer
		self.neighbor = peer.neighbor
		self.negotiated = Negotiated(self.neighbor)
		self.connection = None
		port = os.environ.get('exabgp.tcp.port','') or os.environ.get('exabgp_tcp_port','')
		self.port = int(port) if port.isdigit() else 179

		# XXX: FIXME: check the the -19 is correct (but it is harmless)
		# The message size is the whole BGP message _without_ headers
		self.message_size = Message.MAX_LEN-Message.HEADER_LEN

		from exabgp.configuration.environment import environment
		self.log_routes = environment.settings().log.routes

	# XXX: we use self.peer.neighbor.peer_address when we could use self.neighbor.peer_address

	def __del__ (self):
		self.close('automatic protocol cleanup')

	def me (self,message):
		return "Peer %15s ASN %-7s %s" % (self.peer.neighbor.peer_address,self.peer.neighbor.peer_as,message)

	def accept (self,incoming):
		self.connection = incoming

		if self.peer.neighbor.api['neighbor-changes']:
			self.peer.reactor.processes.connected(self.peer)

		# very important - as we use this function on __init__
		return self

	def connect (self):
		# allows to test the protocol code using modified StringIO with a extra 'pending' function
		if not self.connection:
			peer = self.neighbor.peer_address
			local = self.neighbor.local_address
			md5 = self.neighbor.md5
			ttl = self.neighbor.ttl
			self.connection = Outgoing(peer.afi,peer.ip,local.ip,self.port,md5,ttl)

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

	def close (self,reason='protocol closed, reason unspecified'):
		if self.connection:
			self.logger.network(self.me(reason))

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

			try:
				if self.peer.neighbor.api['neighbor-changes']:
					self.peer.reactor.processes.down(self.peer,reason)
			except ProcessError:
				self.logger.message(self.me('could not send notification of neighbor close to API'))


	def write (self,message):
		if self.neighbor.api['send-packets'] and not self.neighbor.api['consolidate']:
			self.peer.reactor.processes.send(self.peer,ord(message[18]),message[:19],message[19:])
		for boolean in self.connection.writer(message):
			yield boolean

	# Read from network .......................................................

	def read_message (self):
		for length,msg,header,body,notify in self.connection.reader():
			if notify:
				if self.neighbor.api['receive-packets']:
					self.peer.reactor.processes.receive(self.peer,msg,header,body)
				if self.neighbor.api[Message.ID.NOTIFICATION]:
					self.peer.reactor.processes.notification(self.peer,notify.code,notify.subcode,str(notify))
				# XXX: is notify not already Notify class ?
				raise Notify(notify.code,notify.subcode,str(notify))
			if not length:
				yield _NOP

		if self.neighbor.api['receive-packets'] and not self.neighbor.api['consolidate']:
			self.peer.reactor.processes.receive(self.peer,msg,header,body)

		if msg == Message.ID.UPDATE and not self.neighbor.api['receive-parsed'] and not self.log_routes:
			yield _UPDATE
			return

		self.logger.message(self.me('<< %s' % Message.ID.name(msg)))
		try:
			message = Message.unpack_message(msg,body,self.negotiated)
		except (KeyboardInterrupt,SystemExit,Notify):
			raise
		except Exception,e:
			self.logger.message(self.me('Could not decode message %s' % Capability.hex(msg)))
			self.logger.message(self.me('%s' % str(e)))
			raise Notify(2,0,'can not decode update message %s' % Capability.hex(msg))

		if message.TYPE == Notification.TYPE:
			raise message

		if self.neighbor.api[msg]:
			if self.neighbor.api['receive-parsed']:
				if self.neighbor.api['consolidate'] and self.neighbor.api['receive-packets']:
					self.peer.reactor.processes.message(msg,self.peer,message,header,body)
				else:
					self.peer.reactor.processes.message(msg,self.peer,message,'','')

		yield message

		return
		# XXX: FIXME: check it is well 2,4
		raise Notify(2,4,'unknown message received')
Пример #6
0
class Protocol (object):
	decode = True

	def __init__ (self, peer):
		try:
			self.logger = Logger()
		except RuntimeError:
			self.logger = FakeLogger()
		self.peer = peer
		self.neighbor = peer.neighbor
		self.negotiated = Negotiated(self.neighbor)
		self.connection = None

		if self.neighbor.connect:
			self.port = self.neighbor.connect
		elif os.environ.get('exabgp.tcp.port','').isdigit():
			self.port = int(os.environ.get('exabgp.tcp.port'))
		elif os.environ.get('exabgp_tcp_port','').isdigit():
			self.port = int(os.environ.get('exabgp_tcp_port'))
		else:
			self.port = 179

		# XXX: FIXME: check the the -19 is correct (but it is harmless)
		# The message size is the whole BGP message _without_ headers
		self.message_size = Message.MAX_LEN-Message.HEADER_LEN

		from exabgp.configuration.environment import environment
		self.log_routes = environment.settings().log.routes

	# XXX: we use self.peer.neighbor.peer_address when we could use self.neighbor.peer_address

	def __del__ (self):
		self.close('automatic protocol cleanup')

	def me (self, message):
		return "Peer %15s ASN %-7s %s" % (self.peer.neighbor.peer_address,self.peer.neighbor.peer_as,message)

	def accept (self, incoming):
		self.connection = incoming

		if self.peer.neighbor.api['neighbor-changes']:
			self.peer.reactor.processes.connected(self.peer.neighbor)

		# very important - as we use this function on __init__
		return self

	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 = generator.next()
					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

	def close (self, reason='protocol closed, reason unspecified'):
		if self.connection:
			self.logger.network(self.me(reason))

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

			try:
				if self.peer.neighbor.api['neighbor-changes']:
					self.peer.reactor.processes.down(self.peer.neighbor,reason)
			except ProcessError:
				self.logger.message(self.me('could not send notification of neighbor close to API'))

	def _to_api (self,direction,message,raw):
		packets = self.neighbor.api['%s-packets' % direction]
		parsed = self.neighbor.api['%s-parsed' % direction]
		consolidate = self.neighbor.api['%s-consolidate' % direction]

		if consolidate:
			if packets:
				self.peer.reactor.processes.message(self.peer.neighbor,direction,message,raw[:19],raw[19:])
			else:
				self.peer.reactor.processes.message(self.peer.neighbor,direction,message,'','')
		else:
			if packets:
				self.peer.reactor.processes.packets(self.peer.neighbor,direction,int(message.ID),raw[:19],raw[19:])
			if parsed:
				self.peer.reactor.processes.message(message.ID,self.peer.neighbor,direction,message,'','')

	def write (self, message, negotiated=None):
		raw = message.message(negotiated)

		if self.neighbor.api.get('send-%s' % Message.CODE.short(message.ID),False):
			self._to_api('send',message,raw)

		for boolean in self.connection.writer(raw):
			yield boolean

	def send (self,raw):
		if self.neighbor.api.get('send-%s' % Message.CODE.short(ord(raw[19])),False):
			message = Update.unpack_message(raw[19:],self.negotiated)
			self._to_api('send',message,raw)

		for boolean in self.connection.writer(raw):
			yield boolean

	# Read from network .......................................................

	def read_message (self):
		# This will always be defined by the loop but scope leaking upset scrutinizer/pylint
		msg_id = None

		packets = self.neighbor.api['receive-packets']
		consolidate = self.neighbor.api['receive-consolidate']
		parsed = self.neighbor.api['receive-parsed']

		body,header = '',''  # just because pylint/pylama are getting more clever

		for length,msg_id,header,body,notify in self.connection.reader():
			# internal issue
			if notify:
				if self.neighbor.api.get('send-%s' % Message.CODE.NOTIFICATION.SHORT,False):
					if consolidate:
						self.peer.reactor.processes.notification(self.peer.neighbor,'send',notify.code,notify.subcode,str(notify),header,body)
					elif parsed:
						self.peer.reactor.processes.notification(self.peer.neighbor,'send',notify.code,notify.subcode,str(notify),'','')
					elif packets:
						self.peer.reactor.processes.packets(self.peer.neighbor,'send',msg_id,header,body)
				# XXX: is notify not already Notify class ?
				raise Notify(notify.code,notify.subcode,str(notify))

			if not length:
				yield _NOP
				continue

			self.logger.message(self.me('<< %s' % Message.CODE.name(msg_id)))

			for_api = self.neighbor.api.get('receive-%s' % Message.CODE.short(msg_id),False)

			if for_api and packets and not consolidate:
				self.peer.reactor.processes.packets(self.peer.neighbor,'receive',msg_id,header,body)

			if msg_id == Message.CODE.UPDATE:
				if not (for_api or self.log_routes) and not (parsed or consolidate):
					yield _UPDATE
					return

			try:
				message = Message.unpack(msg_id,body,self.negotiated)
			except (KeyboardInterrupt,SystemExit,Notify):
				raise
			except Exception,exc:
				self.logger.message(self.me('Could not decode message "%d"' % msg_id))
				self.logger.message(self.me('%s' % str(exc)))
				self.logger.message(traceback.format_exc())
				raise Notify(1,0,'can not decode update message of type "%d"' % msg_id)
				# raise Notify(5,0,'unknown message received')

			if for_api:
				if consolidate:
					self.peer.reactor.processes.message(msg_id,self.neighbor,'receive',message,header,body)
				elif parsed:
					self.peer.reactor.processes.message(msg_id,self.neighbor,'receive',message,'','')

			try:
				if message.TYPE == Notification.TYPE:
					raise message
			except:
				import pdb; pdb.set_trace()
				pass

			yield message
Пример #7
0
class Protocol (object):
	decode = True

	def __init__ (self,peer):
		try:
			self.logger = Logger()
		except RuntimeError:
			self.logger = FakeLogger()
		self.peer = peer
		self.neighbor = peer.neighbor
		self.negotiated = Negotiated(self.neighbor)
		self.connection = None
		port = os.environ.get('exabgp.tcp.port','') or os.environ.get('exabgp_tcp_port','')
		self.port = int(port) if port.isdigit() else 179

		# XXX: FIXME: check the the -19 is correct (but it is harmless)
		# The message size is the whole BGP message _without_ headers
		self.message_size = Message.MAX_LEN-Message.HEADER_LEN

		from exabgp.configuration.environment import environment
		self.log_routes = environment.settings().log.routes

	# XXX: we use self.peer.neighbor.peer_address when we could use self.neighbor.peer_address

	def __del__ (self):
		self.close('automatic protocol cleanup')

	def me (self,message):
		return "Peer %15s ASN %-7s %s" % (self.peer.neighbor.peer_address,self.peer.neighbor.peer_as,message)

	def accept (self,incoming):
		self.connection = incoming

		self.peer.reactor.processes.reset(self.peer)
		if self.peer.neighbor.api.neighbor_changes:
			self.peer.reactor.processes.connected(self.peer)

		# very important - as we use this function on __init__
		return self

	def connect (self):
		# allows to test the protocol code using modified StringIO with a extra 'pending' function
		if not self.connection:
			peer = self.neighbor.peer_address
			local = self.neighbor.local_address
			md5 = self.neighbor.md5
			ttl = self.neighbor.ttl
			self.connection = Outgoing(peer.afi,peer.ip,local.ip,self.port,md5,ttl)

			connected = False
			try:
				generator = self.connection.establish()
				while True:
					connected = generator.next()
					if not connected:
						yield False
						continue
					self.peer.reactor.processes.reset(self.peer)
					if self.peer.neighbor.api.neighbor_changes:
						self.peer.reactor.processes.connected(self.peer)
					yield True
					return
			except StopIteration:
				# close called by the caller
				# self.close('could not connect to remote end')
				yield False
				return

	def close (self,reason='protocol closed, reason unspecified'):
		if self.connection:
			self.logger.network(self.me(reason))

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

			try:
				if self.peer.neighbor.api.neighbor_changes:
					self.peer.reactor.processes.down(self.peer,reason)
			except ProcessError:
				self.logger.message(self.me('could not send notification of neighbor close to API'))


	def write (self,message):
		if self.neighbor.api.send_packets:
			self.peer.reactor.processes.send(self.peer,ord(message[18]),message[:19],message[19:])
		for boolean in self.connection.writer(message):
			yield boolean

	# Read from network .......................................................

	def read_message (self,comment=''):
		self.peer.reactor.processes.increase(self.peer)

		for length,msg,header,body,notify in self.connection.reader():
			if notify:
				if self.neighbor.api.receive_packets:
					self.peer.reactor.processes.receive(self.peer,msg,header,body)
				if self.neighbor.api.receive_notifications:
					self.peer.reactor.processes.notification(self.peer,notify.code,notify.subcode,str(notify))
				# XXX: is notify not already Notify class ?
				raise Notify(notify.code,notify.subcode,str(notify))
			if not length:
				yield _NOP

		if self.neighbor.api.receive_packets:
			self.peer.reactor.processes.receive(self.peer,msg,header,body)

		if msg == Message.Type.UPDATE:
			self.logger.message(self.me('<< UPDATE'))

			# This could be speed up massively by changing the order of the IF
			if length == 23:
				update = EORFactory()
				if self.neighbor.api.receive_updates:
					if self.neighbor.api.consolidate:
						self.peer.reactor.processes.update(self.peer,update,header,body)
					else:
						self.peer.reactor.processes.update(self.peer,update,'','')
			elif length == 30 and body.startswith(EOR.MP):
				update = EORFactory(body)
				if self.neighbor.api.receive_updates:
					if self.neighbor.api.consolidate:
						self.peer.reactor.processes.update(self.peer,update,header,body)
					else:
						self.peer.reactor.processes.update(self.peer,update,'','')
			elif self.neighbor.api.receive_updates:
				update = UpdateFactory(self.negotiated,body)
				if self.neighbor.api.consolidate:
					self.peer.reactor.processes.update(self.peer,update,header,body)
				else:
					self.peer.reactor.processes.update(self.peer,update,'','')
			elif self.log_routes:
				update = UpdateFactory(self.negotiated,body)
			else:
				update = _UPDATE
			yield update

		elif msg == Message.Type.KEEPALIVE:
			self.logger.message(self.me('<< KEEPALIVE%s' % (' (%s)' % comment if comment else '')))
			if self.neighbor.api.receive_keepalives:
				if self.neighbor.api.consolidate:
					self.peer.reactor.processes.keepalive(self.peer,msg,header,body)
				else:
					self.peer.reactor.processes.keepalive(self.peer,msg,'','')
			yield KeepAlive()

		elif msg == Message.Type.NOTIFICATION:
			self.logger.message(self.me('<< NOTIFICATION'))
			yield NotificationFactory(body)

		elif msg == Message.Type.ROUTE_REFRESH:
			if self.negotiated.refresh != REFRESH.absent:
				self.logger.message(self.me('<< ROUTE-REFRESH'))
				refresh = RouteRefreshFactory(body)
				if self.neighbor.api.receive_refresh:
					if refresh.reserved in (RouteRefresh.start,RouteRefresh.end):
						if self.neighbor.api.consolidate:
							self.peer.reactor.process.refresh(self.peer,refresh,header,body)
						else:
							self.peer.reactor.processes.refresh(self.peer,refresh,'','')
			else:
				# XXX: FIXME: really should raise, we are too nice
				self.logger.message(self.me('<< NOP (un-negotiated type %d)' % msg))
				refresh = UnknownMessageFactory(body)
			yield refresh

		elif msg == Message.Type.OPERATIONAL:
			if self.peer.neighbor.operational:
				operational = OperationalFactory(body)
				what = OperationalGroup[operational.what][0]
				if self.neighbor.api.consolidate:
					self.peer.reactor.processes.operational(self.peer,what,operational,header,body)
				else:
					self.peer.reactor.processes.operational(self.peer,what,operational,'','')
			else:
				operational = _OPERATIONAL
			yield operational

		elif msg == Message.Type.OPEN:
			if self.neighbor.api.receive_opens:
				open_message = OpenFactory(body)
				if self.neighbor.api.consolidate:
					self.peer.reactor.processes.open(self.peer,'received',open_message,header,body)
				else:
					self.peer.reactor.processes.open(self.peer,'received',open_message,'','')
			yield OpenFactory(body)

		else:
			# XXX: FIXME: really should raise, we are too nice
			self.logger.message(self.me('<< NOP (unknow type %d)' % msg))
			yield UnknownMessageFactory(msg)

	def validate_open (self):
		error = self.negotiated.validate(self.neighbor)
		if error is not None:
			raise Notify(*error)

	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 == Notification.TYPE:
			raise received_open

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

		self.logger.message(self.me('<< %s' % received_open))
		yield received_open

	def read_keepalive (self,comment=''):
		for message in self.read_message(comment):
			if message.TYPE == NOP.TYPE:
				yield message
			else:
				break

		if message.TYPE == Notification.TYPE:
			raise message

		if message.TYPE != KeepAlive.TYPE:
			raise Notify(5,2)

		yield message

	#
	# Sending message to peer
	#

	def new_open (self,restarted):
		sent_open = Open(
			4,
			self.neighbor.local_as,
			self.neighbor.router_id.ip,
			Capabilities().new(self.neighbor,restarted),
			self.neighbor.hold_time
		)

		# we do not buffer open message in purpose
		msg_send = sent_open.message()
		for _ in self.write(msg_send):
			yield _NOP

		self.logger.message(self.me('>> %s' % sent_open))
		if self.neighbor.api.receive_opens:
			if self.neighbor.api.consolidate:
				header = msg_send[0:38]
				body = msg_send[38:]
				self.peer.reactor.processes.open(self.peer,'sent',sent_open,header,body)
			else:
				self.peer.reactor.processes.open(self.peer,'sent',sent_open,'','')
		yield sent_open

	def new_keepalive (self,comment=''):
		keepalive = KeepAlive()

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

		self.logger.message(self.me('>> KEEPALIVE%s' % (' (%s)' % comment if comment else '')))

		yield keepalive

	def new_notification (self,notification):
		for _ in self.write(notification.message()):
			yield _NOP
		self.logger.message(self.me('>> NOTIFICATION (%d,%d,"%s")' % (notification.code,notification.subcode,notification.data)))
		yield notification

	def new_update (self):
		updates = self.neighbor.rib.outgoing.updates(self.neighbor.group_updates)
		number = 0
		for update in updates:
			for message in update.messages(self.negotiated):
				number += 1
				for boolean in self.write(message):
					# boolean is a transient network error we already announced
					yield _NOP
		if number:
			self.logger.message(self.me('>> %d UPDATE(s)' % number))
		yield _UPDATE

	def new_eors (self):
		# Send EOR to let our peer know he can perform a RIB update
		if self.negotiated.families:
			for afi,safi in self.negotiated.families:
				eor = EOR(afi,safi).message()
				for _ in self.write(eor):
					yield _NOP
				yield _UPDATE
		else:
			# If we are not sending an EOR, send a keepalive as soon as when finished
			# So the other routers knows that we have no (more) routes to send ...
			# (is that behaviour documented somewhere ??)
			for eor in self.new_keepalive('EOR'):
				yield _NOP
			yield _UPDATE

	def new_operational (self,operational,negotiated):
		for _ in self.write(operational.message(negotiated)):
			yield _NOP
		self.logger.message(self.me('>> OPERATIONAL %s' % str(operational)))
		yield operational

	def new_refresh (self,refresh,negotiated):
		for refresh in refresh.messages(negotiated):
			for _ in self.write(refresh):
				yield _NOP
			self.logger.message(self.me('>> REFRESH %s' % str(refresh)))
			yield refresh
Пример #8
0
class Protocol(object):
    decode = True

    def __init__(self, peer):
        self.peer = peer
        self.neighbor = peer.neighbor
        self.negotiated = Negotiated(self.neighbor)
        self.connection = None

        if self.neighbor['connect']:
            self.port = self.neighbor['connect']
        elif os.environ.get('exabgp.tcp.port', '').isdigit():
            self.port = int(os.environ.get('exabgp.tcp.port'))
        elif os.environ.get('exabgp_tcp_port', '').isdigit():
            self.port = int(os.environ.get('exabgp_tcp_port'))
        else:
            self.port = 179

        from exabgp.environment import getenv

        self.log_routes = peer.neighbor['adj-rib-in'] or getenv().log.routes

    def fd(self):
        if self.connection is None:
            return -1
        return self.connection.fd()

    # XXX: we use self.peer.neighbor['peer-address'] when we could use self.neighbor['peer-address']

    def me(self, message):
        return "%s/%s %s" % (self.peer.neighbor['peer-address'],
                             self.peer.neighbor['peer-as'], message)

    def accept(self, incoming):
        self.connection = incoming

        if self.peer.neighbor.api['neighbor-changes']:
            self.peer.reactor.processes.connected(self.peer.neighbor)

        # very important - as we use this function on __init__
        return self

    def connect(self):
        # allows to test the protocol code using modified StringIO with a extra 'pending' function
        if self.connection:
            return

        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['outgoing-ttl']
        self.connection = Outgoing(afi, peer, local, self.port, md5,
                                   md5_base64, ttl_out)

        for connected in self.connection.establish():
            yield False

        if self.peer.neighbor.api['neighbor-changes']:
            self.peer.reactor.processes.connected(self.peer.neighbor)

        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']

        yield True

    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

    def _to_api(self, direction, message, raw):
        packets = self.neighbor.api['%s-packets' % direction]
        parsed = self.neighbor.api['%s-parsed' % direction]
        consolidate = self.neighbor.api['%s-consolidate' % direction]
        negotiated = self.negotiated if self.neighbor.api[
            'negotiated'] else None

        if consolidate:
            if packets:
                self.peer.reactor.processes.message(message.ID,
                                                    self.peer.neighbor,
                                                    direction, message,
                                                    negotiated, raw[:19],
                                                    raw[19:])
            else:
                self.peer.reactor.processes.message(message.ID,
                                                    self.peer.neighbor,
                                                    direction, message,
                                                    negotiated, b'', b'')
        else:
            if packets:
                self.peer.reactor.processes.packets(self.peer.neighbor,
                                                    direction, int(message.ID),
                                                    negotiated, raw[:19],
                                                    raw[19:])
            if parsed:
                self.peer.reactor.processes.message(message.ID,
                                                    self.peer.neighbor,
                                                    direction, message,
                                                    negotiated, b'', b'')

    def write(self, message, negotiated=None):
        raw = message.message(negotiated)

        code = 'send-%s' % Message.CODE.short(message.ID)
        self.peer.stats[code] = self.peer.stats.get(code, 0) + 1
        if self.neighbor.api.get(code, False):
            self._to_api('send', message, raw)

        for boolean in self.connection.writer(raw):
            yield boolean

    def send(self, raw):
        code = 'send-%s' % Message.CODE.short(raw[18])
        self.peer.stats[code] = self.peer.stats.get(code, 0) + 1
        if self.neighbor.api.get(code, False):
            message = Update.unpack_message(raw[19:], Direction.OUT,
                                            self.negotiated)
            self._to_api('send', message, raw)

        for boolean in self.connection.writer(raw):
            yield boolean

    # Read from network .......................................................

    def read_message(self):
        # This will always be defined by the loop but scope leaking upset scrutinizer/pylint
        msg_id = None

        packets = self.neighbor.api['receive-packets']
        consolidate = self.neighbor.api['receive-consolidate']
        parsed = self.neighbor.api['receive-parsed']

        body, header = b'', b''  # just because pylint/pylama are getting more clever

        for length, msg_id, header, body, notify in self.connection.reader():
            # internal issue
            if notify:
                code = 'receive-%s' % Message.CODE.NOTIFICATION.SHORT
                if self.neighbor.api.get(code, False):
                    if consolidate:
                        self.peer.reactor.processes.notification(
                            self.peer.neighbor, 'receive', notify.code,
                            notify.subcode, str(notify), None, header, body)
                    elif parsed:
                        self.peer.reactor.processes.notification(
                            self.peer.neighbor, 'receive', notify.code,
                            notify.subcode, str(notify), None, b'', b'')
                    elif packets:
                        self.peer.reactor.processes.packets(
                            self.peer.neighbor, 'receive', msg_id, None,
                            header, body)
                # XXX: is notify not already Notify class ?
                raise Notify(notify.code, notify.subcode, str(notify))

            if not length:
                yield _NOP
                continue

            log.debug('<< message of type %s' % Message.CODE.name(msg_id),
                      self.connection.session())

            code = 'receive-%s' % Message.CODE.short(msg_id)
            self.peer.stats[code] = self.peer.stats.get(code, 0) + 1
            for_api = self.neighbor.api.get(code, False)

            if for_api and packets and not consolidate:
                negotiated = self.negotiated if self.neighbor.api.get(
                    'negotiated', False) else None
                self.peer.reactor.processes.packets(self.peer.neighbor,
                                                    'receive', msg_id,
                                                    negotiated, header, body)

            if msg_id == Message.CODE.UPDATE:
                if not self.neighbor['adj-rib-in'] and not (
                        for_api or self.log_routes) and not (parsed
                                                             or consolidate):
                    yield _UPDATE
                    return

            try:
                message = Message.unpack(msg_id, body, Direction.IN,
                                         self.negotiated)
            except (KeyboardInterrupt, SystemExit, Notify):
                raise
            except Exception as exc:
                log.debug('could not decode message "%d"' % msg_id,
                          self.connection.session())
                log.debug('%s' % str(exc), self.connection.session())
                log.debug(traceback.format_exc(), self.connection.session())
                raise Notify(
                    1, 0,
                    'can not decode update message of type "%d"' % msg_id)
                # raise Notify(5,0,'unknown message received')

            if message.TYPE == Update.TYPE:
                if Attribute.CODE.INTERNAL_TREAT_AS_WITHDRAW in message.attributes:
                    for nlri in message.nlris:
                        nlri.action = IN.WITHDRAWN

            if for_api:
                negotiated = self.negotiated if self.neighbor.api.get(
                    'negotiated', False) else None
                if consolidate:
                    self.peer.reactor.processes.message(
                        msg_id, self.neighbor, 'receive', message, negotiated,
                        header, body)
                elif parsed:
                    self.peer.reactor.processes.message(
                        msg_id, self.neighbor, 'receive', message, negotiated,
                        b'', b'')

            if message.TYPE == Notification.TYPE:
                raise message

            if message.TYPE == Update.TYPE and Attribute.CODE.INTERNAL_DISCARD in message.attributes:
                yield _NOP
            else:
                yield message

    def validate_open(self):
        error = self.negotiated.validate(self.neighbor)
        if error is not None:
            raise Notify(*error)

        if self.neighbor.api['negotiated']:
            self.peer.reactor.processes.negotiated(self.peer.neighbor,
                                                   self.negotiated)

        if self.negotiated.mismatch:
            log.warning(
                '--------------------------------------------------------------------',
                self.connection.session())
            log.warning(
                'the connection can not carry the following family/families',
                self.connection.session())
            for reason, (afi, safi) in self.negotiated.mismatch:
                log.warning(
                    ' - %s is not configured for %s/%s' % (reason, afi, safi),
                    self.connection.session())
            log.warning(
                'therefore no routes of this kind can be announced on the connection',
                self.connection.session())
            log.warning(
                '--------------------------------------------------------------------',
                self.connection.session())

    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

    def read_keepalive(self):
        for message in self.read_message():
            if message.TYPE == NOP.TYPE:
                yield message
            else:
                break

        if message.TYPE != KeepAlive.TYPE:
            raise Notify(5, 2)

        yield message

    #
    # Sending message to peer
    #

    def new_open(self):
        if self.neighbor['local-as']:
            local_as = self.neighbor['local-as']
        elif self.negotiated.received_open:
            local_as = self.negotiated.received_open.asn
        else:
            raise RuntimeError('no ASN available for the OPEN message')

        sent_open = Open(
            Version(4),
            local_as,
            self.neighbor['hold-time'],
            self.neighbor['router-id'],
            Capabilities().new(self.neighbor, self.peer._restarted),
        )

        # we do not buffer open message in purpose
        for _ in self.write(sent_open):
            yield _NOP

        log.debug('>> %s' % sent_open, self.connection.session())
        yield sent_open

    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

    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

    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

    def new_eor(self, afi, safi):
        eor = EOR(afi, safi)
        for _ in self.write(eor):
            yield _NOP
        log.debug('>> EOR %s %s' % (afi, safi), self.connection.session())
        yield eor

    def new_eors(self, afi=AFI.undefined, safi=SAFI.undefined):
        # Send EOR to let our peer know he can perform a RIB update
        if self.negotiated.families:
            families = (self.negotiated.families if
                        (afi, safi) == (AFI.undefined, SAFI.undefined) else [
                            (afi, safi),
                        ])
            for eor_afi, eor_safi in families:
                for _ in self.new_eor(eor_afi, eor_safi):
                    yield _
        else:
            # If we are not sending an EOR, send a keepalive as soon as when finished
            # So the other routers knows that we have no (more) routes to send ...
            # (is that behaviour documented somewhere ??)
            for eor in self.new_keepalive('EOR'):
                yield _NOP
            yield _UPDATE

    def new_operational(self, operational, negotiated):
        for _ in self.write(operational, negotiated):
            yield _NOP
        log.debug('>> OPERATIONAL %s' % str(operational),
                  self.connection.session())
        yield operational

    def new_refresh(self, refresh):
        for _ in self.write(refresh, None):
            yield _NOP
        log.debug('>> REFRESH %s' % str(refresh), self.connection.session())
        yield refresh
Пример #9
0
class Protocol (object):
	decode = True

	def __init__ (self,peer):
		try:
			self.logger = Logger()
		except RuntimeError:
			self.logger = FakeLogger()
		self.peer = peer
		self.neighbor = peer.neighbor
		self.negotiated = Negotiated(self.neighbor)
		self.connection = None
		port = os.environ.get('exabgp.tcp.port','')
		self.port = int(port) if port.isdigit() else 179

		# XXX: FIXME: check the the -19 is correct (but it is harmless)
		# The message size is the whole BGP message _without_ headers
		self.message_size = Message.MAX_LEN-Message.HEADER_LEN

	# XXX: we use self.peer.neighbor.peer_address when we could use self.neighbor.peer_address

	def __del__ (self):
		self.close('automatic protocol cleanup')

	def me (self,message):
		return "Peer %15s ASN %-7s %s" % (self.peer.neighbor.peer_address,self.peer.neighbor.peer_as,message)

	def accept (self,incoming):
		self.connection = incoming

		if self.peer.neighbor.api.neighbor_changes:
			self.peer.reactor.processes.connected(self.peer.neighbor.peer_address)

		# very important - as we use this function on __init__
		return self

	def connect (self):
		# allows to test the protocol code using modified StringIO with a extra 'pending' function
		if not self.connection:
			peer = self.neighbor.peer_address
			local = self.neighbor.local_address
			md5 = self.neighbor.md5
			ttl = self.neighbor.ttl
			self.connection = Outgoing(peer.afi,peer.ip,local.ip,self.port,md5,ttl)

			connected = False
			try:
				generator = self.connection.establish()
				while True:
					connected = generator.next()
					if not connected:
						yield False
						continue
					if self.peer.neighbor.api.neighbor_changes:
						self.peer.reactor.processes.connected(self.peer.neighbor.peer_address)
					yield True
					return
			except StopIteration:
				# close called by the caller
				# self.close('could not connect to remote end')
				yield False
				return

	def close (self,reason='protocol closed, reason unspecified'):
		if self.connection:
			self.logger.network(self.me(reason))

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

			try:
				if self.peer.neighbor.api.neighbor_changes:
					self.peer.reactor.processes.down(self.peer.neighbor.peer_address,reason)
			except ProcessError:
				self.logger.message(self.me('could not send notification of neighbor close to API'))


	def write (self,message):
		if self.neighbor.api.send_packets:
			self.peer.reactor.processes.send(self.peer.neighbor.peer_address,ord(message[18]),message[:19],message[19:])
		for boolean in self.connection.writer(message):
			yield boolean

	# Read from network .......................................................

	def read_message (self,comment=''):
		try:
			for length,msg,header,body in self.connection.reader():
				if not length:
					yield _NOP
		except NotifyError,n:
			raise Notify(n.code,n.subcode,str(n))

		if self.neighbor.api.receive_packets:
			self.peer.reactor.processes.receive(self.peer.neighbor.peer_address,msg,header,body)

		if msg == Message.Type.UPDATE:
			self.logger.message(self.me('<< UPDATE'))

			if length == 30 and body.startswith(EOR.PREFIX):
				update = EORFactory(body)
				if self.neighbor.api.receive_routes:
					self.peer.reactor.processes.update(self.peer.neighbor.peer_address,update)
			elif self.neighbor.api.receive_routes:
				update = UpdateFactory(self.negotiated,body)
				if self.neighbor.api.receive_routes:
					self.peer.reactor.processes.update(self.peer.neighbor.peer_address,update)
			else:
				update = _UPDATE
			yield update

		elif msg == Message.Type.KEEPALIVE:
			self.logger.message(self.me('<< KEEPALIVE%s' % (' (%s)' % comment if comment else '')))
			yield KeepAlive()

		elif msg == Message.Type.NOTIFICATION:
			self.logger.message(self.me('<< NOTIFICATION'))
			yield NotificationFactory(body)

		elif msg == Message.Type.ROUTE_REFRESH:
			if self.negotiated.refresh != REFRESH.absent:
				self.logger.message(self.me('<< ROUTE-REFRESH'))
				refresh = RouteRefreshFactory(body)
				if self.neighbor.api.receive_routes:
					if refresh.reserved in (RouteRefresh.start,RouteRefresh.end):
						self.peer.reactor.processes.refresh(self.peer.neighbor.peer_address,refresh)
			else:
				# XXX: FIXME: really should raise, we are too nice
				self.logger.message(self.me('<< NOP (un-negotiated type %d)' % msg))
				refresh = UnknownMessageFactory(body)
			yield refresh

		elif msg == Message.Type.OPERATIONAL:
			if self.peer.neighbor.operational:
				operational = OperationalFactory(body)
				what = OperationalGroup[operational.what][0]
				self.peer.reactor.processes.operational(self.peer.neighbor.peer_address,what,operational)
			else:
				operational = _OPERATIONAL
			yield operational

		elif msg == Message.Type.OPEN:
			yield OpenFactory(body)

		else:
			# XXX: FIXME: really should raise, we are too nice
			self.logger.message(self.me('<< NOP (unknow type %d)' % msg))
			yield UnknownMessageFactory(msg)
Пример #10
0
class Protocol(object):
    decode = True

    def __init__(self, peer):
        try:
            self.logger = Logger()
        except RuntimeError:
            self.logger = FakeLogger()
        self.peer = peer
        self.neighbor = peer.neighbor
        self.negotiated = Negotiated(self.neighbor)
        self.connection = None
        port = os.environ.get('exabgp.tcp.port', '') or os.environ.get(
            'exabgp_tcp_port', '')
        self.port = int(port) if port.isdigit() else 179

        # XXX: FIXME: check the the -19 is correct (but it is harmless)
        # The message size is the whole BGP message _without_ headers
        self.message_size = Message.MAX_LEN - Message.HEADER_LEN

        from exabgp.configuration.environment import environment
        self.log_routes = environment.settings().log.routes

    # XXX: we use self.peer.neighbor.peer_address when we could use self.neighbor.peer_address

    def __del__(self):
        self.close('automatic protocol cleanup')

    def me(self, message):
        return "Peer %15s ASN %-7s %s" % (self.peer.neighbor.peer_address,
                                          self.peer.neighbor.peer_as, message)

    def accept(self, incoming):
        self.connection = incoming

        if self.peer.neighbor.api['neighbor-changes']:
            self.peer.reactor.processes.connected(self.peer)

        # very important - as we use this function on __init__
        return self

    def connect(self):
        # allows to test the protocol code using modified StringIO with a extra 'pending' function
        if not self.connection:
            peer = self.neighbor.peer_address
            local = self.neighbor.local_address
            md5 = self.neighbor.md5
            ttl = self.neighbor.ttl
            self.connection = Outgoing(peer.afi, peer.ip, local.ip, self.port,
                                       md5, ttl)

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

    def close(self, reason='protocol closed, reason unspecified'):
        if self.connection:
            self.logger.network(self.me(reason))

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

            try:
                if self.peer.neighbor.api['neighbor-changes']:
                    self.peer.reactor.processes.down(self.peer, reason)
            except ProcessError:
                self.logger.message(
                    self.
                    me('could not send notification of neighbor close to API'))

    def write(self, message):
        if self.neighbor.api[
                'send-packets'] and not self.neighbor.api['consolidate']:
            self.peer.reactor.processes.send(self.peer, ord(message[18]),
                                             message[:19], message[19:])
        for boolean in self.connection.writer(message):
            yield boolean

    # Read from network .......................................................

    def read_message(self):
        # This will always be defined by the loop but scope leaking upset scrutinizer/pylint
        msg_id = None

        for length, msg_id, header, body, notify in self.connection.reader():
            if notify:
                if self.neighbor.api['receive-packets']:
                    self.peer.reactor.processes.receive(
                        self.peer, msg_id, header, body)
                if self.neighbor.api[Message.CODE.NOTIFICATION]:
                    self.peer.reactor.processes.notification(
                        self.peer, notify.code, notify.subcode, str(notify))
                # XXX: is notify not already Notify class ?
                raise Notify(notify.code, notify.subcode, str(notify))
            if not length:
                yield _NOP

        if self.neighbor.api[
                'receive-packets'] and not self.neighbor.api['consolidate']:
            self.peer.reactor.processes.receive(self.peer, msg_id, header,
                                                body)

        if msg_id == Message.CODE.UPDATE and not self.neighbor.api[
                'receive-parsed'] and not self.log_routes:
            yield _UPDATE
            return

        self.logger.message(self.me('<< %s' % Message.CODE.name(msg_id)))
        try:
            message = Message.unpack(msg_id, body, self.negotiated)
        except (KeyboardInterrupt, SystemExit, Notify):
            raise
        except Exception, exc:
            self.logger.message(
                self.me('Could not decode message "%d"' % msg_id))
            self.logger.message(self.me('%s' % str(exc)))
            # XXX: TODO: add backtrace here
            raise Notify(1, 0,
                         'can not decode update message of type "%d"' % msg_id)
            # raise Notify(5,0,'unknown message received')

        if message.TYPE == Notification.TYPE:
            raise message

        if self.neighbor.api[msg_id]:
            if self.neighbor.api['receive-parsed']:
                if self.neighbor.api['consolidate'] and self.neighbor.api[
                        'receive-packets']:
                    self.peer.reactor.processes.message(
                        msg_id, self.peer, message, header, body)
                else:
                    self.peer.reactor.processes.message(
                        msg_id, self.peer, message, '', '')

        yield message
Пример #11
0
class Protocol(object):
    decode = True

    def __init__(self, peer):
        try:
            self.logger = Logger()
        except RuntimeError:
            self.logger = FakeLogger()
        self.peer = peer
        self.neighbor = peer.neighbor
        self.negotiated = Negotiated(self.neighbor)
        self.connection = None

        if self.neighbor.connect:
            self.port = self.neighbor.connect
        elif os.environ.get('exabgp.tcp.port', '').isdigit():
            self.port = int(os.environ.get('exabgp.tcp.port'))
        elif os.environ.get('exabgp_tcp_port', '').isdigit():
            self.port = int(os.environ.get('exabgp_tcp_port'))
        else:
            self.port = 179

        # XXX: FIXME: check the the -19 is correct (but it is harmless)
        # The message size is the whole BGP message _without_ headers
        self.message_size = Message.MAX_LEN - Message.HEADER_LEN

        from exabgp.configuration.environment import environment
        self.log_routes = environment.settings().log.routes

    # XXX: we use self.peer.neighbor.peer_address when we could use self.neighbor.peer_address

    def __del__(self):
        self.close('automatic protocol cleanup')

    def me(self, message):
        return "Peer %15s ASN %-7s %s" % (self.peer.neighbor.peer_address,
                                          self.peer.neighbor.peer_as, message)

    def accept(self, incoming):
        self.connection = incoming

        if self.peer.neighbor.api['neighbor-changes']:
            self.peer.reactor.processes.connected(self.peer.neighbor)

        # very important - as we use this function on __init__
        return self

    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 = generator.next()
                    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

    def close(self, reason='protocol closed, reason unspecified'):
        if self.connection:
            self.logger.network(self.me(reason))

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

            try:
                if self.peer.neighbor.api['neighbor-changes']:
                    self.peer.reactor.processes.down(self.peer.neighbor,
                                                     reason)
            except ProcessError:
                self.logger.message(
                    self.
                    me('could not send notification of neighbor close to API'))

    def _to_api(self, direction, message, raw):
        packets = self.neighbor.api['%s-packets' % direction]
        parsed = self.neighbor.api['%s-parsed' % direction]
        consolidate = self.neighbor.api['%s-consolidate' % direction]

        if consolidate:
            if packets:
                self.peer.reactor.processes.message(self.peer.neighbor,
                                                    direction, message,
                                                    raw[:19], raw[19:])
            else:
                self.peer.reactor.processes.message(self.peer.neighbor,
                                                    direction, message, '', '')
        else:
            if packets:
                self.peer.reactor.processes.packets(self.peer.neighbor,
                                                    direction, int(message.ID),
                                                    raw[:19], raw[19:])
            if parsed:
                self.peer.reactor.processes.message(message.ID,
                                                    self.peer.neighbor,
                                                    direction, message, '', '')

    def write(self, message, negotiated=None):
        raw = message.message(negotiated)

        if self.neighbor.api.get('send-%s' % Message.CODE.short(message.ID),
                                 False):
            self._to_api('send', message, raw)

        for boolean in self.connection.writer(raw):
            yield boolean

    def send(self, raw):
        if self.neighbor.api.get('send-%s' % Message.CODE.short(ord(raw[19])),
                                 False):
            message = Update.unpack_message(raw[19:], self.negotiated)
            self._to_api('send', message, raw)

        for boolean in self.connection.writer(raw):
            yield boolean

    # Read from network .......................................................

    def read_message(self):
        # This will always be defined by the loop but scope leaking upset scrutinizer/pylint
        msg_id = None

        packets = self.neighbor.api['receive-packets']
        consolidate = self.neighbor.api['receive-consolidate']
        parsed = self.neighbor.api['receive-parsed']

        body, header = '', ''  # just because pylint/pylama are getting more clever

        for length, msg_id, header, body, notify in self.connection.reader():
            # internal issue
            if notify:
                if self.neighbor.api.get(
                        'send-%s' % Message.CODE.NOTIFICATION.SHORT, False):
                    if consolidate:
                        self.peer.reactor.processes.notification(
                            self.peer.neighbor, 'send', notify.code,
                            notify.subcode, str(notify), header, body)
                    elif parsed:
                        self.peer.reactor.processes.notification(
                            self.peer.neighbor, 'send', notify.code,
                            notify.subcode, str(notify), '', '')
                    elif packets:
                        self.peer.reactor.processes.packets(
                            self.peer.neighbor, 'send', msg_id, header, body)
                # XXX: is notify not already Notify class ?
                raise Notify(notify.code, notify.subcode, str(notify))

            if not length:
                yield _NOP
                continue

            self.logger.message(self.me('<< %s' % Message.CODE.name(msg_id)))

            for_api = self.neighbor.api.get(
                'receive-%s' % Message.CODE.short(msg_id), False)

            if for_api and packets and not consolidate:
                self.peer.reactor.processes.packets(self.peer.neighbor,
                                                    'receive', msg_id, header,
                                                    body)

            if msg_id == Message.CODE.UPDATE:
                if not (for_api or self.log_routes) and not (parsed
                                                             or consolidate):
                    yield _UPDATE
                    return

            try:
                message = Message.unpack(msg_id, body, self.negotiated)
            except (KeyboardInterrupt, SystemExit, Notify):
                raise
            except Exception, exc:
                self.logger.message(
                    self.me('Could not decode message "%d"' % msg_id))
                self.logger.message(self.me('%s' % str(exc)))
                self.logger.message(traceback.format_exc())
                raise Notify(
                    1, 0,
                    'can not decode update message of type "%d"' % msg_id)
                # raise Notify(5,0,'unknown message received')

            if for_api:
                if consolidate:
                    self.peer.reactor.processes.message(
                        msg_id, self.neighbor, 'receive', message, header,
                        body)
                elif parsed:
                    self.peer.reactor.processes.message(
                        msg_id, self.neighbor, 'receive', message, '', '')

            if message.TYPE == Notification.TYPE:
                raise message

            yield message
Пример #12
0
class Protocol (object):
	decode = True

	def __init__ (self, peer):
		try:
			self.logger = Logger()
		except RuntimeError:
			self.logger = FakeLogger()
		self.peer = peer
		self.neighbor = peer.neighbor
		self.negotiated = Negotiated(self.neighbor)
		self.connection = None

		if self.neighbor.connect:
			self.port = self.neighbor.connect
		elif os.environ.get('exabgp.tcp.port','').isdigit():
			self.port = int(os.environ.get('exabgp.tcp.port'))
		elif os.environ.get('exabgp_tcp_port','').isdigit():
			self.port = int(os.environ.get('exabgp_tcp_port'))
		else:
			self.port = 179

		# XXX: FIXME: check the the -19 is correct (but it is harmless)
		# The message size is the whole BGP message _without_ headers
		self.message_size = Message.MAX_LEN-Message.HEADER_LEN

		from exabgp.configuration.environment import environment
		self.log_routes = peer.neighbor.adj_rib_in or environment.settings().log.routes

	# XXX: we use self.peer.neighbor.peer_address when we could use self.neighbor.peer_address

	def me (self, message):
		return "%s/%s %s" % (self.peer.neighbor.peer_address,self.peer.neighbor.peer_as,message)

	def accept (self, incoming):
		self.connection = incoming

		if self.peer.neighbor.api['neighbor-changes']:
			self.peer.reactor.processes.connected(self.peer.neighbor)

		# very important - as we use this function on __init__
		return self

	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

	def close (self, reason='protocol closed, reason unspecified'):
		if self.connection:
			self.logger.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:
				self.logger.debug('could not send notification of neighbor close to API',self.connection.session())

	def _to_api (self,direction,message,raw):
		packets = self.neighbor.api['%s-packets' % direction]
		parsed = self.neighbor.api['%s-parsed' % direction]
		consolidate = self.neighbor.api['%s-consolidate' % direction]
		negotiated = self.negotiated if self.neighbor.api['negotiated'] else None

		if consolidate:
			if packets:
				self.peer.reactor.processes.message(self.peer.neighbor,direction,message,negotiated,raw[:19],raw[19:])
			else:
				self.peer.reactor.processes.message(self.peer.neighbor,direction,message,negotiated,b'',b'')
		else:
			if packets:
				self.peer.reactor.processes.packets(self.peer.neighbor,direction,int(message.ID),negotiated,raw[:19],raw[19:])
			if parsed:
				self.peer.reactor.processes.message(message.ID,self.peer.neighbor,direction,message,negotiated,b'',b'')

	def write (self, message, negotiated=None):
		raw = message.message(negotiated)

		code = 'send-%s' % Message.CODE.short(message.ID)
		self.peer.stats[code] = self.peer.stats.get(code,0) + 1
		if self.neighbor.api.get(code,False):
			self._to_api('send',message,raw)

		for boolean in self.connection.writer(raw):
			yield boolean

	def send (self, raw):
		code = 'send-%s' % Message.CODE.short(ordinal(raw[18]))
		self.peer.stats[code] = self.peer.stats.get(code,0) + 1
		if self.neighbor.api.get(code,False):
			message = Update.unpack_message(raw[19:],self.negotiated)
			self._to_api('send',message,raw)

		for boolean in self.connection.writer(raw):
			yield boolean

	# Read from network .......................................................

	def read_message (self):
		# This will always be defined by the loop but scope leaking upset scrutinizer/pylint
		msg_id = None

		packets = self.neighbor.api['receive-packets']
		consolidate = self.neighbor.api['receive-consolidate']
		parsed = self.neighbor.api['receive-parsed']

		body,header = b'',b''  # just because pylint/pylama are getting more clever

		for length,msg_id,header,body,notify in self.connection.reader():
			# internal issue
			if notify:
				code = 'receive-%s' % Message.CODE.NOTIFICATION.SHORT
				if self.neighbor.api.get(code,False):
					if consolidate:
						self.peer.reactor.processes.notification(self.peer.neighbor,'receive',notify.code,notify.subcode,str(notify),None,header,body)
					elif parsed:
						self.peer.reactor.processes.notification(self.peer.neighbor,'receive',notify.code,notify.subcode,str(notify),None,b'',b'')
					elif packets:
						self.peer.reactor.processes.packets(self.peer.neighbor,'receive',msg_id,None,header,body)
				# XXX: is notify not already Notify class ?
				raise Notify(notify.code,notify.subcode,str(notify))

			if not length:
				yield _NOP
				continue

			self.logger.debug('<< message of type %s' % Message.CODE.name(msg_id),self.connection.session())

			code = 'receive-%s' % Message.CODE.short(msg_id)
			self.peer.stats[code] = self.peer.stats.get(code,0) + 1
			for_api = self.neighbor.api.get(code,False)

			if for_api and packets and not consolidate:
				negotiated = self.negotiated if self.neighbor.api.get('negotiated',False) else None
				self.peer.reactor.processes.packets(self.peer.neighbor,'receive',msg_id,negotiated,header,body)

			if msg_id == Message.CODE.UPDATE:
				if not self.neighbor.adj_rib_in and not (for_api or self.log_routes) and not (parsed or consolidate):
					yield _UPDATE
					return

			try:
				message = Message.unpack(msg_id,body,self.negotiated)
			except (KeyboardInterrupt,SystemExit,Notify):
				raise
			except Exception as exc:
				self.logger.debug('could not decode message "%d"' % msg_id,self.connection.session())
				self.logger.debug('%s' % str(exc),self.connection.session())
				self.logger.debug(traceback.format_exc(),self.connection.session())
				raise Notify(1,0,'can not decode update message of type "%d"' % msg_id)
				# raise Notify(5,0,'unknown message received')

			if message.TYPE == Update.TYPE:
				if Attribute.CODE.INTERNAL_TREAT_AS_WITHDRAW in message.attributes:
					for nlri in message.nlris:
						nlri.action = IN.WITHDRAWN

			if for_api:
				negotiated = self.negotiated if self.neighbor.api.get('negotiated',False) else None
				if consolidate:
					self.peer.reactor.processes.message(msg_id,self.neighbor,'receive',message,negotiated,header,body)
				elif parsed:
					self.peer.reactor.processes.message(msg_id,self.neighbor,'receive',message,negotiated,b'',b'')

			if message.TYPE == Notification.TYPE:
				raise message

			if message.TYPE == Update.TYPE and Attribute.CODE.INTERNAL_DISCARD in message.attributes:
				yield _NOP
			else:
				yield message

	def validate_open (self):
		error = self.negotiated.validate(self.neighbor)
		if error is not None:
			raise Notify(*error)

		if self.neighbor.api['negotiated']:
			self.peer.reactor.processes.negotiated(self.peer.neighbor,self.negotiated)

		if self.negotiated.mismatch:
			self.logger.warning('--------------------------------------------------------------------',self.connection.session())
			self.logger.warning('the connection can not carry the following family/families',self.connection.session())
			for reason,(afi,safi) in self.negotiated.mismatch:
				self.logger.warning(' - %s is not configured for %s/%s' % (reason,afi,safi),self.connection.session())
			self.logger.warning('therefore no routes of this kind can be announced on the connection',self.connection.session())
			self.logger.warning('--------------------------------------------------------------------',self.connection.session())

	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)

		self.logger.debug('<< %s' % received_open,self.connection.session())
		yield received_open

	def read_keepalive (self):
		for message in self.read_message():
			if message.TYPE == NOP.TYPE:
				yield message
			else:
				break

		if message.TYPE != KeepAlive.TYPE:
			raise Notify(5,2)

		yield message

	#
	# Sending message to peer
	#

	def new_open (self):
		if self.neighbor.local_as:
			local_as = self.neighbor.local_as
		elif self.negotiated.received_open:
			local_as = self.negotiated.received_open.asn
		else:
			raise RuntimeError('no ASN available for the OPEN message')

		sent_open = Open(
			Version(4),
			local_as,
			self.neighbor.hold_time,
			self.neighbor.router_id,
			Capabilities().new(self.neighbor,self.peer._restarted)
		)

		# we do not buffer open message in purpose
		for _ in self.write(sent_open):
			yield _NOP

		self.logger.debug('>> %s' % sent_open,self.connection.session())
		yield sent_open

	def new_keepalive (self, comment=''):
		keepalive = KeepAlive()

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

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

		yield keepalive

	def new_notification (self, notification):
		for _ in self.write(notification):
			yield _NOP
		self.logger.debug('>> NOTIFICATION (%d,%d,"%s")' % (notification.code,notification.subcode,notification.data),self.connection.session())
		yield notification

	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:
			self.logger.debug('>> %d UPDATE(s)' % number,self.connection.session())
		yield _UPDATE

	def new_eor (self, afi, safi):
		eor = EOR(afi,safi)
		for _ in self.write(eor):
			yield _NOP
		self.logger.debug('>> EOR %s %s' % (afi,safi),self.connection.session())
		yield eor

	def new_eors (self, afi=AFI.undefined,safi=SAFI.undefined):
		# Send EOR to let our peer know he can perform a RIB update
		if self.negotiated.families:
			families = self.negotiated.families if (afi,safi) == (AFI.undefined,SAFI.undefined) else [(afi,safi),]
			for eor_afi,eor_safi in families:
				for _ in self.new_eor(eor_afi,eor_safi):
					yield _
		else:
			# If we are not sending an EOR, send a keepalive as soon as when finished
			# So the other routers knows that we have no (more) routes to send ...
			# (is that behaviour documented somewhere ??)
			for eor in self.new_keepalive('EOR'):
				yield _NOP
			yield _UPDATE

	def new_operational (self, operational, negotiated):
		for _ in self.write(operational,negotiated):
			yield _NOP
		self.logger.debug('>> OPERATIONAL %s' % str(operational),self.connection.session())
		yield operational

	def new_refresh (self, refresh):
		for _ in self.write(refresh,None):
			yield _NOP
		self.logger.debug('>> REFRESH %s' % str(refresh),self.connection.session())
		yield refresh
Пример #13
0
class Protocol(object):
    decode = True

    def __init__(self, peer):
        try:
            self.logger = Logger()
        except RuntimeError:
            self.logger = FakeLogger()
        self.peer = peer
        self.neighbor = peer.neighbor
        self.negotiated = Negotiated(self.neighbor)
        self.connection = None
        port = os.environ.get('exabgp.tcp.port', '') or os.environ.get(
            'exabgp_tcp_port', '')
        self.port = int(port) if port.isdigit() else 179

        # XXX: FIXME: check the the -19 is correct (but it is harmless)
        # The message size is the whole BGP message _without_ headers
        self.message_size = Message.MAX_LEN - Message.HEADER_LEN

        from exabgp.configuration.environment import environment
        self.log_routes = environment.settings().log.routes

    # XXX: we use self.peer.neighbor.peer_address when we could use self.neighbor.peer_address

    def __del__(self):
        self.close('automatic protocol cleanup')

    def me(self, message):
        return "Peer %15s ASN %-7s %s" % (self.peer.neighbor.peer_address,
                                          self.peer.neighbor.peer_as, message)

    def accept(self, incoming):
        self.connection = incoming

        self.peer.reactor.processes.reset(self.peer)
        if self.peer.neighbor.api['neighbor-changes']:
            self.peer.reactor.processes.connected(self.peer)

        # very important - as we use this function on __init__
        return self

    def connect(self):
        # allows to test the protocol code using modified StringIO with a extra 'pending' function
        if not self.connection:
            peer = self.neighbor.peer_address
            local = self.neighbor.local_address
            md5 = self.neighbor.md5
            ttl = self.neighbor.ttl
            self.connection = Outgoing(peer.afi, peer.ip, local.ip, self.port,
                                       md5, ttl)

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

    def close(self, reason='protocol closed, reason unspecified'):
        if self.connection:
            self.logger.network(self.me(reason))

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

            try:
                if self.peer.neighbor.api['neighbor-changes']:
                    self.peer.reactor.processes.down(self.peer, reason)
            except ProcessError:
                self.logger.message(
                    self.
                    me('could not send notification of neighbor close to API'))

    def write(self, message):
        if self.neighbor.api[
                'send-packets'] and not self.neighbor.api['consolidate']:
            self.peer.reactor.processes.send(self.peer, ord(message[18]),
                                             message[:19], message[19:])
        for boolean in self.connection.writer(message):
            yield boolean

    # Read from network .......................................................

    def read_message(self, comment=''):
        for length, msg, header, body, notify in self.connection.reader():
            if notify:
                if self.neighbor.api['receive-packets']:
                    self.peer.reactor.processes.receive(
                        self.peer, msg, header, body)
                if self.neighbor.api[Message.ID.NOTIFICATION]:
                    self.peer.reactor.processes.notification(
                        self.peer, notify.code, notify.subcode, str(notify))
                # XXX: is notify not already Notify class ?
                raise Notify(notify.code, notify.subcode, str(notify))
            if not length:
                yield _NOP

        if self.neighbor.api[
                'receive-packets'] and not self.neighbor.api['consolidate']:
            self.peer.reactor.processes.receive(self.peer, msg, header, body)

        if msg == Message.ID.UPDATE and not self.neighbor.api[
                'receive-parsed'] and not self.log_routes:
            yield _UPDATE
            return

        message = Message.unpack_message(msg, body, self.negotiated)
        self.logger.message(self.me('<< %s' % Message.ID.name(msg)))

        if message.TYPE == Notification.TYPE:
            raise message

        if self.neighbor.api[msg]:
            if self.neighbor.api['receive-parsed']:
                if self.neighbor.api['consolidate'] and self.neighbor.api[
                        'receive-packets']:
                    self.peer.reactor.processes.message(
                        msg, self.peer, message, header, body)
                else:
                    self.peer.reactor.processes.message(
                        msg, self.peer, message, '', '')

        yield message

        return
        # XXX: FIXME: check it is well 2,4
        raise Notify(2, 4, 'unknown message received')

        # elif msg == Message.ID.ROUTE_REFRESH:
        # 	if self.negotiated.refresh != REFRESH.absent:
        # 		self.logger.message(self.me('<< ROUTE-REFRESH'))
        # 		refresh = RouteRefresh.unpack_message(body,self.negotiated)
        # 		if self.neighbor.api.receive_refresh:
        # 			if refresh.reserved in (RouteRefresh.start,RouteRefresh.end):
        # 				if self.neighbor.api.consolidate:
        # 					self.peer.reactor.process.refresh(self.peer,refresh,header,body)
        # 				else:
        # 					self.peer.reactor.processes.refresh(self.peer,refresh,'','')
        # 	else:
        # 		# XXX: FIXME: really should raise, we are too nice
        # 		self.logger.message(self.me('<< NOP (un-negotiated type %d)' % msg))
        # 		refresh = UnknownMessage.unpack_message(body,self.negotiated)
        # 	yield refresh

    def validate_open(self):
        error = self.negotiated.validate(self.neighbor)
        if error is not None:
            raise Notify(*error)

    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 recevied is not an open message (%s)' %
                received_open)

        self.logger.message(self.me('<< %s' % received_open))
        yield received_open

    def read_keepalive(self, comment=''):
        for message in self.read_message(comment):
            if message.TYPE == NOP.TYPE:
                yield message
            else:
                break

        if message.TYPE != KeepAlive.TYPE:
            raise Notify(5, 2)

        yield message

    #
    # Sending message to peer
    #

    def new_open(self, restarted):
        sent_open = Open(4, self.neighbor.local_as, self.neighbor.router_id.ip,
                         Capabilities().new(self.neighbor, restarted),
                         self.neighbor.hold_time)

        # we do not buffer open message in purpose
        msg_send = sent_open.message()
        for _ in self.write(msg_send):
            yield _NOP

        self.logger.message(self.me('>> %s' % sent_open))
        if self.neighbor.api[Message.ID.OPEN]:
            if self.neighbor.api['consolidate']:
                header = msg_send[0:38]
                body = msg_send[38:]
                self.peer.reactor.processes.message(Message.ID.OPEN, self.peer,
                                                    sent_open, header, body,
                                                    'sent')
            else:
                self.peer.reactor.processes.message(Message.ID.OPEN, self.peer,
                                                    sent_open, '', '', 'sent')
        yield sent_open

    def new_keepalive(self, comment=''):
        keepalive = KeepAlive()

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

        self.logger.message(
            self.me('>> KEEPALIVE%s' % (' (%s)' % comment if comment else '')))

        yield keepalive

    def new_notification(self, notification):
        for _ in self.write(notification.message()):
            yield _NOP
        self.logger.message(
            self.me(
                '>> NOTIFICATION (%d,%d,"%s")' %
                (notification.code, notification.subcode, notification.data)))
        yield notification

    def new_update(self):
        updates = self.neighbor.rib.outgoing.updates(
            self.neighbor.group_updates)
        number = 0
        for update in updates:
            for message in update.messages(self.negotiated):
                number += 1
                for boolean in self.write(message):
                    # boolean is a transient network error we already announced
                    yield _NOP
        if number:
            self.logger.message(self.me('>> %d UPDATE(s)' % number))
        yield _UPDATE

    def new_eors(self):
        # Send EOR to let our peer know he can perform a RIB update
        if self.negotiated.families:
            for afi, safi in self.negotiated.families:
                eor = EOR(afi, safi).message()
                for _ in self.write(eor):
                    yield _NOP
                yield _UPDATE
        else:
            # If we are not sending an EOR, send a keepalive as soon as when finished
            # So the other routers knows that we have no (more) routes to send ...
            # (is that behaviour documented somewhere ??)
            for eor in self.new_keepalive('EOR'):
                yield _NOP
            yield _UPDATE

    def new_operational(self, operational, negotiated):
        for _ in self.write(operational.message(negotiated)):
            yield _NOP
        self.logger.message(self.me('>> OPERATIONAL %s' % str(operational)))
        yield operational

    def new_refresh(self, refresh, negotiated):
        for refresh in refresh.messages(negotiated):
            for _ in self.write(refresh):
                yield _NOP
            self.logger.message(self.me('>> REFRESH %s' % str(refresh)))
            yield refresh
Пример #14
0
class Protocol (object):
	decode = True

	def __init__ (self,peer):
		try:
			self.logger = Logger()
		except RuntimeError:
			self.logger = FakeLogger()
		self.peer = peer
		self.neighbor = peer.neighbor
		self.negotiated = Negotiated(self.neighbor)
		self.connection = None
		port = os.environ.get('exabgp.tcp.port','')
		self.port = int(port) if port.isdigit() else 179

		# XXX: FIXME: check the the -19 is correct (but it is harmless)
		# The message size is the whole BGP message _without_ headers
		self.message_size = Message.MAX_LEN-Message.HEADER_LEN

	# XXX: we use self.peer.neighbor.peer_address when we could use self.neighbor.peer_address

	def __del__ (self):
		self.close('automatic protocol cleanup')

	def me (self,message):
		return "Peer %15s ASN %-7s %s" % (self.peer.neighbor.peer_address,self.peer.neighbor.peer_as,message)

	def accept (self,incoming):
		self.connection = incoming

		if self.peer.neighbor.api.neighbor_changes:
			self.peer.reactor.processes.connected(self.peer.neighbor.peer_address)

		# very important - as we use this function on __init__
		return self

	def connect (self):
		# allows to test the protocol code using modified StringIO with a extra 'pending' function
		if not self.connection:
			peer = self.neighbor.peer_address
			local = self.neighbor.local_address
			md5 = self.neighbor.md5
			ttl = self.neighbor.ttl
			self.connection = Outgoing(peer.afi,peer.ip,local.ip,self.port,md5,ttl)

			connected = False
			try:
				generator = self.connection.establish()
				while True:
					connected = generator.next()
					if not connected:
						yield False
						continue
					if self.peer.neighbor.api.neighbor_changes:
						self.peer.reactor.processes.connected(self.peer.neighbor.peer_address)
					yield True
					return
			except StopIteration:
				# close called by the caller
				# self.close('could not connect to remote end')
				yield False
				return

	def close (self,reason='protocol closed, reason unspecified'):
		if self.connection:
			self.logger.network(self.me(reason))

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

			try:
				if self.peer.neighbor.api.neighbor_changes:
					self.peer.reactor.processes.down(self.peer.neighbor.peer_address,reason)
			except ProcessError:
				self.logger.message(self.me('could not send notification of neighbor close to API'))


	def write (self,message):
		if self.neighbor.api.send_packets:
			self.peer.reactor.processes.send(self.peer.neighbor.peer_address,message[18],message[:19],message[19:])
		for boolean in self.connection.writer(message):
			yield boolean

	# Read from network .......................................................

	def read_message (self,comment=''):
		try:
			for length,msg,header,body in self.connection.reader():
				if not length:
					yield _NOP
		except NotifyError,n:
			raise Notify(n.code,n.subcode,str(n))

		if self.neighbor.api.receive_packets:
			self.peer.reactor.processes.receive(self.peer.neighbor.peer_address,msg,header,body)

		if msg == Message.Type.UPDATE:
			self.logger.message(self.me('<< UPDATE'))

			if length == 30 and body.startswith(EOR.PREFIX):
				update = EORFactory(body)
				if self.neighbor.api.receive_routes:
					self.peer.reactor.processes.update(self.peer.neighbor.peer_address,update)
			elif self.neighbor.api.receive_routes:
				update = UpdateFactory(self.negotiated,body)
				if self.neighbor.api.receive_routes:
					self.peer.reactor.processes.update(self.peer.neighbor.peer_address,update)
			else:
				update = _UPDATE
			yield update

		elif msg == Message.Type.KEEPALIVE:
			self.logger.message(self.me('<< KEEPALIVE%s' % (' (%s)' % comment if comment else '')))
			yield KeepAlive()

		elif msg == Message.Type.NOTIFICATION:
			self.logger.message(self.me('<< NOTIFICATION'))
			yield NotificationFactory(body)

		elif msg == Message.Type.ROUTE_REFRESH:
			if self.negotiated.refresh != REFRESH.absent:
				self.logger.message(self.me('<< ROUTE-REFRESH'))
				refresh = RouteRefreshFactory(body)
				if self.neighbor.api.receive_routes:
					if refresh.reserved in (RouteRefresh.start,RouteRefresh.end):
						self.peer.reactor.processes.refresh(self.peer.neighbor.peer_address,refresh)
			else:
				# XXX: FIXME: really should raise, we are too nice
				self.logger.message(self.me('<< NOP (un-negotiated type %d)' % msg))
				refresh = UnknownMessageFactory(body)
			yield refresh

		elif msg == Message.Type.OPERATIONAL:
			if self.peer.neighbor.operational:
				operational = OperationalFactory(body)
				what = OperationalGroup[operational.what][0]
				self.peer.reactor.processes.operational(self.peer.neighbor.peer_address,what,operational)
			else:
				operational = _OPERATIONAL
			yield operational

		elif msg == Message.Type.OPEN:
			yield OpenFactory(body)

		else:
			# XXX: FIXME: really should raise, we are too nice
			self.logger.message(self.me('<< NOP (unknow type %d)' % msg))
			yield UnknownMessageFactory(msg)
Пример #15
0
class Protocol(object):
    decode = True

    def __init__(self, peer):
        try:
            self.logger = Logger()
        except RuntimeError:
            self.logger = FakeLogger()
        self.peer = peer
        self.neighbor = peer.neighbor
        self.negotiated = Negotiated(self.neighbor)
        self.connection = None

        if self.neighbor.connect:
            self.port = self.neighbor.connect
        elif os.environ.get('exabgp.tcp.port', '').isdigit():
            self.port = int(os.environ.get('exabgp.tcp.port'))
        elif os.environ.get('exabgp_tcp_port', '').isdigit():
            self.port = int(os.environ.get('exabgp_tcp_port'))
        else:
            self.port = 179

        # XXX: FIXME: check the the -19 is correct (but it is harmless)
        # The message size is the whole BGP message _without_ headers
        self.message_size = Message.MAX_LEN - Message.HEADER_LEN

        from exabgp.configuration.environment import environment
        self.log_routes = environment.settings().log.routes

    # XXX: we use self.peer.neighbor.peer_address when we could use self.neighbor.peer_address

    def me(self, message):
        return "Peer %15s ASN %-7s %s" % (self.peer.neighbor.peer_address,
                                          self.peer.neighbor.peer_as, message)

    def accept(self, incoming):
        self.connection = incoming

        if self.peer.neighbor.api['neighbor-changes']:
            self.peer.reactor.processes.connected(self.peer.neighbor)

        # very important - as we use this function on __init__
        return self

    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

    def close(self, reason='protocol closed, reason unspecified'):
        if self.connection:
            self.logger.network(self.me(reason))

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

            try:
                if self.peer.neighbor.api['neighbor-changes']:
                    self.peer.reactor.processes.down(self.peer.neighbor,
                                                     reason)
            except ProcessError:
                self.logger.message(
                    self.
                    me('could not send notification of neighbor close to API'))

    def _to_api(self, direction, message, raw):
        packets = self.neighbor.api['%s-packets' % direction]
        parsed = self.neighbor.api['%s-parsed' % direction]
        consolidate = self.neighbor.api['%s-consolidate' % direction]

        if consolidate:
            if packets:
                self.peer.reactor.processes.message(self.peer.neighbor,
                                                    direction, message,
                                                    raw[:19], raw[19:])
            else:
                self.peer.reactor.processes.message(self.peer.neighbor,
                                                    direction, message, '', '')
        else:
            if packets:
                self.peer.reactor.processes.packets(self.peer.neighbor,
                                                    direction, int(message.ID),
                                                    raw[:19], raw[19:])
            if parsed:
                self.peer.reactor.processes.message(message.ID,
                                                    self.peer.neighbor,
                                                    direction, message, '', '')

    def write(self, message, negotiated=None):
        raw = message.message(negotiated)

        if self.neighbor.api.get('send-%s' % Message.CODE.short(message.ID),
                                 False):
            self._to_api('send', message, raw)

        for boolean in self.connection.writer(raw):
            yield boolean

    def send(self, raw):
        if self.neighbor.api.get('send-%s' % Message.CODE.short(ord(raw[18])),
                                 False):
            message = Update.unpack_message(raw[19:], self.negotiated)
            self._to_api('send', message, raw)

        for boolean in self.connection.writer(raw):
            yield boolean

    # Read from network .......................................................

    def read_message(self):
        # This will always be defined by the loop but scope leaking upset scrutinizer/pylint
        msg_id = None

        packets = self.neighbor.api['receive-packets']
        consolidate = self.neighbor.api['receive-consolidate']
        parsed = self.neighbor.api['receive-parsed']

        body, header = '', ''  # just because pylint/pylama are getting more clever

        for length, msg_id, header, body, notify in self.connection.reader():
            # internal issue
            if notify:
                if self.neighbor.api.get(
                        'send-%s' % Message.CODE.NOTIFICATION.SHORT, False):
                    if consolidate:
                        self.peer.reactor.processes.notification(
                            self.peer.neighbor, 'send', notify.code,
                            notify.subcode, str(notify), header, body)
                    elif parsed:
                        self.peer.reactor.processes.notification(
                            self.peer.neighbor, 'send', notify.code,
                            notify.subcode, str(notify), '', '')
                    elif packets:
                        self.peer.reactor.processes.packets(
                            self.peer.neighbor, 'send', msg_id, header, body)
                # XXX: is notify not already Notify class ?
                raise Notify(notify.code, notify.subcode, str(notify))

            if not length:
                yield _NOP
                continue

            self.logger.message(self.me('<< %s' % Message.CODE.name(msg_id)))

            for_api = self.neighbor.api.get(
                'receive-%s' % Message.CODE.short(msg_id), False)

            if for_api and packets and not consolidate:
                self.peer.reactor.processes.packets(self.peer.neighbor,
                                                    'receive', msg_id, header,
                                                    body)

            if msg_id == Message.CODE.UPDATE:
                if not (for_api or self.log_routes) and not (parsed
                                                             or consolidate):
                    yield _UPDATE
                    return

            try:
                message = Message.unpack(msg_id, body, self.negotiated)
            except (KeyboardInterrupt, SystemExit, Notify):
                raise
            except Exception as exc:
                self.logger.message(
                    self.me('Could not decode message "%d"' % msg_id))
                self.logger.message(self.me('%s' % str(exc)))
                self.logger.message(traceback.format_exc())
                raise Notify(
                    1, 0,
                    'can not decode update message of type "%d"' % msg_id)
                # raise Notify(5,0,'unknown message received')

            if message.TYPE == Update.TYPE:
                if Attribute.CODE.INTERNAL_TREAT_AS_WITHDRAW in message.attributes:
                    for nlri in message.nlris:
                        nlri.action = IN.WITHDRAWN

            if for_api:
                if consolidate:
                    self.peer.reactor.processes.message(
                        msg_id, self.neighbor, 'receive', message, header,
                        body)
                elif parsed:
                    self.peer.reactor.processes.message(
                        msg_id, self.neighbor, 'receive', message, '', '')

            if message.TYPE == Notification.TYPE:
                raise message

            if message.TYPE == Update.TYPE and Attribute.CODE.INTERNAL_DISCARD in message.attributes:
                yield _NOP
            else:
                yield message

    def validate_open(self):
        error = self.negotiated.validate(self.neighbor)
        if error is not None:
            raise Notify(*error)

    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)

        self.logger.message(self.me('<< %s' % received_open))
        yield received_open

    def read_keepalive(self):
        for message in self.read_message():
            if message.TYPE == NOP.TYPE:
                yield message
            else:
                break

        if message.TYPE != KeepAlive.TYPE:
            raise Notify(5, 2)

        yield message

    #
    # Sending message to peer
    #

    def new_open(self, restarted):
        sent_open = Open(Version(4), self.neighbor.local_as,
                         self.neighbor.hold_time, self.neighbor.router_id,
                         Capabilities().new(self.neighbor, restarted))

        # we do not buffer open message in purpose
        for _ in self.write(sent_open):
            yield _NOP

        self.logger.message(self.me('>> %s' % sent_open))
        yield sent_open

    def new_keepalive(self, comment=''):
        keepalive = KeepAlive()

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

        self.logger.message(
            self.me('>> KEEPALIVE%s' % (' (%s)' % comment if comment else '')))

        yield keepalive

    def new_notification(self, notification):
        for _ in self.write(notification):
            yield _NOP
        self.logger.message(
            self.me(
                '>> NOTIFICATION (%d,%d,"%s")' %
                (notification.code, notification.subcode, notification.data)))
        yield notification

    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:
            self.logger.message(self.me('>> %d UPDATE(s)' % number))
        yield _UPDATE

    def new_eor(self, afi, safi):
        eor = EOR(afi, safi)
        for _ in self.write(eor):
            yield _NOP
        self.logger.message(self.me('>> EOR %s %s' % (afi, safi)))
        yield eor

    def new_eors(self, afi=AFI.undefined, safi=SAFI.undefined):
        # Send EOR to let our peer know he can perform a RIB update
        if self.negotiated.families:
            families = self.negotiated.families if (afi, safi) == (
                AFI.undefined, SAFI.undefined) else [
                    (afi, safi),
                ]
            for eor_afi, eor_safi in families:
                for _ in self.new_eor(eor_afi, eor_safi):
                    yield _
        else:
            # If we are not sending an EOR, send a keepalive as soon as when finished
            # So the other routers knows that we have no (more) routes to send ...
            # (is that behaviour documented somewhere ??)
            for eor in self.new_keepalive('EOR'):
                yield _NOP
            yield _UPDATE

    def new_operational(self, operational, negotiated):
        for _ in self.write(operational, negotiated):
            yield _NOP
        self.logger.message(self.me('>> OPERATIONAL %s' % str(operational)))
        yield operational

    def new_refresh(self, refresh):
        for _ in self.write(refresh, None):
            yield _NOP
        self.logger.message(self.me('>> REFRESH %s' % str(refresh)))
        yield refresh