Exemple #1
0
    def _open(self, direction, message):
        self.seen_open[direction] = message

        if all(self.seen_open.values()):
            self.negotiated = Negotiated(None)
            self.negotiated.sent(self.seen_open['send'])
            self.negotiated.received(self.seen_open['receive'])
Exemple #2
0
def check_update (neighbor, raw):
	from exabgp.logger import Logger

	logger = Logger()
	logger._parser = True
	logger.parser('\ndecoding routes in configuration')

	n = neighbor[neighbor.keys()[0]]
	p = Peer(n,None)

	path = {}
	for f in known_families():
		if n.add_path:
			path[f] = n.add_path

	capa = Capabilities().new(n,False)
	capa[Capability.CODE.ADD_PATH] = path
	capa[Capability.CODE.MULTIPROTOCOL] = n.families()

	routerid_1 = str(n.router_id)
	routerid_2 = '.'.join(str((int(_)+1) % 250) for _ in str(n.router_id).split('.',-1))

	o1 = Open(4,n.local_as,routerid_1,capa,180)
	o2 = Open(4,n.peer_as,routerid_2,capa,180)
	negotiated = Negotiated(n)
	negotiated.sent(o1)
	negotiated.received(o2)
	# grouped = False

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

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

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

		try:
			# This does not take the BGP header - let's assume we will not break that :)
			update = Update.unpack_message(injected,negotiated)
		except KeyboardInterrupt:
			raise
		except Notify,exc:
			logger.parser('could not parse the message')
			logger.parser(str(exc))
			return False
		except Exception,exc:
			logger.parser('could not parse the message')
			logger.parser(str(exc))
			return False
Exemple #3
0
	def setUp (self):
		# env.log.all = True
		self.negotiated = {}

		for asn4 in (True,False):
			neighbor = FakeNeighbor()
			neighbor.asn4 = asn4

			capa = Capabilities().new(neighbor,False)
			capa[Capability.CODE.MULTIPROTOCOL] = neighbor.families()

			# path = {}
			# for f in NLRI.known_families():
			# 	if neighbor.add_path:
			# 		path[f] = neighbor.add_path
			# capa[Capability.CODE.ADD_PATH] = path

			o1 = Open(4,neighbor.local_as,str(neighbor.local_address),capa,180)
			o2 = Open(4,neighbor.peer_as,str(neighbor.peer_address),capa,180)

			negotiated = Negotiated(neighbor)
			negotiated.sent(o1)
			negotiated.received(o2)

			self.negotiated[asn4] = negotiated
Exemple #4
0
def check_update(neighbor, raw):
    from exabgp.logger import Logger

    logger = Logger()
    logger._parser = True
    logger.parser("\ndecoding routes in configuration")

    n = neighbor[neighbor.keys()[0]]
    p = Peer(n, None)

    path = {}
    for f in known_families():
        if n.add_path:
            path[f] = n.add_path

    capa = Capabilities().new(n, False)
    capa[Capability.ID.ADD_PATH] = path
    capa[Capability.ID.MULTIPROTOCOL_EXTENSIONS] = n.families()

    o1 = Open(4, n.local_as, str(n.local_address), capa, 180)
    o2 = Open(4, n.peer_as, str(n.peer_address), capa, 180)
    negotiated = Negotiated(n)
    negotiated.sent(o1)
    negotiated.received(o2)
    # grouped = False

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

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

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

        try:
            # This does not take the BGP header - let's assume we will not break that :)
            update = Update.unpack_message(injected, negotiated)
        except KeyboardInterrupt:
            raise
        except Notify, e:
            logger.parser("could not parse the message")
            logger.parser(str(e))
            return False
        except Exception, e:
            logger.parser("could not parse the message")
            logger.parser(str(e))
            return False
Exemple #5
0
	def _open (self,direction,message):
		self.seen_open[direction] = message

		if all(self.seen_open.values()):
			self.negotiated = Negotiated(None)
			self.negotiated.sent(self.seen_open['send'])
			self.negotiated.received(self.seen_open['receive'])
Exemple #6
0
    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
