Beispiel #1
0
	def _reader (self, number):
		# The function must not be called if it does not return with no data with a smaller size as parameter
		if not self.io:
			self.close()
			raise NotConnected('Trying to read on a closed TCP connection')
		if number == 0:
			yield b''
			return

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

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

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

					yield b''
			except socket.timeout as exc:
				self.close()
				self.logger.wire("%s %s peer is too slow" % (self.name(),self.peer),source=self.session())
				raise TooSlowError('Timeout while reading data from the network (%s)' % errstr(exc))
			except socket.error as exc:
				if exc.args[0] in error.block:
					message = "%s %s blocking io problem mid-way through reading a message %s, trying to complete" % (self.name(),self.peer,errstr(exc))
					if message != reported:
						reported = message
						self.logger.wire(message,'debug',self.session())
					yield b''
				elif exc.args[0] in error.fatal:
					self.close()
					raise LostConnection('issue reading on the socket: %s' % errstr(exc))
				# what error could it be !
				else:
					self.logger.wire("%s %s undefined error reading on socket" % (self.name(),self.peer),source=self.session())
					raise NetworkError('Problem while reading data from the network (%s)' % errstr(exc))
Beispiel #2
0
    def parse(self, data, negotiated):
        if not data:
            return self

        # We do not care if the attribute are transitive or not as we do not redistribute
        flag = Attribute.Flag(ord(data[0]))
        aid = Attribute.CODE(ord(data[1]))

        if flag & Attribute.Flag.EXTENDED_LENGTH:
            length = unpack('!H', data[2:4])[0]
            offset = 4
        else:
            length = ord(data[2])
            offset = 3

        data = data[offset:]
        left = data[length:]
        attribute = data[:length]

        logger = Logger()
        logger.parser(
            LazyFormat(
                "parsing flag %x type %02x (%s) len %02x %s" %
                (flag, int(aid), aid, length, 'payload ' if length else ''),
                data[:length]))

        # remove the PARTIAL bit before comparaison if the attribute is optional
        if aid in Attribute.attributes_optional:
            aid &= Attribute.Flag.MASK_PARTIAL & 0xFF
            # aid &= ~Attribute.Flag.PARTIAL & 0xFF  # cleaner than above (python use signed integer for ~)

        # handle the attribute if we know it
        if Attribute.registered(aid, flag):
            self.add(Attribute.unpack(aid, flag, attribute, negotiated))
            return self.parse(left, negotiated)
        # XXX: FIXME: we could use a fallback function here like capability

        # if we know the attribute but the flag is not what the RFC says. ignore it.
        if aid in Attribute.attributes_known:
            logger.parser(
                'invalid flag for attribute %s (flag 0x%02X, aid 0x%02X)' %
                (Attribute.CODE.names.get(aid, 'unset'), flag, aid))
            return self.parse(left, negotiated)

        # it is an unknown transitive attribute we need to pass on
        if flag & Attribute.Flag.TRANSITIVE:
            logger.parser(
                'unknown transitive attribute (flag 0x%02X, aid 0x%02X)' %
                (flag, aid))
            self.add(
                GenericAttribute(aid, flag | Attribute.Flag.PARTIAL,
                                 attribute), attribute)
            return self.parse(left, negotiated)

        # it is an unknown non-transitive attribute we can ignore.
        logger.parser(
            'ignoring unknown non-transitive attribute (flag 0x%02X, aid 0x%02X)'
            % (flag, aid))
        return self.parse(left, negotiated)
Beispiel #3
0
    def writer(self, data):
        if not self.io:
            # XXX: FIXME: Make sure it does not hold the cleanup during the closing of the peering session
            yield True
            return
        while not self.writing():
            yield False
        self.logger.debug(LazyFormat('sending TCP payload', data),
                          self.session())
        # The first while is here to setup the try/catch block once as it is very expensive
        while True:
            try:
                while True:
                    if self.defensive and random.randint(0, 2):
                        raise socket.error(errno.EAGAIN,
                                           'raising network error on purpose')

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

                    data = data[number:]
                    if not data:
                        yield True
                        return
                    yield False
            except socket.error as exc:
                if exc.args[0] in error.block:
                    self.logger.debug(
                        '%s %s blocking io problem mid-way through writing a message %s, trying to complete'
                        % (self.name(), self.peer, errstr(exc)),
                        self.session())
                    yield False
                elif exc.errno == errno.EPIPE:
                    # The TCP connection is gone.
                    self.close()
                    raise NetworkError('Broken TCP connection')
                elif exc.args[0] in error.fatal:
                    self.close()
                    self.logger.critical(
                        '%s %s problem sending message (%s)' %
                        (self.name(), self.peer, errstr(exc)), self.session())
                    raise NetworkError(
                        'Problem while writing data to the network (%s)' %
                        errstr(exc))
                # what error could it be !
                else:
                    self.logger.critical(
                        '%s %s undefined error writing on socket' %
                        (self.name(), self.peer), self.session())
                    yield False
