summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ryu/lib/packet/bgp.py9
-rw-r--r--ryu/services/protocols/bgp/bgpspeaker.py12
-rw-r--r--ryu/services/protocols/bgp/peer.py4
-rw-r--r--ryu/services/protocols/bgp/rtconf/common.py26
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]