Exemple #7
0
    def setUp(self):
        #env.log.all = True
        self.negotiated = {}

        for asn4 in (True, False):
            neighbor = Neighbor()
            neighbor.asn4 = asn4

            capa = Capabilities().new(neighbor, False)
            capa[Capability.ID.MULTIPROTOCOL] = neighbor.families()

            # path = {}
            # for f in known_families():
            # 	if neighbor.add_path:
            # 		path[f] = neighbor.add_path
            # capa[Capability.ID.ADD_PATH] = path

            o1 = Open(4, neighbor.local_as, str(neighbor.local_address), capa,
                      180)
            o2 = Open(4, neighbor.peer_as, str(neighbor.peer_address), capa,
                      180)

            negotiated = Negotiated(neighbor)
            negotiated.sent(o1)
            negotiated.received(o2)

            self.negotiated[asn4] = negotiated
Exemple #8
0
def check_update (neighbor, raw):
	from exabgp.logger import Logger

	logger = Logger()
	logger._parser = True
	logger.parser('\ndecoding routes in configuration')

	n = neighbor[neighbor.keys()[0]]
	p = Peer(n,None)

	path = {}
	for f in known_families():
		if n.add_path:
			path[f] = n.add_path

	capa = Capabilities().new(n,False)
	capa[Capability.CODE.ADD_PATH] = path
	capa[Capability.CODE.MULTIPROTOCOL] = n.families()

	routerid_1 = str(n.router_id)
	routerid_2 = '.'.join(str((int(_)+1) % 250) for _ in str(n.router_id).split('.',-1))

	o1 = Open(4,n.local_as,routerid_1,capa,180)
	o2 = Open(4,n.peer_as,routerid_2,capa,180)
	negotiated = Negotiated(n)
	negotiated.sent(o1)
	negotiated.received(o2)
	# grouped = False

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

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

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

		try:
			# This does not take the BGP header - let's assume we will not break that :)
			update = Update.unpack_message(injected,negotiated)
		except KeyboardInterrupt:
			raise
		except Notify,exc:
			logger.parser('could not parse the message','error')
			logger.parser(str(exc),'error')
			return False
		except Exception,exc:
			logger.parser('could not parse the message','error')
			logger.parser(str(exc),'error')
			return False
Exemple #9
0
	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
Exemple #10
0
	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
Exemple #11
0
class Transcoder (object):
	seen_open = {
		'send':    None,
		'receive': None,
	}
	negotiated = None

	json = JSON(json_version)

	def __init__ (self, src='json', dst='json'):
		if src != 'json':
			raise RuntimeError('left as an exercise to the reader')

		if dst != 'json':
			raise RuntimeError('left as an exercise to the reader')

		self.convert = self._from_json
		self.encoder = self.json

	def _state (self):
		self.seen_open['send'] = None
		self.seen_open['receive'] = None
		self.negotiated = None

	def _open (self,direction,message):
		self.seen_open[direction] = message

		if all(self.seen_open.values()):
			self.negotiated = Negotiated(None)
			self.negotiated.sent(self.seen_open['send'])
			self.negotiated.received(self.seen_open['receive'])

	def _from_json (self, string):
		try:
			parsed = json.loads(string)
		except ValueError:
			print >> sys.stderr, 'invalid JSON message'
			sys.exit(1)

		if parsed.get('exabgp','0.0.0') != json_version:
			print >> sys.stderr, 'invalid json version', string
			sys.exit(1)

		content = parsed.get('type','')

		if not content:
			print >> sys.stderr, 'invalid json content', string
			sys.exit(1)

		neighbor = _FakeNeighbor(
			parsed['neighbor']['address']['local'],
			parsed['neighbor']['address']['peer'],
			parsed['neighbor']['asn']['local'],
			parsed['neighbor']['asn']['peer'],
		)

		if content == 'state':
			self._state()
			return string

		direction = parsed['neighbor']['direction']
		category = parsed['neighbor']['message']['category']
		header = parsed['neighbor']['message']['header']
		body = parsed['neighbor']['message']['body']
		raw = ''.join(chr(int(body[_:_+2],16)) for _ in range(0,len(body),2))

		if content == 'open':
			message = Open.unpack_message(raw)
			self._open(direction,message)
			return self.encoder.open(neighbor,direction,message,header,body)

		if content == 'keapalive':
			return self.encoder.keepalive(neighbor,direction,header,body)

		if content == 'notification':
			return self.encoder.notification(neighbor,direction,ord(message[0]),ord(message[1]),message[2:],header,body)

		if not self.negotiated:
			print >> sys.stderr, 'invalid message sequence, open not exchange not complete', string
			sys.exit(1)

		message = Message.unpack(category,raw,self.negotiated)

		if content == 'update':
			return self.encoder.update(neighbor, direction, message, header,body)

		if content == 'eor': # XXX: Should not be required
			return self.encoder.update(neighbor, direction, message, header,body)

		if content == 'refresh':
			return self.json.refresh(neighbor, direction, message, header,body)

		if content == 'operational':
			return self.json.refresh(neighbor, direction, message, header,body)

		raise RuntimeError('the programer is a monkey and forgot a JSON message type')