Beispiel #4
0
	def unpack (cls,afi,safi,data,addpath,nexthop,action):
		if not cls.logger:
			cls.logger = Logger()
		cls.logger.parser(LazyFormat("parsing %s/%s nlri payload " % (afi,safi),data))

		key = '%d/%d' % (afi,safi)
		if key in cls.registered_nlri:
			return cls.registered_nlri[key].unpack(afi,safi,data,addpath,nexthop,action)
		raise Notify(3,0,'trying to decode unknown family %s/%s' % (AFI(afi),SAFI(safi)))
Beispiel #5
0
	def _reader (self,number):
		# The function must not be called if it does not return with no data with a smaller size as parameter
		if not self.io:
			self.close()
			raise NotConnected('Trying to read on a close TCP conncetion')
		if number == 0:
			yield ''
			return
		# XXX: one of the socket option is to recover the size of the buffer
		# XXX: we could use it to not have to put together the string with multiple reads
		# XXX: and get rid of the self.read_timeout option
		while not self.reading():
			yield ''
		data = ''
		while True:
			try:
				while True:
					if self._reading is None:
						self._reading = time.time()
					elif time.time() > self._reading + self.read_timeout:
						self.close()
						self.logger.wire("%s %s peer is too slow (we were told there was data on the socket but we can not read up to what should be there)" % (self.name(),self.peer))
						raise TooSlowError('Waited to read for data on a socket for more than %d second(s)' % self.read_timeout)

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

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

					data += read
					number -= len(read)
					if not number:
						self.logger.wire(LazyFormat("%s %-32s RECEIVED " % (self.name(),'%s / %s' % (self.local,self.peer)),od,read))
						self._reading = None
						yield data
						return
			except socket.timeout,e:
				self.close()
				self.logger.wire("%s %s peer is too slow" % (self.name(),self.peer))
				raise TooSlowError('Timeout while reading data from the network (%s)' % errstr(e))
			except socket.error,e:
				if e.args[0] in error.block:
					self.logger.wire("%s %s blocking io problem mid-way through reading a message %s, trying to complete" % (self.name(),self.peer,errstr(e)),'debug')
				elif e.args[0] in error.fatal:
					self.close()
					raise LostConnection('issue reading on the socket: %s' % errstr(e))
				# what error could it be !
				else:
					self.logger.wire("%s %s undefined error reading on socket" % (self.name(),self.peer))
					raise NetworkError('Problem while reading data from the network (%s)' % errstr(e))
Beispiel #6
0
	def writer (self,data):
		if not self.io:
			# XXX: FIXME: Make sure it does not hold the cleanup during the closing of the peering session
			yield True
			return
		if not self.writing():
			yield False
			return
		self.logger.wire(LazyFormat("%s %-32s SENDING " % (self.name(),'%s / %s' % (self.local,self.peer)),od,data))
		# The first while is here to setup the try/catch block once as it is very expensive
		while True:
			try:
				while True:
					if self._writing is None:
						self._writing = time.time()
					elif time.time() > self._writing + self.read_timeout:
						self.close()
						self.logger.wire("%s %s peer is too slow" % (self.name(),self.peer))
						raise TooSlowError('Waited to write for data on a socket for more than %d second(s)' % self.read_timeout)

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

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

					data = data[nb:]
					if not data:
						self._writing = None
						yield True
						return
					yield False
			except socket.error,e:
				if e.args[0] in error.block:
					self.logger.wire("%s %s blocking io problem mid-way through writing a message %s, trying to complete" % (self.name(),self.peer,errstr(e)),'debug')
				elif e.errno == errno.EPIPE:
					# The TCP connection is gone.
					self.close()
					raise NetworkError('Broken TCP connection')
				elif e.args[0] in error.fatal:
					self.close()
					self.logger.wire("%s %s problem sending message (%s)" % (self.name(),self.peer,errstr(e)))
					raise NetworkError('Problem while writing data to the network (%s)' % errstr(e))
				# what error could it be !
				else:
					self.logger.wire("%s %s undefined error writing on socket" % (self.name(),self.peer))
					yield False
