summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorShinpei Muraoka <shinpei.muraoka@gmail.com>2016-11-22 17:26:58 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2016-12-10 14:57:01 +0900
commit17acdbb6100d8a2335c06b56687645bfd78ec66f (patch)
tree31805e51a9e670f7cc64125cdaf8371a306cda34
parentc4a84cb2464f4249d83ef000c43dea56bd1b8c0c (diff)
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>
-rw-r--r--ryu/lib/packet/bgp.py6
-rw-r--r--ryu/services/protocols/bgp/api/base.py1
-rw-r--r--ryu/services/protocols/bgp/api/prefix.py60
-rw-r--r--ryu/services/protocols/bgp/bgp_sample_conf.py28
-rw-r--r--ryu/services/protocols/bgp/bgpspeaker.py70
-rw-r--r--ryu/services/protocols/bgp/core_managers/table_manager.py29
-rw-r--r--ryu/services/protocols/bgp/info_base/vrf.py79
-rw-r--r--ryu/services/protocols/bgp/utils/validation.py5
-rw-r--r--ryu/tests/unit/services/protocols/bgp/core_managers/test_table_manager.py31
-rw-r--r--ryu/tests/unit/services/protocols/bgp/test_bgpspeaker.py190
10 files changed, 451 insertions, 48 deletions
diff --git a/ryu/lib/packet/bgp.py b/ryu/lib/packet/bgp.py
index 4a52fd86..38787965 100644
--- a/ryu/lib/packet/bgp.py
+++ b/ryu/lib/packet/bgp.py
@@ -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)
diff --git a/ryu/services/protocols/bgp/api/base.py b/ryu/services/protocols/bgp/api/base.py
index 3dd252f9..125fee93 100644
--- a/ryu/services/protocols/bgp/api/base.py
+++ b/ryu/services/protocols/bgp/api/base.py
@@ -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'
diff --git a/ryu/services/protocols/bgp/api/prefix.py b/ryu/services/protocols/bgp/api/prefix.py
index 3cc61aff..e175f176 100644
--- a/ryu/services/protocols/bgp/api/prefix.py
+++ b/ryu/services/protocols/bgp/api/prefix.py
@@ -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
diff --git a/ryu/services/protocols/bgp/bgp_sample_conf.py b/ryu/services/protocols/bgp/bgp_sample_conf.py
index de8caf02..9b9564c3 100644
--- a/ryu/services/protocols/bgp/bgp_sample_conf.py
+++ b/ryu/services/protocols/bgp/bgp_sample_conf.py
@@ -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.
@@ -92,6 +97,17 @@ BGP = {
},
# 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',
'esi': 0,
@@ -110,6 +126,16 @@ BGP = {
'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',
'esi': 0,
diff --git a/ryu/services/protocols/bgp/bgpspeaker.py b/ryu/services/protocols/bgp/bgpspeaker.py
index c56f8dd7..6e90ced8 100644
--- a/ryu/services/protocols/bgp/bgpspeaker.py
+++ b/ryu/services/protocols/bgp/bgpspeaker.py
@@ -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,
diff --git a/ryu/services/protocols/bgp/core_managers/table_manager.py b/ryu/services/protocols/bgp/core_managers/table_manager.py
index be3e8bc6..0e084b82 100644
--- a/ryu/services/protocols/bgp/core_managers/table_manager.py
+++ b/ryu/services/protocols/bgp/core_managers/table_manager.py
@@ -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):
diff --git a/ryu/services/protocols/bgp/info_base/vrf.py b/ryu/services/protocols/bgp/info_base/vrf.py
index f6b50bcc..c4e41efa 100644
--- a/ryu/services/protocols/bgp/info_base/vrf.py
+++ b/ryu/services/protocols/bgp/info_base/vrf.py
@@ -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)
diff --git a/ryu/services/protocols/bgp/utils/validation.py b/ryu/services/protocols/bgp/utils/validation.py
index ff6ed506..35dc4c72 100644
--- a/ryu/services/protocols/bgp/utils/validation.py
+++ b/ryu/services/protocols/bgp/utils/validation.py
@@ -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):
diff --git a/ryu/tests/unit/services/protocols/bgp/core_managers/test_table_manager.py b/ryu/tests/unit/services/protocols/bgp/core_managers/test_table_manager.py
index a53e4c43..48c99e9f 100644
--- a/ryu/tests/unit/services/protocols/bgp/core_managers/test_table_manager.py
+++ b/ryu/tests/unit/services/protocols/bgp/core_managers/test_table_manager.py
@@ -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'
diff --git a/ryu/tests/unit/services/protocols/bgp/test_bgpspeaker.py b/ryu/tests/unit/services/protocols/bgp/test_bgpspeaker.py
index d5e4908e..da0f4827 100644
--- a/ryu/tests/unit/services/protocols/bgp/test_bgpspeaker.py
+++ b/ryu/tests/unit/services/protocols/bgp/test_bgpspeaker.py
@@ -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__)
@@ -36,6 +42,86 @@ class Test_BGPSpeaker(unittest.TestCase):
@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')
def test_evpn_prefix_add_mac_ip_adv(self, mock_call):
# Prepare test data
route_type = bgpspeaker.EVPN_MAC_IP_ADV_ROUTE
@@ -195,6 +281,42 @@ class Test_BGPSpeaker(unittest.TestCase):
'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))
+ @mock.patch('ryu.services.protocols.bgp.bgpspeaker.call')
def test_evpn_prefix_add_ip_prefix_route(self, mock_call):
# Prepare test data
route_type = bgpspeaker.EVPN_IP_PREFIX_ROUTE
@@ -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')
@@ -405,6 +561,40 @@ class Test_BGPSpeaker(unittest.TestCase):
'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))
+ @mock.patch('ryu.services.protocols.bgp.bgpspeaker.call')
def test_evpn_prefix_del_ip_prefix_route(self, mock_call):
# Prepare test data
route_type = bgpspeaker.EVPN_IP_PREFIX_ROUTE