Exemple #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
		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
Exemple #13
0
def check_neighbor (neighbors):
	from exabgp.logger import Logger

	logger = Logger()
	logger._option.parser = True

	if not neighbors:
		logger.parser('\ncould not find neighbor(s) to check')
		return False

	logger.parser('\ndecoding routes in configuration')

	for name in neighbors.keys():
		neighbor = neighbors[name]

		path = {}
		for f in known_families():
			if neighbor.add_path:
				path[f] = neighbor.add_path

		capa = Capabilities().new(neighbor,False)
		if path:
			capa[Capability.CODE.ADD_PATH] = path
		capa[Capability.CODE.MULTIPROTOCOL] = neighbor.families()

		o1 = Open(4,neighbor.local_as,'127.0.0.2',capa,180)
		o2 = Open(4,neighbor.peer_as,'127.0.0.3',capa,180)
		negotiated = Negotiated(neighbor)
		negotiated.sent(o1)
		negotiated.received(o2)
		# grouped = False

		for message in neighbor.rib.outgoing.updates(False):
			pass

		for change1 in neighbor.rib.outgoing.sent_changes():
			str1 = change1.extensive()
			packed = list(Update([change1.nlri],change1.attributes).messages(negotiated))
			pack1 = packed[0]

			logger.parser('parsed route requires %d updates' % len(packed))
			logger.parser('parsed route requires %d updates' % len(packed))
			logger.parser('update size is %d' % len(pack1))

			logger.parser('parsed route %s' % str1)
			logger.parser('parsed hex   %s' % od(pack1))

			# This does not take the BGP header - let's assume we will not break that :)
			try:
				logger.parser('')  # new line

				pack1s = pack1[19:] if pack1.startswith('\xFF'*16) else pack1
				update = Update.unpack_message(pack1s,negotiated)

				change2 = Change(update.nlris[0],update.attributes)
				str2 = change2.extensive()
				pack2 = list(Update([update.nlris[0]],update.attributes).messages(negotiated))[0]

				logger.parser('recoded route %s' % str2)
				logger.parser('recoded hex   %s' % od(pack2))

				str1r = str1.replace(' med 100','').replace(' local-preference 100','').replace(' origin igp','')
				str2r = str2.replace(' med 100','').replace(' local-preference 100','').replace(' origin igp','')

				if ' name ' in str1r:
					parts = str1r.split(' ')
					pos = parts.index('name')
					str1r = ' '.join(parts[:pos] + parts[pos+2:])

				skip = False

				if str1r != str2r:
					if 'attribute [' in str1r and ' 0x00 ' in str1r:
						# we do not decode non-transitive attributes
						logger.parser('skipping string check on update with non-transitive attribute(s)')
						skip = True
					else:
						logger.parser('strings are different:')
						logger.parser('[%s]' % (str1r))
						logger.parser('[%s]' % (str2r))
						return False
				else:
					logger.parser('strings are fine')

				if skip:
					logger.parser('skipping encoding for update with non-transitive attribute(s)')
				elif pack1 != pack2:
					logger.parser('encoding are different')
					logger.parser('[%s]' % (od(pack1)))
					logger.parser('[%s]' % (od(pack2)))
					return False
				else:
					logger.parser('encoding is fine')
					logger.parser('----------------------------------------')

				logger.parser('JSON nlri %s' % change1.nlri.json())
				logger.parser('JSON attr %s' % change1.attributes.json())

			except Notify,exc:
				logger.parser('----------------------------------------')
				logger.parser(str(exc))
				logger.parser('----------------------------------------')
				return False
		neighbor.rib.clear()
