Esempio n. 1
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)
Esempio n. 2
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)
Esempio n. 3
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:
			flag &= Attribute.Flag.MASK_PARTIAL & 0xFF
			# flag &= ~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)
Esempio n. 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.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
Esempio n. 5
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)
Esempio n. 6
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
Esempio n. 7
0
def check_update (neighbor, raw):
	logger = Logger()
	logger._option.parser = True
	logger.parser('\ndecoding routes in configuration')

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

	path = {}
	for f in NLRI.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()
	# capa[Capability.CODE.FOUR_BYTES_ASN] = True

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

	o1 = Open(Version(4),ASN(neighbor.local_as),HoldTime(180),RouterID(routerid_1),capa)
	o2 = Open(Version(4),ASN(neighbor.peer_as),HoldTime(180),RouterID(routerid_2),capa)
	negotiated = Negotiated(neighbor)
	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:
			logger.parser('could not parse the message','error')
			logger.parser(traceback.format_exc(),'error')
			return False
		except StandardError:
			logger.parser('could not parse the message','error')
			logger.parser(traceback.format_exc(),'error')
			return False

		logger.parser('')  # new line
		for number in range(len(update.nlris)):
			change = Change(update.nlris[number],update.attributes)
			logger.parser('decoded %s %s %s' % (decoding,change.nlri.action,change.extensive()))
		logger.parser('update json %s' % Response.JSON(json_version).update(neighbor,'in',update,'',''))

	return True
Esempio n. 8
0
def check_neighbor (neighbors):
	logger = Logger()
	logger._option.parser = True

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

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

		path = {}
		for f in NLRI.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()

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

		o1 = Open(Version(4),ASN(neighbor.local_as),HoldTime(180),RouterID(routerid_1),capa)
		o2 = Open(Version(4),ASN(neighbor.peer_as),HoldTime(180),RouterID(routerid_2),capa)
		negotiated = Negotiated(neighbor)
		negotiated.sent(o1)
		negotiated.received(o2)
		# grouped = False

		for _ 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))

				str1 = str1.replace('attribute [ 0x04 0x80 0x00000064 ]','med 100')
				str1r = str1.lower().replace(' med 100','').replace(' local-preference 100','').replace(' origin igp','')
				str2r = str2.lower().replace(' med 100','').replace(' local-preference 100','').replace(' origin igp','')

				if 'next-hop self' in str1r:
					if ':' in str1r:
						str1r = str1r.replace('next-hop self','next-hop ::1')
					else:
						str1r = str1r.replace('next-hop self','next-hop %s' % neighbor.local_address)

				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()
Esempio n. 9
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
Esempio n. 10
0
	def parse (self, data, negotiated):
		if not data:
			return self

		try:
			# 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]))
		except IndexError:
			self.add(TreatAsWithdraw())
			return self

		try:
			offset = 3
			length = ord(data[2])

			if flag & Attribute.Flag.EXTENDED_LENGTH:
				offset = 4
				length = (length << 8) + ord(data[3])
		except IndexError:
			self.add(TreatAsWithdraw(aid))
			return self

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

		logger = Logger()
		logger.parser(LazyAttribute(flag,aid,length,data[:length]))

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

		if aid in self:
			if aid in self.NO_DUPLICATE:
				raise Notify(3,1,'multiple attribute for %s' % str(Attribute.CODE(attribute.ID)))

			logger.parser('duplicate attribute %s (flag 0x%02X, aid 0x%02X) skipping' % (Attribute.CODE.names.get(aid,'unset'),flag,aid))
			return self.parse(left,negotiated)

		# handle the attribute if we know it
		if Attribute.registered(aid,flag):
			if length == 0 and aid not in self.VALID_ZERO:
				self.add(TreatAsWithdraw(aid))
				return self.parse(left,negotiated)

			try:
				decoded = Attribute.unpack(aid,flag,attribute,negotiated)
			except IndexError, exc:
				if aid in self.TREAT_AS_WITHDRAW:
					decoded = TreatAsWithdraw(aid)
				else:
					raise exc
			except Notify, exc:
				if aid in self.TREAT_AS_WITHDRAW:
					decoded = TreatAsWithdraw()
				elif aid in self.DISCARD:
					decoded = Discard()
				else:
					raise exc