Beispiel #7
0
	def _main (self, direction):
		"""yield True if we want to come back to it asap, None if nothing urgent, and False if stopped"""
		if self._teardown:
			raise Notify(6,3)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

					self._have_routes = True

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

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

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

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

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

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

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

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

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

		# notify our peer of the shutdown
		raise Notify(6,self._teardown)
Beispiel #8
0
    def unpack_message(cls, data, negotiated):
        logger = Logger()

        logger.debug(LazyFormat('parsing UPDATE', data), 'parser')

        length = len(data)

        # This could be speed up massively by changing the order of the IF
        if length == 4 and data == b'\x00\x00\x00\x00':
            return EOR(AFI.ipv4, SAFI.unicast)  # pylint: disable=E1101
        if length == 11 and data.startswith(EOR.NLRI.PREFIX):
            return EOR.unpack_message(data, negotiated)

        withdrawn, _attributes, announced = cls.split(data)

        if not withdrawn:
            logger.debug('withdrawn NLRI none', 'routes')

        attributes = Attributes.unpack(_attributes, negotiated)

        if not announced:
            logger.debug('announced NLRI none', 'routes')

        # Is the peer going to send us some Path Information with the route (AddPath)
        addpath = negotiated.addpath.receive(AFI.ipv4, SAFI.unicast)

        # empty string for NoNextHop, the packed IP otherwise (without the 3/4 bytes of attributes headers)
        nexthop = attributes.get(Attribute.CODE.NEXT_HOP, NoNextHop)
        # nexthop = NextHop.unpack(_nexthop.ton())

        # XXX: NEXTHOP MUST NOT be the IP address of the receiving speaker.

        nlris = []
        while withdrawn:
            nlri, left = NLRI.unpack_nlri(AFI.ipv4, SAFI.unicast, withdrawn,
                                          IN.WITHDRAWN, addpath)
            logger.debug('withdrawn NLRI %s' % nlri, 'routes')
            withdrawn = left
            nlris.append(nlri)

        while announced:
            nlri, left = NLRI.unpack_nlri(AFI.ipv4, SAFI.unicast, announced,
                                          IN.ANNOUNCED, addpath)
            nlri.nexthop = nexthop
            logger.debug('announced NLRI %s' % nlri, 'routes')
            announced = left
            nlris.append(nlri)

        unreach = attributes.pop(MPURNLRI.ID, None)
        reach = attributes.pop(MPRNLRI.ID, None)

        if unreach is not None:
            nlris.extend(unreach.nlris)

        if reach is not None:
            nlris.extend(reach.nlris)

        if not attributes and not nlris:
            # Careful do not use == or != as the comparaison does not work
            if unreach is None and reach is None:
                return EOR(AFI.ipv4, SAFI.unicast)
            if unreach is not None:
                return EOR(unreach.afi, unreach.safi)
            if reach is not None:
                return EOR(reach.afi, reach.safi)
            raise RuntimeError('This was not expected')

        update = Update(nlris, attributes)

        def parsed(_):
            # we need the import in the function as otherwise we have an cyclic loop
            # as this function currently uses Update..
            from exabgp.reactor.api.response import Response
            from exabgp.version import json as json_version
            return 'json %s' % Response.JSON(json_version).update(
                negotiated.neighbor, 'in', update, None, '', '')

        logger.debug(LazyFormat('decoded UPDATE', '', parsed), 'parser')

        return update
