Exemple #1
0
    def setUp(self):
        self.negotiated = {}

        for asn4 in (True, False):
            neighbor = FakeNeighbor()
            neighbor.asn4 = asn4

            capa = Capabilities().new(neighbor, False)
            capa[Capability.CODE.MULTIPROTOCOL] = neighbor.families()

            # path = {}
            # for f in NLRI.known_families():
            # 	if neighbor.add_path:
            # 		path[f] = neighbor.add_path
            # capa[Capability.CODE.ADD_PATH] = path

            o1 = Open(Version(4), ASN(neighbor.local_as), HoldTime(180),
                      RouterID(neighbor.local_address.top()), capa)
            o2 = Open(Version(4), ASN(neighbor.peer_as), HoldTime(180),
                      RouterID(neighbor.peer_address.top()), capa)

            negotiated = Negotiated(neighbor)
            negotiated.sent(o1)
            negotiated.received(o2)

            self.negotiated[asn4] = negotiated
Exemple #2
0
    def setUp(self):
        self.negotiated = {}

        for asn4 in (True, False):
            neighbor = FakeNeighbor()
            neighbor.asn4 = asn4

            capa = Capabilities().new(neighbor, False)
            capa[Capability.CODE.MULTIPROTOCOL] = neighbor.families()

            # path = {}
            # for f in NLRI.known_families():
            # 	if neighbor.add_path:
            # 		path[f] = neighbor.add_path
            # capa[Capability.CODE.ADD_PATH] = path

            routerid_1 = str(neighbor.router_id)
            routerid_2 = '.'.join(
                str((int(_) + 1) % 250)
                for _ in str(neighbor.router_id).split('.', -1))

            o1 = Open(Version(4), ASN(neighbor.local_as), HoldTime(180),
                      RouterID(routerid_1), capa)
            o2 = Open(Version(4), ASN(neighbor.peer_as), HoldTime(180),
                      RouterID(routerid_2), capa)

            negotiated = Negotiated(neighbor)
            negotiated.sent(o1)
            negotiated.received(o2)

            self.negotiated[asn4] = negotiated
Exemple #3
0
 def __init__(self):
     self.update({
         'description': 'a test neighbor',
         'router-id': RouterID('127.0.0.1'),
         'local-address': IPv4('127.0.0.1'),
         'peer-address': IPv4('127.0.0.1'),
         'host-name': 'localhost',
         'domain-name': 'localdomain',
         'peer-as': ASN('65500'),
         'local-as': ASN('65500'),
         'hold-time': HoldTime(180),
         'capability': {
             'asn4': False,
             'add-path': 0,
             'extended_message': False,
             'nexthop': None,
             'route-refresh': False,
             'graceful-restart': False,
             'multi-session': None,
             'add-path': None,
             'aigp': None,
             'operational': None,
             'extended-message': True,
         },
     })
Exemple #4
0
class Open (Message):
	ID = Message.CODE.OPEN
	TYPE = chr(Message.CODE.OPEN)

	def __init__ (self, version, asn, router_id, capabilities, hold_time):
		self.version = Version(version)
		self.asn = ASN(asn)
		self.hold_time = HoldTime(hold_time)
		self.router_id = RouterID(router_id)
		self.capabilities = capabilities

	def message (self):
		return self._message("%s%s%s%s%s" % (
			self.version.pack(),
			self.asn.trans(),
			self.hold_time.pack(),
			self.router_id.pack(),
			self.capabilities.pack()
		))

	def __str__ (self):
		return "OPEN version=%d asn=%d hold_time=%s router_id=%s capabilities=[%s]" % (self.version, self.asn, self.hold_time, self.router_id,self.capabilities)

	@classmethod
	def unpack_message (cls, data, _):
		version = ord(data[0])
		if version != 4:
			# Only version 4 is supported nowdays..
			raise Notify(2,1,data[0])
		asn = unpack('!H',data[1:3])[0]
		hold_time = unpack('!H',data[3:5])[0]
		numeric = unpack('!L',data[5:9])[0]
		router_id = "%d.%d.%d.%d" % (numeric >> 24,(numeric >> 16) & 0xFF,(numeric >> 8) & 0xFF,numeric & 0xFF)
		capabilities = Capabilities.unpack(data[9:])
		return cls(version,asn,router_id,capabilities,hold_time)
Exemple #5
0
class FakeNeighbor(object):
    description = 'a test neighbor'
    router_id = RouterID('127.0.0.1')
    local_address = IPv4('127.0.0.1')
    peer_address = IPv4('127.0.0.1')
    host_name = 'localhost'
    domain_name = 'localdomain'
    peer_as = ASN('65500')
    local_as = ASN('65500')
    hold_time = HoldTime(180)
    asn4 = False
    add_path = 0
    extended_message = False
    nexthop = None

    # capability
    route_refresh = False
    graceful_restart = False
    multisession = None
    add_path = None
    aigp = None
    operational = None

    @staticmethod
    def families():
        return NLRI.known_families()