Esempio n. 11
0
    def parse(self, data, negotiated):
        if not data:
            return self

        try:
            # 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]))
        except IndexError:
            self.add(TreatAsWithdraw())
            return self

        try:
            offset = 3
            length = ord(data[2])

            if flag & Attribute.Flag.EXTENDED_LENGTH:
                offset = 4
                length = (length << 8) + ord(data[3])
        except IndexError:
            self.add(TreatAsWithdraw(aid))
            return self

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

        logger = Logger()
        logger.parser(LazyAttribute(flag, aid, length, data[:length]))

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

        if aid in self:
            if aid in self.NO_DUPLICATE:
                raise Notify(
                    3, 1, 'multiple attribute for %s' %
                    str(Attribute.CODE(attribute.ID)))

            logger.parser(
                'duplicate attribute %s (flag 0x%02X, aid 0x%02X) skipping' %
                (Attribute.CODE.names.get(aid, 'unset'), flag, aid))
            return self.parse(left, negotiated)

        # handle the attribute if we know it
        if Attribute.registered(aid, flag):
            if length == 0 and aid not in self.VALID_ZERO:
                self.add(TreatAsWithdraw(aid))
                return self.parse(left, negotiated)

            try:
                decoded = Attribute.unpack(aid, flag, attribute, negotiated)
            except IndexError, exc:
                if aid in self.TREAT_AS_WITHDRAW:
                    decoded = TreatAsWithdraw(aid)
                else:
                    raise exc
            except Notify, exc:
                if aid in self.TREAT_AS_WITHDRAW:
                    decoded = TreatAsWithdraw()
                elif aid in self.DISCARD:
                    decoded = Discard()
                else:
                    raise exc
Esempio n. 12
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)
Esempio n. 13
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
Esempio n. 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
Esempio n. 15
0
def check_update(neighbor, raw):
    logger = Logger()
    logger._option.parser = True
    logger.parser('\ndecoding routes in configuration')

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

    path = {}
    for f in NLRI.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()
    # capa[Capability.CODE.FOUR_BYTES_ASN] = True

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

    o1 = Open(Version(4), ASN(neighbor.local_as), HoldTime(180),
              RouterID(routerid_1), capa)
    o2 = Open(Version(4), ASN(neighbor.peer_as), HoldTime(180),
              RouterID(routerid_2), capa)
    negotiated = Negotiated(neighbor)
    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:
            logger.parser('could not parse the message', 'error')
            logger.parser(traceback.format_exc(), 'error')
            return False
        except StandardError:
            logger.parser('could not parse the message', 'error')
            logger.parser(traceback.format_exc(), 'error')
            return False

        logger.parser('')  # new line
        for number in range(len(update.nlris)):
            change = Change(update.nlris[number], update.attributes)
            logger.parser('decoded %s %s %s' %
                          (decoding, change.nlri.action, change.extensive()))
        logger.parser(
            'update json %s' %
            Response.JSON(json_version).update(neighbor, 'in', update, '', ''))

    return True
Esempio n. 16
0
    def parse(self, data, negotiated):
        if not data:
            return self

        try:
            # We do not care if the attribute are transitive or not as we do not redistribute
            flag = Attribute.Flag(ordinal(data[0]))
            aid = Attribute.CODE(ordinal(data[1]))
        except IndexError:
            self.add(TreatAsWithdraw())
            return self

        try:
            offset = 3
            length = ordinal(data[2])

            if flag & Attribute.Flag.EXTENDED_LENGTH:
                offset = 4
                length = (length << 8) + ordinal(data[3])
        except IndexError:
            self.add(TreatAsWithdraw(aid))
            return self

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

        logger = Logger()
        logger.parser(LazyAttribute(flag, aid, length, data[:length]))

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

        if aid in self:
            if aid in self.NO_DUPLICATE:
                raise Notify(
                    3, 1, 'multiple attribute for %s' %
                    str(Attribute.CODE(attribute.ID)))

            logger.parser(
                'duplicate attribute %s (flag 0x%02X, aid 0x%02X) skipping' %
                (Attribute.CODE.names.get(aid, 'unset'), flag, aid))
            return self.parse(left, negotiated)

        # handle the attribute if we know it
        if Attribute.registered(aid, flag):
            if length == 0 and aid not in self.VALID_ZERO:
                self.add(TreatAsWithdraw(aid))
                return self.parse(left, negotiated)

            try:
                decoded = Attribute.unpack(aid, flag, attribute, negotiated)
            except IndexError as exc:
                if aid in self.TREAT_AS_WITHDRAW:
                    decoded = TreatAsWithdraw(aid)
                else:
                    raise exc
            except Notify as exc:
                if aid in self.TREAT_AS_WITHDRAW:
                    decoded = TreatAsWithdraw()
                elif aid in self.DISCARD:
                    decoded = Discard()
                else:
                    raise exc
            self.add(decoded)
            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.
        if aid in Attribute.attributes_known:
            if aid in self.TREAT_AS_WITHDRAW:
                logger.parser(
                    'invalid flag for attribute %s (flag 0x%02X, aid 0x%02X) treat as withdraw'
                    % (Attribute.CODE.names.get(aid, 'unset'), flag, aid))
                self.add(TreatAsWithdraw())
            if aid in self.DISCARD:
                logger.parser(
                    'invalid flag for attribute %s (flag 0x%02X, aid 0x%02X) discard'
                    % (Attribute.CODE.names.get(aid, 'unset'), flag, aid))
                return self.parse(left, negotiated)
            # XXX: Check if we are missing any
            logger.parser(
                'invalid flag for attribute %s (flag 0x%02X, aid 0x%02X) unspecified (should not happen)'
                % (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))
            try:
                decoded = GenericAttribute(aid, flag | Attribute.Flag.PARTIAL,
                                           attribute)
            except IndexError:
                decoded = TreatAsWithdraw(aid)
            self.add(decoded, 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)