Exemple #14
0
def check_neighbor(neighbor):
    from exabgp.logger import Logger

    logger = Logger()
    logger._parser = True
    logger.parser("\ndecoding routes in configuration")

    n = neighbor[neighbor.keys()[0]]

    path = {}
    for f in known_families():
        if n.add_path:
            path[f] = n.add_path

    capa = Capabilities().new(n, False)
    capa[Capability.ID.ADD_PATH] = path
    capa[Capability.ID.MULTIPROTOCOL_EXTENSIONS] = n.families()

    o1 = Open(4, n.local_as, str(n.local_address), capa, 180)
    o2 = Open(4, n.peer_as, str(n.peer_address), capa, 180)
    negotiated = Negotiated(n)
    negotiated.sent(o1)
    negotiated.received(o2)
    # grouped = False

    for nei in neighbor.keys():
        for message in neighbor[nei].rib.outgoing.updates(False):
            pass

        for change1 in neighbor[nei].rib.outgoing.sent_changes():
            str1 = change1.extensive()
            packed = list(Update([change1.nlri], change1.attributes).messages(negotiated))
            pack1 = packed[0]

            logger.parser("parsed route requires %d updates" % len(packed))
            logger.parser("update size is %d" % len(pack1))

            logger.parser("parsed route %s" % str1)
            logger.parser("parsed hex   %s" % od(pack1))

            # This does not take the BGP header - let's assume we will not break that :)
            try:
                logger.parser("")  # new line

                pack1s = pack1[19:] if pack1.startswith("\xFF" * 16) else pack1
                update = Update.unpack_message(pack1s, negotiated)

                change2 = Change(update.nlris[0], update.attributes)
                str2 = change2.extensive()
                pack2 = list(Update([update.nlris[0]], update.attributes).messages(negotiated))[0]

                logger.parser("recoded route %s" % str2)
                logger.parser("recoded hex   %s" % od(pack2))

                str1r = str1.replace(" med 100", "").replace(" local-preference 100", "").replace(" origin igp", "")
                str2r = str2.replace(" med 100", "").replace(" local-preference 100", "").replace(" origin igp", "")

                skip = False

                if str1r != str2r:
                    if "attribute [" in str1r and " 0x00 " in str1r:
                        # we do not decode non-transitive attributes
                        logger.parser("skipping string check on update with non-transitive attribute(s)")
                        skip = True
                    else:
                        logger.parser("strings are different:")
                        logger.parser("[%s]" % str1r)
                        logger.parser("[%s]" % str2r)
                        return False
                else:
                    logger.parser("strings are fine")

                if skip:
                    logger.parser("skipping encoding for update with non-transitive attribute(s)")
                elif pack1 != pack2:
                    logger.parser("encoding are different")
                    logger.parser("[%s]" % od(pack1))
                    logger.parser("[%s]" % od(pack2))
                    return False
                else:
                    logger.parser("encoding is fine")
                    logger.parser("----------------------------------------")

            except Notify, e:
                logger.parser("----------------------------------------")
                logger.parser(str(e))
                logger.parser("----------------------------------------")
                return False
Exemple #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
        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