Exemple #6
0
    def __init__(self):
        # self.logger should not be used here as long as we do use deepcopy as it contains a Lock
        self.description = ''
        self.router_id = None
        self.local_address = None
        self.peer_address = None
        self.peer_as = None
        self.local_as = None
        self.hold_time = HoldTime(180)
        self.asn4 = None
        self.add_path = 0
        self.md5 = None
        self.ttl = None
        self.group_updates = None

        self.api = APIOptions()

        self.passive = False

        # capability
        self.route_refresh = False
        self.graceful_restart = False
        self.multisession = None
        self.add_path = None

        self._families = []
        self.rib = None

        self.operational = None
        self.asm = dict()
        self.messages = deque()
Exemple #7
0
class Open(Message):
    ID = Message.CODE.OPEN
    TYPE = chr(Message.CODE.OPEN)

    def __init__(self, version, asn, router_id, capabilities, hold_time):
        self.version = Version(version)
        self.asn = ASN(asn)
        self.hold_time = HoldTime(hold_time)
        self.router_id = RouterID(router_id)
        self.capabilities = capabilities

    def message(self, negotiated=None):
        return self._message(
            "%s%s%s%s%s" %
            (self.version.pack(), self.asn.trans(), self.hold_time.pack(),
             self.router_id.pack(), self.capabilities.pack()))

    def __str__(self):
        return "OPEN version=%d asn=%d hold_time=%s router_id=%s capabilities=[%s]" % (
            self.version, self.asn, self.hold_time, self.router_id,
            self.capabilities)

    @classmethod
    def unpack_message(cls, data, _=None):
        version = ord(data[0])
        if version != 4:
            # Only version 4 is supported nowdays..
            raise Notify(2, 1, data[0])
        asn = unpack('!H', data[1:3])[0]
        hold_time = unpack('!H', data[3:5])[0]
        numeric = unpack('!L', data[5:9])[0]
        router_id = "%d.%d.%d.%d" % (numeric >> 24, (numeric >> 16) & 0xFF,
                                     (numeric >> 8) & 0xFF, numeric & 0xFF)
        capabilities = Capabilities.unpack(data[9:])
        return cls(version, asn, router_id, capabilities, hold_time)
Exemple #8
0
    def _negociate(self):
        sent_capa = self.sent_open.capabilities
        recv_capa = self.received_open.capabilities

        self.holdtime = HoldTime(
            min(self.sent_open.hold_time, self.received_open.hold_time))

        self.addpath.setup(self.sent_open, self.received_open)
        self.asn4 = sent_capa.announced(
            CID.FOUR_BYTES_ASN) and recv_capa.announced(CID.FOUR_BYTES_ASN)
        self.operational = sent_capa.announced(
            CID.OPERATIONAL) and recv_capa.announced(CID.OPERATIONAL)

        self.local_as = self.sent_open.asn
        self.peer_as = self.received_open.asn
        if self.received_open.asn == AS_TRANS:
            self.peer_as = recv_capa[CID.FOUR_BYTES_ASN]

        self.families = []
        if recv_capa.announced(CID.MULTIPROTOCOL_EXTENSIONS) \
        and sent_capa.announced(CID.MULTIPROTOCOL_EXTENSIONS):
            for family in recv_capa[CID.MULTIPROTOCOL_EXTENSIONS]:
                if family in sent_capa[CID.MULTIPROTOCOL_EXTENSIONS]:
                    self.families.append(family)

        self.multisession = sent_capa.announced(
            CID.MULTISESSION_BGP) and recv_capa.announced(CID.MULTISESSION_BGP)

        if self.multisession:
            sent_ms_capa = set(sent_capa[CID.MULTISESSION_BGP])
            recv_ms_capa = set(recv_capa[CID.MULTISESSION_BGP])

            if sent_ms_capa == set([]):
                sent_ms_capa = set([CID.MULTIPROTOCOL_EXTENSIONS])
            if recv_ms_capa == set([]):
                recv_ms_capa = set([CID.MULTIPROTOCOL_EXTENSIONS])

            if sent_ms_capa != recv_ms_capa:
                self.multisession = (
                    2, 8,
                    'multisession, our peer did not reply with the same sessionid'
                )

            # The way we implement MS-BGP, we only send one MP per session
            # therefore we can not collide due to the way we generate the configuration

            for capa in sent_ms_capa:
                # no need to check that the capability exists, we generated it
                # checked it is what we sent and only send MULTIPROTOCOL_EXTENSIONS
                if sent_capa[capa] != recv_capa[capa]:
                    self.multisession = (
                        2, 8,
                        'when checking session id, capability %s did not match'
                        % str(capa))
                    break

        elif sent_capa.announced(CID.MULTISESSION_BGP):
            self.multisession = (2, 9,
                                 'multisession is mandatory with this peer')
