diff options
author | Albert Siersema <albert@mediacaster.nl> | 2017-02-06 14:07:58 +0000 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2017-02-11 14:59:51 +0900 |
commit | 15a12a7a6af10db3e5c3726a6e099e0ed6908007 (patch) | |
tree | d962598822ce71c7a3db22bcbd31f43c9528fa72 | |
parent | e13f46b2563cf7e6023dafe43fb2946711cdc049 (diff) |
allow_local_as_in_count : allow local ASN in AS patch, e.g. "rd auto/route-target both auto"
Cisco/Juniper/Cumulus and undoubtedly more vendors offer a
simplification of the EVPN configuration by not having to
specify individual RD/RT's, e.g. in Cisco NX-OS syntax:
evpn
vni 10311 l2
rd auto
route-target import auto
route-target export auto
This simplifies/unifies the configuration.
All leaf switches/ryu instances share the same ASN (e.g. 65511).
Spine switches in the same layer can share an ASN as well (e.g. 65510).
To facilitate the above, the local ASN has to be accepted in the AS path.
To this end, Cisco includes an 'allowas-in' configuration statement.
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"
This patch offers the possibility to specify the number of accepted
occurrences
of the local ASN in incoming AS paths. The default value is 0, which basically
is the same as the code before this patch: local ASN in the path
indicates a loop.
Non-zero values allow the local ASN to appear the specified number of times in
the AS path. A maximum value of 3 should satisfy most deployments.
Signed-off-by: Albert Siersema <albert@mediacaster.nl>
-rw-r--r-- | ryu/lib/packet/bgp.py | 9 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/bgpspeaker.py | 12 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/peer.py | 4 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/rtconf/common.py | 26 |
4 files changed, 42 insertions, 9 deletions
diff --git a/ryu/lib/packet/bgp.py b/ryu/lib/packet/bgp.py index c787d82e..977b7836 100644 --- a/ryu/lib/packet/bgp.py +++ b/ryu/lib/packet/bgp.py @@ -2806,13 +2806,12 @@ class _BGPPathAttributeAsPathCommon(_PathAttribute): return count - def has_local_as(self, local_as): + def has_local_as(self, local_as, max_count=0): """Check if *local_as* is already present on path list.""" + _count = 0 for as_path_seg in self.value: - for as_num in as_path_seg: - if as_num == local_as: - return True - return False + _count += list(as_path_seg).count(local_as) + return _count > max_count def has_matching_leftmost(self, remote_as): """Check if leftmost AS matches *remote_as*.""" diff --git a/ryu/services/protocols/bgp/bgpspeaker.py b/ryu/services/protocols/bgp/bgpspeaker.py index d82be624..078f1a6c 100644 --- a/ryu/services/protocols/bgp/bgpspeaker.py +++ b/ryu/services/protocols/bgp/bgpspeaker.py @@ -63,6 +63,7 @@ from ryu.services.protocols.bgp.rtconf.common import DEFAULT_LABEL_RANGE 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_COUNT 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_count=0): """Create a new BGPSpeaker object with as_number and router_id to listen on bgp_server_port. @@ -222,7 +224,14 @@ class BGPSpeaker(object): ``label_range`` specifies the range of MPLS labels generated automatically. + + ``allow_local_as_in_count`` maximum number of local AS number + occurrences in AS_PATH. This option is useful for e.g. auto RD/RT + 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). """ + super(BGPSpeaker, self).__init__() settings = { @@ -232,6 +241,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_COUNT: allow_local_as_in_count, } self._core_start(settings) self._init_signal_listeners() diff --git a/ryu/services/protocols/bgp/peer.py b/ryu/services/protocols/bgp/peer.py index 8bf96d62..d380e305 100644 --- a/ryu/services/protocols/bgp/peer.py +++ b/ryu/services/protocols/bgp/peer.py @@ -1622,7 +1622,7 @@ class Peer(Source, Sink, NeighborConfListener, Activity): aspath = umsg_pattrs.get(BGP_ATTR_TYPE_AS_PATH) # Check if AS_PATH has loops. - if aspath.has_local_as(self.local_as): + 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 @@ -1750,7 +1750,7 @@ class Peer(Source, Sink, NeighborConfListener, Activity): aspath = umsg_pattrs.get(BGP_ATTR_TYPE_AS_PATH) # Check if AS_PATH has loops. - if aspath.has_local_as(self.local_as): + 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 diff --git a/ryu/services/protocols/bgp/rtconf/common.py b/ryu/services/protocols/bgp/rtconf/common.py index acf4634f..806ab453 100644 --- a/ryu/services/protocols/bgp/rtconf/common.py +++ b/ryu/services/protocols/bgp/rtconf/common.py @@ -41,6 +41,12 @@ LABEL_RANGE = 'label_range' LABEL_RANGE_MAX = 'max' LABEL_RANGE_MIN = 'min' +# Similar to Cisco command 'allowas-in'. Allows the local ASN in the path. +# Facilitates auto rd, auto rt import/export +# ("rd auto/route-target both auto") and simplified spine/leaf architectures, +# sharing an ASN between e.g. leafs. +ALLOW_LOCAL_AS_IN_COUNT = 'allow_local_as_in_count' + # Configuration that can be set at global level as well as per context # (session/vrf) level # Nested configuration override global or higher level configuration as they @@ -80,6 +86,17 @@ DEFAULT_MED = 0 DEFAULT_MAX_PATH_EXT_RTFILTER_ALL = True +@validate(name=ALLOW_LOCAL_AS_IN_COUNT) +def validate_allow_local_as_in_count(count): + if not isinstance(count, numbers.Integral): + raise ConfigTypeError(desc=('Configuration value for %s has to be ' + 'integral type' % ALLOW_LOCAL_AS_IN_COUNT)) + if count < 0: + raise ConfigValueError(desc='Invalid local AS count %s' % count) + + return count + + @validate(name=LOCAL_AS) def validate_local_as(asn): if asn is None: @@ -208,13 +225,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_COUNT]) 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_COUNT] = compute_optional_conf( + ALLOW_LOCAL_AS_IN_COUNT, 0, **kwargs) self._settings[LABEL_RANGE] = compute_optional_conf( LABEL_RANGE, DEFAULT_LABEL_RANGE, **kwargs) self._settings[REFRESH_STALEPATH_TIME] = compute_optional_conf( @@ -248,6 +268,10 @@ class CommonConf(BaseConf): # ========================================================================= @property + def allow_local_as_in_count(self): + return self._settings[ALLOW_LOCAL_AS_IN_COUNT] + + @property def bgp_conn_retry_time(self): return self._settings[BGP_CONN_RETRY_TIME] |