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)
def attribute(tokeniser): start = tokeniser() if start != '[': raise ValueError('invalid attribute, does not starts with [') code = tokeniser().lower() if not code.startswith('0x'): raise ValueError('invalid attribute, code is not 0x hexadecimal') try: code = int(code[2:], 16) except ValueError: raise ValueError('invalid attribute, code is not 0x hexadecimal') flag = tokeniser().lower() if not flag.startswith('0x'): raise ValueError('invalid attribute, flag is not 0x hexadecimal') try: flag = int(flag[2:], 16) except ValueError: raise ValueError('invalid attribute, flag is not 0x hexadecimal') data = tokeniser().lower() if not data.startswith('0x'): raise ValueError('invalid attribute, data is not 0x hexadecimal') if not len(data) % 2: raise ValueError('invalid attribute, data is not 0x hexadecimal') data = ''.join(chr(int(data[_:_ + 2], 16)) for _ in range(2, len(data), 2)) end = tokeniser() if end != ']': raise ValueError('invalid attribute, does not ends with ]') # XXX: FIXME: class Attribute should have an unpack function which does that from exabgp.bgp.message.update.attribute.generic import GenericAttribute for ((ID, flag), klass) in Attribute.registered_attributes.iteritems(): if code == ID and flag == klass.FLAG: return klass(data) return GenericAttribute(code, flag, data)
'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) def merge_attributes(self): as2path = self[Attribute.CODE.AS_PATH] as4path = self[Attribute.CODE.AS4_PATH] self.remove(Attribute.CODE.AS_PATH)
def parse(self, data, direction, 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(data[0]) aid = Attribute.CODE(data[1]) except IndexError: self.add(TreatAsWithdraw()) return self try: offset = 3 length = data[2] if flag & Attribute.Flag.EXTENDED_LENGTH: offset = 4 length = (length << 8) + data[3] except IndexError: self.add(TreatAsWithdraw(aid)) return self data = data[offset:] left = data[length:] attribute = data[:length] logfunc.debug(lazyattribute(flag, aid, length, data[:length]), 'parser') # 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))) log.debug( 'duplicate attribute %s (flag 0x%02X, aid 0x%02X) skipping' % (Attribute.CODE.names.get(aid, 'unset'), flag, aid), 'parser', ) return self.parse(left, direction, 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, direction, negotiated) try: decoded = Attribute.unpack(aid, flag, attribute, direction, 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, direction, 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: log.debug( 'invalid flag for attribute %s (flag 0x%02X, aid 0x%02X) treat as withdraw' % (Attribute.CODE.names.get(aid, 'unset'), flag, aid), 'parser', ) self.add(TreatAsWithdraw()) if aid in self.DISCARD: log.debug( 'invalid flag for attribute %s (flag 0x%02X, aid 0x%02X) discard' % (Attribute.CODE.names.get(aid, 'unset'), flag, aid), 'parser', ) return self.parse(left, direction, negotiated) # XXX: Check if we are missing any log.debug( 'invalid flag for attribute %s (flag 0x%02X, aid 0x%02X) unspecified (should not happen)' % (Attribute.CODE.names.get(aid, 'unset'), flag, aid), 'parser', ) return self.parse(left, direction, negotiated) # it is an unknown transitive attribute we need to pass on if flag & Attribute.Flag.TRANSITIVE: log.debug('unknown transitive attribute (flag 0x%02X, aid 0x%02X)' % (flag, aid), 'parser') try: decoded = GenericAttribute(aid, flag | Attribute.Flag.PARTIAL, attribute) except IndexError: decoded = TreatAsWithdraw(aid) self.add(decoded, attribute) return self.parse(left, direction, negotiated) # it is an unknown non-transitive attribute we can ignore. log.debug('ignoring unknown non-transitive attribute (flag 0x%02X, aid 0x%02X)' % (flag, aid), 'parser') return self.parse(left, direction, negotiated)