Esempio n. 17
0
def check_neighbor (neighbors):
	logger = Logger()
	logger._option.parser = True

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

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

		path = {}
		for f in NLRI.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()

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

		o1 = Open(Version(4),ASN(neighbor.local_as),HoldTime(180),RouterID(routerid_1),capa)
		o2 = Open(Version(4),ASN(neighbor.peer_as),HoldTime(180),RouterID(routerid_2),capa)
		negotiated = Negotiated(neighbor)
		negotiated.sent(o1)
		negotiated.received(o2)
		# grouped = False

		for _ 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(b'\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))

				str1 = str1.replace('attribute [ 0x04 0x80 0x00000064 ]','med 100')
				str1r = str1.lower().replace(' med 100','').replace(' local-preference 100','').replace(' origin igp','')
				str2r = str2.lower().replace(' med 100','').replace(' local-preference 100','').replace(' origin igp','')

				if 'next-hop self' in str1r:
					if ':' in str1r:
						str1r = str1r.replace('next-hop self','next-hop ::1')
					else:
						str1r = str1r.replace('next-hop self','next-hop %s' % neighbor.local_address)

				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 as exc:
				logger.parser('----------------------------------------')
				logger.parser(str(exc))
				logger.parser('----------------------------------------')
				return False
		neighbor.rib.clear()

	return True
Esempio n. 18
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()
Esempio n. 19
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
Esempio n. 20
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)
Esempio n. 21
0
	def parse (self, data, negotiated):
		if not data:
			return self

		try:
			# 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]))
		except IndexError:
			self.add(TreatAsWithdraw())
			return self

		try:
			offset = 3
			length = ord_(data[2])

			if flag & Attribute.Flag.EXTENDED_LENGTH:
				offset = 4
				length = (length << 8) + ord_(data[3])
		except IndexError:
			self.add(TreatAsWithdraw(aid))
			return self

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

		logger = Logger()
		logger.parser(LazyAttribute(flag,aid,length,data[:length]))

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

		if aid in self:
			if aid in self.NO_DUPLICATE:
				raise Notify(3,1,'multiple attribute for %s' % str(Attribute.CODE(attribute.ID)))

			logger.parser('duplicate attribute %s (flag 0x%02X, aid 0x%02X) skipping' % (Attribute.CODE.names.get(aid,'unset'),flag,aid))
			return self.parse(left,negotiated)

		# handle the attribute if we know it
		if Attribute.registered(aid,flag):
			if length == 0 and aid not in self.VALID_ZERO:
				self.add(TreatAsWithdraw(aid))
				return self.parse(left,negotiated)

			try:
				decoded = Attribute.unpack(aid,flag,attribute,negotiated)
			except IndexError as exc:
				if aid in self.TREAT_AS_WITHDRAW:
					decoded = TreatAsWithdraw(aid)
				else:
					raise exc
			except Notify as exc:
				if aid in self.TREAT_AS_WITHDRAW:
					decoded = TreatAsWithdraw()
				elif aid in self.DISCARD:
					decoded = Discard()
				else:
					raise exc
			self.add(decoded)
			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.
		if aid in Attribute.attributes_known:
			if aid in self.TREAT_AS_WITHDRAW:
				logger.parser('invalid flag for attribute %s (flag 0x%02X, aid 0x%02X) treat as withdraw' % (Attribute.CODE.names.get(aid,'unset'),flag,aid))
				self.add(TreatAsWithdraw())
			if aid in self.DISCARD:
				logger.parser('invalid flag for attribute %s (flag 0x%02X, aid 0x%02X) discard' % (Attribute.CODE.names.get(aid,'unset'),flag,aid))
				return self.parse(left,negotiated)
			# XXX: Check if we are missing any
			logger.parser('invalid flag for attribute %s (flag 0x%02X, aid 0x%02X) unspecified (should not happen)' % (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))
			try:
				decoded = GenericAttribute(aid,flag | Attribute.Flag.PARTIAL,attribute)
			except IndexError:
				decoded = TreatAsWithdraw(aid)
			self.add(decoded,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)
Esempio n. 22
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)
Esempio n. 23
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
Esempio n. 24
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 == 4 and data == '\x00\x00\x00\x00':
			return EOR(AFI.ipv4,SAFI.unicast,IN.ANNOUNCED)  # pylint: disable=E1101
		if length == 11 and data.startswith(EOR.NLRI.PREFIX):
			return EOR.unpack_message(data,negotiated)

		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 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(LazyFormat("parsed withdraw nlri %s payload " % nlri,withdrawn[:len(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(LazyFormat("parsed announce nlri %s payload " % nlri,announced[:len(nlri)]))
			announced = left
			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)
Esempio n. 25
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)
Esempio n. 26
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
Esempio n. 27
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)