Exemple #16
0
def check_neighbor(neighbor):
    from exabgp.logger import Logger

    logger = Logger()
    logger._parser = True
    logger.parser('\ndecoding routes in configuration')

    n = neighbor[neighbor.keys()[0]]

    path = {}
    for f in known_families():
        if n.add_path:
            path[f] = n.add_path

    capa = Capabilities().new(n, False)
    capa[Capability.ID.ADD_PATH] = path
    capa[Capability.ID.MULTIPROTOCOL] = n.families()

    o1 = Open(4, n.local_as, str(n.local_address), capa, 180)
    o2 = Open(4, n.peer_as, str(n.peer_address), capa, 180)
    negotiated = Negotiated(n)
    negotiated.sent(o1)
    negotiated.received(o2)
    #grouped = False

    for nei in neighbor.keys():
        for message in neighbor[nei].rib.outgoing.updates(False):
            pass

        for change1 in neighbor[nei].rib.outgoing.sent_changes():
            str1 = change1.extensive()
            packed = list(
                Update([change1.nlri],
                       change1.attributes).messages(negotiated))
            pack1 = packed[0]

            logger.parser('parsed route requires %d updates' % len(packed))
            logger.parser('update size is %d' % len(pack1))

            logger.parser('parsed route %s' % str1)
            logger.parser('parsed hex   %s' % od(pack1))

            # This does not take the BGP header - let's assume we will not break that :)
            try:
                logger.parser('')  # new line

                pack1s = pack1[19:] if pack1.startswith('\xFF' * 16) else pack1
                update = Update.unpack_message(pack1s, negotiated)

                change2 = Change(update.nlris[0], update.attributes)
                str2 = change2.extensive()
                pack2 = list(
                    Update([update.nlris[0]],
                           update.attributes).messages(negotiated))[0]

                logger.parser('recoded route %s' % str2)
                logger.parser('recoded hex   %s' % od(pack2))

                str1r = str1.replace(' med 100', '').replace(
                    ' local-preference 100', '').replace(' origin igp', '')
                str2r = str2.replace(' med 100', '').replace(
                    ' local-preference 100', '').replace(' origin igp', '')

                skip = False

                if str1r != str2r:
                    if 'attribute [' in str1r and ' 0x00 ' in str1r:
                        # we do not decode non-transitive attributes
                        logger.parser(
                            'skipping string check on update with non-transitive attribute(s)'
                        )
                        skip = True
                    else:
                        logger.parser('strings are different:')
                        logger.parser('[%s]' % str1r)
                        logger.parser('[%s]' % str2r)
                        return False
                else:
                    logger.parser('strings are fine')

                if skip:
                    logger.parser(
                        'skipping encoding for update with non-transitive attribute(s)'
                    )
                elif pack1 != pack2:
                    logger.parser('encoding are different')
                    logger.parser('[%s]' % od(pack1))
                    logger.parser('[%s]' % od(pack2))
                    return False
                else:
                    logger.parser('encoding is fine')
                    logger.parser('----------------------------------------')

            except Notify, e:
                logger.parser('----------------------------------------')
                logger.parser(str(e))
                logger.parser('----------------------------------------')
                return False
Exemple #17
0
def check_neighbor (neighbors):
	from exabgp.logger import Logger

	logger = Logger()
	logger._parser = True

	if not neighbors:
		logger.parser('\ncould not find neighbor(s) to check')
		return False

	logger.parser('\ndecoding routes in configuration')

	for name in neighbors.keys():
		neighbor = neighbors[name]
		neighbor.rib.clear()

		path = {}
		for f in known_families():
			if neighbor.add_path:
				path[f] = neighbor.add_path

		capa = Capabilities().new(neighbor,False)
		capa[Capability.CODE.ADD_PATH] = path
		capa[Capability.CODE.MULTIPROTOCOL] = neighbor.families()

		o1 = Open(4,neighbor.local_as,str(neighbor.local_address),capa,180)
		o2 = Open(4,neighbor.peer_as,str(neighbor.peer_address),capa,180)
		negotiated = Negotiated(neighbor)
		negotiated.sent(o1)
		negotiated.received(o2)
		# grouped = False

		for message in neighbor.rib.outgoing.updates(False):
			pass

		for change1 in neighbor.rib.outgoing.sent_changes():
			str1 = change1.extensive()
			packed = list(Update([change1.nlri],change1.attributes).messages(negotiated))
			pack1 = packed[0]

			logger.parser('parsed route requires %d updates' % len(packed))
			logger.parser('update size is %d' % len(pack1))

			logger.parser('parsed route %s' % str1)
			logger.parser('parsed hex   %s' % od(pack1))

			# This does not take the BGP header - let's assume we will not break that :)
			try:
				logger.parser('')  # new line

				pack1s = pack1[19:] if pack1.startswith('\xFF'*16) else pack1
				update = Update.unpack_message(pack1s,negotiated)

				change2 = Change(update.nlris[0],update.attributes)
				str2 = change2.extensive()
				pack2 = list(Update([update.nlris[0]],update.attributes).messages(negotiated))[0]

				logger.parser('recoded route %s' % str2)
				logger.parser('recoded hex   %s' % od(pack2))

				str1r = str1.replace(' med 100','').replace(' local-preference 100','').replace(' origin igp','')
				str2r = str2.replace(' med 100','').replace(' local-preference 100','').replace(' origin igp','')

				if ' name ' in str1r:
					parts = str1r.split(' ')
					pos = parts.index('name')
					str1r = ' '.join(parts[:pos] + parts[pos+2:])

				skip = False

				if str1r != str2r:
					if 'attribute [' in str1r and ' 0x00 ' in str1r:
						# we do not decode non-transitive attributes
						logger.parser('skipping string check on update with non-transitive attribute(s)')
						skip = True
					else:
						logger.parser('strings are different:')
						logger.parser('[%s]' % (str1r))
						logger.parser('[%s]' % (str2r))
						return False
				else:
						logger.parser('strings are fine')

				if skip:
					logger.parser('skipping encoding for update with non-transitive attribute(s)')
				elif pack1 != pack2:
					logger.parser('encoding are different')
					logger.parser('[%s]' % (od(pack1)))
					logger.parser('[%s]' % (od(pack2)))
					return False
				else:
					logger.parser('encoding is fine')
					logger.parser('----------------------------------------')

			except Notify,exc:
				logger.parser('----------------------------------------')
				logger.parser(str(exc))
				logger.parser('----------------------------------------')
				return False
