mirror of
https://github.com/faucetsdn/ryu.git
synced 2026-05-08 22:06:10 +02:00
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:
parent
e06ec47232
commit
ef3eefb2ad
@ -3112,7 +3112,7 @@ class BGPPathAttributeOriginatorId(_PathAttribute):
|
||||
_VALUE_PACK_STR = '!4s'
|
||||
_ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL
|
||||
_TYPE = {
|
||||
'ascii': [
|
||||
'asciilist': [
|
||||
'value'
|
||||
]
|
||||
}
|
||||
|
||||
@ -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...')
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user