Exemple #9
0
	def _negotiate (self):
		sent_capa = self.sent_open.capabilities
		recv_capa = self.received_open.capabilities

		self.holdtime = HoldTime(min(self.sent_open.hold_time,self.received_open.hold_time))

		self.addpath.setup(self.sent_open,self.received_open)
		self.asn4 = sent_capa.announced(Capability.CODE.FOUR_BYTES_ASN) and recv_capa.announced(Capability.CODE.FOUR_BYTES_ASN)
		self.operational = sent_capa.announced(Capability.CODE.OPERATIONAL) and recv_capa.announced(Capability.CODE.OPERATIONAL)

		self.local_as = self.sent_open.asn
		self.peer_as = self.received_open.asn
		if self.received_open.asn == AS_TRANS and self.asn4:
			self.peer_as = recv_capa.get(Capability.CODE.FOUR_BYTES_ASN,self.peer_as)

		self.families = []
		if \
			recv_capa.announced(Capability.CODE.MULTIPROTOCOL) and \
			sent_capa.announced(Capability.CODE.MULTIPROTOCOL):
			for family in recv_capa[Capability.CODE.MULTIPROTOCOL]:
				if family in sent_capa[Capability.CODE.MULTIPROTOCOL]:
					self.families.append(family)

		if recv_capa.announced(Capability.CODE.ENHANCED_ROUTE_REFRESH) and sent_capa.announced(Capability.CODE.ENHANCED_ROUTE_REFRESH):
			self.refresh = REFRESH.ENHANCED  # pylint: disable=E1101
		elif recv_capa.announced(Capability.CODE.ROUTE_REFRESH) and sent_capa.announced(Capability.CODE.ROUTE_REFRESH):
			self.refresh = REFRESH.NORMAL  # pylint: disable=E1101

		if recv_capa.announced(Capability.CODE.EXTENDED_MESSAGE) and sent_capa.announced(Capability.CODE.EXTENDED_MESSAGE):
			self.msg_size = ExtendedMessage.EXTENDED_SIZE

		self.multisession  = sent_capa.announced(Capability.CODE.MULTISESSION) and recv_capa.announced(Capability.CODE.MULTISESSION)
		self.multisession |= sent_capa.announced(Capability.CODE.MULTISESSION_CISCO) and recv_capa.announced(Capability.CODE.MULTISESSION_CISCO)

		if self.multisession:
			sent_ms_capa = set(sent_capa[Capability.CODE.MULTISESSION])
			recv_ms_capa = set(recv_capa[Capability.CODE.MULTISESSION])

			if sent_ms_capa == set([]):
				sent_ms_capa = set([Capability.CODE.MULTIPROTOCOL])
			if recv_ms_capa == set([]):
				recv_ms_capa = set([Capability.CODE.MULTIPROTOCOL])

			if sent_ms_capa != recv_ms_capa:
				self.multisession = (2,8,'multisession, our peer did not reply with the same sessionid')

			# The way we implement MS-BGP, we only send one MP per session
			# therefore we can not collide due to the way we generate the configuration

			for capa in sent_ms_capa:
				# no need to check that the capability exists, we generated it
				# checked it is what we sent and only send MULTIPROTOCOL
				if sent_capa[capa] != recv_capa[capa]:
					self.multisession = (2,8,'when checking session id, capability %s did not match' % str(capa))
					break

		elif sent_capa.announced(Capability.CODE.MULTISESSION):
			self.multisession = (2,9,'multisession is mandatory with this peer')
Exemple #10
0
 def unpack_message(cls, data, _=None):
     version = data[0]
     if version != 4:
         # Only version 4 is supported nowdays..
         raise Notify(2, 1, bytes(data[0], 'ascii'))
     asn = unpack('!H', data[1:3])[0]
     hold_time = unpack('!H', data[3:5])[0]
     numeric = unpack('!L', data[5:9])[0]
     router_id = "%d.%d.%d.%d" % (numeric >> 24, (numeric >> 16) & 0xFF, (numeric >> 8) & 0xFF, numeric & 0xFF)
     return cls(Version(version), ASN(asn), HoldTime(hold_time), RouterID(router_id), Capabilities.unpack(data[9:]))
Exemple #11
0
    def __init__ (self):
        # self.logger should not be used here as long as we do use deepcopy as it contains a Lock
        self.description = ''
        self.router_id = None
        self.local_address = None
        self.peer_address = None
        self.peer_as = None
        self.local_as = None
        self.hold_time = HoldTime(180)
        self.asn4 = None
        self.add_path = 0
        self.md5 = None
        self.ttl = None
        self.group_updates = None
        self.flush = None
        self.adjribout = None

        self.manual_eor = False

        self.api = APIOptions()

        # passive indicate that we do not establish outgoing connections
        self.passive = False
        # the port to listen on ( zero mean that we do not listen )
        self.listen = 0

        # capability
        self.route_refresh = False
        self.graceful_restart = False
        self.multisession = None
        self.add_path = None
        self.aigp = None

        self._families = []
        self.rib = None

        # The routes we have parsed from the configuration
        self.changes = []
        # On signal update, the previous routes so we can compare what changed
        self.backup_changes = []

        self.operational = None
        self.eor = deque()
        self.asm = dict()

        self.messages = deque()
        self.refresh = deque()

        self.counter = Counter()

        self.ski = []
        self.bgpsec_openssl_lib = []
        self.bgpsec_crypto_init = []
        self.bgpsec_pre_asns = []
        self.bgpsec_pre_skis = []
Exemple #12
0
def hold_time (tokeniser):
	value = tokeniser()
	try:
		holdtime = HoldTime(int(value))
	except ValueError:
		raise ValueError ('"%s" is an invalid hold-time' % value)
	if holdtime < 3 and holdtime != 0:
		raise ValueError('holdtime must be zero or at least three seconds')
	if holdtime > HoldTime.MAX:
		raise ValueError('holdtime must be smaller or equal to %d' % HoldTime.MAX)
	return holdtime
