diff options
author | Shinpei Muraoka <shinpei.muraoka@gmail.com> | 2016-10-27 09:55:22 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2016-10-29 06:32:18 +0900 |
commit | 8d9ce87653566625f31f8dbca460733351bbd4e2 (patch) | |
tree | dcc8520fab43eb9a54806e034dffc94bb57441d2 | |
parent | 6e22fb4a05b5aab3a92ec8db9a25613e99c98a7a (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.py | 1 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/api/prefix.py | 23 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/bgpspeaker.py | 21 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/core_managers/table_manager.py | 10 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/info_base/vrf.py | 18 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/peer.py | 8 | ||||
-rw-r--r-- | ryu/tests/unit/services/protocols/bgp/test_bgpspeaker.py | 97 |
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') |