Beispiel #9
0
    def unpack_message(cls, data, negotiated):
        logger = Logger()

        logger.parser(LazyFormat("parsing UPDATE", data))

        length = len(data)

        # This could be speed up massively by changing the order of the IF
        if length == 4 and data == b'\x00\x00\x00\x00':
            return EOR(AFI(AFI.ipv4), SAFI(SAFI.unicast))  # pylint: disable=E1101
        if length == 11 and data.startswith(EOR.NLRI.PREFIX):
            return EOR.unpack_message(data, negotiated)

        withdrawn, _attributes, announced = cls.split(data)

        if not withdrawn:
            logger.parser("withdrawn NLRI none")

        attributes = Attributes.unpack(_attributes, negotiated)

        if not announced:
            logger.parser("announced NLRI none")

        # Is the peer going to send us some Path Information with the route (AddPath)
        addpath = negotiated.addpath.receive(AFI(AFI.ipv4), SAFI(SAFI.unicast))

        # empty string for NoNextHop, the packed IP otherwise (without the 3/4 bytes of attributes headers)
        nexthop = attributes.get(Attribute.CODE.NEXT_HOP, NoNextHop)
        # nexthop = NextHop.unpack(_nexthop.ton())

        # XXX: NEXTHOP MUST NOT be the IP address of the receiving speaker.

        nlris = []
        while withdrawn:
            nlri, left = NLRI.unpack_nlri(AFI.ipv4, SAFI.unicast, withdrawn,
                                          IN.WITHDRAWN, addpath)
            logger.parser("withdrawn NLRI %s" % nlri)
            withdrawn = left
            nlris.append(nlri)

        while announced:
            nlri, left = NLRI.unpack_nlri(AFI.ipv4, SAFI.unicast, announced,
                                          IN.ANNOUNCED, addpath)
            nlri.nexthop = nexthop
            logger.parser("announced NLRI %s" % nlri)
            announced = left
            nlris.append(nlri)

        unreach = attributes.pop(MPURNLRI.ID, None)
        reach = attributes.pop(MPRNLRI.ID, None)

        if unreach is not None:
            nlris.extend(unreach.nlris)

        if reach is not None:
            nlris.extend(reach.nlris)

        if not attributes and not nlris:
            # Careful do not use == or != as the comparaison does not work
            if unreach is None and reach is None:
                return EOR(AFI(AFI.ipv4), SAFI(SAFI.unicast))
            if unreach is not None:
                return EOR(unreach.afi, unreach.safi)
            if reach is not None:
                return EOR(reach.afi, reach.safi)
            raise RuntimeError('This was not expected')

        return Update(nlris, attributes)
