Ejemplo n.º 1
0
	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:
			if neighbor.peer_address.afi == AFI.ipv4 and not neighbor.auto_discovery:
				neighbor.router_id = neighbor.local_address
			else:
				return self.error.set('missing router-id for the peer, it can not be set using the local-ip')

		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
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
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)