Hi Jordi,
Here is a patch to implements Route Reflector features into Ryu BGPSpeaker.
Could you test my patch?
Thanks,
Iwase
On 2017年02月07日 13:52, Iwase Yusuke wrote:
> Hi Jordi,
>
> Currently, Ryu BGPSpeaker seems not to provide the features for Route
> Reflector.
>
> I found "is_route_server_client" option for connecting to neighbors with
> Route Server mode, though...
>
> http://ryu.readthedocs.io/en/latest/library_bgp_speaker_ref.html#ryu.services.protocols.bgp.bgpspeaker.BGPSpeaker.neighbor_add
>
> Thanks,
> Iwase
>
> On 2017年02月04日 21:52, Jordi Bisbal wrote:
>> Hey,
>>
>> Is there any example of how to implement a Route Reflector (RR) using the
>> BGP speaker library in Ryu? Or do you have any idea on how to configure it?
>> I have already deployed a full mesh IBGP architecture, but I would like to
>> implement it also with a RR.
>>
>> Thank you very much,
>>
>> Jordi Bisbal
>>
>>
>> ------------------------------------------------------------------------------
>> Check out the vibrant tech community on one of the world's most
>> engaging tech sites, SlashDot.org! http://sdm.link/slashdot
>>
>>
>>
>> _______________________________________________
>> Ryu-devel mailing list
>> [email protected]
>> https://lists.sourceforge.net/lists/listinfo/ryu-devel
>>
>From e645aa80020a5f4cc079c1bc614d93baf028bf1e Mon Sep 17 00:00:00 2001
From: IWASE Yusuke <[email protected]>
Date: Tue, 7 Feb 2017 16:58:57 +0900
Subject: [PATCH] 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 <[email protected]>
---
ryu/lib/packet/bgp.py | 2 +-
ryu/services/protocols/bgp/bgpspeaker.py | 28 ++++++++++++++---
ryu/services/protocols/bgp/peer.py | 43 +++++++++++++++++++++++---
ryu/services/protocols/bgp/processor.py | 18 +++++++----
ryu/services/protocols/bgp/rtconf/common.py | 19 +++++++++++-
ryu/services/protocols/bgp/rtconf/neighbors.py | 22 ++++++++++++-
6 files changed, 114 insertions(+), 18 deletions(-)
diff --git a/ryu/lib/packet/bgp.py b/ryu/lib/packet/bgp.py
index 29c10a4..0cb41d1 100644
--- a/ryu/lib/packet/bgp.py
+++ b/ryu/lib/packet/bgp.py
@@ -2761,7 +2761,7 @@ class BGPPathAttributeClusterList(_PathAttribute):
_VALUE_PACK_STR = '!4s'
_ATTR_FLAGS = BGP_ATTR_FLAG_OPTIONAL
_TYPE = {
- 'ascii': [
+ 'asciilist': [
'value'
]
}
diff --git a/ryu/services/protocols/bgp/bgpspeaker.py b/ryu/services/protocols/bgp/bgpspeaker.py
index 6a0025c..981e8cf 100644
--- a/ryu/services/protocols/bgp/bgpspeaker.py
+++ b/ryu/services/protocols/bgp/bgpspeaker.py
@@ -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 (
@@ -84,8 +85,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
@@ -174,7 +179,8 @@ class BGPSpeaker(object):
peer_up_handler=None,
ssh_console=False,
ssh_port=None, ssh_host=None, ssh_host_key=None,
- label_range=DEFAULT_LABEL_RANGE):
+ label_range=DEFAULT_LABEL_RANGE,
+ cluster_id=None):
"""Create a new BGPSpeaker object with as_number and router_id to
listen on bgp_server_port.
@@ -222,6 +228,10 @@ class BGPSpeaker(object):
``label_range`` specifies the range of MPLS labels generated
automatically.
+
+ ``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__()
@@ -232,6 +242,7 @@ class BGPSpeaker(object):
REFRESH_STALEPATH_TIME: refresh_stalepath_time,
REFRESH_MAX_EOR_TIME: refresh_max_eor_time,
LABEL_RANGE: label_range,
+ CLUSTER_ID: cluster_id,
}
self._core_start(settings)
self._init_signal_listeners()
@@ -312,8 +323,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
@@ -364,6 +378,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.
@@ -387,6 +404,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,
diff --git a/ryu/services/protocols/bgp/peer.py b/ryu/services/protocols/bgp/peer.py
index 8bf96d6..4a37fb4 100644
--- a/ryu/services/protocols/bgp/peer.py
+++ b/ryu/services/protocols/bgp/peer.py
@@ -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
@@ -439,6 +443,10 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
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,34 @@ 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(path.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 path.pathattr_map:
+ originator_id = path.source
+ if isinstance(path.source, Peer):
+ originator_id = path.source.ip_address
+ new_pathattr.append(
+ BGPPathAttributeOriginatorId(originator_id))
+
+ # Append CLUSTER_LIST attribute if not already exists.
+ if BGP_ATTR_TYPE_CLUSTER_LIST not in path.pathattr_map:
+ new_pathattr.append(
+ BGPPathAttributeClusterList(
+ [self._common_conf.cluster_id]))
+
+ for t, path_attr in path.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)
+ 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
@@ -2183,8 +2217,9 @@ 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 (not self.is_route_reflector_client
+ and self.remote_as == self._core_service.asn
+ and self.remote_as == path.source.remote_as):
return
# If new best path has community attribute, it should be taken into
diff --git a/ryu/services/protocols/bgp/processor.py b/ryu/services/protocols/bgp/processor.py
index 086b777..089973f 100644
--- a/ryu/services/protocols/bgp/processor.py
+++ b/ryu/services/protocols/bgp/processor.py
@@ -30,6 +30,7 @@ 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_ORIGIN_IGP
from ryu.lib.packet.bgp import BGP_ATTR_ORIGIN_EGP
from ryu.lib.packet.bgp import BGP_ATTR_ORIGIN_INCOMPLETE
@@ -107,7 +108,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:
@@ -462,10 +463,15 @@ 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:
+ if BGP_ATTR_TYPE_ORIGINATOR_ID in path.pathattr_map:
+ originator_id = path.pathattr_map.get(
+ BGP_ATTR_TYPE_ORIGINATOR_ID)
+ return originator_id.value
return path_source.protocol.recv_open_msg.bgp_identifier
path_source1 = path1.source
@@ -482,7 +488,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 +503,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 +513,7 @@ 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
diff --git a/ryu/services/protocols/bgp/rtconf/common.py b/ryu/services/protocols/bgp/rtconf/common.py
index acf4634..f2426de 100644
--- a/ryu/services/protocols/bgp/rtconf/common.py
+++ b/ryu/services/protocols/bgp/rtconf/common.py
@@ -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'
@@ -104,6 +105,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):
@@ -208,7 +219,8 @@ class CommonConf(BaseConf):
LABEL_RANGE, BGP_SERVER_PORT,
TCP_CONN_TIMEOUT,
BGP_CONN_RETRY_TIME,
- MAX_PATH_EXT_RTFILTER_ALL])
+ MAX_PATH_EXT_RTFILTER_ALL,
+ CLUSTER_ID])
def __init__(self, **kwargs):
super(CommonConf, self).__init__(**kwargs)
@@ -230,6 +242,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
@@ -246,6 +260,9 @@ class CommonConf(BaseConf):
# =========================================================================
# Optional attributes with valid defaults.
# =========================================================================
+ @property
+ def cluster_id(self):
+ return self._settings[CLUSTER_ID]
@property
def bgp_conn_retry_time(self):
diff --git a/ryu/services/protocols/bgp/rtconf/neighbors.py b/ryu/services/protocols/bgp/rtconf/neighbors.py
index 3b2d5b7..c02d1ef 100644
--- a/ryu/services/protocols/bgp/rtconf/neighbors.py
+++ b/ryu/services/protocols/bgp/rtconf/neighbors.py
@@ -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(
@@ -560,6 +576,10 @@ class NeighborConf(ConfWithId, ConfWithStats):
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]
--
2.7.4
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, SlashDot.org! http://sdm.link/slashdot
_______________________________________________
Ryu-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ryu-devel