Hello,

See attached patch to support updates with local ASN in the path.
The use case for this patch is auto RD/RT and simplified leaf/spine architectures, see below.

Cisco/Juniper/Cumulus and undoubtedly more vendors offer a simplification/generalization of the EVPN configuration by not having to specify RD/RT's, e.g. in Cisco NX-OS syntax:

evpn
   vni 10311 l2
     rd auto
     route-target import auto
     route-target export auto

See: http://www.cisco.com/c/en/us/products/collateral/switches/nexus-9000-series-switches/guide-c07-734107.html search for "MP-eBGP Design with VTEP Leaf Nodes in the Same BGP Autonomous System"

In short:
To further simplify configuration (and facilitate auto RD) all physical leaf switches share the same ASN (e.g. 65511). All spine switches in turn share an ASN as well (e.g. 65510). This means an AS path has to be accepted with the local ASN in it. To this end, Cisco includes an 'allowas-in' configuration statement.

Currently, my patch is a simple boolean value and a check on this boolean to allow the local ASN in the loop check. A better approach might be test for a maximum number of occurrences of the ASN and/or check if there is a non-local ASN in the path as well.
What do you guys think ?

Cheers,
Albert Siersema

diff -uprN ryu_git/ryu/services/protocols/bgp/bgpspeaker.py ryu_allowasin/ryu/services/protocols/bgp/bgpspeaker.py
--- ryu_git/ryu/services/protocols/bgp/bgpspeaker.py	2017-01-16 18:05:25.372542027 +0100
+++ ryu_allowasin/ryu/services/protocols/bgp/bgpspeaker.py	2017-02-01 15:52:40.465882853 +0100
@@ -63,6 +63,7 @@ from ryu.services.protocols.bgp.rtconf.c
 from ryu.services.protocols.bgp.rtconf.common import REFRESH_MAX_EOR_TIME
 from ryu.services.protocols.bgp.rtconf.common import REFRESH_STALEPATH_TIME
 from ryu.services.protocols.bgp.rtconf.common import LABEL_RANGE
+from ryu.services.protocols.bgp.rtconf.common import ALLOW_LOCAL_AS_IN
 from ryu.services.protocols.bgp.rtconf import neighbors
 from ryu.services.protocols.bgp.rtconf import vrfs
 from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_IPV4
@@ -174,7 +175,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,
+                 allow_local_as_in=False):
         """Create a new BGPSpeaker object with as_number and router_id to
         listen on bgp_server_port.
 
@@ -232,6 +234,7 @@ class BGPSpeaker(object):
             REFRESH_STALEPATH_TIME: refresh_stalepath_time,
             REFRESH_MAX_EOR_TIME: refresh_max_eor_time,
             LABEL_RANGE: label_range,
+            ALLOW_LOCAL_AS_IN: allow_local_as_in,
         }
         self._core_start(settings)
         self._init_signal_listeners()
diff -uprN ryu_git/ryu/services/protocols/bgp/peer.py ryu_allowasin/ryu/services/protocols/bgp/peer.py
--- ryu_git/ryu/services/protocols/bgp/peer.py	2017-01-16 18:05:25.376542037 +0100
+++ ryu_allowasin/ryu/services/protocols/bgp/peer.py	2017-02-01 15:54:35.986179005 +0100
@@ -1623,9 +1623,12 @@ class Peer(Source, Sink, NeighborConfLis
         aspath = umsg_pattrs.get(BGP_ATTR_TYPE_AS_PATH)
         # Check if AS_PATH has loops.
         if aspath.has_local_as(self.local_as):
-            LOG.error('Update message AS_PATH has loops. Ignoring this'
-                      ' UPDATE. %s', update_msg)
-            return
+            if self._common_conf.allow_local_as_in:
+               LOG.info('Allowing update message AS_PATH with own AS {}'.format(update_msg))
+            else:
+                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.
@@ -1751,9 +1754,12 @@ class Peer(Source, Sink, NeighborConfLis
         aspath = umsg_pattrs.get(BGP_ATTR_TYPE_AS_PATH)
         # Check if AS_PATH has loops.
         if aspath.has_local_as(self.local_as):
-            LOG.error('Update message AS_PATH has loops. Ignoring this'
-                      ' UPDATE. %s', update_msg)
-            return
+            if self._common_conf.allow_local_as_in:
+               LOG.info('Allowing update message AS_PATH with own AS {}'.format(update_msg))
+            else:
+                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.
diff -uprN ryu_git/ryu/services/protocols/bgp/rtconf/common.py ryu_allowasin/ryu/services/protocols/bgp/rtconf/common.py
--- ryu_git/ryu/services/protocols/bgp/rtconf/common.py	2017-01-16 18:05:25.376542037 +0100
+++ ryu_allowasin/ryu/services/protocols/bgp/rtconf/common.py	2017-02-01 15:44:45.868665461 +0100
@@ -40,6 +40,7 @@ ROUTER_ID = 'router_id'
 LABEL_RANGE = 'label_range'
 LABEL_RANGE_MAX = 'max'
 LABEL_RANGE_MIN = 'min'
+ALLOW_LOCAL_AS_IN = 'allow_local_as_in'
 
 # Configuration that can be set at global level as well as per context
 # (session/vrf) level
@@ -80,6 +81,11 @@ DEFAULT_MED = 0
 DEFAULT_MAX_PATH_EXT_RTFILTER_ALL = True
 
 
+@validate(name=ALLOW_LOCAL_AS_IN)
+def validate_allow_local_as_in(allowit):
+    return allowit
+
+
 @validate(name=LOCAL_AS)
 def validate_local_as(asn):
     if asn is None:
@@ -208,13 +214,16 @@ 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,
+                                   ALLOW_LOCAL_AS_IN])
 
     def __init__(self, **kwargs):
         super(CommonConf, self).__init__(**kwargs)
 
     def _init_opt_settings(self, **kwargs):
         super(CommonConf, self)._init_opt_settings(**kwargs)
+        self._settings[ALLOW_LOCAL_AS_IN] = compute_optional_conf(
+            ALLOW_LOCAL_AS_IN, False, **kwargs)
         self._settings[LABEL_RANGE] = compute_optional_conf(
             LABEL_RANGE, DEFAULT_LABEL_RANGE, **kwargs)
         self._settings[REFRESH_STALEPATH_TIME] = compute_optional_conf(
@@ -248,6 +257,10 @@ class CommonConf(BaseConf):
     # =========================================================================
 
     @property
+    def allow_local_as_in(self):
+        return self._settings[ALLOW_LOCAL_AS_IN]
+
+    @property
     def bgp_conn_retry_time(self):
         return self._settings[BGP_CONN_RETRY_TIME]
 
------------------------------------------------------------------------------
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