summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--doc/source/library_bgp_speaker_ref.rst6
-rw-r--r--ryu/services/protocols/bgp/api/rtconf.py20
-rw-r--r--ryu/services/protocols/bgp/bgpspeaker.py44
-rw-r--r--ryu/services/protocols/bgp/info_base/base.py217
-rw-r--r--ryu/services/protocols/bgp/peer.py53
-rw-r--r--ryu/services/protocols/bgp/rtconf/neighbors.py15
6 files changed, 354 insertions, 1 deletions
diff --git a/doc/source/library_bgp_speaker_ref.rst b/doc/source/library_bgp_speaker_ref.rst
index 86f520ee..ac3a3be2 100644
--- a/doc/source/library_bgp_speaker_ref.rst
+++ b/doc/source/library_bgp_speaker_ref.rst
@@ -13,3 +13,9 @@ BGPSpeaker class
.. autoclass:: ryu.services.protocols.bgp.info_base.base.PrefixFilter
:members:
+
+.. autoclass:: ryu.services.protocols.bgp.info_base.base.ASPathFilter
+ :members:
+
+.. autoclass:: ryu.services.protocols.bgp.info_base.base.AttributeMap
+ :members:
diff --git a/ryu/services/protocols/bgp/api/rtconf.py b/ryu/services/protocols/bgp/api/rtconf.py
index c11918c7..ce989037 100644
--- a/ryu/services/protocols/bgp/api/rtconf.py
+++ b/ryu/services/protocols/bgp/api/rtconf.py
@@ -150,10 +150,30 @@ def set_neighbor_in_filter(neigh_ip_address, filters):
return True
+@RegisterWithArgChecks(name='neighbor.attribute_map.set',
+ req_args=[neighbors.IP_ADDRESS,
+ neighbors.ATTRIBUTE_MAP])
+def set_neighbor_attribute_map(neigh_ip_address, attribute_maps):
+ """Returns a neighbor attribute_map for given ip address if exists."""
+ core = CORE_MANAGER.get_core_service()
+ peer = core.peer_manager.get_by_addr(neigh_ip_address)
+ peer.attribute_maps = attribute_maps
+ return True
+
+
+@RegisterWithArgChecks(name='neighbor.attribute_map.get',
+ req_args=[neighbors.IP_ADDRESS])
+def get_neighbor_attribute_map(neigh_ip_address):
+ """Returns a neighbor attribute_map for given ip address if exists."""
+ core = CORE_MANAGER.get_core_service()
+ ret = core.peer_manager.get_by_addr(neigh_ip_address).attribute_maps
+ return ret
+
# =============================================================================
# VRF configuration related APIs
# =============================================================================
+
@register(name='vrf.create')
def create_vrf(**kwargs):
vrf_conf = VrfConf(**kwargs)
diff --git a/ryu/services/protocols/bgp/bgpspeaker.py b/ryu/services/protocols/bgp/bgpspeaker.py
index 3cba1f82..9844e087 100644
--- a/ryu/services/protocols/bgp/bgpspeaker.py
+++ b/ryu/services/protocols/bgp/bgpspeaker.py
@@ -526,6 +526,50 @@ class BGPSpeaker(object):
param['port'] = port
call(func_name, **param)
+ def attribute_map_set(self, address, attribute_maps):
+ """This method sets attribute mapping to a neighbor.
+ attribute mapping can be used when you want to apply
+ attribute to BGPUpdate under specific conditions.
+
+ ``address`` specifies the IP address of the neighbor
+
+ ``attribute_maps`` specifies attribute_map list that are used
+ before paths are advertised. All the items in the list must
+ be an instance of AttributeMap class
+
+ We can set AttributeMap to a neighbor as follows;
+
+ pref_filter = PrefixFilter('192.168.103.0/30',
+ PrefixFilter.POLICY_PERMIT)
+
+ attribute_map = AttributeMap([pref_filter],
+ AttributeMap.ATTR_LOCAL_PREF, 250)
+
+ speaker.attribute_map_set('192.168.50.102', [attribute_map])
+
+ """
+
+ func_name = 'neighbor.attribute_map.set'
+ param = {}
+ param[neighbors.IP_ADDRESS] = address
+ param[neighbors.ATTRIBUTE_MAP] = attribute_maps
+ call(func_name, **param)
+
+ def attribute_map_get(self, address):
+ """This method gets in-bound filters of the specified neighbor.
+
+ ``address`` specifies the IP address of the neighbor.
+
+ Returns a list object containing an instance of AttributeMap
+
+ """
+
+ func_name = 'neighbor.attribute_map.get'
+ param = {}
+ param[neighbors.IP_ADDRESS] = address
+ attribute_maps = call(func_name, **param)
+ return attribute_maps
+
@staticmethod
def _check_rf_and_normalize(prefix):
""" check prefix's route_family and if the address is
diff --git a/ryu/services/protocols/bgp/info_base/base.py b/ryu/services/protocols/bgp/info_base/base.py
index 79bba2cb..13f02caf 100644
--- a/ryu/services/protocols/bgp/info_base/base.py
+++ b/ryu/services/protocols/bgp/info_base/base.py
@@ -28,6 +28,8 @@ import netaddr
from ryu.lib.packet.bgp import RF_IPv4_UC
from ryu.lib.packet.bgp import RouteTargetMembershipNLRI
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_EXTENDED_COMMUNITIES
+from ryu.lib.packet.bgp import BGPPathAttributeLocalPref
+from ryu.lib.packet.bgp import BGP_ATTR_TYPE_AS_PATH
from ryu.services.protocols.bgp.base import OrderedDict
from ryu.services.protocols.bgp.constants import VPN_TABLE
@@ -984,3 +986,218 @@ class PrefixFilter(Filter):
policy=self._policy,
ge=self._ge,
le=self._le)
+
+
+class ASPathFilter(Filter):
+ """
+ used to specify a prefix for AS_PATH attribute.
+
+ We can create ASPathFilter object as follows;
+
+ * as_path_filter = ASPathFilter(65000,policy=ASPathFilter.TOP)
+
+ ================ ==================================================
+ Attribute Description
+ ================ ==================================================
+ as_number A AS number used for this filter
+ policy ASPathFilter.POLICY_TOP and PrefixFilter.POLICY_END,
+ ASPathFilter.POLICY_INCLUDE and
+ PrefixFilter.POLICY_NOT_INCLUDE are available.
+ ================ ==================================================
+
+ Meaning of each policy is as follows;
+
+ * POLICY_TOP :
+ Filter checks if the specified AS number is at the top of
+ AS_PATH attribute.
+
+ * POLICY_TOP :
+ Filter checks is the specified AS number
+ is at the last of AS_PATH attribute.
+
+ * POLICY_INCLUDE :
+ Filter checks if specified AS number
+ exists in AS_PATH attribute
+
+ * POLICY_NOT_INCLUDE :
+ opposite to POLICY_INCLUDE
+
+
+ """
+
+ POLICY_TOP = 2
+ POLICY_END = 3
+ POLICY_INCLUDE = 4
+ POLICY_NOT_INCLUDE = 5
+
+ def __init__(self, as_number, policy):
+ super(ASPathFilter, self).__init__(policy)
+ self._as_number = as_number
+
+ def __cmp__(self, other):
+ return cmp(self.as_number, other.as_number)
+
+ def __repr__(self):
+ policy = 'TOP'
+ if self._policy == self.POLICY_INCLUDE:
+ policy = 'INCLUDE'
+ elif self._policy == self.POLICY_NOT_INCLUDE:
+ policy = 'NOT_INCLUDE'
+ elif self._policy == self.POLICY_END:
+ policy = 'END'
+
+ return 'ASPathFilter(as_number=%s,policy=%s)'\
+ % (self._as_number, policy)
+
+ @property
+ def as_number(self):
+ return self._as_number
+
+ @property
+ def policy(self):
+ return self._policy
+
+ def evaluate(self, path):
+ """ This method evaluates as_path list.
+
+ Returns this object's policy and the result of matching.
+ If the specified AS number matches this object's AS number
+ according to the policy,
+ this method returns True as the matching result.
+
+ ``path`` specifies the path.
+
+ """
+
+ path_aspath = path.pathattr_map.get(BGP_ATTR_TYPE_AS_PATH)
+ path_seg_list = path_aspath.path_seg_list
+ path_seg = path_seg_list[0]
+ result = False
+
+ LOG.debug("path_seg : %s", path_seg)
+ if self.policy == ASPathFilter.POLICY_TOP:
+
+ if len(path_seg) > 0 and path_seg[0] == self._as_number:
+ result = True
+
+ elif self.policy == ASPathFilter.POLICY_INCLUDE:
+ for aspath in path_seg:
+ LOG.debug("POLICY_INCLUDE as_number : %s", aspath)
+ if aspath == self._as_number:
+ result = True
+ break
+
+ elif self.policy == ASPathFilter.POLICY_END:
+
+ if len(path_seg) > 0 and path_seg[-1] == self._as_number:
+ result = True
+
+ elif self.policy == ASPathFilter.POLICY_NOT_INCLUDE:
+
+ if self._as_number not in path_seg:
+ result = True
+
+ return self.policy, result
+
+ def clone(self):
+ """ This method clones ASPathFilter object.
+
+ Returns ASPathFilter object that has the same values with the
+ original one.
+
+ """
+
+ return self.__class__(self._as_number,
+ policy=self._policy)
+
+
+class AttributeMap(object):
+ """
+ This class is used to specify an attribute to add if the path matches
+ filters.
+ We can create AttributeMap object as follows;
+
+ pref_filter = PrefixFilter('192.168.103.0/30',
+ PrefixFilter.POLICY_PERMIT)
+
+ attribute_map = AttributeMap([pref_filter],
+ AttributeMap.ATTR_LOCAL_PREF, 250)
+
+ speaker.attribute_map_set('192.168.50.102', [attribute_map])
+
+ AttributeMap.ATTR_LOCAL_PREF means that 250 is set as a
+ local preference value if nlri in the path matches pref_filter.
+
+ ASPathFilter is also available as a filter. ASPathFilter checks if AS_PATH
+ attribute in the path matches AS number in the filter.
+
+ =================== ==================================================
+ Attribute Description
+ =================== ==================================================
+ filters A list of filter.
+ Each object should be a Filter class or its sub-class
+ attr_type A type of attribute to map on filters. Currently
+ AttributeMap.ATTR_LOCAL_PREF is available.
+ attr_value A attribute value
+ =================== ==================================================
+
+ """
+
+ ATTR_LOCAL_PREF = '_local_pref'
+
+ def __init__(self, filters, attr_type, attr_value):
+
+ assert all(isinstance(f, Filter) for f in filters),\
+ 'all the items in filters must be an instance of Filter sub-class'
+ self.filters = filters
+ self.attr_type = attr_type
+ self.attr_value = attr_value
+
+ def evaluate(self, path):
+ """ This method evaluates attributes of the path.
+
+ Returns the cause and result of matching.
+ Both cause and result are returned from filters
+ that this object contains.
+
+ ``path`` specifies the path.
+
+ """
+ result = False
+ cause = None
+
+ for f in self.filters:
+
+ cause, result = f.evaluate(path)
+ if not result:
+ break
+
+ return cause, result
+
+ def get_attribute(self):
+ func = getattr(self, 'get' + self.attr_type)
+ return func()
+
+ def get_local_pref(self):
+ local_pref_attr = BGPPathAttributeLocalPref(value=self.attr_value)
+ return local_pref_attr
+
+ def clone(self):
+ """ This method clones AttributeMap object.
+
+ Returns AttributeMap object that has the same values with the
+ original one.
+
+ """
+
+ cloned_filters = [f.clone() for f in self.filters]
+ return self.__class__(cloned_filters, self.attr_type, self.attr_value)
+
+ def __repr__(self):
+
+ attr_type = 'LOCAL_PREF'\
+ if self.attr_type == self.ATTR_LOCAL_PREF else None
+
+ filter_string = ','.join(repr(f) for f in self.filters)
+ return 'AttributeMap(filters=[%s],attribute_type=%s,attribute_value=%s)'\
+ % (filter_string, attr_type, self.attr_value)
diff --git a/ryu/services/protocols/bgp/peer.py b/ryu/services/protocols/bgp/peer.py
index 49c7151e..14618521 100644
--- a/ryu/services/protocols/bgp/peer.py
+++ b/ryu/services/protocols/bgp/peer.py
@@ -30,6 +30,7 @@ from ryu.services.protocols.bgp import constants as const
from ryu.services.protocols.bgp.model import OutgoingRoute
from ryu.services.protocols.bgp.model import SentRoute
from ryu.services.protocols.bgp.info_base.base import PrefixFilter
+from ryu.services.protocols.bgp.info_base.base import AttributeMap
from ryu.services.protocols.bgp.model import ReceivedRoute
from ryu.services.protocols.bgp.net_ctrl import NET_CONTROLLER
from ryu.services.protocols.bgp.rtconf.neighbors import NeighborConfListener
@@ -349,6 +350,9 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
# Adj-rib-out
self._adj_rib_out = {}
+ # attribute maps
+ self._attribute_maps = {}
+
@property
def remote_as(self):
return self._neigh_conf.remote_as
@@ -409,6 +413,29 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
def check_first_as(self):
return self._neigh_conf.check_first_as
+ @property
+ def attribute_maps(self):
+ return self._attribute_maps['__orig']\
+ if '__orig' in self._attribute_maps else []
+
+ @attribute_maps.setter
+ def attribute_maps(self, attribute_maps):
+ _attr_maps = {}
+ _attr_maps.setdefault('__orig', [])
+
+ for a in attribute_maps:
+ cloned = a.clone()
+ LOG.debug("AttributeMap attr_type: %s, attr_value: %s",
+ cloned.attr_type, cloned.attr_value)
+ attr_list = _attr_maps.setdefault(cloned.attr_type, [])
+ attr_list.append(cloned)
+
+ # preserve original order of attribute_maps
+ _attr_maps['__orig'].append(cloned)
+
+ self._attribute_maps = _attr_maps
+ self.on_update_attribute_maps()
+
def is_mpbgp_cap_valid(self, route_family):
if not self.in_established:
raise ValueError('Invalid request: Peer not in established state')
@@ -568,6 +595,14 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
sent_path.filtered = block
self.enque_outgoing_msg(outgoing_route)
+ def on_update_attribute_maps(self):
+ # resend sent_route in case of filter matching
+ LOG.debug('on_update_attribute_maps fired')
+ for sent_path in self._adj_rib_out.itervalues():
+ LOG.debug('resend path: %s' % sent_path)
+ path = sent_path.path
+ self.enque_outgoing_msg(OutgoingRoute(path))
+
def __str__(self):
return 'Peer(ip: %s, asn: %s)' % (self._neigh_conf.ip_address,
self._neigh_conf.remote_as)
@@ -869,8 +904,24 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
# LOCAL_PREF Attribute.
if not self.is_ebgp_peer():
# For iBGP peers we are required to send local-pref attribute
- # for connected or local prefixes. We send default local-pref.
+ # for connected or local prefixes. We check if the path matches
+ # attribute_maps and set local-pref value.
+ # If the path doesn't match, we set default local-pref 100.
localpref_attr = BGPPathAttributeLocalPref(100)
+ # TODO handle VPNv4Path
+ if isinstance(path, Ipv4Path):
+ if AttributeMap.ATTR_LOCAL_PREF in self._attribute_maps:
+ maps = \
+ self._attribute_maps[AttributeMap.ATTR_LOCAL_PREF]
+ for m in maps:
+ cause, result = m.evaluate(path)
+ LOG.debug(
+ "local_pref evaluation result:%s, cause:%s",
+ result, cause)
+
+ if result:
+ localpref_attr = m.get_attribute()
+ break
# COMMUNITY Attribute.
community_attr = pathattr_map.get(BGP_ATTR_TYPE_COMMUNITIES)
diff --git a/ryu/services/protocols/bgp/rtconf/neighbors.py b/ryu/services/protocols/bgp/rtconf/neighbors.py
index 3f8957ca..2e9fa862 100644
--- a/ryu/services/protocols/bgp/rtconf/neighbors.py
+++ b/ryu/services/protocols/bgp/rtconf/neighbors.py
@@ -63,6 +63,7 @@ from ryu.services.protocols.bgp.utils.validation import is_valid_ipv4
from ryu.services.protocols.bgp.utils.validation import is_valid_old_asn
from ryu.services.protocols.bgp.info_base.base import Filter
from ryu.services.protocols.bgp.info_base.base import PrefixFilter
+from ryu.services.protocols.bgp.info_base.base import AttributeMap
LOG = logging.getLogger('bgpspeaker.rtconf.neighbor')
@@ -79,6 +80,7 @@ IN_FILTER = 'in_filter'
OUT_FILTER = 'out_filter'
IS_ROUTE_SERVER_CLIENT = 'is_route_server_client'
CHECK_FIRST_AS = 'check_first_as'
+ATTRIBUTE_MAP = 'attribute_map'
# Default value constants.
DEFAULT_CAP_GR_NULL = True
@@ -212,6 +214,13 @@ def valid_filter(filter_):
return SUPPORTED_FILTER_VALIDATORS[filter_['type']](filter_)
+def valid_attribute_map(attribute_map):
+ if not isinstance(attribute_map, AttributeMap):
+ raise ConfigTypeError(desc='Invalid AttributeMap: %s' % attribute_map)
+ else:
+ return attribute_map
+
+
@validate(name=IN_FILTER)
def validate_in_filters(filters):
return [valid_filter(filter_) for filter_ in filters]
@@ -222,6 +231,12 @@ def validate_out_filters(filters):
return [valid_filter(filter_) for filter_ in filters]
+@validate(name=ATTRIBUTE_MAP)
+def validate_attribute_maps(attribute_maps):
+ return [valid_attribute_map(attribute_map)
+ for attribute_map in attribute_maps]
+
+
@validate(name=IS_ROUTE_SERVER_CLIENT)
def validate_is_route_server_client(is_route_server_client):
if is_route_server_client not in (True, False):