BGPSpeaker: Support Route Reflector features [RFC4456]

This patch implements the features for acting as a Route Reflector
which defined in RFC4456.

Signed-off-by: IWASE Yusuke <iwase.yusuke0@gmail.com>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
This commit is contained in:
IWASE Yusuke 2017-02-15 15:38:57 +09:00 committed by FUJITA Tomonori
parent e06ec47232
commit ef3eefb2ad
7 changed files with 217 additions and 66 deletions

View File

@ -3112,7 +3112,7 @@ class BGPPathAttributeOriginatorId(_PathAttribute):
_VALUE_PACK_STR = '!4s'
_ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL
_TYPE = {
'ascii': [
'asciilist': [
'value'
]
}

View File

@ -260,6 +260,8 @@ class RyuBGPSpeaker(RyuApp):
REFRESH_MAX_EOR_TIME, DEFAULT_REFRESH_MAX_EOR_TIME)
bgp_settings[LABEL_RANGE] = settings.get(
LABEL_RANGE, DEFAULT_LABEL_RANGE)
bgp_settings['allow_local_as_in_count'] = settings.get(
'allow_local_as_in_count', 0)
# Create BGPSpeaker instance.
LOG.debug('Starting BGPSpeaker...')

View File

@ -55,6 +55,7 @@ from ryu.services.protocols.bgp.api.prefix import (
PMSI_TYPE_INGRESS_REP)
from ryu.services.protocols.bgp.rtconf.common import LOCAL_AS
from ryu.services.protocols.bgp.rtconf.common import ROUTER_ID
from ryu.services.protocols.bgp.rtconf.common import CLUSTER_ID
from ryu.services.protocols.bgp.rtconf.common import BGP_SERVER_PORT
from ryu.services.protocols.bgp.rtconf.common import DEFAULT_BGP_SERVER_PORT
from ryu.services.protocols.bgp.rtconf.common import (
@ -85,8 +86,12 @@ from ryu.services.protocols.bgp.rtconf.neighbors import (
from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CONNECT_MODE
from ryu.services.protocols.bgp.rtconf.neighbors import PEER_NEXT_HOP
from ryu.services.protocols.bgp.rtconf.neighbors import PASSWORD
from ryu.services.protocols.bgp.rtconf.neighbors import IS_ROUTE_SERVER_CLIENT
from ryu.services.protocols.bgp.rtconf.neighbors import IS_NEXT_HOP_SELF
from ryu.services.protocols.bgp.rtconf.neighbors import (
DEFAULT_IS_ROUTE_SERVER_CLIENT, IS_ROUTE_SERVER_CLIENT)
from ryu.services.protocols.bgp.rtconf.neighbors import (
DEFAULT_IS_ROUTE_REFLECTOR_CLIENT, IS_ROUTE_REFLECTOR_CLIENT)
from ryu.services.protocols.bgp.rtconf.neighbors import (
DEFAULT_IS_NEXT_HOP_SELF, IS_NEXT_HOP_SELF)
from ryu.services.protocols.bgp.rtconf.neighbors import CONNECT_MODE
from ryu.services.protocols.bgp.rtconf.neighbors import LOCAL_ADDRESS
from ryu.services.protocols.bgp.rtconf.neighbors import LOCAL_PORT
@ -176,7 +181,8 @@ class BGPSpeaker(object):
ssh_console=False,
ssh_port=None, ssh_host=None, ssh_host_key=None,
label_range=DEFAULT_LABEL_RANGE,
allow_local_as_in_count=0):
allow_local_as_in_count=0,
cluster_id=None):
"""Create a new BGPSpeaker object with as_number and router_id to
listen on bgp_server_port.
@ -230,6 +236,10 @@ class BGPSpeaker(object):
configurations in leaf/spine architecture with shared AS numbers.
The default is 0 and means "local AS number is not allowed in
AS_PATH". To allow local AS, 3 is recommended (Cisco's default).
``cluster_id`` specifies the cluster identifier for Route Reflector.
It must be the string representation of an IPv4 address.
If omitted, "router_id" is used for this field.
"""
super(BGPSpeaker, self).__init__()
@ -242,6 +252,7 @@ class BGPSpeaker(object):
REFRESH_MAX_EOR_TIME: refresh_max_eor_time,
LABEL_RANGE: label_range,
ALLOW_LOCAL_AS_IN_COUNT: allow_local_as_in_count,
CLUSTER_ID: cluster_id,
}
self._core_start(settings)
self._init_signal_listeners()
@ -322,8 +333,11 @@ class BGPSpeaker(object):
enable_enhanced_refresh=DEFAULT_CAP_ENHANCED_REFRESH,
enable_four_octet_as_number=DEFAULT_CAP_FOUR_OCTET_AS_NUMBER,
next_hop=None, password=None, multi_exit_disc=None,
site_of_origins=None, is_route_server_client=False,
is_next_hop_self=False, local_address=None,
site_of_origins=None,
is_route_server_client=DEFAULT_IS_ROUTE_SERVER_CLIENT,
is_route_reflector_client=DEFAULT_IS_ROUTE_REFLECTOR_CLIENT,
is_next_hop_self=DEFAULT_IS_NEXT_HOP_SELF,
local_address=None,
local_port=None, local_as=None,
connect_mode=DEFAULT_CONNECT_MODE):
""" This method registers a new neighbor. The BGP speaker tries to
@ -374,6 +388,9 @@ class BGPSpeaker(object):
``is_route_server_client`` specifies whether this neighbor is a
router server's client or not.
``is_route_reflector_client`` specifies whether this neighbor is a
router reflector's client or not.
``is_next_hop_self`` specifies whether the BGP speaker announces
its own ip address to iBGP neighbor or not as path's next_hop address.
@ -397,6 +414,7 @@ class BGPSpeaker(object):
PEER_NEXT_HOP: next_hop,
PASSWORD: password,
IS_ROUTE_SERVER_CLIENT: is_route_server_client,
IS_ROUTE_REFLECTOR_CLIENT: is_route_reflector_client,
IS_NEXT_HOP_SELF: is_next_hop_self,
CONNECT_MODE: connect_mode,
CAP_ENHANCED_REFRESH: enable_enhanced_refresh,

View File

@ -75,6 +75,8 @@ from ryu.lib.packet.bgp import BGPPathAttributeAsPath
from ryu.lib.packet.bgp import BGPPathAttributeAs4Path
from ryu.lib.packet.bgp import BGPPathAttributeLocalPref
from ryu.lib.packet.bgp import BGPPathAttributeExtendedCommunities
from ryu.lib.packet.bgp import BGPPathAttributeOriginatorId
from ryu.lib.packet.bgp import BGPPathAttributeClusterList
from ryu.lib.packet.bgp import BGPPathAttributeMpReachNLRI
from ryu.lib.packet.bgp import BGPPathAttributeMpUnreachNLRI
from ryu.lib.packet.bgp import BGPPathAttributeCommunities
@ -90,6 +92,8 @@ from ryu.lib.packet.bgp import BGP_ATTR_TYPE_MP_REACH_NLRI
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_MP_UNREACH_NLRI
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_MULTI_EXIT_DISC
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_COMMUNITIES
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_ORIGINATOR_ID
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_CLUSTER_LIST
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_EXTENDED_COMMUNITIES
from ryu.lib.packet.bgp import BGP_ATTR_TYEP_PMSI_TUNNEL_ATTRIBUTE
@ -438,6 +442,10 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
def is_route_server_client(self):
return self._neigh_conf.is_route_server_client
@property
def is_route_reflector_client(self):
return self._neigh_conf.is_route_reflector_client
@property
def check_first_as(self):
return self._neigh_conf.check_first_as
@ -976,8 +984,37 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
new_pathattr.append(mpunreach_attr)
elif self.is_route_server_client:
nlri_list = [path.nlri]
for pathattr in path.pathattr_map.values():
new_pathattr.append(pathattr)
new_pathattr.extend(pathattr_map.values())
elif self.is_route_reflector_client:
nlri_list = [path.nlri]
# Append ORIGINATOR_ID attribute if not already exists.
if BGP_ATTR_TYPE_ORIGINATOR_ID not in pathattr_map:
originator_id = path.source
if originator_id is None:
originator_id = self._common_conf.router_id
elif isinstance(path.source, Peer):
originator_id = path.source.ip_address
new_pathattr.append(
BGPPathAttributeOriginatorId(value=originator_id))
# Append CLUSTER_LIST attribute if not already exists.
if BGP_ATTR_TYPE_CLUSTER_LIST not in pathattr_map:
new_pathattr.append(
BGPPathAttributeClusterList(
[self._common_conf.cluster_id]))
for t, path_attr in pathattr_map.items():
if t == BGP_ATTR_TYPE_CLUSTER_LIST:
# Append own CLUSTER_ID into CLUSTER_LIST attribute
# if already exists.
cluster_list = list(path_attr.value)
if self._common_conf.cluster_id not in cluster_list:
cluster_list.append(self._common_conf.cluster_id)
new_pathattr.append(
BGPPathAttributeClusterList(cluster_list))
else:
new_pathattr.append(path_attr)
else:
# Supported and un-supported/unknown attributes.
origin_attr = None
@ -1503,36 +1540,42 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
Assumes Multiprotocol Extensions capability is supported and enabled.
"""
assert self.state.bgp_state == const.BGP_FSM_ESTABLISHED
# Increment count of update received.
self.state.incr(PeerCounterNames.RECV_UPDATES)
if not self._validate_update_msg(update_msg):
# If update message was not valid for some reason, we ignore its
# routes.
LOG.error('UPDATE message was invalid, hence ignoring its routes.')
return
# Increment count of update received.
mp_reach_attr = update_msg.get_path_attr(BGP_ATTR_TYPE_MP_REACH_NLRI)
mp_unreach_attr = update_msg.get_path_attr(
BGP_ATTR_TYPE_MP_UNREACH_NLRI)
# Extract advertised path attributes and reconstruct AS_PATH attribute
self._extract_and_reconstruct_as_path(update_msg)
nlri_list = update_msg.nlri
withdraw_list = update_msg.withdrawn_routes
# Check if path attributes have loops.
if self._is_looped_path_attrs(update_msg):
return
umsg_pattrs = update_msg.pathattr_map
mp_reach_attr = umsg_pattrs.get(BGP_ATTR_TYPE_MP_REACH_NLRI, None)
if mp_reach_attr:
# Extract advertised paths from given message.
# Extract advertised MP-BGP paths from given message.
self._extract_and_handle_mpbgp_new_paths(update_msg)
mp_unreach_attr = umsg_pattrs.get(BGP_ATTR_TYPE_MP_UNREACH_NLRI, None)
if mp_unreach_attr:
# Extract withdraws from given message.
# Extract MP-BGP withdraws from given message.
self._extract_and_handle_mpbgp_withdraws(mp_unreach_attr)
nlri_list = update_msg.nlri
if nlri_list:
# Extract advertised BGP paths from given message.
self._extract_and_handle_bgp4_new_paths(update_msg)
withdraw_list = update_msg.withdrawn_routes
if withdraw_list:
# Extract BGP withdraws from given message.
self._extract_and_handle_bgp4_withdraws(withdraw_list)
def _extract_and_reconstruct_as_path(self, update_msg):
@ -1597,6 +1640,48 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
as_path = self._construct_as_path_attr(as_path, as4_path)
update_msg.path_attributes.append(as_path)
def _is_looped_path_attrs(self, update_msg):
"""
Extracts path attributes from the given UPDATE message and checks
if the given attributes have loops or not.
:param update_msg: UPDATE message instance.
:return: True if attributes have loops. Otherwise False.
"""
umsg_pattrs = update_msg.pathattr_map
recv_open_msg = self.protocol.recv_open_msg
# Check if AS_PATH has loops.
aspath = umsg_pattrs.get(BGP_ATTR_TYPE_AS_PATH)
if (aspath is not None
and aspath.has_local_as(
self.local_as,
max_count=self._common_conf.allow_local_as_in_count)):
LOG.error(
'AS_PATH on UPDATE message has loops. '
'Ignoring this message: %s',
update_msg)
return
# Check if ORIGINATOR_ID has loops. [RFC4456]
originator_id = umsg_pattrs.get(BGP_ATTR_TYPE_ORIGINATOR_ID, None)
if (originator_id
and recv_open_msg.bgp_identifier == originator_id):
LOG.error(
'ORIGINATOR_ID on UPDATE message has loops. '
'Ignoring this message: %s',
update_msg)
return
# Check if CLUSTER_LIST has loops. [RFC4456]
cluster_list = umsg_pattrs.get(BGP_ATTR_TYPE_CLUSTER_LIST, None)
if (cluster_list
and self._common_conf.cluster_id in cluster_list.value):
LOG.error(
'CLUSTER_LIST on UPDATE message has loops. '
'Ignoring this message: %s', update_msg)
return
def _extract_and_handle_bgp4_new_paths(self, update_msg):
"""Extracts new paths advertised in the given update message's
*MpReachNlri* attribute.
@ -1611,23 +1696,8 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
processing.
"""
umsg_pattrs = update_msg.pathattr_map
msg_rf = RF_IPv4_UC
# Check if this route family is among supported route families.
if msg_rf not in SUPPORTED_GLOBAL_RF:
LOG.info(('Received route for route family %s which is'
' not supported. Ignoring paths from this UPDATE: %s') %
(msg_rf, update_msg))
return
aspath = umsg_pattrs.get(BGP_ATTR_TYPE_AS_PATH)
# Check if AS_PATH has loops.
if aspath.has_local_as(self.local_as, max_count=self._common_conf.allow_local_as_in_count):
LOG.error('Update message AS_PATH has loops. Ignoring this'
' UPDATE. %s', update_msg)
return
next_hop = update_msg.get_path_attr(BGP_ATTR_TYPE_NEXT_HOP).value
# Nothing to do if we do not have any new NLRIs in this message.
msg_nlri_list = update_msg.nlri
if not msg_nlri_list:
@ -1684,16 +1754,6 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
processing.
"""
msg_rf = RF_IPv4_UC
# Check if this route family is among supported route families.
if msg_rf not in SUPPORTED_GLOBAL_RF:
LOG.info(
(
'Received route for route family %s which is'
' not supported. Ignoring withdraws form this UPDATE.'
) % msg_rf
)
return
w_nlris = withdraw_list
if not w_nlris:
# If this is EOR of some kind, handle it
@ -1748,13 +1808,6 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
(msg_rf, update_msg))
return
aspath = umsg_pattrs.get(BGP_ATTR_TYPE_AS_PATH)
# Check if AS_PATH has loops.
if aspath.has_local_as(self.local_as, max_count=self._common_conf.allow_local_as_in_count):
LOG.error('Update message AS_PATH has loops. Ignoring this'
' UPDATE. %s', update_msg)
return
if msg_rf in (RF_IPv4_VPN, RF_IPv6_VPN):
# Check if we have Extended Communities Attribute.
# TODO(PH): Check if RT_NLRI afi/safi will ever have this attribute
@ -1784,6 +1837,7 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
return
next_hop = mpreach_nlri_attr.next_hop
# Nothing to do if we do not have any new NLRIs in this message.
msg_nlri_list = mpreach_nlri_attr.nlri
if not msg_nlri_list:
@ -1846,11 +1900,9 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
# Check if this route family is among supported route families.
if msg_rf not in SUPPORTED_GLOBAL_RF:
LOG.info(
(
'Received route for route family %s which is'
' not supported. Ignoring withdraws form this UPDATE.'
) % msg_rf
)
'Received route family %s is not supported. '
'Ignoring withdraw routes on this UPDATE message.',
msg_rf)
return
w_nlris = mp_unreach_attr.withdrawn_routes
@ -2183,8 +2235,14 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
# routing information contained in that UPDATE message to other
# internal peers (unless the speaker acts as a BGP Route
# Reflector) [RFC4271].
if (self.remote_as == self._core_service.asn and
self.remote_as == path.source.remote_as):
if (self.remote_as == self._core_service.asn
and self.remote_as == path.source.remote_as
and isinstance(path.source, Peer)
and not path.source.is_route_reflector_client
and not self.is_route_reflector_client):
LOG.debug(
'Skipping sending iBGP route to iBGP peer %s AS %s',
self.ip_address, self.remote_as)
return
# If new best path has community attribute, it should be taken into

View File

@ -30,6 +30,8 @@ from ryu.lib.packet.bgp import BGP_ATTR_TYPE_AS_PATH
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_LOCAL_PREF
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_MULTI_EXIT_DISC
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_ORIGIN
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_ORIGINATOR_ID
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_CLUSTER_LIST
from ryu.lib.packet.bgp import BGP_ATTR_ORIGIN_IGP
from ryu.lib.packet.bgp import BGP_ATTR_ORIGIN_EGP
from ryu.lib.packet.bgp import BGP_ATTR_ORIGIN_INCOMPLETE
@ -107,7 +109,7 @@ class BgpProcessor(Activity):
dest_processed = 0
LOG.debug('Processing destination...')
while (dest_processed < self.work_units_per_cycle and
not self._dest_queue.is_empty()):
not self._dest_queue.is_empty()):
# We process the first destination in the queue.
next_dest = self._dest_queue.pop_first()
if next_dest:
@ -169,6 +171,7 @@ BPR_MED = 'MED'
BPR_ASN = 'ASN'
BPR_IGP_COST = 'IGP Cost'
BPR_ROUTER_ID = 'Router ID'
BPR_CLUSTER_LIST = 'Cluster List'
def _compare_by_version(path1, path2):
@ -212,6 +215,8 @@ def compute_best_path(local_asn, path1, path2):
9. Select the route with the lowest IGP cost to the next hop.
10. Select the route received from the peer with the lowest BGP
router ID.
11. Select the route received from the peer with the shorter
CLUSTER_LIST length.
Returns None if best-path among given paths cannot be computed else best
path.
@ -251,10 +256,13 @@ def compute_best_path(local_asn, path1, path2):
if best_path is None:
best_path = _cmp_by_router_id(local_asn, path1, path2)
best_path_reason = BPR_ROUTER_ID
if best_path is None:
best_path = _cmp_by_cluster_list(path1, path2)
best_path_reason = BPR_CLUSTER_LIST
if best_path is None:
best_path_reason = BPR_UNKNOWN
return (best_path, best_path_reason)
return best_path, best_path_reason
def _cmp_by_reachable_nh(path1, path2):
@ -462,10 +470,14 @@ def _cmp_by_router_id(local_asn, path1, path2):
else:
return path_source.remote_as
def get_router_id(path_source, local_bgp_id):
def get_router_id(path, local_bgp_id):
path_source = path.source
if path_source is None:
return local_bgp_id
else:
originator_id = path.get_pattr(BGP_ATTR_TYPE_ORIGINATOR_ID)
if originator_id:
return originator_id.value
return path_source.protocol.recv_open_msg.bgp_identifier
path_source1 = path1.source
@ -482,7 +494,7 @@ def _cmp_by_router_id(local_asn, path1, path2):
is_ebgp2 = asn2 != local_asn
# If both paths are from eBGP peers, then according to RFC we need
# not tie break using router id.
if (is_ebgp1 and is_ebgp2):
if is_ebgp1 and is_ebgp2:
return None
if ((is_ebgp1 is True and is_ebgp2 is False) or
@ -497,8 +509,8 @@ def _cmp_by_router_id(local_asn, path1, path2):
local_bgp_id = path_source2.protocol.sent_open_msg.bgp_identifier
# Get router ids.
router_id1 = get_router_id(path_source1, local_bgp_id)
router_id2 = get_router_id(path_source2, local_bgp_id)
router_id1 = get_router_id(path1, local_bgp_id)
router_id2 = get_router_id(path2, local_bgp_id)
# If both router ids are same/equal we cannot decide.
# This case is possible since router ids are arbitrary.
@ -507,7 +519,31 @@ def _cmp_by_router_id(local_asn, path1, path2):
# Select the path with lowest router Id.
from ryu.services.protocols.bgp.utils.bgp import from_inet_ptoi
if (from_inet_ptoi(router_id1) < from_inet_ptoi(router_id2)):
if from_inet_ptoi(router_id1) < from_inet_ptoi(router_id2):
return path1
else:
return path2
def _cmp_by_cluster_list(path1, path2):
"""Selects the route received from the peer with the shorter
CLUSTER_LIST length. [RFC4456]
The CLUSTER_LIST length is evaluated as zero if a route does not
carry the CLUSTER_LIST attribute.
"""
def _get_cluster_list_len(path):
c_list = path.get_pattr(BGP_ATTR_TYPE_CLUSTER_LIST)
if c_list is None:
return 0
else:
return len(c_list.value)
c_list_len1 = _get_cluster_list_len(path1)
c_list_len2 = _get_cluster_list_len(path2)
if c_list_len1 < c_list_len2:
return path1
elif c_list_len1 > c_list_len2:
return path2
else:
return None

View File

@ -37,6 +37,7 @@ LOG = logging.getLogger('bgpspeaker.rtconf.common')
# Global configuration settings.
LOCAL_AS = 'local_as'
ROUTER_ID = 'router_id'
CLUSTER_ID = 'cluster_id'
LABEL_RANGE = 'label_range'
LABEL_RANGE_MAX = 'max'
LABEL_RANGE_MIN = 'min'
@ -121,6 +122,16 @@ def validate_router_id(router_id):
return router_id
@validate(name=CLUSTER_ID)
def validate_router_id(cluster_id):
if not isinstance(cluster_id, str):
raise ConfigTypeError(conf_name=CLUSTER_ID)
if not is_valid_ipv4(cluster_id):
raise ConfigValueError(desc='Invalid cluster id %s' % cluster_id)
return cluster_id
@validate(name=REFRESH_STALEPATH_TIME)
def validate_refresh_stalepath_time(rst):
if not isinstance(rst, numbers.Integral):
@ -226,7 +237,8 @@ class CommonConf(BaseConf):
TCP_CONN_TIMEOUT,
BGP_CONN_RETRY_TIME,
MAX_PATH_EXT_RTFILTER_ALL,
ALLOW_LOCAL_AS_IN_COUNT])
ALLOW_LOCAL_AS_IN_COUNT,
CLUSTER_ID])
def __init__(self, **kwargs):
super(CommonConf, self).__init__(**kwargs)
@ -250,6 +262,8 @@ class CommonConf(BaseConf):
self._settings[MAX_PATH_EXT_RTFILTER_ALL] = compute_optional_conf(
MAX_PATH_EXT_RTFILTER_ALL, DEFAULT_MAX_PATH_EXT_RTFILTER_ALL,
**kwargs)
self._settings[CLUSTER_ID] = compute_optional_conf(
CLUSTER_ID, kwargs[ROUTER_ID], **kwargs)
# =========================================================================
# Required attributes
@ -266,6 +280,9 @@ class CommonConf(BaseConf):
# =========================================================================
# Optional attributes with valid defaults.
# =========================================================================
@property
def cluster_id(self):
return self._settings[CLUSTER_ID]
@property
def allow_local_as_in_count(self):

View File

@ -86,6 +86,7 @@ PASSWORD = 'password'
IN_FILTER = 'in_filter'
OUT_FILTER = 'out_filter'
IS_ROUTE_SERVER_CLIENT = 'is_route_server_client'
IS_ROUTE_REFLECTOR_CLIENT = 'is_route_reflector_client'
CHECK_FIRST_AS = 'check_first_as'
ATTRIBUTE_MAP = 'attribute_map'
IS_NEXT_HOP_SELF = 'is_next_hop_self'
@ -110,6 +111,7 @@ DEFAULT_CAP_RTC = False
DEFAULT_IN_FILTER = []
DEFAULT_OUT_FILTER = []
DEFAULT_IS_ROUTE_SERVER_CLIENT = False
DEFAULT_IS_ROUTE_REFLECTOR_CLIENT = False
DEFAULT_CHECK_FIRST_AS = False
DEFAULT_IS_NEXT_HOP_SELF = False
DEFAULT_CONNECT_MODE = CONNECT_MODE_BOTH
@ -264,6 +266,15 @@ def validate_is_route_server_client(is_route_server_client):
return is_route_server_client
@validate(name=IS_ROUTE_REFLECTOR_CLIENT)
def validate_is_route_reflector_client(is_route_reflector_client):
if is_route_reflector_client not in (True, False):
raise ConfigValueError(desc='Invalid is_route_reflector_client(%s)' %
is_route_reflector_client)
return is_route_reflector_client
@validate(name=CHECK_FIRST_AS)
def validate_check_first_as(check_first_as):
if check_first_as not in (True, False):
@ -312,7 +323,9 @@ class NeighborConf(ConfWithId, ConfWithStats):
LOCAL_ADDRESS, LOCAL_PORT, LOCAL_AS,
PEER_NEXT_HOP, PASSWORD,
IN_FILTER, OUT_FILTER,
IS_ROUTE_SERVER_CLIENT, CHECK_FIRST_AS,
IS_ROUTE_SERVER_CLIENT,
IS_ROUTE_REFLECTOR_CLIENT,
CHECK_FIRST_AS,
IS_NEXT_HOP_SELF, CONNECT_MODE])
def __init__(self, **kwargs):
@ -351,6 +364,9 @@ class NeighborConf(ConfWithId, ConfWithStats):
self._settings[IS_ROUTE_SERVER_CLIENT] = compute_optional_conf(
IS_ROUTE_SERVER_CLIENT,
DEFAULT_IS_ROUTE_SERVER_CLIENT, **kwargs)
self._settings[IS_ROUTE_REFLECTOR_CLIENT] = compute_optional_conf(
IS_ROUTE_REFLECTOR_CLIENT,
DEFAULT_IS_ROUTE_REFLECTOR_CLIENT, **kwargs)
self._settings[CHECK_FIRST_AS] = compute_optional_conf(
CHECK_FIRST_AS, DEFAULT_CHECK_FIRST_AS, **kwargs)
self._settings[IS_NEXT_HOP_SELF] = compute_optional_conf(
@ -559,6 +575,10 @@ class NeighborConf(ConfWithId, ConfWithStats):
def is_route_server_client(self):
return self._settings[IS_ROUTE_SERVER_CLIENT]
@property
def is_route_reflector_client(self):
return self._settings[IS_ROUTE_REFLECTOR_CLIENT]
@property
def check_first_as(self):
return self._settings[CHECK_FIRST_AS]