Beispiel #10
0
    def _factory(self, data):
        if not data:
            return self

        # We do not care if the attribute are transitive or not as we do not redistribute
        flag = Flag(ord(data[0]))
        code = AID(ord(data[1]))

        if flag & Flag.EXTENDED_LENGTH:
            length = unpack('!H', data[2:4])[0]
            offset = 4
        else:
            length = ord(data[2])
            offset = 3

        if self.hasmp:
            if code not in (AID.MP_REACH_NLRI, AID.MP_UNREACH_NLRI):
                self.cacheable = False
                self.prefix = ''
        else:
            self.prefix += data[:offset + length]

        data = data[offset:]
        next = data[length:]
        attribute = data[:length]

        logger = Logger()
        logger.parser(
            LazyFormat(
                "parsing flag %x type %02x (%s) len %02x %s" %
                (flag, int(code), code, length, 'payload ' if length else ''),
                od, data[:length]))

        if code == AID.ORIGIN and flag.matches(Origin.FLAG):
            # This if block should never be called anymore ...
            if not self.add_from_cache(code, attribute):
                self.add(Origin(ord(attribute)), attribute)
            return self.factory(next)

        # only 2-4% of duplicated data - is it worth to cache ?
        if code == AID.AS_PATH and flag.matches(ASPath.FLAG):
            if length:
                # we store the AS4_PATH as AS_PATH, do not over-write
                if not self.has(code):
                    if not self.add_from_cache(code, attribute):
                        self.add(self.__new_ASPath(attribute), attribute)
            return self.factory(next)

        if code == AID.AS4_PATH and flag.matches(AS4Path.FLAG):
            if length:
                # ignore the AS4_PATH on new spekers as required by RFC 4893 section 4.1
                if not self.negotiated.asn4:
                    # This replace the old AS_PATH
                    if not self.add_from_cache(code, attribute):
                        self.add(self.__new_ASPath4(attribute), attribute)
            return self.factory(next)

        if code == AID.NEXT_HOP and flag.matches(NextHop.FLAG):
            # XXX: FIXME: we are double caching the NH (once in the class, once here)
            if not self.add_from_cache(code, attribute):
                self.add(cachedNextHop(attribute), attribute)
            return self.factory(next)

        if code == AID.MED and flag.matches(MED.FLAG):
            if not self.add_from_cache(code, attribute):
                self.add(MED(attribute), attribute)
            return self.factory(next)

        if code == AID.LOCAL_PREF and flag.matches(LocalPreference.FLAG):
            if not self.add_from_cache(code, attribute):
                self.add(LocalPreference(attribute), attribute)
            return self.factory(next)

        if code == AID.ATOMIC_AGGREGATE and flag.matches(AtomicAggregate.FLAG):
            if not self.add_from_cache(code, attribute):
                raise Notify(
                    3, 2, 'invalid ATOMIC_AGGREGATE %s' %
                    [hex(ord(_)) for _ in attribute])
            return self.factory(next)

        if code == AID.AGGREGATOR and flag.matches(Aggregator.FLAG):
            # AS4_AGGREGATOR are stored as AGGREGATOR - so do not overwrite if exists
            if not self.has(code):
                if not self.add_from_cache(AID.AGGREGATOR, attribute):
                    self.add(Aggregator(attribute), attribute)
            return self.factory(next)

        if code == AID.AS4_AGGREGATOR and flag.matches(Aggregator.FLAG):
            if not self.add_from_cache(AID.AGGREGATOR, attribute):
                self.add(Aggregator(attribute), attribute)
            return self.factory(next)

        if code == AID.COMMUNITY and flag.matches(Communities.FLAG):
            if not self.add_from_cache(code, attribute):
                self.add(self.__new_communities(attribute), attribute)
            return self.factory(next)

        if code == AID.ORIGINATOR_ID and flag.matches(OriginatorID.FLAG):
            if not self.add_from_cache(code, attribute):
                self.add(OriginatorID(AFI.ipv4, SAFI.unicast, data[:4]),
                         attribute)
            return self.factory(next)

        if code == AID.CLUSTER_LIST and flag.matches(ClusterList.FLAG):
            if not self.add_from_cache(code, attribute):
                self.add(ClusterList(attribute), attribute)
            return self.factory(next)

        if code == AID.EXTENDED_COMMUNITY and flag.matches(ECommunities.FLAG):
            if not self.add_from_cache(code, attribute):
                self.add(self.__new_extended_communities(attribute), attribute)
            return self.factory(next)

        if code == AID.AIGP and flag.matches(AIGP.FLAG):
            if self.negotiated.neighbor.aigp:
                if not self.add_from_cache(code, attribute):
                    self.add(AIGP(attribute), attribute)
            return self.factory(next)

        if code == AID.MP_UNREACH_NLRI and flag.matches(MPURNLRI.FLAG):
            self.hasmp = True

            # -- Reading AFI/SAFI
            data = data[:length]
            afi, safi = unpack('!HB', data[:3])
            offset = 3
            data = data[offset:]

            if (afi, safi) not in self.negotiated.families:
                raise Notify(
                    3, 0,
                    'presented a non-negotiated family %d/%d' % (afi, safi))

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

            # XXX: we do assume that it is an EOR. most likely harmless
            if not data:
                self.mp_withdraw.append(NLRIEOR(afi, safi, IN.announced))
                return self.factory(next)

            while data:
                length, nlri = self.nlriFactory(afi, safi, data, addpath, None,
                                                IN.withdrawn)
                self.mp_withdraw.append(nlri)
                data = data[length:]
                logger.parser(
                    LazyFormat("parsed withdraw mp nlri %s payload " % nlri,
                               od, data[:length]))

            return self.factory(next)

        if code == AID.MP_REACH_NLRI and flag.matches(MPRNLRI.FLAG):
            self.hasmp = True

            data = data[:length]
            # -- Reading AFI/SAFI
            afi, safi = unpack('!HB', data[:3])
            offset = 3

            # we do not want to accept unknown families
            if (afi, safi) not in self.negotiated.families:
                raise Notify(
                    3, 0,
                    'presented a non-negotiated family %d/%d' % (afi, safi))

            # -- Reading length of next-hop
            len_nh = ord(data[offset])
            offset += 1

            rd = 0

            # check next-hope size
            if afi == AFI.ipv4:
                if safi in (SAFI.unicast, SAFI.multicast):
                    if len_nh != 4:
                        raise Notify(
                            3, 0,
                            'invalid ipv4 unicast/multicast next-hop length %d expected 4'
                            % len_nh)
                elif safi in (SAFI.mpls_vpn, ):
                    if len_nh != 12:
                        raise Notify(
                            3, 0,
                            'invalid ipv4 mpls_vpn next-hop length %d expected 12'
                            % len_nh)
                    rd = 8
                elif safi in (SAFI.flow_ip, ):
                    if len_nh not in (0, 4):
                        raise Notify(
                            3, 0,
                            'invalid ipv4 flow_ip next-hop length %d expected 4'
                            % len_nh)
                elif safi in (SAFI.flow_vpn, ):
                    if len_nh not in (0, 4):
                        raise Notify(
                            3, 0,
                            'invalid ipv4 flow_vpn next-hop length %d expected 4'
                            % len_nh)
            elif afi == AFI.ipv6:
                if safi in (SAFI.unicast, ):
                    if len_nh not in (16, 32):
                        raise Notify(
                            3, 0,
                            'invalid ipv6 unicast next-hop length %d expected 16 or 32'
                            % len_nh)
                elif safi in (SAFI.mpls_vpn, ):
                    if len_nh not in (24, 40):
                        raise Notify(
                            3, 0,
                            'invalid ipv6 mpls_vpn next-hop length %d expected 24 or 40'
                            % len_nh)
                    rd = 8
                elif safi in (SAFI.flow_ip, ):
                    if len_nh not in (0, 16, 32):
                        raise Notify(
                            3, 0,
                            'invalid ipv6 flow_ip next-hop length %d expected 0, 16 or 32'
                            % len_nh)
                elif safi in (SAFI.flow_vpn, ):
                    if len_nh not in (0, 16, 32):
                        raise Notify(
                            3, 0,
                            'invalid ipv6 flow_vpn next-hop length %d expected 0, 16 or 32'
                            % len_nh)
            size = len_nh - rd

            # XXX: FIXME: GET IT FROM CACHE HERE ?
            nh = data[offset + rd:offset + rd + size]

            # chech the RD is well zero
            if rd and sum([int(ord(_)) for _ in data[offset:8]]) != 0:
                raise Notify(
                    3, 0,
                    "MP_REACH_NLRI next-hop's route-distinguisher must be zero"
                )

            offset += len_nh

            # Skip a reserved bit as somone had to bug us !
            reserved = ord(data[offset])
            offset += 1

            if reserved != 0:
                raise Notify(3, 0,
                             'the reserved bit of MP_REACH_NLRI is not zero')

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

            # Reading the NLRIs
            data = data[offset:]

            while data:
                length, nlri = self.nlriFactory(afi, safi, data, addpath, nh,
                                                IN.announced)
                self.mp_announce.append(nlri)
                logger.parser(
                    LazyFormat("parsed announce mp nlri %s payload " % nlri,
                               od, data[:length]))
                data = data[length:]
            return self.factory(next)

        if flag & Flag.TRANSITIVE:
            if code in self.known_attributes:
                # XXX: FIXME: we should really close the session
                logger.parser(
                    'ignoring implemented invalid transitive attribute (code 0x%02X, flag 0x%02X)'
                    % (code, flag))
                return self.factory(next)

            if not self.add_from_cache(code, attribute):
                self.add(UnknownAttribute(code, flag, attribute), attribute)
            return self.factory(next)

        logger.parser(
            'ignoring non-transitive attribute (code 0x%02X, flag 0x%02X)' %
            (code, flag))
        return self.factory(next)
