def ip_label (tokeniser,afi,safi): ipmask = prefix(tokeniser) nlri = Label(afi,safi,OUT.ANNOUNCE) nlri.cidr = CIDR(ipmask.pack(),ipmask.mask) change = Change( nlri, Attributes() ) while True: command = tokeniser() if not command: break action = ParseLabel.action.get(command,'') if action == 'attribute-add': change.attributes.add(ParseLabel.known[command](tokeniser)) elif action == 'nlri-set': change.nlri.assign(ParseLabel.assign[command],ParseLabel.known[command](tokeniser)) elif action == 'nexthop-and-attribute': nexthop,attribute = ParseLabel.known[command](tokeniser) change.nlri.nexthop = nexthop change.attributes.add(attribute) else: raise ValueError('route: unknown command "%s"' % command) return [change]
def ip_multicast(tokeniser, afi, safi): action = OUT.ANNOUNCE if tokeniser.announce else OUT.WITHDRAW ipmask = prefix(tokeniser) nlri = INET(afi, safi, action) nlri.cidr = CIDR(ipmask.pack(), ipmask.mask) change = Change(nlri, Attributes()) while True: command = tokeniser() if not command: break action = AnnounceIP.action.get(command, '') if action == 'attribute-add': change.attributes.add(AnnounceIP.known[command](tokeniser)) elif action == 'nlri-set': change.nlri.assign(AnnounceIP.assign[command], AnnounceIP.known[command](tokeniser)) elif action == 'nexthop-and-attribute': nexthop, attribute = AnnounceIP.known[command](tokeniser) change.nlri.nexthop = nexthop change.attributes.add(attribute) else: raise ValueError('unknown command "%s"' % command) return [change]
def vpls(tokeniser): change = Change(VPLS(None, None, None, None, None), Attributes()) while True: command = tokeniser() if not command: break action = ParseVPLS.action[command] if 'nlri-set' in action: change.nlri.assign(ParseVPLS.assign[command], ParseL2VPN.known[command](tokeniser)) elif 'attribute-add' in action: change.attributes.add(ParseL2VPN.known[command](tokeniser)) elif action == 'nexthop-and-attribute': nexthop, attribute = ParseVPLS.known[command](tokeniser) change.nlri.nexthop = nexthop change.attributes.add(attribute) else: raise ValueError('vpls: unknown command "%s"' % command) return [ change, ]
def route(tokeniser): change = Change(Flow(), Attributes()) while True: command = tokeniser() if not command: break action = ParseFlow.action[command] if action == 'nlri-add': for adding in ParseFlow.known[command](tokeniser): change.nlri.add(adding) elif action == 'attribute-add': change.attributes.add(ParseFlow.known[command](tokeniser)) elif action == 'nexthop-and-attribute': nexthop, attribute = ParseFlow.known[command](tokeniser) change.nlri.nexthop = nexthop change.attributes.add(attribute) elif action == 'nop': pass # yes nothing to do ! else: raise ValueError('flow: unknown command "%s"' % command) if change.nlri.rd is not RouteDistinguisher.NORD: change.nlri.safi = SAFI.flow_vpn return [change]
def ip_vpn(tokeniser, afi, safi): action = OUT.ANNOUNCE if tokeniser.announce else OUT.WITHDRAW ipmask = prefix(tokeniser) nlri = IPVPN(afi, safi, action) nlri.cidr = CIDR(ipmask.pack(), ipmask.mask) change = Change(nlri, Attributes()) while True: command = tokeniser() if not command: break action = AnnounceVPN.action.get(command, '') if action == 'attribute-add': change.attributes.add(AnnounceVPN.known[command](tokeniser)) elif action == 'nlri-set': change.nlri.assign(AnnounceVPN.assign[command], AnnounceVPN.known[command](tokeniser)) elif action == 'nexthop-and-attribute': nexthop, attribute = AnnounceVPN.known[command](tokeniser) change.nlri.nexthop = nexthop change.attributes.add(attribute) else: raise ValueError('unknown command "%s"' % command) if not AnnounceVPN.check(change, afi): raise ValueError( 'invalid announcement (missing next-hop, label or rd ?)') return [change]
def flow(tokeniser, afi, safi): change = Change(Flow(afi, safi, OUT.ANNOUNCE), Attributes()) while True: command = tokeniser() if not command: break action = AnnounceFlow.action[command] if action == 'nlri-add': for adding in AnnounceFlow.known[command](tokeniser): change.nlri.add(adding) elif action == 'attribute-add': change.attributes.add(AnnounceFlow.known[command](tokeniser)) elif action == 'nexthop-and-attribute': nexthop, attribute = AnnounceFlow.known[command](tokeniser) change.nlri.nexthop = nexthop change.attributes.add(attribute) elif action == 'nop': pass # yes nothing to do ! else: raise ValueError('flow: unknown command "%s"' % command) return [change]
def ls_unicast(tokeniser): change = Change(LS_UNICAST(None, None, None, None, None, None, None), Attributes()) while True: command = tokeniser() if not command: break action = ParseLSUNICAST.action[command] if 'nlri-set' in action: change.nlri.assign( ParseLSUNICAST.assign[command], ParseTrafficEngineering.known[command](tokeniser)) elif 'attribute-add' in action: change.attributes.add( ParseTrafficEngineering.known[command](tokeniser)) elif action == 'nexthop-and-attribute': nexthop, attribute = ParseLSUNICAST.known[command](tokeniser) change.nlri.nexthop = nexthop change.attributes.add(attribute) else: raise ValueError('ls_unicast: unknown command "%s"' % command) return change
def inet(tokeniser): ipmask = prefix(tokeniser) inet = INET(afi=IP.toafi(ipmask.top()), safi=IP.tosafi(ipmask.top()), action=OUT.UNSET) inet.cidr = CIDR(ipmask.ton(), ipmask.mask) return Change(inet, Attributes())
def mpls(tokeniser): ipmask = prefix(tokeniser) mpls = IPVPN(afi=IP.toafi(ipmask.top()), safi=IP.tosafi(ipmask.top()), action=OUT.ANNOUNCE) mpls.cidr = CIDR(ipmask.ton(), ipmask.mask) return Change(mpls, Attributes())
def attributes(tokeniser): ipmask = prefix(lambda: tokeniser.tokens[-1]) if 'rd' in tokeniser.tokens or 'route-distinguisher' in tokeniser.tokens: nlri = IPVPN(IP.toafi(ipmask.top()), SAFI.mpls_vpn, OUT.ANNOUNCE) elif 'label' in tokeniser.tokens: nlri = Labelled(IP.toafi(ipmask.top()), SAFI.nlri_mpls, OUT.ANNOUNCE) else: nlri = INET(IP.toafi(ipmask.top()), IP.tosafi(ipmask.top()), OUT.ANNOUNCE) nlri.cidr = CIDR(ipmask.pack(), ipmask.mask) change = Change(nlri, Attributes()) while True: command = tokeniser() if not command: return [] if command == 'nlri': break action = ParseStatic.action[command] if action == 'attribute-add': change.attributes.add(ParseStatic.known[command](tokeniser)) elif action == 'nlri-set': change.nlri.assign(ParseStatic.assign[command], ParseStatic.known[command](tokeniser)) elif action == 'nexthop-and-attribute': nexthop, attribute = ParseStatic.known[command](tokeniser) change.nlri.nexthop = nexthop change.attributes.add(attribute) else: raise ValueError('route: unknown command "%s"' % command) attributes = change.attributes nexthop = change.nlri.nexthop changes = [] while True: nlri = tokeniser.peek() if not nlri: break ipmask = prefix(tokeniser) new = Change( change.nlri.__class__(change.nlri.afi, change.nlri.safi, OUT.UNSET), attributes) new.nlri.cidr = CIDR(ipmask.pack(), ipmask.mask) new.nlri.nexthop = nexthop changes.append(new) return changes
def route (tokeniser): action = OUT.ANNOUNCE if tokeniser.announce else OUT.WITHDRAW ipmask = prefix(tokeniser) check = lambda change,afi: True if 'rd' in tokeniser.tokens or 'route-distinguisher' in tokeniser.tokens: nlri = IPVPN(IP.toafi(ipmask.top()),SAFI.mpls_vpn,action) check = AnnounceVPN.check elif 'label' in tokeniser.tokens: nlri = Label(IP.toafi(ipmask.top()),SAFI.nlri_mpls,action) check = AnnounceLabel.check else: nlri = INET(IP.toafi(ipmask.top()), IP.tosafi(ipmask.top()), action) check = AnnouncePath.check nlri.cidr = CIDR(ipmask.pack(),ipmask.mask) change = Change( nlri, Attributes() ) while True: command = tokeniser() if not command: break if command == 'label': nlri.labels = label(tokeniser) continue if command == 'rd' or command == 'route-distinguisher': nlri.rd = route_distinguisher(tokeniser) continue action = ParseStatic.action.get(command,'') if action == 'attribute-add': change.attributes.add(ParseStatic.known[command](tokeniser)) elif action == 'nlri-set': change.nlri.assign(ParseStatic.assign[command],ParseStatic.known[command](tokeniser)) elif action == 'nexthop-and-attribute': nexthop,attribute = ParseStatic.known[command](tokeniser) change.nlri.nexthop = nexthop change.attributes.add(attribute) else: raise ValueError('unknown command "%s"' % command) if not check(change,nlri.afi): raise ValueError('invalid route (missing next-hop, label or rd ?)') return list(ParseStatic.split(change))
def insert_vpls (self, scope, name, command, tokens=None): try: attributes = Attributes() change = Change(VPLS(None,None,None,None,None),attributes) except ValueError: return self.error.set(self.syntax) if 'announce' not in scope[-1]: scope[-1]['announce'] = [] scope[-1]['announce'].append(change) return True
def route(tokeniser): ipmask = prefix(tokeniser) if 'rd' in tokeniser.tokens or 'route-distinguisher' in tokeniser.tokens: nlri = IPVPN(IP.toafi(ipmask.top()), SAFI.mpls_vpn, OUT.ANNOUNCE) elif 'label' in tokeniser.tokens: nlri = Label(IP.toafi(ipmask.top()), SAFI.nlri_mpls, OUT.ANNOUNCE) else: nlri = INET(IP.toafi(ipmask.top()), IP.tosafi(ipmask.top()), OUT.ANNOUNCE) nlri.cidr = CIDR(ipmask.pack(), ipmask.mask) change = Change(nlri, Attributes()) while True: command = tokeniser() if not command: break if command == 'label': nlri.labels = label(tokeniser) continue if command == 'rd' or command == 'route-distinguisher': nlri.rd = route_distinguisher(tokeniser) continue action = ParseStatic.action.get(command, '') if action == 'attribute-add': change.attributes.add(ParseStatic.known[command](tokeniser)) elif action == 'nlri-set': change.nlri.assign(ParseStatic.assign[command], ParseStatic.known[command](tokeniser)) elif action == 'nexthop-and-attribute': nexthop, attribute = ParseStatic.known[command](tokeniser) change.nlri.nexthop = nexthop change.attributes.add(attribute) else: raise ValueError('route: unknown command "%s"' % command) return list(ParseStatic.split(change))
def insert_static_route(self, scope, name, command, tokens): try: ip = tokens.pop(0) except IndexError: return self.error.set(self.syntax) try: ip, mask = ip.split('/') mask = int(mask) except ValueError: mask = 32 try: if 'rd' in tokens: klass = MPLS elif 'route-distinguisher' in tokens: klass = MPLS elif 'label' in tokens: klass = MPLS else: klass = INET # nexthop must be false and its str return nothing .. an empty string does that update = Change( klass(afi=IP.toafi(ip), safi=IP.tosafi(ip), packed=IP.pton(ip), mask=mask, nexthop=None, action=OUT.ANNOUNCE), Attributes()) except ValueError: return self.error.set(self.syntax) if 'announce' not in scope[-1]: scope[-1]['announce'] = [] scope[-1]['announce'].append(update) return True
def updates (self, grouped): if self._changes: dict_nlri = self._modify_nlri for family in self._seen: for change in six.itervalues(self._seen[family]): 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,dict_change in dict_sorted.items(): if not dict_change: continue updates = {} changed = dict_change.values() attributes = dict_attr[attr_index].attributes for change in dict_change.values(): updates.setdefault(change.nlri.family(),[]).append(change.nlri) nlri_index = change.index() # only yield once we have a consistent state, otherwise it will go wrong # as we will try to modify things we are iterating over and using if grouped: for nlris in six.itervalues(updates): yield Update(nlris, attributes) else: for nlris in six.itervalues(updates): for nlri in nlris: yield Update([nlri,], attributes) 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) self._modify_sorted = {} self._modify_nlri = {} 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
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 attributes (tokeniser): action = OUT.ANNOUNCE if tokeniser.announce else OUT.WITHDRAW ipmask = prefix(lambda: tokeniser.tokens[-1]) tokeniser.afi = ipmask.afi if 'rd' in tokeniser.tokens or 'route-distinguisher' in tokeniser.tokens: nlri = IPVPN(IP.toafi(ipmask.top()), SAFI.mpls_vpn, action) elif 'label' in tokeniser.tokens: nlri = Label(IP.toafi(ipmask.top()), SAFI.nlri_mpls, action) else: nlri = INET(IP.toafi(ipmask.top()), IP.tosafi(ipmask.top()), action) nlri.cidr = CIDR(ipmask.pack(),ipmask.mask) attr = Attributes() labels = None rd = None while True: command = tokeniser() if not command: return [] if command == 'nlri': break if command == 'label': labels = label(tokeniser) continue if command == 'rd' or command == 'route-distinguisher': rd = route_distinguisher(tokeniser) continue action = ParseStatic.action[command] if action == 'attribute-add': attr.add(ParseStatic.known[command](tokeniser)) elif action == 'nlri-set': nlri.assign(ParseStatic.assign[command],ParseStatic.known[command](tokeniser)) elif action == 'nexthop-and-attribute': nexthop,attribute = ParseStatic.known[command](tokeniser) nlri.nexthop = nexthop attr.add(attribute) else: raise ValueError('unknown command "%s"' % command) changes = [] while True: peeked_nlri = tokeniser.peek() if not peeked_nlri: break ipmask = prefix(tokeniser) new = Change( nlri.__class__( nlri.afi, nlri.safi, OUT.UNSET ), attr ) new.nlri.cidr = CIDR(ipmask.pack(),ipmask.mask) if labels: new.nlri.labels = labels if rd: new.nlri.rd = rd new.nlri.nexthop = nlri.nexthop changes.append(new) return changes
def flow(tokeniser): return Change(Flow(), Attributes())
def ls_unicast(tokeniser): return Change(LS_UNICAST(None, None, None, None, None, None, None), 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: updates = [] nlris = [] for change in dict_change.values(): if change.nlri.afi == AFI.ipv4: nlris.append(change.nlri) continue updates.append(Update([change.nlri], attributes)) if nlris: updates.append(Update(nlris, attributes)) nlris = [] 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 for update in updates: 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
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() 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 vpls(tokeniser): return Change(VPLS(None, None, None, None, None), Attributes())
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_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 attributes (tokeniser): ipmask = prefix(lambda: tokeniser.tokens[-1]) if 'rd' in tokeniser.tokens or 'route-distinguisher' in tokeniser.tokens: nlri = IPVPN(IP.toafi(ipmask.top()),SAFI.mpls_vpn,OUT.ANNOUNCE) elif 'label' in tokeniser.tokens: nlri = Label(IP.toafi(ipmask.top()),SAFI.nlri_mpls,OUT.ANNOUNCE) else: nlri = INET(IP.toafi(ipmask.top()),IP.tosafi(ipmask.top()),OUT.ANNOUNCE) nlri.cidr = CIDR(ipmask.pack(),ipmask.mask) attr = Attributes() labels = None rd = None while True: command = tokeniser() if not command: return [] if command == 'nlri': break if command == 'label': labels = label(tokeniser) continue if command == 'rd' or command == 'route-distinguisher': rd = route_distinguisher(tokeniser) continue action = ParseStatic.action[command] if action == 'attribute-add': attr.add(ParseStatic.known[command](tokeniser)) elif action == 'nlri-set': nlri.assign(ParseStatic.assign[command],ParseStatic.known[command](tokeniser)) elif action == 'nexthop-and-attribute': nexthop,attribute = ParseStatic.known[command](tokeniser) nlri.nexthop = nexthop attr.add(attribute) else: raise ValueError('route: unknown command "%s"' % command) changes = [] while True: peeked_nlri = tokeniser.peek() if not peeked_nlri: break ipmask = prefix(tokeniser) new = Change( nlri.__class__( nlri.afi, nlri.safi, OUT.UNSET ), attr ) new.nlri.cidr = CIDR(ipmask.pack(),ipmask.mask) if labels: new.nlri.labels = labels if rd: new.nlri.rd = rd new.nlri.nexthop = nlri.nexthop changes.append(new) return changes
def __init__(self, afi, safi, action=None): Message.__init__(self) 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 == 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)