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 post(self): local = self.scope.pop_context(self.name) neighbor = Neighbor() # XXX: use the right class for the data type # XXX: we can use the scope.nlri interface ( and rename it ) to set some values neighbor.router_id = local.get('router-id', None) neighbor.peer_address = local.get('peer-address', None) neighbor.local_address = local.get('local-address', None) neighbor.local_as = local.get('local-as', None) neighbor.peer_as = local.get('peer-as', None) neighbor.passive = local.get('passive', False) neighbor.listen = local.get('listen', 0) neighbor.hold_time = local.get('hold-time', HoldTime(180)) neighbor.host_name = local.get('host-name', _hostname()) neighbor.domain_name = local.get('domain-name', _domainname()) neighbor.md5 = local.get('md5', None) neighbor.description = local.get('description', '') neighbor.flush = local.get('auto-flush', True) neighbor.adjribout = local.get('adj-rib-out', True) neighbor.aigp = local.get('aigp', None) neighbor.ttl = local.get('ttl-security', None) neighbor.group_updates = local.get('group-updates', True) neighbor.manual_eor = local.get('manual-eor', False) neighbor.api = ParseAPI.extract() # capabilities capability = local.get('capability', {}) neighbor.add_path = capability.get('add-path', 0) neighbor.asn4 = capability.get('asn4', True) neighbor.multisession = capability.get('multi-session', False) neighbor.operational = capability.get('operational', False) neighbor.route_refresh = capability.get('route-refresh', 0) if capability.get('graceful-restart', False) is not False: neighbor.graceful_restart = capability.get( 'graceful-restart', 0) or int(neighbor.hold_time) families = [] for family in ParseFamily.convert.keys(): for pair in local.get('family', {}).get(family, []): families.append(pair) families = families or NLRI.known_families() if (AFI.ipv4, SAFI.unicast) not in families: families.append((AFI(AFI.ipv4), SAFI(SAFI.unicast))) for family in families: neighbor.add_family(family) neighbor.changes = [] for section in ('static', 'l2vpn', 'flow'): routes = local.get(section, {}).get('routes', []) for route in routes: route.nlri.action = OUT.ANNOUNCE neighbor.changes.extend(routes) messages = local.get('operational', {}).get('routes', []) if not neighbor.router_id: neighbor.router_id = neighbor.local_address if neighbor.route_refresh: if neighbor.adjribout: self.logger.configuration( 'route-refresh requested, enabling adj-rib-out') missing = neighbor.missing() if missing: return self.error.set('incomplete neighbor, missing %s' % missing) if neighbor.local_address.afi != neighbor.peer_address.afi: return self.error.set( 'local-address and peer-address must be of the same family') if neighbor.peer_address.top() in self._neighbors: return self.error.set('duplicate peer definition %s' % neighbor.peer_address.top()) self._neighbors.append(neighbor.peer_address.top()) # check we are not trying to announce routes without the right MP announcement for change in neighbor.changes: family = change.nlri.family() if family not in families and family != (AFI.ipv4, SAFI.unicast): return self.error.set( 'Trying to announce a route of type %s,%s when we are not announcing the family to our peer' % change.nlri.family()) def _init_neighbor(neighbor): families = neighbor.families() for change in neighbor.changes: if change.nlri.family() in families: # This add the family to neighbor.families() neighbor.rib.outgoing.insert_announced_watchdog(change) for message in messages: if message.family() in families: if message.name == 'ASM': neighbor.asm[message.family()] = message else: neighbor.messages.append(message) self.neighbors[neighbor.name()] = neighbor # create one neighbor object per family for multisession if neighbor.multisession and len(neighbor.families()) > 1: for family in neighbor.families(): # XXX: FIXME: Ok, it works but it takes LOTS of memory .. m_neighbor = deepcopy(neighbor) m_neighbor.make_rib() m_neighbor.rib.outgoing.families = [family] _init_neighbor(m_neighbor) else: neighbor.make_rib() _init_neighbor(neighbor) return True
def __init__(self, what, afi, safi, data=''): Operational.__init__(self, what) self.afi = AFI(afi) self.safi = SAFI(safi) self.data = data
def __str__(self): family = '%s %s' % (AFI(self.afi), SAFI(self.safi)) payload = ' payload %s' % od(self.data) if self.data else '' return 'parsing family %-18s%s' % (family, payload)
class ParseFamily(Basic): syntax = \ 'syntax:\n' \ 'family {\n' \ ' all; # default if not family block is present, announce all we know\n' \ ' minimal # use the AFI/SAFI required to announce the routes in the configuration\n' \ ' \n' \ ' ipv4 unicast;\n' \ ' ipv4 multicast;\n' \ ' ipv4 nlri-mpls;\n' \ ' ipv4 mpls-vpn;\n' \ ' ipv4 flow;\n' \ ' ipv4 flow-vpn;\n' \ ' ipv6 unicast;\n' \ ' ipv6 flow;\n' \ ' ipv6 flow-vpn;\n' \ ' l2vpn vpls;\n' \ ' l2vpn evpn;\n' \ '}\n' convert = { 'ipv4': { 'unicast': (AFI(AFI.ipv4), SAFI(SAFI.unicast)), 'multicast': (AFI(AFI.ipv4), SAFI(SAFI.multicast)), 'nlri-mpls': (AFI(AFI.ipv4), SAFI(SAFI.nlri_mpls)), 'mpls-vpn': (AFI(AFI.ipv4), SAFI(SAFI.mpls_vpn)), 'flow': (AFI(AFI.ipv4), SAFI(SAFI.flow_ip)), 'flow-vpn': (AFI(AFI.ipv4), SAFI(SAFI.flow_vpn)), }, 'ipv6': { 'unicast': (AFI(AFI.ipv6), SAFI(SAFI.unicast)), 'mpls-vpn': (AFI(AFI.ipv6), SAFI(SAFI.mpls_vpn)), 'flow': (AFI(AFI.ipv6), SAFI(SAFI.flow_ip)), 'flow-vpn': (AFI(AFI.ipv6), SAFI(SAFI.flow_vpn)), }, 'l2vpn': { 'vpls': (AFI(AFI.l2vpn), SAFI(SAFI.vpls)), 'evpn': (AFI(AFI.l2vpn), SAFI(SAFI.evpn)), } } def __init__(self, error): self.error = error self._family = False def clear(self): self._family = False def _set_family(self, scope, tokens, afi): if self._family: return self.error.set('ipv4 can not be used with all or minimal') try: safi = tokens.pop(0).lower() except IndexError: return self.error.set('missing family safi') pair = self.convert[afi].get(safi, None) if pair: scope[-1]['families'].append(pair) return True return self.error.set('unvalid safi %s for afi %s' % (safi, afi)) def ipv4(self, scope, name, command, tokens): return self._set_family(scope, tokens, 'ipv4') def ipv6(self, scope, name, command, tokens): return self._set_family(scope, tokens, 'ipv6') def l2vpn(self, scope, name, command, tokens): return self._set_family(scope, tokens, 'l2vpn') def minimal(self, scope, name, command, tokens): if scope[-1]['families']: return self.error.set( 'minimal can not be used with any other options') scope[-1]['families'] = 'minimal' self._family = True return True def all(self, scope, name, command, tokens): if scope[-1]['families']: return self.error.set('all can not be used with any other options') scope[-1]['families'] = 'all' self._family = True return True
def __init__(self, afi, safi): self.afi = AFI(afi) self.safi = SAFI(safi)
def factory(self, data): while len(data) >= 4: afi, safi = unpack('!HH', data[4:]) self.families.append(AFI(afi), SAFI(safi)) if data: raise Notify(2, 0, 'trailing data while parsing route-refresh')
# 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) EMPTY_MPRNLRI = MPRNLRI(AFI(AFI.undefined), SAFI(SAFI.undefined), [])
def unpack_message (cls,data,negotiated): header_length = len(EOR.NLRI.PREFIX) return cls(AFI(data[header_length]),SAFI(data[header_length+1:header_length+3]))
def CapabilitiesFactory(data): capabilities = Capabilities() option_len = ord(data[0]) if option_len: data = data[1:] while data: key, value, data = _key_values('parameter', data) # Paramaters must only be sent once. if key == Parameter.AUTHENTIFICATION_INFORMATION: raise Notify(2, 5) if key == Parameter.CAPABILITIES: while value: k, capv, value = _key_values('capability', value) # Multiple Capabilities can be present in a single attribute #if r: # raise Notify(2,0,"Bad length for OPEN %s (size mismatch) %s" % ('capability',hexa(value))) if k == CapabilityID.MULTIPROTOCOL_EXTENSIONS: if k not in capabilities: capabilities[k] = MultiProtocol() afi = AFI(unpack('!H', capv[:2])[0]) safi = SAFI(ord(capv[3])) capabilities[k].append((afi, safi)) continue if k == CapabilityID.GRACEFUL_RESTART: restart = unpack('!H', capv[:2])[0] restart_flag = restart >> 12 restart_time = restart & Graceful.TIME_MASK value_gr = capv[2:] families = [] while value_gr: afi = AFI(unpack('!H', value_gr[:2])[0]) safi = SAFI(ord(value_gr[2])) flag_family = ord(value_gr[0]) families.append((afi, safi, flag_family)) value_gr = value_gr[4:] capabilities[k] = Graceful(restart_flag, restart_time, families) continue if k == CapabilityID.FOUR_BYTES_ASN: capabilities[k] = ASN(unpack('!L', capv[:4])[0]) continue if k == CapabilityID.CISCO_ROUTE_REFRESH: capabilities[k] = RouteRefresh() continue if k == CapabilityID.ROUTE_REFRESH: capabilities[k] = RouteRefresh() continue if k == CapabilityID.ENHANCED_ROUTE_REFRESH: capabilities[k] = EnhancedRouteRefresh() continue if k == CapabilityID.MULTISESSION_BGP: capabilities[k] = MultiSession() continue if k == CapabilityID.MULTISESSION_BGP_RFC: capabilities[k] = MultiSession() continue if k == CapabilityID.ADD_PATH: if k not in capabilities: capabilities[k] = AddPath() value_ad = capv while value_ad: afi = AFI(unpack('!H', value_ad[:2])[0]) safi = SAFI(ord(value_ad[2])) sr = ord(value_ad[3]) capabilities[k].add_path(afi, safi, sr) value_ad = value_ad[4:] if k == CapabilityID.OPERATIONAL: capabilities[k] = Operational() continue if k not in capabilities: capabilities[k] = UnknownCapability( k, [ord(_) for _ in capv]) else: raise Notify(2, 0, 'Unknow OPEN parameter %s' % hex(key)) return capabilities
class NLRI(Family): __slots__ = [] EOR = False registered_nlri = dict() registered_families = [(AFI(AFI.ipv4), SAFI(SAFI.multicast))] logger = None def index(self): return '%s%s%s' % (self.afi, self.safi, self.pack()) # remove this when code restructure is finished def pack(self, addpath=None): raise Exception('unimplemented') def __lt__(self, other): raise RuntimeError('comparing NLRI for ordering does not make sense') def __le__(self, other): raise RuntimeError('comparing NRLI for ordering does not make sense') def __gt__(self, other): raise RuntimeError('comparing NLRI for ordering does not make sense') def __ge__(self, other): raise RuntimeError('comparing NLRI for ordering does not make sense') @classmethod def register(cls, afi, safi): def register_nlri(klass): cls.registered_nlri['%d/%d' % (afi, safi)] = klass new = (AFI(afi), SAFI(safi)) if new in cls.registered_nlri: raise RuntimeError('Tried to register %s/%s twice' % new) cls.registered_families.append(new) return klass return register_nlri @staticmethod def known_families(): # we do not want to take the risk of the caller modifying the list by accident # it can not be a generator return list(NLRI.registered_families) @classmethod def unpack(cls, afi, safi, data, addpath, nexthop, action): if not cls.logger: cls.logger = Logger() cls.logger.parser(LazyNLRI(afi, safi, data)) key = '%d/%d' % (afi, safi) if key in cls.registered_nlri: return cls.registered_nlri[key].unpack(afi, safi, data, addpath, nexthop, action) raise Notify( 3, 0, 'trying to decode unknown family %s/%s' % (AFI(afi), SAFI(safi))) @staticmethod def _nlri(afi, safi, bgp, action, addpath): labels = [] rd = '' if addpath: path_identifier = bgp[:4] bgp = bgp[4:] else: path_identifier = None mask = ord(bgp[0]) bgp = bgp[1:] if SAFI(safi).has_label(): while bgp and mask >= 8: label = int(unpack('!L', chr(0) + bgp[:3])[0]) bgp = bgp[3:] mask -= 24 # 3 bytes # The last 4 bits are the bottom of Stack # The last bit is set for the last label labels.append(label >> 4) # This is a route withdrawal if label == 0x800000 and action == IN.WITHDRAWN: break # This is a next-hop if label == 0x000000: break if label & 1: break if SAFI(safi).has_rd(): mask -= 8 * 8 # the 8 bytes of the route distinguisher rd = bgp[:8] bgp = bgp[8:] if mask < 0: raise Notify(3, 10, 'invalid length in NLRI prefix') if not bgp and mask: raise Notify( 3, 10, 'not enough data for the mask provided to decode the NLRI') size = CIDR.size(mask) if len(bgp) < size: raise Notify( 3, 10, 'could not decode route with AFI %d sand SAFI %d' % (afi, safi)) network, bgp = bgp[:size], bgp[size:] padding = '\0' * (IP.length(afi) - size) prefix = network + padding return labels, rd, path_identifier, mask, size, prefix, bgp
def __str__(self): family = '%s %s' % (AFI(self.afi), SAFI(self.safi)) path = 'with path-information' if self.addpath else 'without path-information' payload = od(self.data) if self.data else 'none' return 'NLRI %-18s %-28s payload %s' % (family, path, payload)
class EVPN(object): registered_evpn = dict() # NEED to be defined in the subclasses CODE = -1 NAME = 'unknown' SHORT_NAME = 'unknown' # lower case to match the class Address API afi = AFI(AFI.l2vpn) safi = SAFI(SAFI.evpn) def __init__(self, packed): self.packed = packed def _prefix(self): return "evpn:%s:" % (self.registered_evpn.get(self.CODE, self).SHORT_NAME.lower()) def __str__(self): return "evpn:%s:%s" % (self.registered_evpn.get( self.CODE, self).SHORT_NAME.lower(), '0x' + ''.join('%02x' % ord(_) for _ in self.packed)) def __repr__(self): return str(self) def pack(self): return pack('!BB', self.CODE, len(self.packed)) + self.packed def __len__(self): return len(self.packed) + 2 # For subtype 2 (MAC/IP advertisement route), # we will have to ignore a part of the route, so this method will be overridden def __cmp__(self, other): if not isinstance(other, EVPN): return -1 if self.CODE != other.CODE: return -1 if self.packed != other.packed: return -1 return 0 def __hash__(self): return hash("%s:%s:%s:%s" % (self.afi, self.safi, self.CODE, self.packed)) @staticmethod def register_evpn(klass): EVPN.registered_evpn[klass.CODE] = klass @classmethod def unpack(cls, data): code = ord(data[0]) length = ord(data[1]) if code in cls.registered_evpn: return cls.registered_evpn[code].unpack(data[length + 1:]) klass = cls(data[length + 1:]) klass.CODE = code return klass
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 unpack_nlri(cls, afi, safi, bgp, action, addpath): nlri = cls(afi, safi, action) if addpath: if len(bgp) <= 4: raise ValueError("Trying to extract path-information but we do not have enough data") nlri.path_info = PathInfo(bgp[:4]) bgp = bgp[4:] mask = bgp[0] bgp = bgp[1:] _, rd_size = Family.size.get((afi, safi), (0, 0)) rd_mask = rd_size * 8 if safi.has_label(): labels = [] while mask - rd_mask >= 24: label = int(unpack('!L', bytes([0]) + bgp[:3])[0]) bgp = bgp[3:] mask -= 24 # 3 bytes # The last 4 bits are the bottom of Stack # The last bit is set for the last label labels.append(label >> 4) # This is a route withdrawal if label == 0x800000 and action == IN.WITHDRAWN: break # This is a next-hop if label == 0x000000: break if label & 1: break nlri.labels = Labels(labels) if rd_size: mask -= rd_mask # the route distinguisher rd = bgp[:rd_size] bgp = bgp[rd_size:] nlri.rd = RouteDistinguisher(rd) if mask < 0: raise Notify(3, 10, 'invalid length in NLRI prefix') if not bgp and mask: raise Notify(3, 10, 'not enough data for the mask provided to decode the NLRI') size = CIDR.size(mask) if len(bgp) < size: raise Notify(3, 10, 'could not decode nlri with family %s (AFI %d) %s (SAFI %d)' % (AFI(afi), int(afi), SAFI(safi), int(safi))) network, bgp = bgp[:size], bgp[size:] nlri.cidr = CIDR(network + bytes(IP.length(afi) - size), mask) return nlri, bgp
class NLRI (Family): __slots__ = ['action'] EOR = False registered_nlri = dict() registered_families = [(AFI(AFI.ipv4), SAFI(SAFI.multicast))] logger = None def __init__ (self, afi, safi, action=OUT.UNSET): Family.__init__(self,afi,safi) self.action = action def assign (self, name, value): setattr(self,name,value) def _index (self): return '%s%s' % (self.afi,self.safi) def index (self): raise NotImplementedError('NLRI index not implemented') # remove this when code restructure is finished def pack (self, negotiated=None): raise RuntimeError('deprecated API') def pack_nlri (self, negotiated=None): raise Exception('unimplemented in NLRI children class') def __eq__ (self,other): return self.index() == other.index() def __ne__ (self,other): return not self.__eq__(other) def __lt__ (self, other): raise RuntimeError('comparing NLRI for ordering does not make sense') def __le__ (self, other): raise RuntimeError('comparing NRLI for ordering does not make sense') def __gt__ (self, other): raise RuntimeError('comparing NLRI for ordering does not make sense') def __ge__ (self, other): raise RuntimeError('comparing NLRI for ordering does not make sense') @classmethod def has_label (cls): return False @classmethod def has_rd (cls): return False @classmethod def register (cls, afi, safi, force=False): def register_nlri (klass): new = (AFI(afi),SAFI(safi)) if new in cls.registered_nlri: if force: # python has a bug and does not allow %ld/%ld (pypy does) cls.registered_nlri['%s/%s' % new] = klass else: raise RuntimeError('Tried to register %s/%s twice' % new) else: # python has a bug and does not allow %ld/%ld (pypy does) cls.registered_nlri['%s/%s' % new] = klass cls.registered_families.append(new) return klass return register_nlri @staticmethod def known_families (): # we do not want to take the risk of the caller modifying the list by accident # it can not be a generator return list(NLRI.registered_families) @classmethod def unpack_nlri (cls, afi, safi, data, action, addpath): if not cls.logger: cls.logger = Logger() cls.logger.parser(LazyNLRI(afi,safi,data)) key = '%s/%s' % (AFI(afi),SAFI(safi)) if key in cls.registered_nlri: return cls.registered_nlri[key].unpack_nlri(afi,safi,data,action,addpath) raise Notify(3,0,'trying to decode unknown family %s/%s' % (AFI(afi),SAFI(safi)))
class ParseFamily(Section): syntax = \ 'family {\n' \ ' all; # default if not family block is present, announce all we know\n' \ ' \n' \ ' ipv4 unicast;\n' \ ' ipv4 multicast;\n' \ ' ipv4 nlri-mpls;\n' \ ' ipv4 mpls-vpn;\n' \ ' ipv4 flow;\n' \ ' ipv4 flow-vpn;\n' \ ' ipv6 unicast;\n' \ ' ipv6 flow;\n' \ ' ipv6 flow-vpn;\n' \ ' l2vpn vpls;\n' \ ' l2vpn evpn;\n' \ '}' convert = { 'ipv4': { 'unicast': (AFI(AFI.ipv4), SAFI(SAFI.unicast)), 'multicast': (AFI(AFI.ipv4), SAFI(SAFI.multicast)), 'nlri-mpls': (AFI(AFI.ipv4), SAFI(SAFI.nlri_mpls)), 'mpls-vpn': (AFI(AFI.ipv4), SAFI(SAFI.mpls_vpn)), 'flow': (AFI(AFI.ipv4), SAFI(SAFI.flow_ip)), 'flow-vpn': (AFI(AFI.ipv4), SAFI(SAFI.flow_vpn)), }, 'ipv6': { 'unicast': (AFI(AFI.ipv6), SAFI(SAFI.unicast)), 'nlri-mpls': (AFI(AFI.ipv6), SAFI(SAFI.nlri_mpls)), 'mpls-vpn': (AFI(AFI.ipv6), SAFI(SAFI.mpls_vpn)), 'flow': (AFI(AFI.ipv6), SAFI(SAFI.flow_ip)), 'flow-vpn': (AFI(AFI.ipv6), SAFI(SAFI.flow_vpn)), }, 'l2vpn': { 'vpls': (AFI(AFI.l2vpn), SAFI(SAFI.vpls)), 'evpn': (AFI(AFI.l2vpn), SAFI(SAFI.evpn)), }, 'bgpls': { 'bgp-ls': (AFI(AFI.bgpls), SAFI(SAFI.bgp_ls)), 'bgp-ls-vpn': (AFI(AFI.bgpls), SAFI(SAFI.bgp_ls_vpn)), }, } action = { 'ipv4': 'append-command', 'ipv6': 'append-command', 'l2vpn': 'append-command', 'bgpls': 'append-command', } name = 'family' def __init__(self, tokeniser, scope, error, logger): Section.__init__(self, tokeniser, scope, error, logger) self.known = { 'ipv4': self.ipv4, 'ipv6': self.ipv6, 'l2vpn': self.l2vpn, 'bgpls': self.bgpls, } self._all = '' self._seen = [] def clear(self): self._all = False self._seen = [] def pre(self): self.scope.to_context() self.clear() return True def post(self): return True def _family(self, tokeniser, afi): if self._all: raise ValueError('can not add any family once family all is set') safi = tokeniser().lower() pair = self.convert[afi].get(safi, None) if not pair: raise ValueError('invalid afi/safi pair %s/%s' % (afi, safi)) if pair in self._seen: raise ValueError('duplicate afi/safi pair %s/%s' % (afi, safi)) self._seen.append(pair) return pair def ipv4(self, tokeniser): return self._family(tokeniser, 'ipv4') def ipv6(self, tokeniser): return self._family(tokeniser, 'ipv6') def l2vpn(self, tokeniser): return self._family(tokeniser, 'l2vpn') def bgpls(self, tokeniser): return self._family(tokeniser, 'bgpls') def minimal(self, tokeniser): raise ValueError('family minimal is deprecated') def all(self, tokeniser): if self._all or self._seen: return self.error.set('all can not be used with any other options') self._all = True for pair in NLRI.known_families(): self._seen.append(pair)
def __init__(self, afi, safi, reserved=0): self.afi = AFI(afi) self.safi = SAFI(safi) self.reserved = Reserved(reserved)
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)