mirror of
https://github.com/faucetsdn/ryu.git
synced 2026-05-04 20:06:09 +02:00
BGPSpeaker: Support Ethernet VPN update messages
This patch enables BGPSpeaker to advertise BGP EVPN routes and store the advertised BGP EVPN routes from the neighbors. TODO: - To support the VRF table for BGP EVPN routes. This patch supports the global table only. - To implement Multihoming Functions. Currently, ONLY Single-Homing is supported. Signed-off-by: IWASE Yusuke <iwase.yusuke0@gmail.com> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
This commit is contained in:
parent
55e0097545
commit
985e2557b2
@ -6,8 +6,8 @@ Introduction
|
||||
============
|
||||
|
||||
Ryu BGP speaker library helps you to enable your code to speak BGP
|
||||
protocol. The library supports ipv4, ipv4 vpn, and ipv6 vpn address
|
||||
families.
|
||||
protocol. The library supports IPv4, IPv4 MPLS-labeled VPN, IPv6
|
||||
MPLS-labeled VPN and L2VPN EVPN address families.
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
@ -39,6 +39,7 @@ from ryu.lib.packet import stream_parser
|
||||
from ryu.lib import addrconv
|
||||
from ryu.lib import type_desc
|
||||
from ryu.lib.pack_utils import msg_pack_into
|
||||
from ryu.utils import binary_str
|
||||
|
||||
reduce = six.moves.reduce
|
||||
|
||||
@ -666,7 +667,7 @@ class _RouteDistinguisher(StringifyMixin, _TypeDisp, _Value):
|
||||
|
||||
@property
|
||||
def formatted_str(self):
|
||||
return "%s:%s" % (str(self.admin), str(self.assigned))
|
||||
return "%s:%s" % (self.admin, self.assigned)
|
||||
|
||||
|
||||
@_RouteDistinguisher.register_type(_RouteDistinguisher.TWO_OCTET_AS)
|
||||
@ -1046,6 +1047,8 @@ class EvpnEsi(StringifyMixin, _TypeDisp, _Value):
|
||||
AS_BASED = 0x05
|
||||
MAX = 0xff # Reserved
|
||||
|
||||
_TYPE_NAME = None # must be defined in subclass
|
||||
|
||||
def __init__(self, type_=None):
|
||||
if type_ is None:
|
||||
type_ = self._rev_lookup_type(self.__class__)
|
||||
@ -1063,12 +1066,19 @@ class EvpnEsi(StringifyMixin, _TypeDisp, _Value):
|
||||
msg_pack_into(EvpnEsi._PACK_STR, buf, 0, self.type)
|
||||
return six.binary_type(buf + self.serialize_value())
|
||||
|
||||
@property
|
||||
def formatted_str(self):
|
||||
return '%s(%s)' % (
|
||||
self._TYPE_NAME,
|
||||
','.join(str(getattr(self, v)) for v in self._VALUE_FIELDS))
|
||||
|
||||
|
||||
@EvpnEsi.register_unknown_type()
|
||||
class EvpnUnknownEsi(EvpnEsi):
|
||||
"""
|
||||
ESI value for unknown type
|
||||
"""
|
||||
_TYPE_NAME = 'unknown'
|
||||
_VALUE_PACK_STR = '!9s'
|
||||
_VALUE_FIELDS = ['value']
|
||||
|
||||
@ -1076,6 +1086,10 @@ class EvpnUnknownEsi(EvpnEsi):
|
||||
super(EvpnUnknownEsi, self).__init__(type_)
|
||||
self.value = value
|
||||
|
||||
@property
|
||||
def formatted_str(self):
|
||||
return '%s(%s)' % (self._TYPE_NAME, binary_str(self.value))
|
||||
|
||||
|
||||
@EvpnEsi.register_type(EvpnEsi.ARBITRARY)
|
||||
class EvpnArbitraryEsi(EvpnEsi):
|
||||
@ -1085,6 +1099,7 @@ class EvpnArbitraryEsi(EvpnEsi):
|
||||
This type indicates an arbitrary 9-octet ESI value,
|
||||
which is managed and configured by the operator.
|
||||
"""
|
||||
_TYPE_NAME = 'arbitrary'
|
||||
_VALUE_PACK_STR = '!9s'
|
||||
_VALUE_FIELDS = ['value']
|
||||
|
||||
@ -1092,6 +1107,10 @@ class EvpnArbitraryEsi(EvpnEsi):
|
||||
super(EvpnArbitraryEsi, self).__init__(type_)
|
||||
self.value = value
|
||||
|
||||
@property
|
||||
def formatted_str(self):
|
||||
return '%s(%s)' % (self._TYPE_NAME, binary_str(self.value))
|
||||
|
||||
|
||||
@EvpnEsi.register_type(EvpnEsi.LACP)
|
||||
class EvpnLACPEsi(EvpnEsi):
|
||||
@ -1102,6 +1121,7 @@ class EvpnLACPEsi(EvpnEsi):
|
||||
this ESI type indicates an auto-generated ESI value
|
||||
determined from LACP.
|
||||
"""
|
||||
_TYPE_NAME = 'lacp'
|
||||
_VALUE_PACK_STR = '!6sHx'
|
||||
_VALUE_FIELDS = ['mac_addr', 'port_key']
|
||||
_TYPE = {
|
||||
@ -1139,6 +1159,7 @@ class EvpnL2BridgeEsi(EvpnEsi):
|
||||
The ESI Value is auto-generated and determined based
|
||||
on the Layer 2 bridge protocol.
|
||||
"""
|
||||
_TYPE_NAME = 'l2_bridge'
|
||||
_VALUE_PACK_STR = '!6sHx'
|
||||
_VALUE_FIELDS = ['mac_addr', 'priority']
|
||||
_TYPE = {
|
||||
@ -1174,6 +1195,7 @@ class EvpnMacBasedEsi(EvpnEsi):
|
||||
This type indicates a MAC-based ESI Value that
|
||||
can be auto-generated or configured by the operator.
|
||||
"""
|
||||
_TYPE_NAME = 'mac_based'
|
||||
_VALUE_PACK_STR = '!6s3s'
|
||||
_VALUE_FIELDS = ['mac_addr', 'local_disc']
|
||||
_TYPE = {
|
||||
@ -1210,6 +1232,7 @@ class EvpnRouterIDEsi(EvpnEsi):
|
||||
This type indicates a router-ID ESI Value that
|
||||
can be auto-generated or configured by the operator.
|
||||
"""
|
||||
_TYPE_NAME = 'router_id'
|
||||
_VALUE_PACK_STR = '!4sIx'
|
||||
_VALUE_FIELDS = ['router_id', 'local_disc']
|
||||
_TYPE = {
|
||||
@ -1246,6 +1269,7 @@ class EvpnASBasedEsi(EvpnEsi):
|
||||
ESI Value that can be auto-generated or configured by
|
||||
the operator.
|
||||
"""
|
||||
_TYPE_NAME = 'as_based'
|
||||
_VALUE_PACK_STR = '!IIx'
|
||||
_VALUE_FIELDS = ['as_number', 'local_disc']
|
||||
|
||||
@ -1277,6 +1301,18 @@ class EvpnNLRI(StringifyMixin, _TypeDisp):
|
||||
INCLUSIVE_MULTICAST_ETHERNET_TAG = 0x03
|
||||
ETHERNET_SEGMENT = 0x04
|
||||
|
||||
ROUTE_TYPE_NAME = None # must be defined in subclass
|
||||
|
||||
# Dictionary of ROUTE_TYPE_NAME to subclass.
|
||||
# e.g.)
|
||||
# _NAMES = {'eth_ad': EvpnEthernetAutoDiscoveryNLRI, ...}
|
||||
_NAMES = {}
|
||||
|
||||
# List of the fields considered to be part of the prefix in the NLRI.
|
||||
# This list should be defined in subclasses to format NLRI string
|
||||
# representation.
|
||||
NLRI_PREFIX_FIELDS = []
|
||||
|
||||
def __init__(self, type_=None, length=None):
|
||||
if type_ is None:
|
||||
type_ = self._rev_lookup_type(self.__class__)
|
||||
@ -1284,6 +1320,26 @@ class EvpnNLRI(StringifyMixin, _TypeDisp):
|
||||
self.length = length
|
||||
self.route_dist = None # should be initialized in subclass
|
||||
|
||||
@classmethod
|
||||
def register_type(cls, type_):
|
||||
cls._TYPES = cls._TYPES.copy()
|
||||
cls._NAMES = cls._NAMES.copy()
|
||||
|
||||
def _register_type(subcls):
|
||||
cls._TYPES[type_] = subcls
|
||||
cls._NAMES[subcls.ROUTE_TYPE_NAME] = subcls
|
||||
cls._REV_TYPES = None
|
||||
return subcls
|
||||
|
||||
return _register_type
|
||||
|
||||
@classmethod
|
||||
def _lookup_type_name(cls, type_name):
|
||||
try:
|
||||
return cls._NAMES[type_name]
|
||||
except KeyError:
|
||||
return EvpnUnknownNLRI
|
||||
|
||||
@classmethod
|
||||
def parser(cls, buf):
|
||||
(route_type, length) = struct.unpack_from(
|
||||
@ -1390,12 +1446,32 @@ class EvpnNLRI(StringifyMixin, _TypeDisp):
|
||||
label = label << 4 | 1
|
||||
return six.binary_type(_LabelledAddrPrefix._label_to_bin(label))
|
||||
|
||||
@property
|
||||
def prefix(self):
|
||||
def _format(i):
|
||||
pairs = []
|
||||
for k in i.NLRI_PREFIX_FIELDS:
|
||||
v = getattr(i, k)
|
||||
if k == 'esi':
|
||||
pairs.append('%s:%s' % (k, v.formatted_str))
|
||||
else:
|
||||
pairs.append('%s:%s' % (k, v))
|
||||
return ','.join(pairs)
|
||||
|
||||
return '%s(%s)' % (self.ROUTE_TYPE_NAME, _format(self))
|
||||
|
||||
@property
|
||||
def formatted_nlri_str(self):
|
||||
return '%s:%s' % (self.route_dist, self.prefix)
|
||||
|
||||
|
||||
@EvpnNLRI.register_unknown_type()
|
||||
class EvpnUnknownNLRI(EvpnNLRI):
|
||||
"""
|
||||
Unknown route type specific EVPN NLRI
|
||||
"""
|
||||
ROUTE_TYPE_NAME = 'unknown'
|
||||
NLRI_PREFIX_FIELDS = ['value']
|
||||
|
||||
def __init__(self, value, type_, length=None):
|
||||
super(EvpnUnknownNLRI, self).__init__(type_, length)
|
||||
@ -1410,12 +1486,17 @@ class EvpnUnknownNLRI(EvpnNLRI):
|
||||
def serialize_value(self):
|
||||
return self.value
|
||||
|
||||
@property
|
||||
def formatted_nlri_str(self):
|
||||
return '%s(%s)' % (self.ROUTE_TYPE_NAME, binary_str(self.value))
|
||||
|
||||
|
||||
@EvpnNLRI.register_type(EvpnNLRI.ETHERNET_AUTO_DISCOVERY)
|
||||
class EvpnEthernetAutoDiscoveryNLRI(EvpnNLRI):
|
||||
"""
|
||||
Ethernet A-D route type specific EVPN NLRI
|
||||
"""
|
||||
ROUTE_TYPE_NAME = 'eth_ad'
|
||||
|
||||
# +---------------------------------------+
|
||||
# | Route Distinguisher (RD) (8 octets) |
|
||||
@ -1427,6 +1508,7 @@ class EvpnEthernetAutoDiscoveryNLRI(EvpnNLRI):
|
||||
# | MPLS Label (3 octets) |
|
||||
# +---------------------------------------+
|
||||
_PACK_STR = "!8s10sI3s"
|
||||
NLRI_PREFIX_FIELDS = ['esi', 'ethernet_tag_id']
|
||||
|
||||
def __init__(self, route_dist, esi, ethernet_tag_id, mpls_label,
|
||||
type_=None, length=None):
|
||||
@ -1456,12 +1538,17 @@ class EvpnEthernetAutoDiscoveryNLRI(EvpnNLRI):
|
||||
self._PACK_STR, route_dist.serialize(), self.esi.serialize(),
|
||||
self.ethernet_tag_id, self._mpls_label_to_bin(self.mpls_label))
|
||||
|
||||
@property
|
||||
def label_list(self):
|
||||
return [self.mpls_label]
|
||||
|
||||
|
||||
@EvpnNLRI.register_type(EvpnNLRI.MAC_IP_ADVERTISEMENT)
|
||||
class EvpnMacIPAdvertisementNLRI(EvpnNLRI):
|
||||
"""
|
||||
MAC/IP Advertisement route type specific EVPN NLRI
|
||||
"""
|
||||
ROUTE_TYPE_NAME = 'mac_ip_adv'
|
||||
|
||||
# +---------------------------------------+
|
||||
# | RD (8 octets) |
|
||||
@ -1483,6 +1570,8 @@ class EvpnMacIPAdvertisementNLRI(EvpnNLRI):
|
||||
# | MPLS Label2 (0 or 3 octets) |
|
||||
# +---------------------------------------+
|
||||
_PACK_STR = "!8s10sIB6sB%ds3s%ds"
|
||||
# Note: mac_addr_len and ip_addr_len are omitted for readability.
|
||||
NLRI_PREFIX_FIELDS = ['ethernet_tag_id', 'mac_addr', 'ip_addr']
|
||||
_TYPE = {
|
||||
'ascii': [
|
||||
'mac_addr',
|
||||
@ -1561,12 +1650,17 @@ class EvpnMacIPAdvertisementNLRI(EvpnNLRI):
|
||||
self.ip_addr_len, ip_addr,
|
||||
mpls_label1, mpls_label2)
|
||||
|
||||
@property
|
||||
def label_list(self):
|
||||
return self.mpls_labels
|
||||
|
||||
|
||||
@EvpnNLRI.register_type(EvpnNLRI.INCLUSIVE_MULTICAST_ETHERNET_TAG)
|
||||
class EvpnInclusiveMulticastEthernetTagNLRI(EvpnNLRI):
|
||||
"""
|
||||
Inclusive Multicast Ethernet Tag route type specific EVPN NLRI
|
||||
"""
|
||||
ROUTE_TYPE_NAME = 'multicast_etag'
|
||||
|
||||
# +---------------------------------------+
|
||||
# | RD (8 octets) |
|
||||
@ -1579,6 +1673,7 @@ class EvpnInclusiveMulticastEthernetTagNLRI(EvpnNLRI):
|
||||
# | (4 or 16 octets) |
|
||||
# +---------------------------------------+
|
||||
_PACK_STR = '!8sIB%ds'
|
||||
NLRI_PREFIX_FIELDS = ['ethernet_tag_id', 'ip_addr']
|
||||
_TYPE = {
|
||||
'ascii': [
|
||||
'ip_addr'
|
||||
@ -1624,6 +1719,7 @@ class EvpnEthernetSegmentNLRI(EvpnNLRI):
|
||||
"""
|
||||
Ethernet Segment route type specific EVPN NLRI
|
||||
"""
|
||||
ROUTE_TYPE_NAME = 'eth_seg'
|
||||
|
||||
# +---------------------------------------+
|
||||
# | RD (8 octets) |
|
||||
@ -1636,6 +1732,7 @@ class EvpnEthernetSegmentNLRI(EvpnNLRI):
|
||||
# | (4 or 16 octets) |
|
||||
# +---------------------------------------+
|
||||
_PACK_STR = '!8s10sB%ds'
|
||||
NLRI_PREFIX_FIELDS = ['esi', 'ip_addr']
|
||||
_TYPE = {
|
||||
'ascii': [
|
||||
'ip_addr'
|
||||
|
||||
@ -49,6 +49,7 @@ Int2 = IntDescr(2)
|
||||
Int3 = IntDescr(3)
|
||||
Int4 = IntDescr(4)
|
||||
Int8 = IntDescr(8)
|
||||
Int9 = IntDescr(9)
|
||||
Int16 = IntDescr(16)
|
||||
|
||||
|
||||
|
||||
@ -43,6 +43,12 @@ VPN_LABEL = 'label'
|
||||
API_SYM = 'name'
|
||||
ORIGIN_RD = 'origin_rd'
|
||||
ROUTE_FAMILY = 'route_family'
|
||||
EVPN_ROUTE_TYPE = 'route_type'
|
||||
EVPN_ESI = 'esi'
|
||||
EVPN_ETHERNET_TAG_ID = 'ethernet_tag_id'
|
||||
MAC_ADDR = 'mac_addr'
|
||||
IP_ADDR = 'ip_addr'
|
||||
MPLS_LABELS = 'mpls_labels'
|
||||
|
||||
# API call registry
|
||||
_CALL_REGISTRY = {}
|
||||
|
||||
@ -18,6 +18,14 @@
|
||||
"""
|
||||
import logging
|
||||
|
||||
from ryu.lib.packet.bgp import EvpnMacIPAdvertisementNLRI
|
||||
from ryu.lib.packet.bgp import EvpnInclusiveMulticastEthernetTagNLRI
|
||||
from ryu.services.protocols.bgp.api.base import EVPN_ROUTE_TYPE
|
||||
from ryu.services.protocols.bgp.api.base import EVPN_ESI
|
||||
from ryu.services.protocols.bgp.api.base import EVPN_ETHERNET_TAG_ID
|
||||
from ryu.services.protocols.bgp.api.base import MAC_ADDR
|
||||
from ryu.services.protocols.bgp.api.base import IP_ADDR
|
||||
from ryu.services.protocols.bgp.api.base import MPLS_LABELS
|
||||
from ryu.services.protocols.bgp.api.base import NEXT_HOP
|
||||
from ryu.services.protocols.bgp.api.base import PREFIX
|
||||
from ryu.services.protocols.bgp.api.base import RegisterWithArgChecks
|
||||
@ -28,6 +36,7 @@ from ryu.services.protocols.bgp.base import PREFIX_ERROR_CODE
|
||||
from ryu.services.protocols.bgp.base import validate
|
||||
from ryu.services.protocols.bgp.core import BgpCoreError
|
||||
from ryu.services.protocols.bgp.core_manager import CORE_MANAGER
|
||||
from ryu.services.protocols.bgp.rtconf.base import ConfigValueError
|
||||
from ryu.services.protocols.bgp.rtconf.base import RuntimeConfigError
|
||||
from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF
|
||||
from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV4
|
||||
@ -36,6 +45,14 @@ from ryu.services.protocols.bgp.utils import validation
|
||||
|
||||
LOG = logging.getLogger('bgpspeaker.api.prefix')
|
||||
|
||||
# Constants used in API calls for EVPN
|
||||
EVPN_MAC_IP_ADV_ROUTE = EvpnMacIPAdvertisementNLRI.ROUTE_TYPE_NAME
|
||||
EVPN_MULTICAST_ETAG_ROUTE = EvpnInclusiveMulticastEthernetTagNLRI.ROUTE_TYPE_NAME
|
||||
SUPPORTED_EVPN_ROUTE_TYPES = [
|
||||
EVPN_MAC_IP_ADV_ROUTE,
|
||||
EVPN_MULTICAST_ETAG_ROUTE,
|
||||
]
|
||||
|
||||
|
||||
@add_bgp_error_metadata(code=PREFIX_ERROR_CODE,
|
||||
sub_code=1,
|
||||
@ -55,6 +72,49 @@ def is_valid_next_hop(next_hop_addr):
|
||||
return validation.is_valid_ipv4(next_hop_addr)
|
||||
|
||||
|
||||
@validate(name=EVPN_ROUTE_TYPE)
|
||||
def is_valid_evpn_route_type(route_type):
|
||||
if route_type not in SUPPORTED_EVPN_ROUTE_TYPES:
|
||||
raise ConfigValueError(conf_name=EVPN_ROUTE_TYPE,
|
||||
conf_value=route_type)
|
||||
|
||||
|
||||
@validate(name=EVPN_ESI)
|
||||
def is_valid_esi(esi):
|
||||
if not validation.is_valid_esi(esi):
|
||||
raise ConfigValueError(conf_name=EVPN_ESI,
|
||||
conf_value=esi)
|
||||
|
||||
|
||||
@validate(name=EVPN_ETHERNET_TAG_ID)
|
||||
def is_valid_ethernet_tag_id(ethernet_tag_id):
|
||||
if not validation.is_valid_ethernet_tag_id(ethernet_tag_id):
|
||||
raise ConfigValueError(conf_name=EVPN_ETHERNET_TAG_ID,
|
||||
conf_value=ethernet_tag_id)
|
||||
|
||||
|
||||
@validate(name=MAC_ADDR)
|
||||
def is_valid_mac_addr(addr):
|
||||
if not validation.is_valid_mac(addr):
|
||||
raise ConfigValueError(conf_name=MAC_ADDR,
|
||||
conf_value=addr)
|
||||
|
||||
|
||||
@validate(name=IP_ADDR)
|
||||
def is_valid_ip_addr(addr):
|
||||
if not (validation.is_valid_ipv4(addr)
|
||||
or validation.is_valid_ipv6(addr)):
|
||||
raise ConfigValueError(conf_name=IP_ADDR,
|
||||
conf_value=addr)
|
||||
|
||||
|
||||
@validate(name=MPLS_LABELS)
|
||||
def is_valid_mpls_labels(labels):
|
||||
if not validation.is_valid_mpls_labels(labels):
|
||||
raise ConfigValueError(conf_name=MPLS_LABELS,
|
||||
conf_value=labels)
|
||||
|
||||
|
||||
@RegisterWithArgChecks(name='prefix.add_local',
|
||||
req_args=[ROUTE_DISTINGUISHER, PREFIX, NEXT_HOP],
|
||||
opt_args=[VRF_RF])
|
||||
@ -93,3 +153,29 @@ def delete_local(route_dist, prefix, route_family=VRF_RF_IPV4):
|
||||
VRF_RF: route_family}]
|
||||
except BgpCoreError as e:
|
||||
raise PrefixError(desc=e)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# BGP EVPN Routes related APIs
|
||||
# =============================================================================
|
||||
|
||||
@RegisterWithArgChecks(name='evpn_prefix.add_local',
|
||||
req_args=[EVPN_ROUTE_TYPE, ROUTE_DISTINGUISHER,
|
||||
NEXT_HOP],
|
||||
opt_args=[EVPN_ESI, EVPN_ETHERNET_TAG_ID, MAC_ADDR,
|
||||
IP_ADDR])
|
||||
def add_evpn_local(route_type, route_dist, next_hop, **kwargs):
|
||||
tm = CORE_MANAGER.get_core_service().table_manager
|
||||
tm.add_to_global_evpn_table(route_type, route_dist, next_hop, **kwargs)
|
||||
return True
|
||||
|
||||
|
||||
@RegisterWithArgChecks(name='evpn_prefix.delete_local',
|
||||
req_args=[EVPN_ROUTE_TYPE, ROUTE_DISTINGUISHER],
|
||||
opt_args=[EVPN_ESI, EVPN_ETHERNET_TAG_ID, MAC_ADDR,
|
||||
IP_ADDR])
|
||||
def delete_evpn_local(route_type, route_dist, **kwargs):
|
||||
tm = CORE_MANAGER.get_core_service().table_manager
|
||||
tm.add_to_global_evpn_table(route_type, route_dist, is_withdraw=True,
|
||||
**kwargs)
|
||||
return True
|
||||
|
||||
@ -35,6 +35,7 @@ from ryu.lib.packet.bgp import RF_IPv4_UC
|
||||
from ryu.lib.packet.bgp import RF_IPv6_UC
|
||||
from ryu.lib.packet.bgp import RF_IPv4_VPN
|
||||
from ryu.lib.packet.bgp import RF_IPv6_VPN
|
||||
from ryu.lib.packet.bgp import RF_L2_EVPN
|
||||
from ryu.lib.packet.bgp import RF_RTC_UC
|
||||
from ryu.services.protocols.bgp.utils.circlist import CircularListType
|
||||
from ryu.services.protocols.bgp.utils.evtlet import LoopingCall
|
||||
@ -48,12 +49,14 @@ OrderedDict = OrderedDict
|
||||
|
||||
|
||||
# Currently supported address families.
|
||||
SUPPORTED_GLOBAL_RF = set([RF_IPv4_UC,
|
||||
RF_IPv6_UC,
|
||||
RF_IPv4_VPN,
|
||||
RF_RTC_UC,
|
||||
RF_IPv6_VPN
|
||||
])
|
||||
SUPPORTED_GLOBAL_RF = {
|
||||
RF_IPv4_UC,
|
||||
RF_IPv6_UC,
|
||||
RF_IPv4_VPN,
|
||||
RF_RTC_UC,
|
||||
RF_IPv6_VPN,
|
||||
RF_L2_EVPN,
|
||||
}
|
||||
|
||||
|
||||
# Various error codes
|
||||
|
||||
@ -23,9 +23,16 @@ from ryu.services.protocols.bgp.core_manager import CORE_MANAGER
|
||||
from ryu.services.protocols.bgp.signals.emit import BgpSignalBus
|
||||
from ryu.services.protocols.bgp.api.base import call
|
||||
from ryu.services.protocols.bgp.api.base import PREFIX
|
||||
from ryu.services.protocols.bgp.api.base import EVPN_ROUTE_TYPE
|
||||
from ryu.services.protocols.bgp.api.base import EVPN_ESI
|
||||
from ryu.services.protocols.bgp.api.base import EVPN_ETHERNET_TAG_ID
|
||||
from ryu.services.protocols.bgp.api.base import IP_ADDR
|
||||
from ryu.services.protocols.bgp.api.base import MAC_ADDR
|
||||
from ryu.services.protocols.bgp.api.base import NEXT_HOP
|
||||
from ryu.services.protocols.bgp.api.base import ROUTE_DISTINGUISHER
|
||||
from ryu.services.protocols.bgp.api.base import ROUTE_FAMILY
|
||||
from ryu.services.protocols.bgp.api.prefix import EVPN_MAC_IP_ADV_ROUTE
|
||||
from ryu.services.protocols.bgp.api.prefix import EVPN_MULTICAST_ETAG_ROUTE
|
||||
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 BGP_SERVER_PORT
|
||||
@ -44,6 +51,7 @@ from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_IPV4
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_IPV6
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_VPNV4
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_VPNV6
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_EVPN
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_ENHANCED_REFRESH
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_FOUR_OCTET_AS_NUMBER
|
||||
from ryu.services.protocols.bgp.rtconf.base import MULTI_EXIT_DISC
|
||||
@ -51,6 +59,7 @@ from ryu.services.protocols.bgp.rtconf.base import SITE_OF_ORIGINS
|
||||
from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CAP_MBGP_IPV4
|
||||
from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CAP_MBGP_VPNV4
|
||||
from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CAP_MBGP_VPNV6
|
||||
from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CAP_MBGP_EVPN
|
||||
from ryu.services.protocols.bgp.rtconf.neighbors import (
|
||||
DEFAULT_CAP_ENHANCED_REFRESH, DEFAULT_CAP_FOUR_OCTET_AS_NUMBER)
|
||||
from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CONNECT_MODE
|
||||
@ -237,6 +246,7 @@ class BGPSpeaker(object):
|
||||
enable_ipv4=DEFAULT_CAP_MBGP_IPV4,
|
||||
enable_vpnv4=DEFAULT_CAP_MBGP_VPNV4,
|
||||
enable_vpnv6=DEFAULT_CAP_MBGP_VPNV6,
|
||||
enable_evpn=DEFAULT_CAP_MBGP_EVPN,
|
||||
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,
|
||||
@ -264,6 +274,9 @@ class BGPSpeaker(object):
|
||||
``enable_vpnv6`` enables VPNv6 address family for this
|
||||
neighbor. The default is False.
|
||||
|
||||
``enable_evpn`` enables Ethernet VPN address family for this
|
||||
neighbor. The default is False.
|
||||
|
||||
``enable_enhanced_refresh`` enables Enhanced Route Refresh for this
|
||||
neighbor. The default is False.
|
||||
|
||||
@ -320,11 +333,13 @@ class BGPSpeaker(object):
|
||||
bgp_neighbor[CAP_MBGP_IPV6] = False
|
||||
bgp_neighbor[CAP_MBGP_VPNV4] = enable_vpnv4
|
||||
bgp_neighbor[CAP_MBGP_VPNV6] = enable_vpnv6
|
||||
bgp_neighbor[CAP_MBGP_EVPN] = enable_evpn
|
||||
elif netaddr.valid_ipv6(address):
|
||||
bgp_neighbor[CAP_MBGP_IPV4] = False
|
||||
bgp_neighbor[CAP_MBGP_IPV6] = True
|
||||
bgp_neighbor[CAP_MBGP_VPNV4] = False
|
||||
bgp_neighbor[CAP_MBGP_VPNV6] = False
|
||||
bgp_neighbor[CAP_MBGP_EVPN] = enable_evpn
|
||||
else:
|
||||
# FIXME: should raise an exception
|
||||
pass
|
||||
@ -467,6 +482,98 @@ class BGPSpeaker(object):
|
||||
|
||||
call(func_name, **networks)
|
||||
|
||||
def evpn_prefix_add(self, route_type, route_dist, esi=0,
|
||||
ethernet_tag_id=None, mac_addr=None, ip_addr=None,
|
||||
next_hop=None):
|
||||
""" This method adds a new EVPN route to be advertised.
|
||||
|
||||
``route_type`` specifies one of the EVPN route type name. The
|
||||
supported route types are EVPN_MAC_IP_ADV_ROUTE and
|
||||
EVPN_MULTICAST_ETAG_ROUTE.
|
||||
|
||||
``route_dist`` specifies a route distinguisher value.
|
||||
|
||||
``esi`` is an integer value to specify the Ethernet Segment
|
||||
Identifier. 0 is the default and denotes a single-homed site.
|
||||
|
||||
``ethernet_tag_id`` specifies the Ethernet Tag ID.
|
||||
|
||||
``mac_addr`` specifies a MAC address to advertise.
|
||||
|
||||
``ip_addr`` specifies an IPv4 or IPv6 address to advertise.
|
||||
|
||||
``next_hop`` specifies the next hop address for this prefix.
|
||||
"""
|
||||
func_name = 'evpn_prefix.add_local'
|
||||
|
||||
# Check the default values
|
||||
if not next_hop:
|
||||
next_hop = '0.0.0.0'
|
||||
|
||||
# Set required arguments
|
||||
kwargs = {EVPN_ROUTE_TYPE: route_type,
|
||||
ROUTE_DISTINGUISHER: route_dist,
|
||||
NEXT_HOP: next_hop}
|
||||
|
||||
# Set route type specific arguments
|
||||
if route_type == EVPN_MAC_IP_ADV_ROUTE:
|
||||
kwargs.update({
|
||||
EVPN_ESI: esi,
|
||||
EVPN_ETHERNET_TAG_ID: ethernet_tag_id,
|
||||
MAC_ADDR: mac_addr,
|
||||
IP_ADDR: ip_addr,
|
||||
})
|
||||
elif route_type == EVPN_MULTICAST_ETAG_ROUTE:
|
||||
kwargs.update({
|
||||
EVPN_ETHERNET_TAG_ID: ethernet_tag_id,
|
||||
IP_ADDR: ip_addr,
|
||||
})
|
||||
else:
|
||||
raise ValueError('Unsupported EVPN route type: %s' % route_type)
|
||||
|
||||
call(func_name, **kwargs)
|
||||
|
||||
def evpn_prefix_del(self, route_type, route_dist, esi=0,
|
||||
ethernet_tag_id=None, mac_addr=None, ip_addr=None):
|
||||
""" This method deletes an advertised EVPN route.
|
||||
|
||||
``route_type`` specifies one of the EVPN route type name.
|
||||
|
||||
``route_dist`` specifies a route distinguisher value.
|
||||
|
||||
``esi`` is an integer value to specify the Ethernet Segment
|
||||
Identifier. 0 is the default and denotes a single-homed site.
|
||||
|
||||
``ethernet_tag_id`` specifies the Ethernet Tag ID.
|
||||
|
||||
``mac_addr`` specifies a MAC address to advertise.
|
||||
|
||||
``ip_addr`` specifies an IPv4 or IPv6 address to advertise.
|
||||
"""
|
||||
func_name = 'evpn_prefix.delete_local'
|
||||
|
||||
# Set required arguments
|
||||
kwargs = {EVPN_ROUTE_TYPE: route_type,
|
||||
ROUTE_DISTINGUISHER: route_dist}
|
||||
|
||||
# Set route type specific arguments
|
||||
if route_type == EVPN_MAC_IP_ADV_ROUTE:
|
||||
kwargs.update({
|
||||
EVPN_ESI: esi,
|
||||
EVPN_ETHERNET_TAG_ID: ethernet_tag_id,
|
||||
MAC_ADDR: mac_addr,
|
||||
IP_ADDR: ip_addr,
|
||||
})
|
||||
elif route_type == EVPN_MULTICAST_ETAG_ROUTE:
|
||||
kwargs.update({
|
||||
EVPN_ETHERNET_TAG_ID: ethernet_tag_id,
|
||||
IP_ADDR: ip_addr,
|
||||
})
|
||||
else:
|
||||
raise ValueError('Unsupported EVPN route type: %s' % route_type)
|
||||
|
||||
call(func_name, **kwargs)
|
||||
|
||||
def vrf_add(self, route_dist, import_rts, export_rts, site_of_origins=None,
|
||||
route_family=RF_VPN_V4, multi_exit_disc=None):
|
||||
""" This method adds a new vrf used for VPN.
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import logging
|
||||
import netaddr
|
||||
from collections import OrderedDict
|
||||
|
||||
import netaddr
|
||||
|
||||
from ryu.services.protocols.bgp.base import SUPPORTED_GLOBAL_RF
|
||||
from ryu.services.protocols.bgp.info_base.rtc import RtcTable
|
||||
from ryu.services.protocols.bgp.info_base.ipv4 import Ipv4Path
|
||||
@ -14,20 +15,27 @@ from ryu.services.protocols.bgp.info_base.vpnv6 import Vpnv6Path
|
||||
from ryu.services.protocols.bgp.info_base.vpnv6 import Vpnv6Table
|
||||
from ryu.services.protocols.bgp.info_base.vrf4 import Vrf4Table
|
||||
from ryu.services.protocols.bgp.info_base.vrf6 import Vrf6Table
|
||||
from ryu.services.protocols.bgp.info_base.evpn import EvpnPath
|
||||
from ryu.services.protocols.bgp.info_base.evpn import EvpnTable
|
||||
from ryu.services.protocols.bgp.rtconf import vrfs
|
||||
from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV4
|
||||
from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV6
|
||||
from ryu.services.protocols.bgp.rtconf.vrfs import SUPPORTED_VRF_RF
|
||||
|
||||
from ryu.lib import type_desc
|
||||
from ryu.lib.packet.bgp import RF_IPv4_UC
|
||||
from ryu.lib.packet.bgp import RF_IPv6_UC
|
||||
from ryu.lib.packet.bgp import RF_IPv4_VPN
|
||||
from ryu.lib.packet.bgp import RF_IPv6_VPN
|
||||
from ryu.lib.packet.bgp import RF_L2_EVPN
|
||||
from ryu.lib.packet.bgp import RF_RTC_UC
|
||||
from ryu.lib.packet.bgp import BGPPathAttributeOrigin
|
||||
from ryu.lib.packet.bgp import BGPPathAttributeAsPath
|
||||
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_ORIGIN
|
||||
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_AS_PATH
|
||||
from ryu.lib.packet.bgp import BGP_ATTR_ORIGIN_IGP
|
||||
from ryu.lib.packet.bgp import EvpnArbitraryEsi
|
||||
from ryu.lib.packet.bgp import EvpnNLRI
|
||||
from ryu.lib.packet.bgp import IPAddrPrefix
|
||||
from ryu.lib.packet.bgp import IP6AddrPrefix
|
||||
|
||||
@ -76,7 +84,7 @@ class TableCoreManager(object):
|
||||
def remove_vrf_by_vrf_conf(self, vrf_conf):
|
||||
|
||||
route_family = vrf_conf.route_family
|
||||
assert route_family in (vrfs.VRF_RF_IPV4, vrfs.VRF_RF_IPV6)
|
||||
assert route_family in SUPPORTED_VRF_RF
|
||||
table_id = (vrf_conf.route_dist, route_family)
|
||||
|
||||
vrf_table = self._tables.pop(table_id)
|
||||
@ -171,10 +179,10 @@ class TableCoreManager(object):
|
||||
global_table = self.get_ipv6_table()
|
||||
elif route_family == RF_IPv4_VPN:
|
||||
global_table = self.get_vpn4_table()
|
||||
|
||||
elif route_family == RF_IPv6_VPN:
|
||||
global_table = self.get_vpn6_table()
|
||||
|
||||
elif route_family == RF_L2_EVPN:
|
||||
global_table = self.get_evpn_table()
|
||||
elif route_family == RF_RTC_UC:
|
||||
global_table = self.get_rtc_table()
|
||||
|
||||
@ -245,6 +253,20 @@ class TableCoreManager(object):
|
||||
|
||||
return vpn_table
|
||||
|
||||
def get_evpn_table(self):
|
||||
"""Returns global EVPN table.
|
||||
|
||||
Creates the table if it does not exist.
|
||||
"""
|
||||
evpn_table = self._global_tables.get(RF_L2_EVPN)
|
||||
# Lazy initialization of the table.
|
||||
if not evpn_table:
|
||||
evpn_table = EvpnTable(self._core_service, self._signal_bus)
|
||||
self._global_tables[RF_L2_EVPN] = evpn_table
|
||||
self._tables[(None, RF_L2_EVPN)] = evpn_table
|
||||
|
||||
return evpn_table
|
||||
|
||||
def get_rtc_table(self):
|
||||
"""Returns global RTC table.
|
||||
|
||||
@ -528,6 +550,41 @@ class TableCoreManager(object):
|
||||
# add to global ipv4 table and propagates to neighbors
|
||||
self.learn_path(new_path)
|
||||
|
||||
def add_to_global_evpn_table(self, route_type, route_dist, next_hop=None,
|
||||
is_withdraw=False, **kwargs):
|
||||
"""Adds BGP EVPN Route to global EVPN Table with given `next_hop`.
|
||||
|
||||
If `is_withdraw` is set to `True`, removes the given route from
|
||||
global EVPN Table.
|
||||
"""
|
||||
|
||||
# construct EVPN NLRI instance
|
||||
subclass = EvpnNLRI._lookup_type_name(route_type)
|
||||
kwargs['route_dist'] = route_dist
|
||||
esi = kwargs.get('esi', None)
|
||||
if esi is not None:
|
||||
# Note: Currently, we support arbitrary 9-octet ESI value only.
|
||||
kwargs['esi'] = EvpnArbitraryEsi(type_desc.Int9.from_user(esi))
|
||||
nlri = subclass(**kwargs)
|
||||
|
||||
# set mandatory path attributes
|
||||
origin = BGPPathAttributeOrigin(BGP_ATTR_ORIGIN_IGP)
|
||||
aspath = BGPPathAttributeAsPath([[]])
|
||||
pathattrs = OrderedDict()
|
||||
pathattrs[BGP_ATTR_TYPE_ORIGIN] = origin
|
||||
pathattrs[BGP_ATTR_TYPE_AS_PATH] = aspath
|
||||
|
||||
# set the default next_hop address
|
||||
if next_hop is None:
|
||||
next_hop = '0.0.0.0'
|
||||
|
||||
new_path = EvpnPath(source=None, nlri=nlri, src_ver_num=1,
|
||||
pattrs=pathattrs, nexthop=next_hop,
|
||||
is_withdraw=is_withdraw)
|
||||
|
||||
# add to global EVPN table and propagates to neighbors
|
||||
self.learn_path(new_path)
|
||||
|
||||
def remove_from_vrf(self, route_dist, prefix, route_family):
|
||||
"""Removes `prefix` from VRF identified by `route_dist`.
|
||||
|
||||
|
||||
86
ryu/services/protocols/bgp/info_base/evpn.py
Normal file
86
ryu/services/protocols/bgp/info_base/evpn.py
Normal file
@ -0,0 +1,86 @@
|
||||
# Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Defines data types and models required specifically for EVPN support.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from ryu.lib.packet.bgp import EvpnNLRI
|
||||
from ryu.lib.packet.bgp import RF_L2_EVPN
|
||||
|
||||
from ryu.services.protocols.bgp.info_base.base import Path
|
||||
from ryu.services.protocols.bgp.info_base.base import Table
|
||||
from ryu.services.protocols.bgp.info_base.base import Destination
|
||||
from ryu.services.protocols.bgp.info_base.base import NonVrfPathProcessingMixin
|
||||
|
||||
LOG = logging.getLogger('bgpspeaker.info_base.evpn')
|
||||
|
||||
|
||||
class EvpnDest(Destination, NonVrfPathProcessingMixin):
|
||||
"""EVPN Destination
|
||||
|
||||
Store EVPN paths.
|
||||
"""
|
||||
ROUTE_FAMILY = RF_L2_EVPN
|
||||
|
||||
def _best_path_lost(self):
|
||||
old_best_path = self._best_path
|
||||
NonVrfPathProcessingMixin._best_path_lost(self)
|
||||
self._core_service._signal_bus.best_path_changed(old_best_path, True)
|
||||
|
||||
def _new_best_path(self, best_path):
|
||||
NonVrfPathProcessingMixin._new_best_path(self, best_path)
|
||||
self._core_service._signal_bus.best_path_changed(best_path, False)
|
||||
|
||||
|
||||
class EvpnTable(Table):
|
||||
"""Global table to store EVPN routing information.
|
||||
|
||||
Uses `EvpnDest` to store destination information for each known EVPN
|
||||
paths.
|
||||
"""
|
||||
ROUTE_FAMILY = RF_L2_EVPN
|
||||
VPN_DEST_CLASS = EvpnDest
|
||||
|
||||
def __init__(self, core_service, signal_bus):
|
||||
super(EvpnTable, self).__init__(None, core_service, signal_bus)
|
||||
|
||||
def _table_key(self, nlri):
|
||||
"""Return a key that will uniquely identify this NLRI inside
|
||||
this table.
|
||||
"""
|
||||
return nlri.formatted_nlri_str
|
||||
|
||||
def _create_dest(self, nlri):
|
||||
return self.VPN_DEST_CLASS(self, nlri)
|
||||
|
||||
def __str__(self):
|
||||
return '%s(scope_id: %s, rf: %s)' % (
|
||||
self.__class__.__name__, self.scope_id, self.route_family
|
||||
)
|
||||
|
||||
|
||||
class EvpnPath(Path):
|
||||
"""Represents a way of reaching an EVPN destination."""
|
||||
ROUTE_FAMILY = RF_L2_EVPN
|
||||
VRF_PATH_CLASS = None # defined in init - anti cyclic import hack
|
||||
NLRI_CLASS = EvpnNLRI
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(EvpnPath, self).__init__(*args, **kwargs)
|
||||
# TODO:
|
||||
# To support the VRF table for BGP EVPN routes.
|
||||
@ -13,7 +13,7 @@ from ryu.services.protocols.bgp.operator.commands.responses import \
|
||||
|
||||
|
||||
class RibBase(Command, RouteFormatterMixin):
|
||||
supported_families = ['ipv4', 'ipv6', 'vpnv4', 'rtfilter', 'vpnv6']
|
||||
supported_families = ['ipv4', 'ipv6', 'vpnv4', 'rtfilter', 'vpnv6', 'evpn']
|
||||
|
||||
|
||||
class Rib(RibBase):
|
||||
|
||||
@ -6,6 +6,7 @@ from ryu.lib.packet.bgp import RF_IPv4_UC
|
||||
from ryu.lib.packet.bgp import RF_IPv6_UC
|
||||
from ryu.lib.packet.bgp import RF_IPv4_VPN
|
||||
from ryu.lib.packet.bgp import RF_IPv6_VPN
|
||||
from ryu.lib.packet.bgp import RF_L2_EVPN
|
||||
from ryu.lib.packet.bgp import RF_RTC_UC
|
||||
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_ORIGIN
|
||||
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_AS_PATH
|
||||
@ -82,10 +83,12 @@ class InternalApi(object):
|
||||
'ipv6': RF_IPv6_UC,
|
||||
'vpnv4': RF_IPv4_VPN,
|
||||
'vpnv6': RF_IPv6_VPN,
|
||||
'evpn': RF_L2_EVPN,
|
||||
'rtfilter': RF_RTC_UC
|
||||
}
|
||||
if addr_family not in rfs:
|
||||
raise WrongParamError('Unknown or unsupported family')
|
||||
raise WrongParamError('Unknown or unsupported family: %s' %
|
||||
addr_family)
|
||||
|
||||
rf = rfs.get(addr_family)
|
||||
table_manager = self.get_core_service().table_manager
|
||||
|
||||
@ -44,6 +44,7 @@ CAP_MBGP_IPV4 = 'cap_mbgp_ipv4'
|
||||
CAP_MBGP_IPV6 = 'cap_mbgp_ipv6'
|
||||
CAP_MBGP_VPNV4 = 'cap_mbgp_vpnv4'
|
||||
CAP_MBGP_VPNV6 = 'cap_mbgp_vpnv6'
|
||||
CAP_MBGP_EVPN = 'cap_mbgp_evpn'
|
||||
CAP_RTC = 'cap_rtc'
|
||||
RTC_AS = 'rtc_as'
|
||||
HOLD_TIME = 'hold_time'
|
||||
@ -172,15 +173,15 @@ class BaseConf(object):
|
||||
return self._settings.copy()
|
||||
|
||||
@classmethod
|
||||
def get_valid_evts(self):
|
||||
def get_valid_evts(cls):
|
||||
return set()
|
||||
|
||||
@classmethod
|
||||
def get_req_settings(self):
|
||||
def get_req_settings(cls):
|
||||
return set()
|
||||
|
||||
@classmethod
|
||||
def get_opt_settings(self):
|
||||
def get_opt_settings(cls):
|
||||
return set()
|
||||
|
||||
@abstractmethod
|
||||
@ -582,8 +583,8 @@ def validate_stats_time(stats_time):
|
||||
@validate(name=CAP_REFRESH)
|
||||
def validate_cap_refresh(crefresh):
|
||||
if crefresh not in (True, False):
|
||||
raise ConfigTypeError(desc='Invalid Refresh capability settings: %s '
|
||||
' boolean value expected' % crefresh)
|
||||
raise ConfigTypeError(desc='Invalid Refresh capability settings: %s. '
|
||||
'Boolean value expected' % crefresh)
|
||||
return crefresh
|
||||
|
||||
|
||||
@ -591,7 +592,7 @@ def validate_cap_refresh(crefresh):
|
||||
def validate_cap_enhanced_refresh(cer):
|
||||
if cer not in (True, False):
|
||||
raise ConfigTypeError(desc='Invalid Enhanced Refresh capability '
|
||||
'settings: %s boolean value expected' % cer)
|
||||
'settings: %s. Boolean value expected' % cer)
|
||||
return cer
|
||||
|
||||
|
||||
@ -606,8 +607,8 @@ def validate_cap_four_octet_as_number(cfoan):
|
||||
@validate(name=CAP_MBGP_IPV4)
|
||||
def validate_cap_mbgp_ipv4(cmv4):
|
||||
if cmv4 not in (True, False):
|
||||
raise ConfigTypeError(desc='Invalid Enhanced Refresh capability '
|
||||
'settings: %s boolean value expected' % cmv4)
|
||||
raise ConfigTypeError(desc='Invalid MP-BGP IPv4 capability '
|
||||
'settings: %s. Boolean value expected' % cmv4)
|
||||
|
||||
return cmv4
|
||||
|
||||
@ -615,8 +616,8 @@ def validate_cap_mbgp_ipv4(cmv4):
|
||||
@validate(name=CAP_MBGP_IPV6)
|
||||
def validate_cap_mbgp_ipv6(cmv6):
|
||||
if cmv6 not in (True, False):
|
||||
raise ConfigTypeError(desc='Invalid Enhanced Refresh capability '
|
||||
'settings: %s boolean value expected' % cmv6)
|
||||
raise ConfigTypeError(desc='Invalid MP-BGP IPv6 capability '
|
||||
'settings: %s. Boolean value expected' % cmv6)
|
||||
|
||||
return cmv6
|
||||
|
||||
@ -624,8 +625,8 @@ def validate_cap_mbgp_ipv6(cmv6):
|
||||
@validate(name=CAP_MBGP_VPNV4)
|
||||
def validate_cap_mbgp_vpnv4(cmv4):
|
||||
if cmv4 not in (True, False):
|
||||
raise ConfigTypeError(desc='Invalid Enhanced Refresh capability '
|
||||
'settings: %s boolean value expected' % cmv4)
|
||||
raise ConfigTypeError(desc='Invalid MP-BGP VPNv4 capability '
|
||||
'settings: %s. Boolean value expected' % cmv4)
|
||||
|
||||
return cmv4
|
||||
|
||||
@ -633,12 +634,20 @@ def validate_cap_mbgp_vpnv4(cmv4):
|
||||
@validate(name=CAP_MBGP_VPNV6)
|
||||
def validate_cap_mbgp_vpnv6(cmv6):
|
||||
if cmv6 not in (True, False):
|
||||
raise ConfigTypeError(desc='Invalid Enhanced Refresh capability '
|
||||
'settings: %s boolean value expected' % cmv6)
|
||||
raise ConfigTypeError(desc='Invalid MP-BGP VPNv6 capability '
|
||||
'settings: %s. Boolean value expected' % cmv6)
|
||||
|
||||
return cmv6
|
||||
|
||||
|
||||
@validate(name=CAP_MBGP_EVPN)
|
||||
def validate_cap_mbgp_evpn(cmevpn):
|
||||
if cmevpn not in (True, False):
|
||||
raise ConfigTypeError(desc='Invalid Ethernet VPN capability '
|
||||
'settings: %s. Boolean value expected' % cmevpn)
|
||||
return cmevpn
|
||||
|
||||
|
||||
@validate(name=CAP_RTC)
|
||||
def validate_cap_rtc(cap_rtc):
|
||||
if cap_rtc not in (True, False):
|
||||
@ -688,7 +697,7 @@ def validate_soo_list(soo_list):
|
||||
unique_rts = set(soo_list)
|
||||
if len(unique_rts) != len(soo_list):
|
||||
raise ConfigValueError(desc='Duplicate value provided in %s' %
|
||||
(soo_list))
|
||||
soo_list)
|
||||
return soo_list
|
||||
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ from ryu.lib.packet.bgp import RF_IPv4_UC
|
||||
from ryu.lib.packet.bgp import RF_IPv6_UC
|
||||
from ryu.lib.packet.bgp import RF_IPv4_VPN
|
||||
from ryu.lib.packet.bgp import RF_IPv6_VPN
|
||||
from ryu.lib.packet.bgp import RF_L2_EVPN
|
||||
from ryu.lib.packet.bgp import RF_RTC_UC
|
||||
from ryu.lib.packet.bgp import BGPOptParamCapabilityFourOctetAsNumber
|
||||
from ryu.lib.packet.bgp import BGPOptParamCapabilityEnhancedRouteRefresh
|
||||
@ -45,6 +46,7 @@ from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_IPV4
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_IPV6
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_VPNV4
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_VPNV6
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_EVPN
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_REFRESH
|
||||
from ryu.services.protocols.bgp.rtconf.base import CAP_RTC
|
||||
from ryu.services.protocols.bgp.rtconf.base import compute_optional_conf
|
||||
@ -100,6 +102,7 @@ DEFAULT_CAP_MBGP_IPV4 = True
|
||||
DEFAULT_CAP_MBGP_IPV6 = False
|
||||
DEFAULT_CAP_MBGP_VPNV4 = False
|
||||
DEFAULT_CAP_MBGP_VPNV6 = False
|
||||
DEFAULT_CAP_MBGP_EVPN = False
|
||||
DEFAULT_HOLD_TIME = 40
|
||||
DEFAULT_ENABLED = True
|
||||
DEFAULT_CAP_RTC = False
|
||||
@ -302,7 +305,7 @@ class NeighborConf(ConfWithId, ConfWithStats):
|
||||
CAP_FOUR_OCTET_AS_NUMBER,
|
||||
CAP_MBGP_IPV4, CAP_MBGP_IPV6,
|
||||
CAP_MBGP_VPNV4, CAP_MBGP_VPNV6,
|
||||
CAP_RTC, RTC_AS, HOLD_TIME,
|
||||
CAP_RTC, CAP_MBGP_EVPN, RTC_AS, HOLD_TIME,
|
||||
ENABLED, MULTI_EXIT_DISC, MAX_PREFIXES,
|
||||
ADVERTISE_PEER_AS, SITE_OF_ORIGINS,
|
||||
LOCAL_ADDRESS, LOCAL_PORT, LOCAL_AS,
|
||||
@ -328,6 +331,8 @@ class NeighborConf(ConfWithId, ConfWithStats):
|
||||
CAP_MBGP_IPV6, DEFAULT_CAP_MBGP_IPV6, **kwargs)
|
||||
self._settings[CAP_MBGP_VPNV4] = compute_optional_conf(
|
||||
CAP_MBGP_VPNV4, DEFAULT_CAP_MBGP_VPNV4, **kwargs)
|
||||
self._settings[CAP_MBGP_EVPN] = compute_optional_conf(
|
||||
CAP_MBGP_EVPN, DEFAULT_CAP_MBGP_EVPN, **kwargs)
|
||||
self._settings[CAP_MBGP_VPNV6] = compute_optional_conf(
|
||||
CAP_MBGP_VPNV6, DEFAULT_CAP_MBGP_VPNV6, **kwargs)
|
||||
self._settings[HOLD_TIME] = compute_optional_conf(
|
||||
@ -492,6 +497,10 @@ class NeighborConf(ConfWithId, ConfWithStats):
|
||||
def cap_mbgp_vpnv6(self):
|
||||
return self._settings[CAP_MBGP_VPNV6]
|
||||
|
||||
@property
|
||||
def cap_mbgp_evpn(self):
|
||||
return self._settings[CAP_MBGP_EVPN]
|
||||
|
||||
@property
|
||||
def cap_rtc(self):
|
||||
return self._settings[CAP_RTC]
|
||||
@ -607,6 +616,11 @@ class NeighborConf(ConfWithId, ConfWithStats):
|
||||
BGPOptParamCapabilityMultiprotocol(
|
||||
RF_RTC_UC.afi, RF_RTC_UC.safi))
|
||||
|
||||
if self.cap_mbgp_evpn:
|
||||
mbgp_caps.append(
|
||||
BGPOptParamCapabilityMultiprotocol(
|
||||
RF_L2_EVPN.afi, RF_L2_EVPN.safi))
|
||||
|
||||
if mbgp_caps:
|
||||
capabilities[BGP_CAP_MULTIPROTOCOL] = mbgp_caps
|
||||
|
||||
@ -631,7 +645,7 @@ class NeighborConf(ConfWithId, ConfWithStats):
|
||||
self.enabled)
|
||||
|
||||
def __str__(self):
|
||||
return 'Neighbor: %s' % (self.ip_address)
|
||||
return 'Neighbor: %s' % self.ip_address
|
||||
|
||||
|
||||
class NeighborsConf(BaseConf):
|
||||
|
||||
@ -25,6 +25,7 @@ from ryu.lib.packet.bgp import (
|
||||
RF_IPv6_UC,
|
||||
RF_IPv4_VPN,
|
||||
RF_IPv6_VPN,
|
||||
RF_L2_EVPN,
|
||||
RF_RTC_UC,
|
||||
RouteTargetMembershipNLRI,
|
||||
BGP_ATTR_TYPE_MULTI_EXIT_DISC,
|
||||
@ -41,6 +42,7 @@ from ryu.services.protocols.bgp.info_base.ipv4 import Ipv4Path
|
||||
from ryu.services.protocols.bgp.info_base.ipv6 import Ipv6Path
|
||||
from ryu.services.protocols.bgp.info_base.vpnv4 import Vpnv4Path
|
||||
from ryu.services.protocols.bgp.info_base.vpnv6 import Vpnv6Path
|
||||
from ryu.services.protocols.bgp.info_base.evpn import EvpnPath
|
||||
|
||||
|
||||
LOG = logging.getLogger('utils.bgp')
|
||||
@ -50,6 +52,7 @@ _ROUTE_FAMILY_TO_PATH_MAP = {RF_IPv4_UC: Ipv4Path,
|
||||
RF_IPv6_UC: Ipv6Path,
|
||||
RF_IPv4_VPN: Vpnv4Path,
|
||||
RF_IPv6_VPN: Vpnv6Path,
|
||||
RF_L2_EVPN: EvpnPath,
|
||||
RF_RTC_UC: RtcPath}
|
||||
|
||||
|
||||
|
||||
@ -17,11 +17,25 @@
|
||||
Module provides utilities for validation.
|
||||
"""
|
||||
import numbers
|
||||
import re
|
||||
import socket
|
||||
|
||||
import six
|
||||
|
||||
|
||||
def is_valid_mac(mac):
|
||||
"""Returns True if the given MAC address is valid.
|
||||
|
||||
The given MAC address should be a colon hexadecimal notation string.
|
||||
|
||||
Samples:
|
||||
- valid address: aa:bb:cc:dd:ee:ff, 11:22:33:44:55:66
|
||||
- invalid address: aa:bb:cc:dd, 11-22-33-44-55-66, etc.
|
||||
"""
|
||||
return bool(re.match(r'^' + r'[\:\-]'.join([r'([0-9a-f]{2})'] * 6)
|
||||
+ r'$', mac.lower()))
|
||||
|
||||
|
||||
def is_valid_ipv4(ipv4):
|
||||
"""Returns True if given is a valid ipv4 address.
|
||||
|
||||
@ -190,14 +204,25 @@ def is_valid_mpls_label(label):
|
||||
A value of 3 represents the "Implicit NULL Label".
|
||||
Values 4-15 are reserved.
|
||||
"""
|
||||
valid = True
|
||||
|
||||
if (not isinstance(label, numbers.Integral) or
|
||||
(label >= 4 and label <= 15) or
|
||||
(4 <= label <= 15) or
|
||||
(label < 0 or label > 2 ** 20)):
|
||||
valid = False
|
||||
return False
|
||||
|
||||
return valid
|
||||
return True
|
||||
|
||||
|
||||
def is_valid_mpls_labels(labels):
|
||||
"""Returns True if the given value is a list of valid MPLS labels.
|
||||
"""
|
||||
if not isinstance(labels, (list, tuple)):
|
||||
return False
|
||||
|
||||
for label in labels:
|
||||
if not is_valid_mpls_label(label):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def is_valid_route_dist(route_dist):
|
||||
@ -237,3 +262,17 @@ def is_valid_ext_comm_attr(attr):
|
||||
is_valid = False
|
||||
|
||||
return is_valid
|
||||
|
||||
|
||||
def is_valid_esi(esi):
|
||||
"""Returns True if the given EVPN Ethernet SegmentEthernet ID is valid."""
|
||||
# Note: Currently, only integer type value is supported
|
||||
return isinstance(esi, numbers.Integral)
|
||||
|
||||
|
||||
def is_valid_ethernet_tag_id(etag_id):
|
||||
"""Returns True if the given EVPN Ethernet Tag ID is valid.
|
||||
|
||||
Ethernet Tag ID should be a 32-bit field number.
|
||||
"""
|
||||
return isinstance(etag_id, numbers.Integral) and 0 <= etag_id <= 0xffffffff
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user