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 NoIP, the packed IP otherwise (without the 3/4 bytes of attributes headers) _nexthop = attributes.get(Attribute.CODE.NEXT_HOP,NoIP) nexthop = _nexthop.packed # XXX: NEXTHOP MUST NOT be the IP address of the receiving speaker. 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,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,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 __init__(self, afi, safi, action=None): self.nlris = [ EOR.NLRI(afi, safi, action), ] self.attributes = 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 == 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 NoIP, the packed IP otherwise (without the 3/4 bytes of attributes headers) _nexthop = attributes.get(Attribute.CODE.NEXT_HOP, NoIP) nexthop = _nexthop.packed # XXX: NEXTHOP MUST NOT be the IP address of the receiving speaker. 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, 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, 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 updates(self, grouped): if self._changes: dict_nlri = self._modify_nlri for family in self._seen: for change in self._seen[family].itervalues(): if change.index() not in self._modify_nlri: change.nlri.action = OUT.WITHDRAW self.insert_announced(change, True) for new in self._changes: self.insert_announced(new, True) self._changes = None # end of changes rr_announced = [] for afi, safi in self._enhanced_refresh_start: rr_announced.append((afi, safi)) yield Update(RouteRefresh(afi, safi, RouteRefresh.start), Attributes()) dict_sorted = self._modify_sorted dict_nlri = self._modify_nlri dict_attr = self._cache_attribute for attr_index, full_dict_change in dict_sorted.items(): if self.cache: dict_change = {} for nlri_index, change in full_dict_change.iteritems(): family = change.nlri.family() announced = self._seen.get(family, {}) if change.nlri.action == OUT.ANNOUNCE: if nlri_index in announced: old_change = announced[nlri_index] # it is a duplicate route if old_change.attributes.index( ) == change.attributes.index( ) and old_change.nlri.nexthop.index( ) == change.nlri.nexthop.index(): continue elif change.nlri.action == OUT.WITHDRAW: if nlri_index not in announced: if dict_nlri[ nlri_index].nlri.action == OUT.ANNOUNCE: continue dict_change[nlri_index] = change else: dict_change = full_dict_change if not dict_change: continue attributes = dict_attr[attr_index].attributes # we NEED the copy provided by list() here as insert_announced can be called while we iterate changed = list(dict_change.itervalues()) if grouped: update = Update( [dict_nlri[nlri_index].nlri for nlri_index in dict_change], attributes) for change in changed: nlri_index = change.index() del dict_sorted[attr_index][nlri_index] del dict_nlri[nlri_index] # only yield once we have a consistent state, otherwise it will go wrong # as we will try to modify things we are using yield update else: updates = [] for change in changed: updates.append(Update([ change.nlri, ], attributes)) nlri_index = change.index() del dict_sorted[attr_index][nlri_index] del dict_nlri[nlri_index] # only yield once we have a consistent state, otherwise it will go wrong # as we will try to modify things we are using for update in updates: yield update if self.cache: announced = self._seen for change in changed: if change.nlri.action == OUT.ANNOUNCE: announced.setdefault(change.nlri.family(), {})[change.index()] = change else: family = change.nlri.family() if family in announced: announced[family].pop(change.index(), None) if rr_announced: for afi, safi in rr_announced: self._enhanced_refresh_start.remove((afi, safi)) yield Update(RouteRefresh(afi, safi, RouteRefresh.end), Attributes()) for change in self._enhanced_refresh_delay: self.insert_announced(change, True) self.enhanced_refresh_delay = [] for update in self.updates(grouped): yield update