Exemple #18
0
class Transcoder(object):
    seen_open = {
        'send': None,
        'receive': None,
    }
    negotiated = None

    json = JSON(json_version)

    def __init__(self, src='json', dst='json'):
        if src != 'json':
            raise RuntimeError('left as an exercise to the reader')

        if dst != 'json':
            raise RuntimeError('left as an exercise to the reader')

        self.convert = self._from_json
        self.encoder = self.json

    def _state(self):
        self.seen_open['send'] = None
        self.seen_open['receive'] = None
        self.negotiated = None

    def _open(self, direction, message):
        self.seen_open[direction] = message

        if all(self.seen_open.values()):
            self.negotiated = Negotiated(None)
            self.negotiated.sent(self.seen_open['send'])
            self.negotiated.received(self.seen_open['receive'])

    def _from_json(self, string):
        try:
            parsed = json.loads(string)
        except ValueError:
            print >> sys.stderr, 'invalid JSON message'
            sys.exit(1)

        if parsed.get('exabgp', '0.0.0') != json_version:
            print >> sys.stderr, 'invalid json version', string
            sys.exit(1)

        content = parsed.get('type', '')

        if not content:
            print >> sys.stderr, 'invalid json content', string
            sys.exit(1)

        neighbor = _FakeNeighbor(
            parsed['neighbor']['address']['local'],
            parsed['neighbor']['address']['peer'],
            parsed['neighbor']['asn']['local'],
            parsed['neighbor']['asn']['peer'],
        )

        if content == 'state':
            self._state()
            return string

        direction = parsed['neighbor']['direction']
        category = parsed['neighbor']['message']['category']
        header = parsed['neighbor']['message']['header']
        body = parsed['neighbor']['message']['body']
        raw = ''.join(
            chr(int(body[_:_ + 2], 16)) for _ in range(0, len(body), 2))

        if content == 'open':
            message = Open.unpack_message(raw)
            self._open(direction, message)
            return self.encoder.open(neighbor, direction, message, header,
                                     body)

        if content == 'keapalive':
            return self.encoder.keepalive(neighbor, direction, header, body)

        if content == 'notification':
            return self.encoder.notification(neighbor, direction,
                                             ord(message[0]), ord(message[1]),
                                             message[2:], header, body)

        if not self.negotiated:
            print >> sys.stderr, 'invalid message sequence, open not exchange not complete', string
            sys.exit(1)

        message = Message.unpack(category, raw, self.negotiated)

        if content == 'update':
            return self.encoder.update(neighbor, direction, message, header,
                                       body)

        if content == 'eor':  # XXX: Should not be required
            return self.encoder.update(neighbor, direction, message, header,
                                       body)

        if content == 'refresh':
            return self.json.refresh(neighbor, direction, message, header,
                                     body)

        if content == 'operational':
            return self.json.refresh(neighbor, direction, message, header,
                                     body)

        raise RuntimeError(
            'the programer is a monkey and forgot a JSON message type')