mirror of
https://github.com/faucetsdn/ryu.git
synced 2026-01-25 02:21:45 +01:00
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>
This commit is contained in:
parent
6e22fb4a05
commit
8d9ce87653
@ -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 = {}
|
||||
|
||||
@ -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*.
|
||||
"""
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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`
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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())
|
||||
|
||||
|
||||
@ -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')
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user