Skip to content

Commit

Permalink
BGPSpeaker: Support to advertise PMSI Tunnel Attribute
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
shinpeimuraoka authored and fujita committed Oct 28, 2016
1 parent 6e22fb4 commit 8d9ce87
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 4 deletions.
1 change: 1 addition & 0 deletions ryu/services/protocols/bgp/api/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
MPLS_LABELS = 'mpls_labels'
TUNNEL_TYPE = 'tunnel_type'
EVPN_VNI = 'vni'
PMSI_TUNNEL_TYPE = 'pmsi_tunnel_type'

# API call registry
_CALL_REGISTRY = {}
Expand Down
23 changes: 22 additions & 1 deletion ryu/services/protocols/bgp/api/prefix.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

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
Expand All @@ -33,6 +34,7 @@
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
Expand Down Expand Up @@ -69,6 +71,17 @@
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,
Expand Down Expand Up @@ -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])
Expand Down Expand Up @@ -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*.
"""
Expand Down
21 changes: 20 additions & 1 deletion ryu/services/protocols/bgp/bgpspeaker.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,14 @@
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
Expand Down Expand Up @@ -533,7 +537,8 @@ def prefix_del(self, prefix, route_dist=None):

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
Expand All @@ -560,6 +565,11 @@ def evpn_prefix_add(self, route_type, route_dist, esi=0,
``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'

Expand Down Expand Up @@ -592,6 +602,15 @@ def evpn_prefix_add(self, route_type, route_dist, esi=0,
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)

Expand Down
10 changes: 8 additions & 2 deletions ryu/services/protocols/bgp/core_managers/table_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ def import_single_vpn_path_to_all_vrfs(self, vpn_path, path_rts=None):

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`.
Expand All @@ -496,6 +496,11 @@ def update_vrf_table(self, route_dist, prefix=None, next_hop=None,
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
Expand Down Expand Up @@ -554,7 +559,8 @@ def update_vrf_table(self, route_dist, prefix=None, next_hop=None,
# 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`
Expand Down
18 changes: 18 additions & 0 deletions ryu/services/protocols/bgp/info_base/vrf.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,16 @@
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
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

Expand Down Expand Up @@ -271,6 +274,21 @@ def insert_vrf_path(self, nlri, next_hop=None,
# 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]
Expand Down
8 changes: 8 additions & 0 deletions ryu/services/protocols/bgp/peer.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
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
Expand Down Expand Up @@ -988,6 +989,7 @@ def _construct_update(self, outgoing_route):
extcomm_attr = None
community_attr = None
localpref_attr = None
pmsi_tunnel_attr = None
unknown_opttrans_attrs = None
nlri_list = [path.nlri]

Expand Down Expand Up @@ -1164,6 +1166,10 @@ def _construct_update(self, outgoing_route):
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)
Expand Down Expand Up @@ -1192,6 +1198,8 @@ def _construct_update(self, outgoing_route):
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())

Expand Down
97 changes: 97 additions & 0 deletions ryu/tests/unit/services/protocols/bgp/test_bgpspeaker.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,100 @@ def test_evpn_prefix_del_invalid_route_type(self, mock_call):
# 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')

0 comments on commit 8d9ce87

Please sign in to comment.