Beispiel #11
0
    def unpack_message(cls, data, negotiated):
        logger = Logger()

        length = len(data)

        # This could be speed up massively by changing the order of the IF
        if length == 23:
            return EOR(AFI.ipv4, SAFI.unicast, IN.announced)
        if length == 30 and data.startswith(EOR.NLRI.PREFIX):
            return EOR.unpack_message(data)

        withdrawn, _attributes, announced = cls.split(data)
        attributes = Attributes.unpack(_attributes, negotiated)

        if not withdrawn:
            logger.parser("no withdrawn NLRI")
        if not announced:
            logger.parser("no announced NLRI")

        # Is the peer going to send us some Path Information with the route (AddPath)
        addpath = negotiated.addpath.receive(AFI(AFI.ipv4), SAFI(SAFI.unicast))

        # empty string for NoIP, the packed IP otherwise (without the 3/4 bytes of attributes headers)
        nexthop = attributes.get(Attribute.ID.NEXT_HOP, NoIP).packed

        nlris = []
        while withdrawn:
            length, nlri = NLRI.unpack(AFI.ipv4, SAFI.unicast, withdrawn,
                                       addpath, nexthop, IN.withdrawn)
            logger.parser(
                LazyFormat("parsed withdraw nlri %s payload " % nlri, od,
                           withdrawn[:len(nlri)]))
            withdrawn = withdrawn[length:]
            nlris.append(nlri)

        while announced:
            length, nlri = NLRI.unpack(AFI.ipv4, SAFI.unicast, announced,
                                       addpath, nexthop, IN.announced)
            logger.parser(
                LazyFormat("parsed announce nlri %s payload " % nlri, od,
                           announced[:len(nlri)]))
            announced = announced[length:]
            nlris.append(nlri)

        # required for 'is' comparaison
        UNREACH = [
            EMPTY_MPURNLRI,
        ]
        REACH = [
            EMPTY_MPRNLRI,
        ]

        unreach = attributes.pop(MPURNLRI.ID, UNREACH)
        reach = attributes.pop(MPRNLRI.ID, REACH)

        for mpr in unreach:
            nlris.extend(mpr.nlris)

        for mpr in reach:
            nlris.extend(mpr.nlris)

        if not attributes and not nlris:
            # Careful do not use == or != as the comparaison does not work
            if unreach is UNREACH and reach is REACH:
                return EOR(AFI(AFI.ipv4), SAFI(SAFI.unicast))
            if unreach is not UNREACH:
                return EOR(unreach[0].afi, unreach[0].safi)
            if reach is not REACH:
                return EOR(reach[0].afi, reach[0].safi)
            raise RuntimeError('This was not expected')

        return Update(nlris, attributes)
