summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorShinpei Muraoka <shinpei.muraoka@gmail.com>2016-10-27 09:55:22 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2016-10-29 06:32:18 +0900
commit8d9ce87653566625f31f8dbca460733351bbd4e2 (patch)
treedcc8520fab43eb9a54806e034dffc94bb57441d2
parent6e22fb4a05b5aab3a92ec8db9a25613e99c98a7a (diff)
BGPSpeaker: Support to advertise PMSI Tunnel Attribute
This patch adds support to advertise the BGP PMSI Tunnel Attribute for the Path attributes. Signed-off-by: Shinpei Muraoka <shinpei.muraoka@gmail.com> Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r--ryu/services/protocols/bgp/api/base.py1
-rw-r--r--ryu/services/protocols/bgp/api/prefix.py23
-rw-r--r--ryu/services/protocols/bgp/bgpspeaker.py21
-rw-r--r--ryu/services/protocols/bgp/core_managers/table_manager.py10
-rw-r--r--ryu/services/protocols/bgp/info_base/vrf.py18
-rw-r--r--ryu/services/protocols/bgp/peer.py8
-rw-r--r--ryu/tests/unit/services/protocols/bgp/test_bgpspeaker.py97
7 files changed, 174 insertions, 4 deletions
diff --git a/ryu/services/protocols/bgp/api/base.py b/ryu/services/protocols/bgp/api/base.py
index b96a4f36..11fd747e 100644
--- a/ryu/services/protocols/bgp/api/base.py
+++ b/ryu/services/protocols/bgp/api/base.py
@@ -50,6 +50,7 @@ IP_ADDR = 'ip_addr'
MPLS_LABELS = 'mpls_labels'
TUNNEL_TYPE = 'tunnel_type'
EVPN_VNI = 'vni'
+PMSI_TUNNEL_TYPE = 'pmsi_tunnel_type'
# API call registry
_CALL_REGISTRY = {}
diff --git a/ryu/services/protocols/bgp/api/prefix.py b/ryu/services/protocols/bgp/api/prefix.py
index 8963f513..43ddc676 100644
--- a/ryu/services/protocols/bgp/api/prefix.py
+++ b/ryu/services/protocols/bgp/api/prefix.py
@@ -20,6 +20,7 @@ import logging
from ryu.lib.packet.bgp import EvpnMacIPAdvertisementNLRI
from ryu.lib.packet.bgp import EvpnInclusiveMulticastEthernetTagNLRI
+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
@@ -33,6 +34,7 @@ from ryu.services.protocols.bgp.api.base import ROUTE_DISTINGUISHER
from ryu.services.protocols.bgp.api.base import VPN_LABEL
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.base import add_bgp_error_metadata
from ryu.services.protocols.bgp.base import PREFIX_ERROR_CODE
from ryu.services.protocols.bgp.base import validate
@@ -69,6 +71,17 @@ SUPPORTED_TUNNEL_TYPES = [
TUNNEL_TYPE_MPLS_IN_GRE,
TUNNEL_TYPE_VXLAN_GRE,
]
+# Constants for PMSI Tunnel Attribute
+PMSI_TYPE_NO_TUNNEL_INFO = (
+ BGPPathAttributePmsiTunnel.TYPE_NO_TUNNEL_INFORMATION_PRESENT
+)
+PMSI_TYPE_INGRESS_REP = (
+ BGPPathAttributePmsiTunnel.TYPE_INGRESS_REPLICATION
+)
+SUPPORTED_PMSI_TUNNEL_TYPES = [
+ PMSI_TYPE_NO_TUNNEL_INFO,
+ PMSI_TYPE_INGRESS_REP,
+]
@add_bgp_error_metadata(code=PREFIX_ERROR_CODE,
@@ -152,6 +165,13 @@ def is_valid_tunnel_type(tunnel_type):
conf_value=tunnel_type)
+@validate(name=PMSI_TUNNEL_TYPE)
+def is_valid_pmsi_tunnel_type(pmsi_tunnel_type):
+ if pmsi_tunnel_type not in SUPPORTED_PMSI_TUNNEL_TYPES:
+ raise ConfigValueError(conf_name=PMSI_TUNNEL_TYPE,
+ conf_value=pmsi_tunnel_type)
+
+
@RegisterWithArgChecks(name='prefix.add_local',
req_args=[ROUTE_DISTINGUISHER, PREFIX, NEXT_HOP],
opt_args=[VRF_RF])
@@ -201,7 +221,8 @@ def delete_local(route_dist, prefix, route_family=VRF_RF_IPV4):
req_args=[EVPN_ROUTE_TYPE, ROUTE_DISTINGUISHER,
NEXT_HOP],
opt_args=[EVPN_ESI, EVPN_ETHERNET_TAG_ID, MAC_ADDR,
- IP_ADDR, EVPN_VNI, TUNNEL_TYPE])
+ 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*.
"""
diff --git a/ryu/services/protocols/bgp/bgpspeaker.py b/ryu/services/protocols/bgp/bgpspeaker.py
index 54ce24cc..4934e411 100644
--- a/ryu/services/protocols/bgp/bgpspeaker.py
+++ b/ryu/services/protocols/bgp/bgpspeaker.py
@@ -33,10 +33,14 @@ from ryu.services.protocols.bgp.api.base import ROUTE_DISTINGUISHER
from ryu.services.protocols.bgp.api.base import ROUTE_FAMILY
from ryu.services.protocols.bgp.api.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_MAC_IP_ADV_ROUTE
from ryu.services.protocols.bgp.api.prefix import EVPN_MULTICAST_ETAG_ROUTE
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 (
+ PMSI_TYPE_NO_TUNNEL_INFO,
+ PMSI_TYPE_INGRESS_REP)
from ryu.services.protocols.bgp.operator import ssh
from ryu.services.protocols.bgp.rtconf.common import LOCAL_AS
from ryu.services.protocols.bgp.rtconf.common import ROUTER_ID
@@ -533,7 +537,8 @@ class BGPSpeaker(object):
def evpn_prefix_add(self, route_type, route_dist, esi=0,
ethernet_tag_id=None, mac_addr=None, ip_addr=None,
- vni=None, next_hop=None, tunnel_type=None):
+ vni=None, next_hop=None, tunnel_type=None,
+ pmsi_tunnel_type=None):
""" This method adds a new EVPN route to be advertised.
``route_type`` specifies one of the EVPN route type name. The
@@ -560,6 +565,11 @@ class BGPSpeaker(object):
``tunnel_type`` specifies the data plane encapsulation type
to advertise. By the default, this encapsulation attribute is
not advertised.
+
+ ```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
+ EVPN_MULTICAST_ETAG_ROUTE.
"""
func_name = 'evpn_prefix.add_local'
@@ -592,6 +602,15 @@ class BGPSpeaker(object):
EVPN_ETHERNET_TAG_ID: ethernet_tag_id,
IP_ADDR: ip_addr,
})
+
+ # Set PMSI Tunnel Attribute arguments
+ if pmsi_tunnel_type in [
+ PMSI_TYPE_NO_TUNNEL_INFO,
+ PMSI_TYPE_INGRESS_REP]:
+ kwargs[PMSI_TUNNEL_TYPE] = pmsi_tunnel_type
+ elif pmsi_tunnel_type is not None:
+ raise ValueError('Unsupported PMSI tunnel type: %s' %
+ pmsi_tunnel_type)
else:
raise ValueError('Unsupported EVPN route type: %s' % route_type)
diff --git a/ryu/services/protocols/bgp/core_managers/table_manager.py b/ryu/services/protocols/bgp/core_managers/table_manager.py
index aaa67cb5..be3e8bc6 100644
--- a/ryu/services/protocols/bgp/core_managers/table_manager.py
+++ b/ryu/services/protocols/bgp/core_managers/table_manager.py
@@ -483,7 +483,7 @@ 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, **kwargs):
+ is_withdraw=False, pmsi_tunnel_type=None, **kwargs):
"""Update a BGP route in the VRF table identified by `route_dist`
with the given `next_hop`.
@@ -496,6 +496,11 @@ 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.
+` `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
+ EVPN_MULTICAST_ETAG_ROUTE.
+
Returns assigned VPN label.
"""
from ryu.services.protocols.bgp.core import BgpCoreError
@@ -554,7 +559,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, tunnel_type=tunnel_type,
+ pmsi_tunnel_type=pmsi_tunnel_type)
def update_global_table(self, prefix, next_hop=None, is_withdraw=False):
"""Update a BGP route in the Global table for the given `prefix`
diff --git a/ryu/services/protocols/bgp/info_base/vrf.py b/ryu/services/protocols/bgp/info_base/vrf.py
index d3bbe75e..96812976 100644
--- a/ryu/services/protocols/bgp/info_base/vrf.py
+++ b/ryu/services/protocols/bgp/info_base/vrf.py
@@ -24,6 +24,7 @@ import six
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_TYPE_EXTENDED_COMMUNITIES
+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
@@ -31,6 +32,8 @@ 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 BGPPathAttributePmsiTunnel
+from ryu.lib.packet.bgp import PmsiTunnelIdIngressReplication
from ryu.lib.packet.bgp import RF_L2_EVPN
from ryu.lib.packet.bgp import EvpnMacIPAdvertisementNLRI
@@ -271,6 +274,21 @@ class VrfTable(Table):
# 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:
+ from ryu.services.protocols.bgp.api.prefix import (
+ PMSI_TYPE_INGRESS_REP)
+ if pmsi_tunnel_type == PMSI_TYPE_INGRESS_REP:
+ tunnel_id = PmsiTunnelIdIngressReplication(
+ tunnel_endpoint_ip=self._core_service.router_id)
+ else: # pmsi_tunnel_type == PMSI_TYPE_NO_TUNNEL_INFO
+ tunnel_id = None
+ pattrs[BGP_ATTR_TYEP_PMSI_TUNNEL_ATTRIBUTE] = \
+ BGPPathAttributePmsiTunnel(pmsi_flags=0,
+ 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]
diff --git a/ryu/services/protocols/bgp/peer.py b/ryu/services/protocols/bgp/peer.py
index 9accd089..8bf96d62 100644
--- a/ryu/services/protocols/bgp/peer.py
+++ b/ryu/services/protocols/bgp/peer.py
@@ -91,6 +91,7 @@ from ryu.lib.packet.bgp import BGP_ATTR_TYPE_MP_UNREACH_NLRI
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_MULTI_EXIT_DISC
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_COMMUNITIES
from ryu.lib.packet.bgp import BGP_ATTR_TYPE_EXTENDED_COMMUNITIES
+from ryu.lib.packet.bgp import BGP_ATTR_TYEP_PMSI_TUNNEL_ATTRIBUTE
from ryu.lib.packet.bgp import BGPTwoOctetAsSpecificExtendedCommunity
from ryu.lib.packet.bgp import BGPIPv4AddressSpecificExtendedCommunity
@@ -988,6 +989,7 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
extcomm_attr = None
community_attr = None
localpref_attr = None
+ pmsi_tunnel_attr = None
unknown_opttrans_attrs = None
nlri_list = [path.nlri]
@@ -1164,6 +1166,10 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
communities=communities
)
+ pmsi_tunnel_attr = pathattr_map.get(
+ BGP_ATTR_TYEP_PMSI_TUNNEL_ATTRIBUTE
+ )
+
# UNKNOWN Attributes.
# Get optional transitive path attributes
unknown_opttrans_attrs = bgp_utils.get_unknown_opttrans_attr(path)
@@ -1192,6 +1198,8 @@ class Peer(Source, Sink, NeighborConfListener, Activity):
new_pathattr.append(community_attr)
if extcomm_attr:
new_pathattr.append(extcomm_attr)
+ if pmsi_tunnel_attr:
+ new_pathattr.append(pmsi_tunnel_attr)
if unknown_opttrans_attrs:
new_pathattr.extend(unknown_opttrans_attrs.values())
diff --git a/ryu/tests/unit/services/protocols/bgp/test_bgpspeaker.py b/ryu/tests/unit/services/protocols/bgp/test_bgpspeaker.py
index 67ef3e97..535f8a0e 100644
--- a/ryu/tests/unit/services/protocols/bgp/test_bgpspeaker.py
+++ b/ryu/tests/unit/services/protocols/bgp/test_bgpspeaker.py
@@ -318,3 +318,100 @@ class Test_BGPSpeaker(unittest.TestCase):
# Check
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_add_pmsi_no_tunnel_info(self, mock_call):
+ # Prepare test data
+ route_type = bgpspeaker.EVPN_MULTICAST_ETAG_ROUTE
+ route_dist = '65000:100'
+ ethernet_tag_id = 200
+ next_hop = '0.0.0.0'
+ ip_addr = '192.168.0.1'
+ pmsi_tunnel_type = bgpspeaker.PMSI_TYPE_NO_TUNNEL_INFO
+ expected_kwargs = {
+ 'route_type': route_type,
+ 'route_dist': route_dist,
+ 'ethernet_tag_id': ethernet_tag_id,
+ 'next_hop': next_hop,
+ 'ip_addr': ip_addr,
+ 'pmsi_tunnel_type': pmsi_tunnel_type,
+ }
+
+ # Test
+ speaker = bgpspeaker.BGPSpeaker(65000, '10.0.0.1')
+ speaker.evpn_prefix_add(
+ route_type=route_type,
+ route_dist=route_dist,
+ ethernet_tag_id=ethernet_tag_id,
+ ip_addr=ip_addr,
+ pmsi_tunnel_type=pmsi_tunnel_type,
+ )
+
+ # 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_pmsi_ingress_rep(self, mock_call):
+ # Prepare test data
+ route_type = bgpspeaker.EVPN_MULTICAST_ETAG_ROUTE
+ route_dist = '65000:100'
+ ethernet_tag_id = 200
+ next_hop = '0.0.0.0'
+ ip_addr = '192.168.0.1'
+ pmsi_tunnel_type = bgpspeaker.PMSI_TYPE_INGRESS_REP
+ expected_kwargs = {
+ 'route_type': route_type,
+ 'route_dist': route_dist,
+ 'ethernet_tag_id': ethernet_tag_id,
+ 'next_hop': next_hop,
+ 'ip_addr': ip_addr,
+ 'pmsi_tunnel_type': pmsi_tunnel_type,
+ }
+
+ # Test
+ speaker = bgpspeaker.BGPSpeaker(65000, '10.0.0.1')
+ speaker.evpn_prefix_add(
+ route_type=route_type,
+ route_dist=route_dist,
+ ethernet_tag_id=ethernet_tag_id,
+ ip_addr=ip_addr,
+ pmsi_tunnel_type=pmsi_tunnel_type,
+ )
+
+ # Check
+ mock_call.assert_called_with(
+ 'evpn_prefix.add_local', **expected_kwargs)
+
+ @raises(ValueError)
+ @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_invalid_pmsi_tunnel_type(self, mock_call):
+ # Prepare test data
+ route_type = bgpspeaker.EVPN_MULTICAST_ETAG_ROUTE
+ route_dist = '65000:100'
+ ethernet_tag_id = 200
+ next_hop = '0.0.0.0'
+ ip_addr = '192.168.0.1'
+ pmsi_tunnel_type = 1
+
+ # Test
+ speaker = bgpspeaker.BGPSpeaker(65000, '10.0.0.1')
+ speaker.evpn_prefix_add(
+ route_type=route_type,
+ route_dist=route_dist,
+ ethernet_tag_id=ethernet_tag_id,
+ ip_addr=ip_addr,
+ pmsi_tunnel_type=pmsi_tunnel_type,
+ )
+
+ # Check
+ mock_call.assert_called_with(
+ 'evpn_prefix.add_local', 'Invalid arguments detected')