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 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)
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)
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
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)
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
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
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()
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
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
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
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)
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
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
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
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)
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
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()
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
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)
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)
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)
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
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)
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)
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
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)