def _NLRIFactory (afi,safi,bgp,has_multiple_path,nexthop,action): if has_multiple_path: path_identifier = bgp[:4] bgp = bgp[4:] length = 4 else: path_identifier = '' length = 0 labels,rd,mask,size,prefix,left = _nlrifactory(afi,safi,bgp,action) nlri = NLRI(afi,safi,prefix,mask,cachedNextHop(nexthop),action) if path_identifier: nlri.path_info = PathInfo(packed=path_identifier) if labels: nlri.labels = Labels(labels) if rd: nlri.rd = RouteDistinguisher(rd) return length + len(bgp) - len(left),nlri
def _NLRIFactory(afi, safi, bgp, has_multiple_path, nexthop, action): if has_multiple_path: path_identifier = bgp[:4] bgp = bgp[4:] length = 4 else: path_identifier = '' length = 0 labels, rd, mask, size, prefix, left = _nlrifactory(afi, safi, bgp, action) nlri = NLRI(afi, safi, prefix, mask, cachedNextHop(nexthop), action) if path_identifier: nlri.path_info = PathInfo(packed=path_identifier) if labels: nlri.labels = Labels(labels) if rd: nlri.rd = RouteDistinguisher(rd) return length + len(bgp) - len(left), nlri
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 _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