mirror of
https://github.com/faucetsdn/ryu.git
synced 2026-05-08 13:56:09 +02:00
BGPSpeaker: Support Ethernet A-D Route and Ethernet Segment Route
This patch supports Ethernet Auto-discovery Route and Ethernet Segment Route in BGPSpeaker. Signed-off-by: Shinpei Muraoka <shinpei.muraoka@gmail.com> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
This commit is contained in:
parent
c4a84cb246
commit
17acdbb610
@ -1310,6 +1310,9 @@ class EvpnNLRI(StringifyMixin, _TypeDisp):
|
||||
|
||||
ROUTE_TYPE_NAME = None # must be defined in subclass
|
||||
|
||||
# Reserved value for Ethernet Tag ID.
|
||||
MAX_ET = 0xFFFFFFFF
|
||||
|
||||
# Dictionary of ROUTE_TYPE_NAME to subclass.
|
||||
# e.g.)
|
||||
# _NAMES = {'eth_ad': EvpnEthernetAutoDiscoveryNLRI, ...}
|
||||
@ -3075,6 +3078,9 @@ class BGPEvpnEsiLabelExtendedCommunity(_ExtendedCommunity):
|
||||
_VALUE_PACK_STR = '!BB2x3s'
|
||||
_VALUE_FIELDS = ['subtype', 'flags']
|
||||
|
||||
# Classification for Flags.
|
||||
SINGLE_ACTIVE_BIT = 1 << 0
|
||||
|
||||
def __init__(self, label=None, mpls_label=None, vni=None, **kwargs):
|
||||
super(BGPEvpnEsiLabelExtendedCommunity, self).__init__()
|
||||
self.do_init(BGPEvpnEsiLabelExtendedCommunity, self, kwargs)
|
||||
|
||||
@ -45,6 +45,7 @@ ROUTE_FAMILY = 'route_family'
|
||||
EVPN_ROUTE_TYPE = 'route_type'
|
||||
EVPN_ESI = 'esi'
|
||||
EVPN_ETHERNET_TAG_ID = 'ethernet_tag_id'
|
||||
REDUNDANCY_MODE = 'redundancy_mode'
|
||||
MAC_ADDR = 'mac_addr'
|
||||
IP_ADDR = 'ip_addr'
|
||||
IP_PREFIX = 'ip_prefix'
|
||||
|
||||
@ -18,13 +18,18 @@
|
||||
"""
|
||||
import logging
|
||||
|
||||
from ryu.lib.packet.bgp import EvpnEsi
|
||||
from ryu.lib.packet.bgp import EvpnNLRI
|
||||
from ryu.lib.packet.bgp import EvpnEthernetAutoDiscoveryNLRI
|
||||
from ryu.lib.packet.bgp import EvpnMacIPAdvertisementNLRI
|
||||
from ryu.lib.packet.bgp import EvpnInclusiveMulticastEthernetTagNLRI
|
||||
from ryu.lib.packet.bgp import EvpnEthernetSegmentNLRI
|
||||
from ryu.lib.packet.bgp import EvpnIpPrefixNLRI
|
||||
from ryu.lib.packet.bgp import BGPPathAttributePmsiTunnel
|
||||
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 REDUNDANCY_MODE
|
||||
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 IP_PREFIX
|
||||
@ -53,16 +58,49 @@ from ryu.services.protocols.bgp.utils import validation
|
||||
|
||||
LOG = logging.getLogger('bgpspeaker.api.prefix')
|
||||
|
||||
# Maximum value of the Ethernet Tag ID
|
||||
EVPN_MAX_ET = EvpnNLRI.MAX_ET
|
||||
|
||||
# ESI Types
|
||||
ESI_TYPE_ARBITRARY = EvpnEsi.ARBITRARY
|
||||
ESI_TYPE_LACP = EvpnEsi.LACP
|
||||
ESI_TYPE_L2_BRIDGE = EvpnEsi.L2_BRIDGE
|
||||
ESI_TYPE_MAC_BASED = EvpnEsi.MAC_BASED
|
||||
ESI_TYPE_ROUTER_ID = EvpnEsi.ROUTER_ID
|
||||
ESI_TYPE_AS_BASED = EvpnEsi.AS_BASED
|
||||
SUPPORTED_ESI_TYPES = [
|
||||
ESI_TYPE_ARBITRARY,
|
||||
ESI_TYPE_LACP,
|
||||
ESI_TYPE_L2_BRIDGE,
|
||||
ESI_TYPE_MAC_BASED,
|
||||
ESI_TYPE_ROUTER_ID,
|
||||
ESI_TYPE_AS_BASED,
|
||||
]
|
||||
|
||||
# Constants used in API calls for EVPN
|
||||
EVPN_ETH_AUTO_DISCOVERY = EvpnEthernetAutoDiscoveryNLRI.ROUTE_TYPE_NAME
|
||||
EVPN_MAC_IP_ADV_ROUTE = EvpnMacIPAdvertisementNLRI.ROUTE_TYPE_NAME
|
||||
EVPN_MULTICAST_ETAG_ROUTE = EvpnInclusiveMulticastEthernetTagNLRI.ROUTE_TYPE_NAME
|
||||
EVPN_MULTICAST_ETAG_ROUTE = (
|
||||
EvpnInclusiveMulticastEthernetTagNLRI.ROUTE_TYPE_NAME)
|
||||
EVPN_ETH_SEGMENT = EvpnEthernetSegmentNLRI.ROUTE_TYPE_NAME
|
||||
EVPN_IP_PREFIX_ROUTE = EvpnIpPrefixNLRI.ROUTE_TYPE_NAME
|
||||
SUPPORTED_EVPN_ROUTE_TYPES = [
|
||||
EVPN_ETH_AUTO_DISCOVERY,
|
||||
EVPN_MAC_IP_ADV_ROUTE,
|
||||
EVPN_MULTICAST_ETAG_ROUTE,
|
||||
EVPN_ETH_SEGMENT,
|
||||
EVPN_IP_PREFIX_ROUTE,
|
||||
]
|
||||
|
||||
# Constants for ESI Label extended community
|
||||
REDUNDANCY_MODE_ALL_ACTIVE = 'all_active'
|
||||
REDUNDANCY_MODE_SINGLE_ACTIVE = 'single_active'
|
||||
REDUNDANCY_MODE_TYPES = [
|
||||
None,
|
||||
REDUNDANCY_MODE_ALL_ACTIVE,
|
||||
REDUNDANCY_MODE_SINGLE_ACTIVE,
|
||||
]
|
||||
|
||||
# Constants for BGP Tunnel Encapsulation Attribute
|
||||
TUNNEL_TYPE_VXLAN = 'vxlan'
|
||||
TUNNEL_TYPE_NVGRE = 'nvgre'
|
||||
@ -134,6 +172,13 @@ def is_valid_ethernet_tag_id(ethernet_tag_id):
|
||||
conf_value=ethernet_tag_id)
|
||||
|
||||
|
||||
@validate(name=REDUNDANCY_MODE)
|
||||
def is_valid_redundancy_mode(redundancy_mode):
|
||||
if redundancy_mode not in REDUNDANCY_MODE_TYPES:
|
||||
raise ConfigValueError(conf_name=REDUNDANCY_MODE,
|
||||
conf_value=redundancy_mode)
|
||||
|
||||
|
||||
@validate(name=MAC_ADDR)
|
||||
def is_valid_mac_addr(addr):
|
||||
if not validation.is_valid_mac(addr):
|
||||
@ -241,12 +286,19 @@ def delete_local(route_dist, prefix, route_family=VRF_RF_IPV4):
|
||||
@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, IP_PREFIX, GW_IP_ADDR, EVPN_VNI,
|
||||
TUNNEL_TYPE, PMSI_TUNNEL_TYPE])
|
||||
opt_args=[EVPN_ESI, EVPN_ETHERNET_TAG_ID,
|
||||
REDUNDANCY_MODE, MAC_ADDR, IP_ADDR, IP_PREFIX,
|
||||
GW_IP_ADDR, EVPN_VNI, TUNNEL_TYPE,
|
||||
PMSI_TUNNEL_TYPE])
|
||||
def add_evpn_local(route_type, route_dist, next_hop, **kwargs):
|
||||
"""Adds EVPN route from VRF identified by *route_dist*.
|
||||
"""
|
||||
|
||||
if(route_type in [EVPN_ETH_AUTO_DISCOVERY, EVPN_ETH_SEGMENT]
|
||||
and kwargs['esi'] == 0):
|
||||
raise ConfigValueError(conf_name=EVPN_ESI,
|
||||
conf_value=kwargs['esi'])
|
||||
|
||||
try:
|
||||
# Create new path and insert into appropriate VRF table.
|
||||
tm = CORE_MANAGER.get_core_service().table_manager
|
||||
|
||||
@ -3,11 +3,16 @@ import os
|
||||
from ryu.services.protocols.bgp.bgpspeaker import RF_VPN_V4
|
||||
from ryu.services.protocols.bgp.bgpspeaker import RF_VPN_V6
|
||||
from ryu.services.protocols.bgp.bgpspeaker import RF_L2_EVPN
|
||||
from ryu.services.protocols.bgp.bgpspeaker import EVPN_MAX_ET
|
||||
from ryu.services.protocols.bgp.bgpspeaker import ESI_TYPE_LACP
|
||||
from ryu.services.protocols.bgp.bgpspeaker import ESI_TYPE_MAC_BASED
|
||||
from ryu.services.protocols.bgp.bgpspeaker import EVPN_ETH_AUTO_DISCOVERY
|
||||
from ryu.services.protocols.bgp.bgpspeaker import EVPN_MAC_IP_ADV_ROUTE
|
||||
from ryu.services.protocols.bgp.bgpspeaker import TUNNEL_TYPE_VXLAN
|
||||
from ryu.services.protocols.bgp.bgpspeaker import EVPN_MULTICAST_ETAG_ROUTE
|
||||
from ryu.services.protocols.bgp.bgpspeaker import EVPN_ETH_SEGMENT
|
||||
from ryu.services.protocols.bgp.bgpspeaker import EVPN_IP_PREFIX_ROUTE
|
||||
|
||||
from ryu.services.protocols.bgp.bgpspeaker import REDUNDANCY_MODE_SINGLE_ACTIVE
|
||||
|
||||
# =============================================================================
|
||||
# BGP configuration.
|
||||
@ -91,6 +96,17 @@ BGP = {
|
||||
'route_dist': '65001:150',
|
||||
},
|
||||
# Example of EVPN prefix
|
||||
{
|
||||
'route_type': EVPN_ETH_AUTO_DISCOVERY,
|
||||
'route_dist': '65001:200',
|
||||
'esi': {
|
||||
'type': ESI_TYPE_LACP,
|
||||
'mac_addr': 'aa:bb:cc:dd:ee:ff',
|
||||
'port_key': 100,
|
||||
},
|
||||
'ethernet_tag_id': EVPN_MAX_ET,
|
||||
'redundancy_mode': REDUNDANCY_MODE_SINGLE_ACTIVE,
|
||||
},
|
||||
{
|
||||
'route_type': EVPN_MAC_IP_ADV_ROUTE,
|
||||
'route_dist': '65001:200',
|
||||
@ -109,6 +125,16 @@ BGP = {
|
||||
'ethernet_tag_id': 0,
|
||||
'ip_addr': '10.40.1.1',
|
||||
},
|
||||
{
|
||||
'route_type': EVPN_ETH_SEGMENT,
|
||||
'route_dist': '65001:200',
|
||||
'esi': {
|
||||
'type': ESI_TYPE_MAC_BASED,
|
||||
'mac_addr': 'aa:bb:cc:dd:ee:ff',
|
||||
'local_disc': 100,
|
||||
},
|
||||
'ip_addr': '172.17.0.1',
|
||||
},
|
||||
{
|
||||
'route_type': EVPN_IP_PREFIX_ROUTE,
|
||||
'route_dist': '65001:200',
|
||||
|
||||
@ -26,6 +26,7 @@ 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 REDUNDANCY_MODE
|
||||
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
|
||||
@ -36,9 +37,17 @@ from ryu.services.protocols.bgp.api.base import ROUTE_FAMILY
|
||||
from ryu.services.protocols.bgp.api.base import EVPN_VNI
|
||||
from ryu.services.protocols.bgp.api.base import TUNNEL_TYPE
|
||||
from ryu.services.protocols.bgp.api.base import PMSI_TUNNEL_TYPE
|
||||
from ryu.services.protocols.bgp.api.prefix import EVPN_MAX_ET
|
||||
from ryu.services.protocols.bgp.api.prefix import ESI_TYPE_LACP
|
||||
from ryu.services.protocols.bgp.api.prefix import ESI_TYPE_L2_BRIDGE
|
||||
from ryu.services.protocols.bgp.api.prefix import ESI_TYPE_MAC_BASED
|
||||
from ryu.services.protocols.bgp.api.prefix import EVPN_ETH_AUTO_DISCOVERY
|
||||
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.api.prefix import EVPN_ETH_SEGMENT
|
||||
from ryu.services.protocols.bgp.api.prefix import EVPN_IP_PREFIX_ROUTE
|
||||
from ryu.services.protocols.bgp.api.prefix import REDUNDANCY_MODE_ALL_ACTIVE
|
||||
from ryu.services.protocols.bgp.api.prefix import REDUNDANCY_MODE_SINGLE_ACTIVE
|
||||
from ryu.services.protocols.bgp.api.prefix import TUNNEL_TYPE_VXLAN
|
||||
from ryu.services.protocols.bgp.api.prefix import TUNNEL_TYPE_NVGRE
|
||||
from ryu.services.protocols.bgp.api.prefix import (
|
||||
@ -538,18 +547,25 @@ class BGPSpeaker(object):
|
||||
def evpn_prefix_add(self, route_type, route_dist, esi=0,
|
||||
ethernet_tag_id=None, mac_addr=None, ip_addr=None,
|
||||
ip_prefix=None, gw_ip_addr=None, vni=None,
|
||||
next_hop=None, tunnel_type=None,
|
||||
pmsi_tunnel_type=None):
|
||||
next_hop=None, tunnel_type=None, pmsi_tunnel_type=None,
|
||||
redundancy_mode=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,
|
||||
EVPN_MULTICAST_ETAG_ROUTE and EVPN_IP_PREFIX_ROUTE.
|
||||
supported route types are EVPN_ETH_AUTO_DISCOVERY,
|
||||
EVPN_MAC_IP_ADV_ROUTE, EVPN_MULTICAST_ETAG_ROUTE, EVPN_ETH_SEGMENT
|
||||
and EVPN_IP_PREFIX_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.
|
||||
``esi`` is an value to specify the Ethernet Segment Identifier.
|
||||
0 is the default and denotes a single-homed site.
|
||||
If you want to advertise esi other than 0,
|
||||
it must be set as a dictionary type.
|
||||
The following keys and values must be set.
|
||||
- type: specifies one of the ESI type.
|
||||
The remaining keys and values are the same as the argument of
|
||||
the class corresponding to esi_type.
|
||||
|
||||
``ethernet_tag_id`` specifies the Ethernet Tag ID.
|
||||
|
||||
@ -576,6 +592,10 @@ class BGPSpeaker(object):
|
||||
used to encode the multicast tunnel identifier.
|
||||
This field is advertised only if route_type is
|
||||
EVPN_MULTICAST_ETAG_ROUTE.
|
||||
|
||||
``redundancy_mode`` specifies a redundancy mode type.
|
||||
The supported redundancy mode types are REDUNDANCY_MODE_ALL_ACTIVE
|
||||
and REDUNDANCY_MODE_SINGLE_ACTIVE.
|
||||
"""
|
||||
func_name = 'evpn_prefix.add_local'
|
||||
|
||||
@ -593,7 +613,16 @@ class BGPSpeaker(object):
|
||||
kwargs[TUNNEL_TYPE] = tunnel_type
|
||||
|
||||
# Set route type specific arguments
|
||||
if route_type == EVPN_MAC_IP_ADV_ROUTE:
|
||||
if route_type == EVPN_ETH_AUTO_DISCOVERY:
|
||||
# REDUNDANCY_MODE is parameter for extended community
|
||||
kwargs.update({
|
||||
EVPN_ESI: esi,
|
||||
EVPN_ETHERNET_TAG_ID: ethernet_tag_id,
|
||||
REDUNDANCY_MODE: redundancy_mode,
|
||||
})
|
||||
if vni is not None:
|
||||
kwargs[EVPN_VNI] = vni
|
||||
elif route_type == EVPN_MAC_IP_ADV_ROUTE:
|
||||
kwargs.update({
|
||||
EVPN_ESI: esi,
|
||||
EVPN_ETHERNET_TAG_ID: ethernet_tag_id,
|
||||
@ -617,6 +646,11 @@ class BGPSpeaker(object):
|
||||
elif pmsi_tunnel_type is not None:
|
||||
raise ValueError('Unsupported PMSI tunnel type: %s' %
|
||||
pmsi_tunnel_type)
|
||||
elif route_type == EVPN_ETH_SEGMENT:
|
||||
kwargs.update({
|
||||
EVPN_ESI: esi,
|
||||
IP_ADDR: ip_addr,
|
||||
})
|
||||
elif route_type == EVPN_IP_PREFIX_ROUTE:
|
||||
kwargs.update({
|
||||
EVPN_ESI: esi,
|
||||
@ -641,8 +675,14 @@ class BGPSpeaker(object):
|
||||
|
||||
``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.
|
||||
``esi`` is an value to specify the Ethernet Segment Identifier.
|
||||
0 is the default and denotes a single-homed site.
|
||||
If you want to advertise esi other than 0,
|
||||
it must be set as a dictionary type.
|
||||
The following keys and values must be set.
|
||||
- type: specifies one of the ESI type.
|
||||
The remaining keys and values are the same as the argument of
|
||||
the class corresponding to esi_type.
|
||||
|
||||
``ethernet_tag_id`` specifies the Ethernet Tag ID.
|
||||
|
||||
@ -659,7 +699,12 @@ class BGPSpeaker(object):
|
||||
ROUTE_DISTINGUISHER: route_dist}
|
||||
|
||||
# Set route type specific arguments
|
||||
if route_type == EVPN_MAC_IP_ADV_ROUTE:
|
||||
if route_type == EVPN_ETH_AUTO_DISCOVERY:
|
||||
kwargs.update({
|
||||
EVPN_ESI: esi,
|
||||
EVPN_ETHERNET_TAG_ID: ethernet_tag_id,
|
||||
})
|
||||
elif route_type == EVPN_MAC_IP_ADV_ROUTE:
|
||||
kwargs.update({
|
||||
EVPN_ESI: esi,
|
||||
EVPN_ETHERNET_TAG_ID: ethernet_tag_id,
|
||||
@ -671,6 +716,11 @@ class BGPSpeaker(object):
|
||||
EVPN_ETHERNET_TAG_ID: ethernet_tag_id,
|
||||
IP_ADDR: ip_addr,
|
||||
})
|
||||
elif route_type == EVPN_ETH_SEGMENT:
|
||||
kwargs.update({
|
||||
EVPN_ESI: esi,
|
||||
IP_ADDR: ip_addr,
|
||||
})
|
||||
elif route_type == EVPN_IP_PREFIX_ROUTE:
|
||||
kwargs.update({
|
||||
EVPN_ETHERNET_TAG_ID: ethernet_tag_id,
|
||||
|
||||
@ -32,6 +32,7 @@ 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 EvpnEsi
|
||||
from ryu.lib.packet.bgp import EvpnArbitraryEsi
|
||||
from ryu.lib.packet.bgp import EvpnNLRI
|
||||
from ryu.lib.packet.bgp import EvpnMacIPAdvertisementNLRI
|
||||
@ -483,7 +484,8 @@ class TableCoreManager(object):
|
||||
|
||||
def update_vrf_table(self, route_dist, prefix=None, next_hop=None,
|
||||
route_family=None, route_type=None, tunnel_type=None,
|
||||
is_withdraw=False, pmsi_tunnel_type=None, **kwargs):
|
||||
is_withdraw=False, redundancy_mode=None,
|
||||
pmsi_tunnel_type=None, **kwargs):
|
||||
"""Update a BGP route in the VRF table identified by `route_dist`
|
||||
with the given `next_hop`.
|
||||
|
||||
@ -496,6 +498,8 @@ class TableCoreManager(object):
|
||||
If `route_family` is VRF_RF_L2_EVPN, `route_type` and `kwargs`
|
||||
are required to construct EVPN NLRI and `prefix` is ignored.
|
||||
|
||||
``redundancy_mode`` specifies a redundancy mode type.
|
||||
|
||||
` `pmsi_tunnel_type` specifies the type of the PMSI tunnel attribute
|
||||
used to encode the multicast tunnel identifier.
|
||||
This field is advertised only if route_type is
|
||||
@ -522,6 +526,8 @@ class TableCoreManager(object):
|
||||
desc='VRF table does not exist: route_dist=%s, '
|
||||
'route_family=%s' % (route_dist, route_family))
|
||||
|
||||
vni = kwargs.get('vni', None)
|
||||
|
||||
if route_family == VRF_RF_IPV4:
|
||||
if not is_valid_ipv4_prefix(prefix):
|
||||
raise BgpCoreError(desc='Invalid IPv4 prefix: %s' % prefix)
|
||||
@ -541,14 +547,20 @@ class TableCoreManager(object):
|
||||
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))
|
||||
if 'vni' in kwargs:
|
||||
# Disable to generate MPLS labels, because encapsulation type
|
||||
# is not MPLS.
|
||||
if isinstance(esi, dict):
|
||||
esi_type = esi.get('type', 0)
|
||||
esi_class = EvpnEsi._lookup_type(esi_type)
|
||||
kwargs['esi'] = esi_class.from_jsondict(esi)
|
||||
else: # isinstance(esi, numbers.Integral)
|
||||
kwargs['esi'] = EvpnArbitraryEsi(
|
||||
type_desc.Int9.from_user(esi))
|
||||
if vni is not None:
|
||||
# Disable to generate MPLS labels,
|
||||
# because encapsulation type is not MPLS.
|
||||
from ryu.services.protocols.bgp.api.prefix import (
|
||||
TUNNEL_TYPE_VXLAN, TUNNEL_TYPE_NVGRE)
|
||||
assert tunnel_type in [TUNNEL_TYPE_VXLAN, TUNNEL_TYPE_NVGRE]
|
||||
assert tunnel_type in [
|
||||
None, TUNNEL_TYPE_VXLAN, TUNNEL_TYPE_NVGRE]
|
||||
gen_lbl = False
|
||||
prefix = subclass(**kwargs)
|
||||
else:
|
||||
@ -559,7 +571,8 @@ class TableCoreManager(object):
|
||||
# withdrawal. Hence multiple withdrawals have not side effect.
|
||||
return vrf_table.insert_vrf_path(
|
||||
nlri=prefix, next_hop=next_hop, gen_lbl=gen_lbl,
|
||||
is_withdraw=is_withdraw, tunnel_type=tunnel_type,
|
||||
is_withdraw=is_withdraw, redundancy_mode=redundancy_mode,
|
||||
vni=vni, tunnel_type=tunnel_type,
|
||||
pmsi_tunnel_type=pmsi_tunnel_type)
|
||||
|
||||
def update_global_table(self, prefix, next_hop=None, is_withdraw=False):
|
||||
|
||||
@ -28,10 +28,13 @@ from ryu.lib.packet.bgp import BGP_ATTR_TYEP_PMSI_TUNNEL_ATTRIBUTE
|
||||
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_MULTI_EXIT_DISC
|
||||
from ryu.lib.packet.bgp import BGPPathAttributeOrigin
|
||||
from ryu.lib.packet.bgp import BGPPathAttributeAsPath
|
||||
from ryu.lib.packet.bgp import EvpnEthernetSegmentNLRI
|
||||
from ryu.lib.packet.bgp import BGPPathAttributeExtendedCommunities
|
||||
from ryu.lib.packet.bgp import BGPTwoOctetAsSpecificExtendedCommunity
|
||||
from ryu.lib.packet.bgp import BGPPathAttributeMultiExitDisc
|
||||
from ryu.lib.packet.bgp import BGPEncapsulationExtendedCommunity
|
||||
from ryu.lib.packet.bgp import BGPEvpnEsiLabelExtendedCommunity
|
||||
from ryu.lib.packet.bgp import BGPEvpnEsImportRTExtendedCommunity
|
||||
from ryu.lib.packet.bgp import BGPPathAttributePmsiTunnel
|
||||
from ryu.lib.packet.bgp import PmsiTunnelIdIngressReplication
|
||||
from ryu.lib.packet.bgp import RF_L2_EVPN
|
||||
@ -221,6 +224,28 @@ class VrfTable(Table):
|
||||
label_list = []
|
||||
vrf_conf = self.vrf_conf
|
||||
if not is_withdraw:
|
||||
table_manager = self._core_service.table_manager
|
||||
if gen_lbl and next_hop:
|
||||
# Label per next_hop demands we use a different label
|
||||
# per next_hop. Here connected interfaces are advertised per
|
||||
# VRF.
|
||||
label_key = (vrf_conf.route_dist, next_hop)
|
||||
nh_label = table_manager.get_nexthop_label(label_key)
|
||||
if not nh_label:
|
||||
nh_label = table_manager.get_next_vpnv4_label()
|
||||
table_manager.set_nexthop_label(label_key, nh_label)
|
||||
label_list.append(nh_label)
|
||||
|
||||
elif gen_lbl:
|
||||
# If we do not have next_hop, get a new label.
|
||||
label_list.append(table_manager.get_next_vpnv4_label())
|
||||
|
||||
# Set MPLS labels with the generated labels
|
||||
if gen_lbl and isinstance(nlri, EvpnMacIPAdvertisementNLRI):
|
||||
nlri.mpls_labels = label_list[:2]
|
||||
elif gen_lbl and isinstance(nlri, EvpnIpPrefixNLRI):
|
||||
nlri.mpls_label = label_list[0]
|
||||
|
||||
# Create a dictionary for path-attrs.
|
||||
pattrs = OrderedDict()
|
||||
|
||||
@ -232,6 +257,15 @@ class VrfTable(Table):
|
||||
EXPECTED_ORIGIN)
|
||||
pattrs[BGP_ATTR_TYPE_AS_PATH] = BGPPathAttributeAsPath([])
|
||||
communities = []
|
||||
|
||||
# Set ES-Import Route Target
|
||||
if isinstance(nlri, EvpnEthernetSegmentNLRI):
|
||||
subtype = 2
|
||||
es_import = nlri.esi.mac_addr
|
||||
communities.append(BGPEvpnEsImportRTExtendedCommunity(
|
||||
subtype=subtype,
|
||||
es_import=es_import))
|
||||
|
||||
for rt in vrf_conf.export_rts:
|
||||
as_num, local_admin = rt.split(':')
|
||||
subtype = 2
|
||||
@ -253,28 +287,35 @@ class VrfTable(Table):
|
||||
communities.append(
|
||||
BGPEncapsulationExtendedCommunity.from_str(tunnel_type))
|
||||
|
||||
# Set ESI Label Extended Community
|
||||
redundancy_mode = kwargs.get('redundancy_mode', None)
|
||||
if redundancy_mode is not None:
|
||||
subtype = 1
|
||||
flags = 0
|
||||
|
||||
from ryu.services.protocols.bgp.api.prefix import (
|
||||
REDUNDANCY_MODE_SINGLE_ACTIVE)
|
||||
if redundancy_mode == REDUNDANCY_MODE_SINGLE_ACTIVE:
|
||||
flags |= BGPEvpnEsiLabelExtendedCommunity.SINGLE_ACTIVE_BIT
|
||||
|
||||
vni = kwargs.get('vni', None)
|
||||
if vni is not None:
|
||||
communities.append(BGPEvpnEsiLabelExtendedCommunity(
|
||||
subtype=subtype,
|
||||
flags=flags,
|
||||
vni=vni))
|
||||
else:
|
||||
communities.append(BGPEvpnEsiLabelExtendedCommunity(
|
||||
subtype=subtype,
|
||||
flags=flags,
|
||||
mpls_label=label_list[0]))
|
||||
|
||||
pattrs[BGP_ATTR_TYPE_EXTENDED_COMMUNITIES] = \
|
||||
BGPPathAttributeExtendedCommunities(communities=communities)
|
||||
if vrf_conf.multi_exit_disc:
|
||||
pattrs[BGP_ATTR_TYPE_MULTI_EXIT_DISC] = \
|
||||
BGPPathAttributeMultiExitDisc(vrf_conf.multi_exit_disc)
|
||||
|
||||
table_manager = self._core_service.table_manager
|
||||
if gen_lbl and next_hop:
|
||||
# Label per next_hop demands we use a different label
|
||||
# per next_hop. Here connected interfaces are advertised per
|
||||
# VRF.
|
||||
label_key = (vrf_conf.route_dist, next_hop)
|
||||
nh_label = table_manager.get_nexthop_label(label_key)
|
||||
if not nh_label:
|
||||
nh_label = table_manager.get_next_vpnv4_label()
|
||||
table_manager.set_nexthop_label(label_key, nh_label)
|
||||
label_list.append(nh_label)
|
||||
|
||||
elif gen_lbl:
|
||||
# If we do not have next_hop, get a new label.
|
||||
label_list.append(table_manager.get_next_vpnv4_label())
|
||||
|
||||
# Set PMSI Tunnel Attribute
|
||||
pmsi_tunnel_type = kwargs.get('pmsi_tunnel_type', None)
|
||||
if pmsi_tunnel_type is not None:
|
||||
@ -290,12 +331,6 @@ class VrfTable(Table):
|
||||
tunnel_type=pmsi_tunnel_type,
|
||||
tunnel_id=tunnel_id)
|
||||
|
||||
# Set MPLS labels with the generated labels
|
||||
if gen_lbl and isinstance(nlri, EvpnMacIPAdvertisementNLRI):
|
||||
nlri.mpls_labels = label_list[:2]
|
||||
elif gen_lbl and isinstance(nlri, EvpnIpPrefixNLRI):
|
||||
nlri.mpls_label = label_list[0]
|
||||
|
||||
puid = self.VRF_PATH_CLASS.create_puid(
|
||||
vrf_conf.route_dist, nlri.prefix)
|
||||
|
||||
|
||||
@ -240,8 +240,9 @@ def is_valid_ext_comm_attr(attr):
|
||||
|
||||
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)
|
||||
if isinstance(esi, numbers.Integral):
|
||||
return 0 <= esi <= 0xffffffffffffffffff
|
||||
return isinstance(esi, dict)
|
||||
|
||||
|
||||
def is_valid_ethernet_tag_id(etag_id):
|
||||
|
||||
@ -31,8 +31,12 @@ from ryu.lib.packet.bgp import BGP_ATTR_TYPE_AS_PATH
|
||||
from ryu.lib.packet.bgp import IPAddrPrefix
|
||||
from ryu.lib.packet.bgp import IP6AddrPrefix
|
||||
from ryu.lib.packet.bgp import EvpnArbitraryEsi
|
||||
from ryu.lib.packet.bgp import EvpnLACPEsi
|
||||
from ryu.lib.packet.bgp import EvpnEthernetAutoDiscoveryNLRI
|
||||
from ryu.lib.packet.bgp import EvpnMacIPAdvertisementNLRI
|
||||
from ryu.lib.packet.bgp import EvpnInclusiveMulticastEthernetTagNLRI
|
||||
from ryu.services.protocols.bgp.bgpspeaker import EVPN_MAX_ET
|
||||
from ryu.services.protocols.bgp.bgpspeaker import ESI_TYPE_LACP
|
||||
from ryu.services.protocols.bgp.core import BgpCoreError
|
||||
from ryu.services.protocols.bgp.core_managers import table_manager
|
||||
from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV4
|
||||
@ -115,7 +119,7 @@ class Test_TableCoreManager(unittest.TestCase):
|
||||
next_hop, route_family, route_type,
|
||||
**kwargs)
|
||||
|
||||
def test_update_vrf_table_l2_evpn_with_esi(self):
|
||||
def test_update_vrf_table_l2_evpn_with_esi_int(self):
|
||||
# Prepare test data
|
||||
route_dist = '65000:100'
|
||||
prefix_str = None # should be ignored
|
||||
@ -139,6 +143,31 @@ class Test_TableCoreManager(unittest.TestCase):
|
||||
next_hop, route_family, route_type,
|
||||
**kwargs)
|
||||
|
||||
def test_update_vrf_table_l2_evpn_with_esi_dict(self):
|
||||
# Prepare test data
|
||||
route_dist = '65000:100'
|
||||
prefix_str = None # should be ignored
|
||||
kwargs = {
|
||||
'ethernet_tag_id': EVPN_MAX_ET,
|
||||
}
|
||||
esi = EvpnLACPEsi(mac_addr='aa:bb:cc:dd:ee:ff', port_key=100)
|
||||
prefix_inst = EvpnEthernetAutoDiscoveryNLRI(
|
||||
route_dist=route_dist,
|
||||
esi=esi,
|
||||
**kwargs)
|
||||
next_hop = '0.0.0.0'
|
||||
route_family = VRF_RF_L2_EVPN
|
||||
route_type = EvpnEthernetAutoDiscoveryNLRI.ROUTE_TYPE_NAME
|
||||
kwargs['esi'] = {
|
||||
'type': ESI_TYPE_LACP,
|
||||
'mac_addr': 'aa:bb:cc:dd:ee:ff',
|
||||
'port_key': 100,
|
||||
}
|
||||
|
||||
self._test_update_vrf_table(prefix_inst, route_dist, prefix_str,
|
||||
next_hop, route_family, route_type,
|
||||
**kwargs)
|
||||
|
||||
def test_update_vrf_table_l2_evpn_without_esi(self):
|
||||
# Prepare test data
|
||||
route_dist = '65000:100'
|
||||
|
||||
@ -23,6 +23,12 @@ except ImportError:
|
||||
from nose.tools import raises
|
||||
|
||||
from ryu.services.protocols.bgp import bgpspeaker
|
||||
from ryu.services.protocols.bgp.bgpspeaker import EVPN_MAX_ET
|
||||
from ryu.services.protocols.bgp.bgpspeaker import ESI_TYPE_LACP
|
||||
from ryu.services.protocols.bgp.api.prefix import ESI_TYPE_L2_BRIDGE
|
||||
from ryu.services.protocols.bgp.bgpspeaker import ESI_TYPE_MAC_BASED
|
||||
from ryu.services.protocols.bgp.api.prefix import REDUNDANCY_MODE_ALL_ACTIVE
|
||||
from ryu.services.protocols.bgp.api.prefix import REDUNDANCY_MODE_SINGLE_ACTIVE
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -33,6 +39,86 @@ class Test_BGPSpeaker(unittest.TestCase):
|
||||
Test case for bgp.bgpspeaker.BGPSpeaker
|
||||
"""
|
||||
|
||||
@mock.patch('ryu.services.protocols.bgp.bgpspeaker.BGPSpeaker.__init__',
|
||||
mock.MagicMock(return_value=None))
|
||||
@mock.patch('ryu.services.protocols.bgp.bgpspeaker.call')
|
||||
def test_evpn_prefix_add_eth_auto_discovery(self, mock_call):
|
||||
# Prepare test data
|
||||
route_type = bgpspeaker.EVPN_ETH_AUTO_DISCOVERY
|
||||
route_dist = '65000:100'
|
||||
esi = {
|
||||
'type': ESI_TYPE_LACP,
|
||||
'mac_addr': 'aa:bb:cc:dd:ee:ff',
|
||||
'port_key': 100,
|
||||
}
|
||||
ethernet_tag_id = EVPN_MAX_ET
|
||||
redundancy_mode = REDUNDANCY_MODE_ALL_ACTIVE
|
||||
next_hop = '0.0.0.0'
|
||||
expected_kwargs = {
|
||||
'route_type': route_type,
|
||||
'route_dist': route_dist,
|
||||
'esi': esi,
|
||||
'ethernet_tag_id': ethernet_tag_id,
|
||||
'redundancy_mode': redundancy_mode,
|
||||
'next_hop': next_hop,
|
||||
}
|
||||
|
||||
# Test
|
||||
speaker = bgpspeaker.BGPSpeaker(65000, '10.0.0.1')
|
||||
speaker.evpn_prefix_add(
|
||||
route_type=route_type,
|
||||
route_dist=route_dist,
|
||||
esi=esi,
|
||||
ethernet_tag_id=ethernet_tag_id,
|
||||
redundancy_mode=redundancy_mode,
|
||||
)
|
||||
|
||||
# Check
|
||||
mock_call.assert_called_with(
|
||||
'evpn_prefix.add_local', **expected_kwargs)
|
||||
|
||||
@mock.patch(
|
||||
'ryu.services.protocols.bgp.bgpspeaker.BGPSpeaker.__init__',
|
||||
mock.MagicMock(return_value=None))
|
||||
@mock.patch('ryu.services.protocols.bgp.bgpspeaker.call')
|
||||
def test_evpn_prefix_add_eth_auto_discovery_vni(self, mock_call):
|
||||
# Prepare test data
|
||||
route_type = bgpspeaker.EVPN_ETH_AUTO_DISCOVERY
|
||||
route_dist = '65000:100'
|
||||
esi = {
|
||||
'type': ESI_TYPE_L2_BRIDGE,
|
||||
'mac_addr': 'aa:bb:cc:dd:ee:ff',
|
||||
'priority': 100,
|
||||
}
|
||||
ethernet_tag_id = EVPN_MAX_ET
|
||||
redundancy_mode = REDUNDANCY_MODE_SINGLE_ACTIVE
|
||||
vni = 500
|
||||
next_hop = '0.0.0.0'
|
||||
expected_kwargs = {
|
||||
'route_type': route_type,
|
||||
'route_dist': route_dist,
|
||||
'esi': esi,
|
||||
'ethernet_tag_id': ethernet_tag_id,
|
||||
'redundancy_mode': redundancy_mode,
|
||||
'vni': vni,
|
||||
'next_hop': next_hop,
|
||||
}
|
||||
|
||||
# Test
|
||||
speaker = bgpspeaker.BGPSpeaker(65000, '10.0.0.1')
|
||||
speaker.evpn_prefix_add(
|
||||
route_type=route_type,
|
||||
route_dist=route_dist,
|
||||
esi=esi,
|
||||
ethernet_tag_id=ethernet_tag_id,
|
||||
redundancy_mode=redundancy_mode,
|
||||
vni=vni
|
||||
)
|
||||
|
||||
# Check
|
||||
mock_call.assert_called_with(
|
||||
'evpn_prefix.add_local', **expected_kwargs)
|
||||
|
||||
@mock.patch('ryu.services.protocols.bgp.bgpspeaker.BGPSpeaker.__init__',
|
||||
mock.MagicMock(return_value=None))
|
||||
@mock.patch('ryu.services.protocols.bgp.bgpspeaker.call')
|
||||
@ -191,6 +277,42 @@ class Test_BGPSpeaker(unittest.TestCase):
|
||||
mock_call.assert_called_with(
|
||||
'evpn_prefix.add_local', **expected_kwargs)
|
||||
|
||||
@mock.patch(
|
||||
'ryu.services.protocols.bgp.bgpspeaker.BGPSpeaker.__init__',
|
||||
mock.MagicMock(return_value=None))
|
||||
@mock.patch('ryu.services.protocols.bgp.bgpspeaker.call')
|
||||
def test_evpn_prefix_add_eth_segment(self, mock_call):
|
||||
# Prepare test data
|
||||
route_type = bgpspeaker.EVPN_ETH_SEGMENT
|
||||
route_dist = '65000:100'
|
||||
esi = {
|
||||
'type': ESI_TYPE_MAC_BASED,
|
||||
'mac_addr': 'aa:bb:cc:dd:ee:ff',
|
||||
'local_disc': 100,
|
||||
}
|
||||
ip_addr = '192.168.0.1'
|
||||
next_hop = '0.0.0.0'
|
||||
expected_kwargs = {
|
||||
'route_type': route_type,
|
||||
'route_dist': route_dist,
|
||||
'esi': esi,
|
||||
'ip_addr': ip_addr,
|
||||
'next_hop': next_hop,
|
||||
}
|
||||
|
||||
# Test
|
||||
speaker = bgpspeaker.BGPSpeaker(65000, '10.0.0.1')
|
||||
speaker.evpn_prefix_add(
|
||||
route_type=route_type,
|
||||
route_dist=route_dist,
|
||||
esi=esi,
|
||||
ip_addr=ip_addr,
|
||||
)
|
||||
|
||||
# Check
|
||||
mock_call.assert_called_with(
|
||||
'evpn_prefix.add_local', **expected_kwargs)
|
||||
|
||||
@mock.patch(
|
||||
'ryu.services.protocols.bgp.bgpspeaker.BGPSpeaker.__init__',
|
||||
mock.MagicMock(return_value=None))
|
||||
@ -303,6 +425,40 @@ class Test_BGPSpeaker(unittest.TestCase):
|
||||
mock_call.assert_called_with(
|
||||
'evpn_prefix.add_local', 'Invalid arguments detected')
|
||||
|
||||
@mock.patch(
|
||||
'ryu.services.protocols.bgp.bgpspeaker.BGPSpeaker.__init__',
|
||||
mock.MagicMock(return_value=None))
|
||||
@mock.patch('ryu.services.protocols.bgp.bgpspeaker.call')
|
||||
def test_evpn_prefix_del_auto_discovery(self, mock_call):
|
||||
# Prepare test data
|
||||
route_type = bgpspeaker.EVPN_ETH_AUTO_DISCOVERY
|
||||
route_dist = '65000:100'
|
||||
esi = {
|
||||
'type': ESI_TYPE_LACP,
|
||||
'mac_addr': 'aa:bb:cc:dd:ee:ff',
|
||||
'port_key': 100,
|
||||
}
|
||||
ethernet_tag_id = EVPN_MAX_ET
|
||||
expected_kwargs = {
|
||||
'route_type': route_type,
|
||||
'route_dist': route_dist,
|
||||
'esi': esi,
|
||||
'ethernet_tag_id': ethernet_tag_id,
|
||||
}
|
||||
|
||||
# Test
|
||||
speaker = bgpspeaker.BGPSpeaker(65000, '10.0.0.1')
|
||||
speaker.evpn_prefix_del(
|
||||
route_type=route_type,
|
||||
route_dist=route_dist,
|
||||
esi=esi,
|
||||
ethernet_tag_id=ethernet_tag_id,
|
||||
)
|
||||
|
||||
# Check
|
||||
mock_call.assert_called_with(
|
||||
'evpn_prefix.delete_local', **expected_kwargs)
|
||||
|
||||
@mock.patch('ryu.services.protocols.bgp.bgpspeaker.BGPSpeaker.__init__',
|
||||
mock.MagicMock(return_value=None))
|
||||
@mock.patch('ryu.services.protocols.bgp.bgpspeaker.call')
|
||||
@ -401,6 +557,40 @@ class Test_BGPSpeaker(unittest.TestCase):
|
||||
mock_call.assert_called_with(
|
||||
'evpn_prefix.delete_local', 'Invalid arguments detected')
|
||||
|
||||
@mock.patch(
|
||||
'ryu.services.protocols.bgp.bgpspeaker.BGPSpeaker.__init__',
|
||||
mock.MagicMock(return_value=None))
|
||||
@mock.patch('ryu.services.protocols.bgp.bgpspeaker.call')
|
||||
def test_evpn_prefix_del_eth_segment(self, mock_call):
|
||||
# Prepare test data
|
||||
route_type = bgpspeaker.EVPN_ETH_SEGMENT
|
||||
route_dist = '65000:100'
|
||||
esi = {
|
||||
'esi_type': ESI_TYPE_MAC_BASED,
|
||||
'mac_addr': 'aa:bb:cc:dd:ee:ff',
|
||||
'local_disc': 100,
|
||||
}
|
||||
ip_addr = '192.168.0.1'
|
||||
expected_kwargs = {
|
||||
'route_type': route_type,
|
||||
'route_dist': route_dist,
|
||||
'esi': esi,
|
||||
'ip_addr': ip_addr,
|
||||
}
|
||||
|
||||
# Test
|
||||
speaker = bgpspeaker.BGPSpeaker(65000, '10.0.0.1')
|
||||
speaker.evpn_prefix_del(
|
||||
route_type=route_type,
|
||||
route_dist=route_dist,
|
||||
esi=esi,
|
||||
ip_addr=ip_addr,
|
||||
)
|
||||
|
||||
# Check
|
||||
mock_call.assert_called_with(
|
||||
'evpn_prefix.delete_local', **expected_kwargs)
|
||||
|
||||
@mock.patch(
|
||||
'ryu.services.protocols.bgp.bgpspeaker.BGPSpeaker.__init__',
|
||||
mock.MagicMock(return_value=None))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user