Exemple #13
0
	def __init__ (self):
		# self.logger should not be used here as long as we do use deepcopy as it contains a Lock
		self.description = ''
		self.router_id = None
		self.host_name = 'localhost'.encode('utf-8')
		self.domain_name = 'localdomain'.encode('utf-8')
		self.local_address = None
		self.peer_address = None
		self.peer_as = None
		self.local_as = None
		self.hold_time = HoldTime(180)
		self.asn4 = None
		self.add_path = 0
		self.md5 = None
		self.ttl = None
		self.group_updates = None
		self.flush = None
		self.adjribout = None

		self.api = APIOptions()

		# passive indicate that we do not establish outgoing connections
		self.passive = False
		# the port to listen on ( zero mean that we do not listen )
		self.listen = 0

		# capability
		self.route_refresh = False
		self.graceful_restart = False
		self.multisession = None
		self.add_path = None
		self.aigp = None

		self._families = []
		self.rib = None

		# The routes we have parsed from the configuration
		self.changes = []
		# On signal update, the previous routes so we can compare what changed
		self.backup_changes = []

		self.operational = None
		self.eor = deque()
		self.asm = dict()

		self.messages = deque()
		self.refresh = deque()

		self.counter = Counter()
		# It is possible to :
		# - have multiple exabgp toward one peer on the same host ( use of pid )
		# - have more than once connection toward a peer
		# - each connection has it own neihgbor (hence why identificator is not in Protocol)
		self.uid = '%d-%s' % (os.getpid(),uuid.uuid1())
Exemple #14
0
    def __init__(self):
        self.sent_open = None
        self.received_open = None

        self.holdtime = HoldTime(0)
        self.local_as = ASN(0)
        self.peer_as = ASN(0)
        self.families = []
        self.asn4 = False
        self.addpath = RequirePath()
        self.multisession = False
        self.msg_size = 4096 - 19
        self.operational = False
Exemple #15
0
def holdtime(tokeniser):
    value = tokeniser()
    try:
        hold_time = HoldTime(value)
    except ValueError:
        raise ValueError('"%s" is an invalid hold-time' % value)

    if hold_time < 3 and hold_time != 0:
        raise ValueError('holdtime must be zero or at least three seconds')
    # XXX: FIXME: add HoldTime.MAX and reference it ( pow -1 )
    if hold_time >= pow(2, 16):
        raise ValueError('holdtime must be smaller than %d' % pow(2, 16))
    return hold_time
Exemple #16
0
    def __init__(self, neighbor):
        self.neighbor = neighbor

        self.sent_open = None
        self.received_open = None

        self.holdtime = HoldTime(0)
        self.local_as = ASN(0)
        self.peer_as = ASN(0)
        self.families = []
        self.asn4 = False
        self.addpath = RequirePath()
        self.multisession = False
        self.msg_size = 4096
        self.operational = False
        self.refresh = REFRESH.absent
        self.aigp = None
Exemple #17
0
    def __init__(self, neighbor):
        self.neighbor = neighbor

        self.sent_open = None
        self.received_open = None

        self.holdtime = HoldTime(0)
        self.local_as = ASN(0)
        self.peer_as = ASN(0)
        self.families = []
        self.asn4 = False
        self.addpath = RequirePath()
        self.multisession = False
        self.msg_size = self.MAX_SIZE
        self.operational = False
        self.refresh = REFRESH.ABSENT  # pylint: disable=E1101
        self.aigp = None
Exemple #18
0
	def __init__ (self):
		# self.logger should not be used here as long as we do use deepcopy as it contains a Lock
		self.description = ''
		self.router_id = None
		self.local_address = None
		self.peer_address = None
		self.peer_as = None
		self.local_as = None
		self.hold_time = HoldTime(180)
		self.asn4 = None
		self.add_path = 0
		self.md5 = None
		self.ttl = None
		self.group_updates = None
		self.flush = None
		self.adjribout = None

		self.api = APIOptions()

		self.passive = False

		# capability
		self.route_refresh = False
		self.graceful_restart = False
		self.multisession = None
		self.add_path = None
		self.aigp = None

		self._families = []
		self.rib = None

		# The routes we have parsed from the configuration
		self.changes = []
		# On signal update, the previous routes so we can compare what changed
		self.backup_changes = []

		self.operational = None
		self.eor = deque()
		self.asm = dict()

		self.messages = deque()
		self.refresh = deque()

		self.counter = Counter()
Exemple #19
0
    def __init__(self, neighbor):
        self.neighbor = neighbor

        self.sent_open = None
        self.received_open = None

        self.holdtime = HoldTime(0)
        self.local_as = ASN(0)
        self.peer_as = ASN(0)
        self.families = []
        self.nexthop = []
        self.asn4 = False
        self.addpath = RequirePath()
        self.multisession = False
        self.msg_size = ExtendedMessage.INITIAL_SIZE
        self.operational = False
        self.refresh = REFRESH.ABSENT  # pylint: disable=E1101
        self.aigp = neighbor['capability']['aigp']
        self.mismatch = []
