def check_nlri(neighbor, routes): option.enabled['parser'] = True announced = _hexa(routes) negotiated = _negotiated(neighbor) afi, safi = neighbor.families()[0] # Is the peer going to send us some Path Information with the route (AddPath) addpath = negotiated.addpath.send(afi, safi) nlris = [] try: while announced: log.debug('parsing NLRI %s' % announced, 'parser') nlri, announced = NLRI.unpack_nlri(afi, safi, announced, IN.ANNOUNCED, addpath) nlris.append(nlri) except Exception as exc: log.error('could not parse the nlri', 'parser') from exabgp.debug import string_exception log.error(string_exception(exc), 'parser') if getenv().debug.pdb: raise return False log.debug('', 'parser') # new line for nlri in nlris: log.info('nlri json %s' % nlri.json(), 'parser') return True
def unpack(cls, data, direction, negotiated): nlris = [] # -- Reading AFI/SAFI afi, safi = unpack('!HB', data[:3]) offset = 3 data = data[offset:] if negotiated and (afi, safi) not in negotiated.families: raise Notify( 3, 0, 'presented a non-negotiated family %s %s' % (AFI.create(afi), SAFI.create(safi))) # Do we need to handle Path Information with the route (AddPath) if direction == Direction.IN: addpath = negotiated.addpath.receive(afi, safi) else: addpath = negotiated.addpath.send(afi, safi) while data: nlri, data = NLRI.unpack_nlri(afi, safi, data, IN.WITHDRAWN, addpath) # allow unpack_nlri to return none for "treat as withdraw" controlled by NLRI.unpack_nlri if nlri: nlris.append(nlri) return cls(afi, safi, nlris)
def unpack (cls, data, negotiated): nlris = [] # -- Reading AFI/SAFI afi,safi = unpack('!HB',data[:3]) offset = 3 data = data[offset:] if negotiated and (afi,safi) not in negotiated.families: raise Notify(3,0,'presented a non-negotiated family %s %s' % (AFI.create(afi),SAFI.create(safi))) # Is the peer going to send us some Path Information with the route (AddPath) addpath = negotiated.addpath.receive(afi,safi) while data: nlri,data = NLRI.unpack_nlri(afi,safi,data,IN.WITHDRAWN,addpath) nlris.append(nlri) return cls(afi,safi,nlris)
def unpack_message(cls, data, negotiated): logger = Logger() logger.debug(LazyFormat('parsing UPDATE', data), 'parser') length = len(data) # This could be speed up massively by changing the order of the IF if length == 4 and data == b'\x00\x00\x00\x00': return EOR(AFI.ipv4, SAFI.unicast) # pylint: disable=E1101 if length == 11 and data.startswith(EOR.NLRI.PREFIX): return EOR.unpack_message(data, negotiated) withdrawn, _attributes, announced = cls.split(data) if not withdrawn: logger.debug('withdrawn NLRI none', 'routes') attributes = Attributes.unpack(_attributes, negotiated) if not announced: logger.debug('announced NLRI none', 'routes') # Is the peer going to send us some Path Information with the route (AddPath) addpath = negotiated.addpath.receive(AFI.ipv4, SAFI.unicast) # empty string for NoNextHop, the packed IP otherwise (without the 3/4 bytes of attributes headers) nexthop = attributes.get(Attribute.CODE.NEXT_HOP, NoNextHop) # nexthop = NextHop.unpack(_nexthop.ton()) # XXX: NEXTHOP MUST NOT be the IP address of the receiving speaker. nlris = [] while withdrawn: nlri, left = NLRI.unpack_nlri(AFI.ipv4, SAFI.unicast, withdrawn, IN.WITHDRAWN, addpath) logger.debug('withdrawn NLRI %s' % nlri, 'routes') withdrawn = left nlris.append(nlri) while announced: nlri, left = NLRI.unpack_nlri(AFI.ipv4, SAFI.unicast, announced, IN.ANNOUNCED, addpath) nlri.nexthop = nexthop logger.debug('announced NLRI %s' % nlri, 'routes') announced = left nlris.append(nlri) unreach = attributes.pop(MPURNLRI.ID, None) reach = attributes.pop(MPRNLRI.ID, None) if unreach is not None: nlris.extend(unreach.nlris) if reach is not None: nlris.extend(reach.nlris) if not attributes and not nlris: # Careful do not use == or != as the comparaison does not work if unreach is None and reach is None: return EOR(AFI.ipv4, SAFI.unicast) if unreach is not None: return EOR(unreach.afi, unreach.safi) if reach is not None: return EOR(reach.afi, reach.safi) raise RuntimeError('This was not expected') update = Update(nlris, attributes) def parsed(_): # we need the import in the function as otherwise we have an cyclic loop # as this function currently uses Update.. from exabgp.reactor.api.response import Response from exabgp.version import json as json_version return 'json %s' % Response.JSON(json_version).update( negotiated.neighbor, 'in', update, None, '', '') logger.debug(LazyFormat('decoded UPDATE', '', parsed), 'parser') return update
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() 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 unpack(cls, data, negotiated): nlris = [] # -- Reading AFI/SAFI afi, safi = unpack('!HB', data[:3]) offset = 3 # we do not want to accept unknown families if negotiated and (afi, safi) not in 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-hop 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 safi in (SAFI.rtc, ): if len_nh not in (4, 16): raise Notify( 3, 0, 'invalid ipv4 rtc 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) elif afi == AFI.l2vpn: if len_nh != 4: Notify(3, 0, 'invalid l2vpn next-hop length %d expected 4' % len_nh) elif afi == AFI.bgpls: if len_nh != 4: Notify(3, 0, 'invalid bgpls next-hop length %d expected 4' % len_nh) size = len_nh - rd # XXX: FIXME: GET IT FROM CACHE HERE ? nhs = data[offset + rd:offset + rd + size] nexthops = [nhs[pos:pos + 16] for pos in range(0, len(nhs), 16)] # 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 = negotiated.addpath.receive(afi, safi) # Reading the NLRIs data = data[offset:] if not data: raise Notify( 3, 0, 'No data to decode in an MPREACHNLRI but it is not an EOR %d/%d' % (afi, safi)) while data: if nexthops: for nexthop in nexthops: nlri, left = NLRI.unpack_nlri(afi, safi, data, IN.ANNOUNCED, addpath) nlri.nexthop = NextHop.unpack(nexthop) nlris.append(nlri) else: nlri, left = NLRI.unpack_nlri(afi, safi, data, IN.ANNOUNCED, addpath) nlris.append(nlri) if left == data: raise RuntimeError("sub-calls should consume data") # logger.parser(LazyFormat("parsed announce mp nlri %s payload " % nlri,data[:length])) data = left return cls(afi, safi, nlris)
def unpack(cls, data, negotiated): nlris = [] # -- Reading AFI/SAFI _afi, _safi = unpack('!HB', data[:3]) afi, safi = AFI.create(_afi), SAFI.create(_safi) offset = 3 # we do not want to accept unknown families if negotiated and (afi, safi) not in negotiated.families: raise Notify( 3, 0, 'presented a non-negotiated family %s/%s' % (afi, safi)) # -- Reading length of next-hop len_nh = ordinal(data[offset]) offset += 1 if (afi, safi) not in Family.size: raise Notify(3, 0, 'unsupported %s %s' % (afi, safi)) length, rd = Family.size[(afi, safi)] if len_nh not in length: raise Notify( 3, 0, 'invalid %s %s next-hop length %d expected %s' % (afi, safi, len_nh, ' or '.join(str(_) for _ in length))) size = len_nh - rd # XXX: FIXME: GET IT FROM CACHE HERE ? nhs = data[offset + rd:offset + rd + size] nexthops = [nhs[pos:pos + 16] for pos in range(0, len(nhs), 16)] # chech the RD is well zero if rd and sum([int(ordinal(_)) 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 = ordinal(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 = negotiated.addpath.receive(afi, safi) # Reading the NLRIs data = data[offset:] if not data: raise Notify( 3, 0, 'No data to decode in an MPREACHNLRI but it is not an EOR %d/%d' % (afi, safi)) while data: if nexthops: for nexthop in nexthops: nlri, left = NLRI.unpack_nlri(afi, safi, data, IN.ANNOUNCED, addpath) # allow unpack_nlri to return none for "treat as withdraw" controlled by NLRI.unpack_nlri if nlri: nlri.nexthop = NextHop.unpack(nexthop) nlris.append(nlri) else: nlri, left = NLRI.unpack_nlri(afi, safi, data, IN.ANNOUNCED, addpath) # allow unpack_nlri to return none for "treat as withdraw" controlled by NLRI.unpack_nlri if nlri: nlris.append(nlri) if left == data: raise RuntimeError("sub-calls should consume data") data = left return cls(afi, safi, nlris)
def unpack_message (cls, data, negotiated): logger = Logger() logger.debug(LazyFormat('parsing UPDATE',data),'parser') length = len(data) # This could be speed up massively by changing the order of the IF if length == 4 and data == b'\x00\x00\x00\x00': return EOR(AFI.ipv4,SAFI.unicast) # pylint: disable=E1101 if length == 11 and data.startswith(EOR.NLRI.PREFIX): return EOR.unpack_message(data,negotiated) withdrawn, _attributes, announced = cls.split(data) if not withdrawn: logger.debug('withdrawn NLRI none','parser') attributes = Attributes.unpack(_attributes,negotiated) if not announced: logger.debug('announced NLRI none','parser') # Is the peer going to send us some Path Information with the route (AddPath) addpath = negotiated.addpath.receive(AFI.ipv4,SAFI.unicast) # empty string for NoNextHop, the packed IP otherwise (without the 3/4 bytes of attributes headers) nexthop = attributes.get(Attribute.CODE.NEXT_HOP,NoNextHop) # nexthop = NextHop.unpack(_nexthop.ton()) # XXX: NEXTHOP MUST NOT be the IP address of the receiving speaker. nlris = [] while withdrawn: nlri,left = NLRI.unpack_nlri(AFI.ipv4,SAFI.unicast,withdrawn,IN.WITHDRAWN,addpath) logger.debug('withdrawn NLRI %s' % nlri,'parser') withdrawn = left nlris.append(nlri) while announced: nlri,left = NLRI.unpack_nlri(AFI.ipv4,SAFI.unicast,announced,IN.ANNOUNCED,addpath) nlri.nexthop = nexthop logger.debug('announced NLRI %s' % nlri,'parser') announced = left nlris.append(nlri) unreach = attributes.pop(MPURNLRI.ID,None) reach = attributes.pop(MPRNLRI.ID,None) if unreach is not None: nlris.extend(unreach.nlris) if reach is not None: nlris.extend(reach.nlris) if not attributes and not nlris: # Careful do not use == or != as the comparaison does not work if unreach is None and reach is None: return EOR(AFI.ipv4,SAFI.unicast) if unreach is not None: return EOR(unreach.afi,unreach.safi) if reach is not None: return EOR(reach.afi,reach.safi) raise RuntimeError('This was not expected') return Update(nlris,attributes)
def unpack (cls, data, negotiated): nlris = [] # -- Reading AFI/SAFI afi,safi = unpack('!HB',data[:3]) offset = 3 # we do not want to accept unknown families if negotiated and (afi,safi) not in 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-hop 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 safi in (SAFI.rtc,): if len_nh not in (4,16): raise Notify(3,0,'invalid ipv4 rtc 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) elif afi == AFI.l2vpn: if len_nh != 4: Notify(3,0,'invalid l2vpn next-hop length %d expected 4' % len_nh) elif afi == AFI.bgpls: if len_nh != 4: Notify(3,0,'invalid bgpls next-hop length %d expected 4' % len_nh) size = len_nh - rd # XXX: FIXME: GET IT FROM CACHE HERE ? nhs = data[offset+rd:offset+rd+size] nexthops = [nhs[pos:pos+16] for pos in range(0,len(nhs),16)] # 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 = negotiated.addpath.receive(afi,safi) # Reading the NLRIs data = data[offset:] if not data: raise Notify(3,0,'No data to decode in an MPREACHNLRI but it is not an EOR %d/%d' % (afi,safi)) while data: if nexthops: for nexthop in nexthops: nlri,left = NLRI.unpack_nlri(afi,safi,data,IN.ANNOUNCED,addpath) nlri.nexthop = NextHop.unpack(nexthop) nlris.append(nlri) else: nlri,left = NLRI.unpack_nlri(afi,safi,data,IN.ANNOUNCED,addpath) nlris.append(nlri) if left == data: raise RuntimeError("sub-calls should consume data") # logger.parser(LazyFormat("parsed announce mp nlri %s payload " % nlri,data[:length])) data = left return cls(afi,safi,nlris)
def unpack (cls, data, negotiated): nlris = [] # -- Reading AFI/SAFI _afi,_safi = unpack('!HB',data[:3]) afi,safi = AFI.create(_afi),SAFI.create(_safi) offset = 3 # we do not want to accept unknown families if negotiated and (afi,safi) not in negotiated.families: raise Notify(3,0,'presented a non-negotiated family %s/%s' % (afi,safi)) # -- Reading length of next-hop len_nh = ordinal(data[offset]) offset += 1 if (afi,safi) not in Family.size: raise Notify(3,0,'unsupported %s %s' % (afi,safi)) length,rd = Family.size[(afi,safi)] if len_nh not in length: raise Notify(3,0,'invalid %s %s next-hop length %d expected %s' % (afi,safi,len_nh,' or '.join(str(_) for _ in length))) size = len_nh - rd # XXX: FIXME: GET IT FROM CACHE HERE ? nhs = data[offset+rd:offset+rd+size] nexthops = [nhs[pos:pos+16] for pos in range(0,len(nhs),16)] # chech the RD is well zero if rd and sum([int(ordinal(_)) 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 = ordinal(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 = negotiated.addpath.receive(afi,safi) # Reading the NLRIs data = data[offset:] if not data: raise Notify(3,0,'No data to decode in an MPREACHNLRI but it is not an EOR %d/%d' % (afi,safi)) while data: if nexthops: for nexthop in nexthops: nlri,left = NLRI.unpack_nlri(afi,safi,data,IN.ANNOUNCED,addpath) nlri.nexthop = NextHop.unpack(nexthop) nlris.append(nlri) else: nlri,left = NLRI.unpack_nlri(afi,safi,data,IN.ANNOUNCED,addpath) nlris.append(nlri) if left == data: raise RuntimeError("sub-calls should consume data") data = left return cls(afi,safi,nlris)