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

Reply via email to