Exemple #20
0
    def holdtime(self, scope, name, command, tokens):
        if not len(tokens) == 1:
            return self.error.set('hold-time required')

        try:
            holdtime = HoldTime(tokens[0])
        except ValueError:
            return self.error.set('"%s" is an invalid hold-time' %
                                  ' '.join(tokens))

        if holdtime < 3 and holdtime != 0:
            return self.error.set(
                'holdtime must be zero or at least three seconds')
        if holdtime >= pow(2, 16):
            return self.error.set('holdtime must be smaller than %d' %
                                  pow(2, 16))

        scope[-1][command] = holdtime
        return True
Exemple #21
0
class Neighbor:
    description = 'a test neighbor'
    router_id = RouterID('127.0.0.1')
    local_address = IPv4('127.0.0.1')
    peer_address = IPv4('127.0.0.1')
    peer_as = ASN('65500')
    local_as = ASN('65500')
    hold_time = HoldTime(180)
    asn4 = False
    add_path = 0

    # capability
    route_refresh = False
    graceful_restart = False
    multisession = None
    add_path = None
    aigp = None

    @staticmethod
    def families():
        return known_families()
Exemple #22
0
class Open (Message):
	TYPE = chr(Message.Type.OPEN)

	def __init__ (self,version,asn,router_id,capabilities,hold_time):
		self.version = Version(version)
		self.asn = ASN(asn)
		self.hold_time = HoldTime(hold_time)
		self.router_id = RouterID(router_id)
		self.capabilities = capabilities

	def message (self):
		return self._message("%s%s%s%s%s" % (
			self.version.pack(),
			self.asn.trans(),
			self.hold_time.pack(),
			self.router_id.pack(),
			self.capabilities.pack()
		))

	def __str__ (self):
		return "OPEN version=%d asn=%d hold_time=%s router_id=%s capabilities=[%s]" % (self.version, self.asn, self.hold_time, self.router_id,self.capabilities)
