예제 #1
0
	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)
예제 #2
0
    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
예제 #3
0
 def __init__(self, what, afi, safi, data=''):
     Operational.__init__(self, what)
     self.afi = AFI(afi)
     self.safi = SAFI(safi)
     self.data = data
예제 #4
0
 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)
예제 #5
0
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
예제 #6
0
 def __init__(self, afi, safi):
     self.afi = AFI(afi)
     self.safi = SAFI(safi)
예제 #7
0
 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')
예제 #8
0
        # 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), [])
예제 #9
0
	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]))
예제 #10
0
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
예제 #11
0
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
예제 #12
0
파일: logger.py 프로젝트: x11us/exabgp
 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)
예제 #13
0
파일: nlri.py 프로젝트: xw2060/exabgp
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
예제 #14
0
    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)
예제 #15
0
파일: inet.py 프로젝트: Exa-Networks/exabgp
    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
예제 #16
0
파일: nlri.py 프로젝트: cys3c/exabgp
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)))
예제 #17
0
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)
예제 #18
0
 def __init__(self, afi, safi, reserved=0):
     self.afi = AFI(afi)
     self.safi = SAFI(safi)
     self.reserved = Reserved(reserved)
예제 #19
0
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)