Beispiel #12
0
	def _main (self,direction):
		"""yield True if we want to come back to it asap, None if nothing urgent, and False if stopped"""

		if self._teardown:
			raise Notify(6,3)

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

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

		send_eor = True
		new_routes = None
		self._resend_routes = SEND.normal
		send_families = []

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

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

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

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

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

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

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

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

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

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

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

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

				# SEND REFRESH
				if self.neighbor.route_refresh:
					if not refresh:
						new_refresh = self.neighbor.refresh.popleft() if self.neighbor.refresh else None
						if new_refresh:
							enhanced_negotiated = True if proto.negotiated.refresh == REFRESH.enhanced else False
							refresh = proto.new_refresh(new_refresh,enhanced_negotiated)

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

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

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

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

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

				# Go to other Peers
				yield ACTION.immediate if new_routes or message.TYPE != NOP.TYPE or self.neighbor.messages else ACTION.later

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

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

		# notify our peer of the shutdown
		raise Notify(6,self._teardown)
Beispiel #13
0
    def _main(self):
        """yield True if we want to come back to it asap, None if nothing urgent, and False if stopped"""
        if self._teardown:
            raise Notify(6, 3)

        self.neighbor.rib.incoming.clear()

        include_withdraw = False

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                if new_routes:
                    count = 1 if self.neighbor.rate_limit > 0 else 25
                    try:
                        for _ in range(count):
                            # This can raise a NetworkError
                            next(new_routes)
                    except StopIteration:
                        new_routes = None
                        include_withdraw = True

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

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

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

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

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

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

        # notify our peer of the shutdown
        raise Notify(6, self._teardown)
Beispiel #14
0
    def _main(self, direction):
        "yield True if we want to come back to it asap, None if nothing urgent, and False if stopped"

        if self._teardown:
            raise Notify(6, 3)

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

        # Announce to the process BGP is up
        self.logger.network('Connected to peer %s (%s)' %
                            (self.neighbor.name(), direction))
        if self.neighbor.api.neighbor_changes:
            try:
                self.reactor.processes.up(self.neighbor.peer_address)
            except ProcessError:
                # Can not find any better error code than 6,0 !
                # XXX: We can not restart the program so this will come back again and again - FIX
                # XXX: In the main loop we do exit on this kind of error
                raise Notify(6, 0, 'ExaBGP Internal error, sorry.')

        send_eor = True
        new_routes = None
        self._resend_routes = True

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                # Go to other Peers
                yield ACTION.immediate if new_routes or message.TYPE != NOP.TYPE else ACTION.later

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

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

        # notify our peer of the shutdown
        raise Notify(6, self._teardown)