Exemple #23
0
class Neighbor(dict):
    class Capability(dict):
        defaults = {
            'asn4': True,
            'extended-message': True,
            'graceful-restart': False,
            'multi-session': False,
            'operational': False,
            'add-path': 0,
            'route-refresh': 0,
            'nexthop': None,
            'aigp': None,
        }

    defaults = {
        # Those are the field from the configuration
        'description': '',
        'router-id': None,
        'local-address': None,
        'peer-address': None,
        'local-as': None,
        'peer-as': None,
        # passive indicate that we do not establish outgoing connections
        'passive': False,
        # the port to listen on ( zero mean that we do not listen )
        'listen': 0,
        # the port to connect to
        'connect': 0,
        'hold-time': HoldTime(180),
        'rate-limit': 0,
        'host-name': host(),
        'domain-name': domain(),
        'group-updates': True,
        'auto-flush': True,
        'adj-rib-in': True,
        'adj-rib-out': True,
        'manual-eor': False,
        # XXX: this should be under an MD5 sub-dict/object ?
        'md5-password': None,
        'md5-base64': False,
        'md5-ip': None,
        'outgoing-ttl': None,
        'incoming-ttl': None,
    }

    _GLOBAL = {'uid': 1}

    def __init__(self):
        # super init
        self.update(self.defaults)

        # Those are subconf
        self.api = None  # XXX: not scriptable - is replaced outside the class

        # internal or calculated field
        self['capability'] = self.Capability.defaults.copy()

        # local_address uses auto discovery
        self.auto_discovery = False

        self.range_size = 1

        # was this Neighbor generated from a range
        self.generated = False

        self._families = []
        self._nexthop = []
        self._addpath = []
        self.rib = None

        # The routes we have parsed from the configuration
        self.changes = []
        # On signal update, the previous routes so we can compare what changed
        self.backup_changes = []

        self.eor = deque()
        self.asm = dict()

        self.messages = deque()
        self.refresh = deque()

        self.counter = Counter()
        # It is possible to :
        # - have multiple exabgp toward one peer on the same host ( use of pid )
        # - have more than once connection toward a peer
        # - each connection has it own neihgbor (hence why identificator is not in Protocol)
        self.uid = '%d' % self._GLOBAL['uid']
        self._GLOBAL['uid'] += 1

    def missing(self):
        if self['local-as'] is None:
            return 'incomplete neighbor, missing local-address'
        if self['local-as'] is None:
            return 'incomplete neighbor, missing local-as'
        if self['peer-as'] is None:
            return 'incomplete neighbor, missing peer-as'
        return ''

    def infer(self):
        if self['md5-ip'] is None:
            self['md5-ip'] = self['local-address']

        if self['capability']['graceful-restart'] == 0:
            self['capability']['graceful-restart'] = int(self['hold-time'])

    def id(self):
        return 'neighbor-%s' % self.uid

    # This set must be unique between peer, not full draft-ietf-idr-bgp-multisession-07
    def index(self):
        if self['listen'] != 0:
            return 'peer-ip %s listen %d' % (self['peer-address'],
                                             self['listen'])
        return self.name()

    def make_rib(self):
        self.rib = RIB(self.name(), self['adj-rib-in'], self['adj-rib-out'],
                       self._families)

    # will resend all the routes once we reconnect
    def reset_rib(self):
        self.rib.reset()
        self.messages = deque()
        self.refresh = deque()

    # back to square one, all the routes are removed
    def clear_rib(self):
        self.rib.clear()
        self.messages = deque()
        self.refresh = deque()

    def name(self):
        if self['capability']['multi-session']:
            session = '/'.join("%s-%s" % (afi.name(), safi.name())
                               for (afi, safi) in self.families())
        else:
            session = 'in-open'
        return "neighbor %s local-ip %s local-as %s peer-as %s router-id %s family-allowed %s" % (
            self['peer-address'],
            self['local-address']
            if self['peer-address'] is not None else 'auto',
            self['local-as'] if self['local-as'] is not None else 'auto',
            self['peer-as'] if self['peer-as'] is not None else 'auto',
            self['router-id'],
            session,
        )

    def families(self):
        # this list() is important .. as we use the function to modify self._families
        return list(self._families)

    def nexthops(self):
        # this list() is important .. as we use the function to modify self._nexthop
        return list(self._nexthop)

    def addpaths(self):
        # this list() is important .. as we use the function to modify self._add_path
        return list(self._addpath)

    def add_family(self, family):
        # the families MUST be sorted for neighbor indexing name to be predictable for API users
        # this list() is important .. as we use the function to modify self._families
        if family not in self.families():
            afi, safi = family
            d = dict()
            d[afi] = [
                safi,
            ]
            for afi, safi in self._families:
                d.setdefault(afi, []).append(safi)
            self._families = [(afi, safi) for afi in sorted(d)
                              for safi in sorted(d[afi])]

    def add_nexthop(self, afi, safi, nhafi):
        if (afi, safi, nhafi) not in self._nexthop:
            self._nexthop.append((afi, safi, nhafi))

    def add_addpath(self, family):
        # the families MUST be sorted for neighbor indexing name to be predictable for API users
        # this list() is important .. as we use the function to modify self._add_path
        if family not in self.addpaths():
            afi, safi = family
            d = dict()
            d[afi] = [
                safi,
            ]
            for afi, safi in self._addpath:
                d.setdefault(afi, []).append(safi)
            self._addpath = [(afi, safi) for afi in sorted(d)
                             for safi in sorted(d[afi])]

    def remove_family(self, family):
        if family in self.families():
            self._families.remove(family)

    def remove_nexthop(self, afi, safi, nhafi):
        if (afi, safi, nhafi) in self.nexthops():
            self._nexthop.remove((afi, safi, nhafi))

    def remove_addpath(self, family):
        if family in self.addpaths():
            self._addpath.remove(family)

    def missing(self):
        if self['local-address'] is None and not self.auto_discovery:
            return 'local-address'
        if self['listen'] > 0 and self.auto_discovery:
            return 'local-address'
        if self['peer-address'] is None:
            return 'peer-address'
        if self.auto_discovery and not self['router-id']:
            return 'router-id'
        if self['peer-address'].afi == AFI.ipv6 and not self['router-id']:
            return 'router-id'
        return ''

    # This function only compares the neighbor BUT NOT ITS ROUTES
    def __eq__(self, other):
        # Comparing local_address is skipped in the case where either
        # peer is configured to auto discover its local address. In
        # this case it can happen that one local_address is None and
        # the other one will be set to the auto disocvered IP address.
        auto_discovery = self.auto_discovery or other.auto_discovery
        return (self['router-id'] == other['router-id']
                and self['local-as'] == other['local-as']
                and self['peer-address'] == other['peer-address']
                and self['peer-as'] == other['peer-as']
                and self['passive'] == other['passive']
                and self['listen'] == other['listen']
                and self['connect'] == other['connect']
                and self['hold-time'] == other['hold-time']
                and self['rate-limit'] == other['rate-limit']
                and self['host-name'] == other['host-name']
                and self['domain-name'] == other['domain-name']
                and self['md5-password'] == other['md5-password']
                and self['md5-ip'] == other['md5-ip']
                and self['incoming-ttl'] == other['incoming-ttl']
                and self['outgoing-ttl'] == other['outgoing-ttl']
                and self['group-updates'] == other['group-updates']
                and self['auto-flush'] == other['auto-flush']
                and self['adj-rib-in'] == other['adj-rib-in']
                and self['adj-rib-out'] == other['adj-rib-out']
                and (auto_discovery
                     or self['local-address'] == other['local-address'])
                and self['capability'] == other['capability']
                and self.auto_discovery == other.auto_discovery
                and self.families() == other.families())

    def __ne__(self, other):
        return not self.__eq__(other)

    def string(self, with_changes=True):
        changes = ''
        if with_changes:
            changes += '\nstatic { '
            for change in self.rib.outgoing.queued_changes():
                changes += '\n\t\t%s' % change.extensive()
            changes += '\n}'

        families = ''
        for afi, safi in self.families():
            families += '\n\t\t%s %s;' % (afi.name(), safi.name())

        nexthops = ''
        for afi, safi, nexthop in self.nexthops():
            nexthops += '\n\t\t%s %s %s;' % (afi.name(), safi.name(),
                                             nexthop.name())

        addpaths = ''
        for afi, safi in self.addpaths():
            addpaths += '\n\t\t%s %s;' % (afi.name(), safi.name())

        codes = Message.CODE

        _extension_global = {
            'neighbor-changes': 'neighbor-changes',
            'negotiated': 'negotiated',
            'fsm': 'fsm',
            'signal': 'signal',
        }

        _extension_receive = {
            'receive-packets': 'packets',
            'receive-parsed': 'parsed',
            'receive-consolidate': 'consolidate',
            'receive-%s' % codes.NOTIFICATION.SHORT: 'notification',
            'receive-%s' % codes.OPEN.SHORT: 'open',
            'receive-%s' % codes.KEEPALIVE.SHORT: 'keepalive',
            'receive-%s' % codes.UPDATE.SHORT: 'update',
            'receive-%s' % codes.ROUTE_REFRESH.SHORT: 'refresh',
            'receive-%s' % codes.OPERATIONAL.SHORT: 'operational',
        }

        _extension_send = {
            'send-packets': 'packets',
            'send-parsed': 'parsed',
            'send-consolidate': 'consolidate',
            'send-%s' % codes.NOTIFICATION.SHORT: 'notification',
            'send-%s' % codes.OPEN.SHORT: 'open',
            'send-%s' % codes.KEEPALIVE.SHORT: 'keepalive',
            'send-%s' % codes.UPDATE.SHORT: 'update',
            'send-%s' % codes.ROUTE_REFRESH.SHORT: 'refresh',
            'send-%s' % codes.OPERATIONAL.SHORT: 'operational',
        }

        apis = ''

        for process in self.api.get('processes', []):
            _global = []
            _receive = []
            _send = []

            for api, name in _extension_global.items():
                _global.extend([
                    '\t\t%s;\n' % name,
                ] if process in self.api[api] else [])

            for api, name in _extension_receive.items():
                _receive.extend([
                    '\t\t\t%s;\n' % name,
                ] if process in self.api[api] else [])

            for api, name in _extension_send.items():
                _send.extend([
                    '\t\t\t%s;\n' % name,
                ] if process in self.api[api] else [])

            _api = '\tapi {\n'
            _api += '\t\tprocesses [ %s ];\n' % process
            _api += ''.join(_global)
            if _receive:
                _api += '\t\treceive {\n'
                _api += ''.join(_receive)
                _api += '\t\t}\n'
            if _send:
                _api += '\t\tsend {\n'
                _api += ''.join(_send)
                _api += '\t\t}\n'
            _api += '\t}\n'

            apis += _api

        returned = (
            'neighbor %s {\n'
            '\tdescription "%s";\n'
            '\trouter-id %s;\n'
            '\thost-name %s;\n'
            '\tdomain-name %s;\n'
            '\tlocal-address %s;\n'
            '\tlocal-as %s;\n'
            '\tpeer-as %s;\n'
            '\thold-time %s;\n'
            '\trate-limit %s;\n'
            '\tmanual-eor %s;\n'
            '%s%s%s%s%s%s%s%s%s%s%s\n'
            '\tcapability {\n'
            '%s%s%s%s%s%s%s%s%s\t}\n'
            '\tfamily {%s\n'
            '\t}\n'
            '\tnexthop {%s\n'
            '\t}\n'
            '\tadd-path {%s\n'
            '\t}\n'
            '%s'
            '%s'
            '}' % (
                self['peer-address'],
                self['description'],
                self['router-id'],
                self['host-name'],
                self['domain-name'],
                self['local-address'] if not self.auto_discovery else 'auto',
                self['local-as'],
                self['peer-as'],
                self['hold-time'],
                'disable' if self['rate-limit'] == 0 else self['rate-limit'],
                'true' if self['manual-eor'] else 'false',
                '\n\tpassive %s;\n' % ('true' if self['passive'] else 'false'),
                '\n\tlisten %d;\n' % self['listen'] if self['listen'] else '',
                '\n\tconnect %d;\n' %
                self['connect'] if self['connect'] else '',
                '\tgroup-updates %s;\n' %
                ('true' if self['group-updates'] else 'false'),
                '\tauto-flush %s;\n' %
                ('true' if self['auto-flush'] else 'false'),
                '\tadj-rib-in %s;\n' %
                ('true' if self['adj-rib-in'] else 'false'),
                '\tadj-rib-out %s;\n' %
                ('true' if self['adj-rib-out'] else 'false'),
                '\tmd5-password "%s";\n' %
                self['md5-password'] if self['md5-password'] else '',
                '\tmd5-base64 %s;\n' %
                ('true' if self['md5-base64'] is True else
                 'false' if self['md5-base64'] is False else 'auto'),
                '\tmd5-ip "%s";\n' %
                self['md5-ip'] if not self.auto_discovery else '',
                '\toutgoing-ttl %s;\n' %
                self['outgoing-ttl'] if self['outgoing-ttl'] else '',
                '\tincoming-ttl %s;\n' %
                self['incoming-ttl'] if self['incoming-ttl'] else '',
                '\t\tasn4 %s;\n' %
                ('enable' if self['capability']['asn4'] else 'disable'),
                '\t\troute-refresh %s;\n' %
                ('enable'
                 if self['capability']['route-refresh'] else 'disable'),
                '\t\tgraceful-restart %s;\n' %
                (self['capability']['graceful-restart']
                 if self['capability']['graceful-restart'] else 'disable'),
                '\t\tnexthop %s;\n' %
                ('enable' if self['capability']['nexthop'] else 'disable'),
                '\t\tadd-path %s;\n' %
                (AddPath.string[self['capability']['add-path']]
                 if self['capability']['add-path'] else 'disable'),
                '\t\tmulti-session %s;\n' %
                ('enable'
                 if self['capability']['multi-session'] else 'disable'),
                '\t\toperational %s;\n' %
                ('enable' if self['capability']['operational'] else 'disable'),
                '\t\taigp %s;\n' %
                ('enable' if self['capability']['aigp'] else 'disable'),
                families,
                nexthops,
                addpaths,
                apis,
                changes,
            ))

        # '\t\treceive {\n%s\t\t}\n' % receive if receive else '',
        # '\t\tsend {\n%s\t\t}\n' % send if send else '',
        return returned.replace('\t', '  ')

    def __str__(self):
        return self.string(False)
    def post(self):
        for inherit in self.scope.pop('inherit', []):
            data = self.scope.template('neighbor', inherit)
            self.scope.inherit(data)
        local = self.scope.get()

        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.connect = local.get('connect', 0)
        neighbor.hold_time = local.get('hold-time', HoldTime(180))
        neighbor.host_name = local.get('host-name', host())
        neighbor.domain_name = local.get('domain-name', domain())
        neighbor.md5_password = local.get('md5-password', None)
        neighbor.md5_base64 = local.get('md5-base64', None)
        neighbor.md5_ip = local.get('md5-ip', neighbor.local_address)
        neighbor.description = local.get('description', '')
        neighbor.flush = local.get('auto-flush', True)
        neighbor.adj_rib_out = local.get('adj-rib-out', True)
        neighbor.adj_rib_in = local.get('adj-rib-in', True)
        neighbor.aigp = local.get('aigp', None)
        neighbor.ttl_out = local.get('outgoing-ttl', None)
        neighbor.ttl_in = local.get('incoming-ttl', None)
        neighbor.group_updates = local.get('group-updates', True)
        neighbor.manual_eor = local.get('manual-eor', False)

        capability = local.get('capability', {})
        neighbor.add_path = capability.get('add-path', 0)
        neighbor.asn4 = capability.get('asn4', True)
        neighbor.extended_message = capability.get('extended-message', 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)

        neighbor.api = ParseAPI.flatten(local.pop('api', {}))

        families = []
        for family in ParseFamily.convert:
            for pair in local.get('family', {}).get(family, []):
                families.append(pair)

        families = families or NLRI.known_families()

        for family in families:
            neighbor.add_family(family)

        if neighbor.add_path:
            add_path = local.get('add-path', {})
            if add_path:
                for family in ParseAddPath.convert:
                    for pair in add_path.get(family, []):
                        if pair not in families:
                            self.logger.debug(
                                'skipping add-path family %s as it is not negotiated'
                                % pair, 'configuration')
                            continue
                        neighbor.add_addpath(pair)
            else:
                for family in families:
                    neighbor.add_addpath(family)

        neighbor.changes = []
        neighbor.changes.extend(self.scope.pop_routes())

        # old format
        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)

        routes = local.get('routes', [])
        for route in routes:
            route.nlri.action = OUT.ANNOUNCE
        neighbor.changes.extend(routes)

        messages = local.get('operational', {}).get('routes', [])

        if neighbor.local_address is None:
            neighbor.auto_discovery = True
            neighbor.local_address = None
            neighbor.md5_ip = None

        if not neighbor.router_id and neighbor.peer_address.afi == AFI.ipv4 and not neighbor.auto_discovery:
            neighbor.router_id = neighbor.local_address

        if neighbor.route_refresh:
            if neighbor.adj_rib_out:
                self.logger.debug(
                    'route-refresh requested, enabling adj-rib-out',
                    'configuration')

        missing = neighbor.missing()
        if missing:
            return self.error.set('incomplete neighbor, missing %s' % missing)

        if not neighbor.auto_discovery and neighbor.local_address.afi != neighbor.peer_address.afi:
            return self.error.set(
                'local-address and peer-address must be of the same family')
        neighbor.range_size = neighbor.peer_address.mask.size()

        if neighbor.range_size > 1 and not neighbor.passive:
            return self.error.set(
                'can only use ip ranges for the peer address with passive neighbors'
            )

        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())

        if neighbor.md5_password:
            try:
                md5 = base64.b64decode(
                    neighbor.md5_password
                ) if neighbor.md5_base64 else neighbor.md5_password
            except TypeError as e:
                return self.error.set(
                    "Invalid base64 encoding of MD5 password.")
            else:
                if len(md5) > 80:
                    return self.error.set(
                        'MD5 password must be no larger than 80 characters')

        # 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.add_to_rib_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
Exemple #25
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
Exemple #26
0
	def __init__ (self, version, asn, router_id, capabilities, hold_time):
		self.version = Version(version)
		self.asn = ASN(asn)
		self.hold_time = HoldTime(hold_time)
		self.router_id = RouterID(router_id)
		self.capabilities = capabilities
Exemple #27
0
 def __init__(self, version, asn, router_id, capabilities, hold_time):
     self.version = Version(version)
     self.asn = ASN(asn)
     self.hold_time = HoldTime(hold_time)
     self.router_id = RouterID(router_id)
     self.capabilities = capabilities