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:
Shinpei Muraoka 2016-11-22 17:26:58 +09:00 committed by FUJITA Tomonori
parent c4a84cb246
commit 17acdbb610
10 changed files with 451 additions and 48 deletions

View File

@ -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)

View File

@ -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'

View File

@ -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

View File

@ -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',

View File

@ -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,

View File

@ -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):

View File

@ -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)

View File

@ -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):

View File

@ -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'

View File

@ -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))