Beispiel #15
0
def _FlowNLRIFactory(afi, safi, nexthop, bgp, action):
    logger = Logger()
    logger.parser(LazyFormat("parsing flow nlri payload ", od, bgp))

    total = len(bgp)
    length, bgp = ord(bgp[0]), bgp[1:]

    if length & 0xF0 == 0xF0:  # bigger than 240
        extra, bgp = ord(bgp[0]), bgp[1:]
        length = ((length & 0x0F) << 16) + extra

    if length > len(bgp):
        raise Notify(3, 10, 'invalid length at the start of the the flow')

    bgp = bgp[:length]
    nlri = FlowNLRI(afi, safi)
    nlri.action = action

    if nexthop:
        nlri.nexthop = cachedNextHop(nexthop)

    if safi == SAFI.flow_vpn:
        nlri.rd = RouteDistinguisher(bgp[:8])
        bgp = bgp[8:]

    seen = []

    while bgp:
        what, bgp = ord(bgp[0]), bgp[1:]

        if what not in decode.get(afi, {}):
            raise Notify(
                3, 10,
                'unknown flowspec component received for address family %d' %
                what)

        seen.append(what)
        if sorted(seen) != seen:
            raise Notify(
                3, 10, 'components are not sent in the right order %s' % seen)

        decoder = decode[afi][what]
        klass = factory[afi][what]

        if decoder == 'prefix':
            if afi == AFI.ipv4:
                _, rd, mask, size, prefix, left = _nlrifactory(
                    afi, safi, bgp, action)
                adding = klass(prefix, mask)
                if not nlri.add(adding):
                    raise Notify(
                        3, 10,
                        'components are incompatible (two sources, two destinations, mix ipv4/ipv6) %s'
                        % seen)
                logger.parser(
                    LazyFormat(
                        "added flow %s (%s) payload " % (klass.NAME, adding),
                        od, bgp[:-len(left)]))
                bgp = left
            else:
                byte, bgp = bgp[1], bgp[0] + bgp[2:]
                offset = ord(byte)
                _, rd, mask, size, prefix, left = _nlrifactory(
                    afi, safi, bgp, action)
                adding = klass(prefix, mask, offset)
                if not nlri.add(adding):
                    raise Notify(
                        3, 10,
                        'components are incompatible (two sources, two destinations, mix ipv4/ipv6) %s'
                        % seen)
                logger.parser(
                    LazyFormat(
                        "added flow %s (%s) payload " % (klass.NAME, adding),
                        od, bgp[:-len(left)]))
                bgp = left
        else:
            end = False
            while not end:
                byte, bgp = ord(bgp[0]), bgp[1:]
                end = CommonOperator.eol(byte)
                operator = CommonOperator.operator(byte)
                length = CommonOperator.length(byte)
                value, bgp = bgp[:length], bgp[length:]
                adding = klass.decoder(value)
                nlri.add(klass(operator, adding))
                logger.parser(
                    LazyFormat(
                        "added flow %s (%s) operator %d len %d payload " %
                        (klass.NAME, adding, byte, length), od, value))

    return total - len(bgp), nlri
Beispiel #16
0
def UpdateFactory(negotiated, data):
    logger = Logger()

    length = len(data)

    lw, withdrawn, data = defix(data)

    if len(withdrawn) != lw:
        raise Notify(
            3, 1, 'invalid withdrawn routes length, not enough data available')

    la, attribute, announced = defix(data)

    if len(attribute) != la:
        raise Notify(
            3, 1,
            'invalid total path attribute length, not enough data available')

    if 2 + lw + 2 + la + len(announced) != length:
        raise Notify(
            3, 1,
            'error in BGP message length, not enough data for the size announced'
        )

    attributes = AttributesFactory(NLRIFactory, negotiated, attribute)

    # Is the peer going to send us some Path Information with the route (AddPath)
    addpath = negotiated.addpath.receive(AFI(AFI.ipv4), SAFI(SAFI.unicast))
    nho = attributes.get(AID.NEXT_HOP, None)
    nh = nho.packed if nho else None

    if not withdrawn:
        logger.parser(LazyFormat("parsed no withdraw nlri", od, ''))

    nlris = []
    while withdrawn:
        length, nlri = NLRIFactory(AFI.ipv4, SAFI.unicast_multicast, withdrawn,
                                   addpath, nh, IN.withdrawn)
        logger.parser(
            LazyFormat("parsed withdraw nlri %s payload " % nlri, od,
                       withdrawn[:len(nlri)]))
        withdrawn = withdrawn[length:]
        nlris.append(nlri)

    if not announced:
        logger.parser(LazyFormat("parsed no announced nlri", od, ''))

    while announced:
        length, nlri = NLRIFactory(AFI.ipv4, SAFI.unicast_multicast, announced,
                                   addpath, nh, IN.announced)
        logger.parser(
            LazyFormat("parsed announce nlri %s payload " % nlri, od,
                       announced[:len(nlri)]))
        announced = announced[length:]
        nlris.append(nlri)

    for nlri in attributes.mp_withdraw:
        nlris.append(nlri)

    for nlri in attributes.mp_announce:
        nlris.append(nlri)

    return Update(nlris, attributes)