summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorShinpei Muraoka <shinpei.muraoka@gmail.com>2016-11-09 10:44:05 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2016-11-14 13:21:08 +0900
commit444c29fd735bd5c08b0726a4a58f5880af122061 (patch)
tree52666a4e7f35cceb68403426fc6bfe16ffe8aa39
parent972c31f0e6f32ae40417a79001353328b965728f (diff)
BGPSpeaker: Support to advertise EVPN IP Prefix route
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.py2
-rw-r--r--ryu/services/protocols/bgp/api/prefix.py27
-rw-r--r--ryu/services/protocols/bgp/bgp_sample_conf.py17
-rw-r--r--ryu/services/protocols/bgp/bgpspeaker.py35
-rw-r--r--ryu/services/protocols/bgp/info_base/vrf.py3
-rw-r--r--ryu/tests/unit/services/protocols/bgp/test_bgpspeaker.py112
6 files changed, 189 insertions, 7 deletions
diff --git a/ryu/services/protocols/bgp/api/base.py b/ryu/services/protocols/bgp/api/base.py
index 11fd747e..3dd252f9 100644
--- a/ryu/services/protocols/bgp/api/base.py
+++ b/ryu/services/protocols/bgp/api/base.py
@@ -47,6 +47,8 @@ EVPN_ESI = 'esi'
EVPN_ETHERNET_TAG_ID = 'ethernet_tag_id'
MAC_ADDR = 'mac_addr'
IP_ADDR = 'ip_addr'
+IP_PREFIX = 'ip_prefix'
+GW_IP_ADDR = 'gw_ip_addr'
MPLS_LABELS = 'mpls_labels'
TUNNEL_TYPE = 'tunnel_type'
EVPN_VNI = 'vni'
diff --git a/ryu/services/protocols/bgp/api/prefix.py b/ryu/services/protocols/bgp/api/prefix.py
index 43ddc676..3cc61aff 100644
--- a/ryu/services/protocols/bgp/api/prefix.py
+++ b/ryu/services/protocols/bgp/api/prefix.py
@@ -20,12 +20,15 @@ import logging
from ryu.lib.packet.bgp import EvpnMacIPAdvertisementNLRI
from ryu.lib.packet.bgp import EvpnInclusiveMulticastEthernetTagNLRI
+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 MAC_ADDR
from ryu.services.protocols.bgp.api.base import IP_ADDR
+from ryu.services.protocols.bgp.api.base import IP_PREFIX
+from ryu.services.protocols.bgp.api.base import GW_IP_ADDR
from ryu.services.protocols.bgp.api.base import MPLS_LABELS
from ryu.services.protocols.bgp.api.base import NEXT_HOP
from ryu.services.protocols.bgp.api.base import PREFIX
@@ -53,9 +56,11 @@ LOG = logging.getLogger('bgpspeaker.api.prefix')
# Constants used in API calls for EVPN
EVPN_MAC_IP_ADV_ROUTE = EvpnMacIPAdvertisementNLRI.ROUTE_TYPE_NAME
EVPN_MULTICAST_ETAG_ROUTE = EvpnInclusiveMulticastEthernetTagNLRI.ROUTE_TYPE_NAME
+EVPN_IP_PREFIX_ROUTE = EvpnIpPrefixNLRI.ROUTE_TYPE_NAME
SUPPORTED_EVPN_ROUTE_TYPES = [
EVPN_MAC_IP_ADV_ROUTE,
EVPN_MULTICAST_ETAG_ROUTE,
+ EVPN_IP_PREFIX_ROUTE,
]
# Constants for BGP Tunnel Encapsulation Attribute
@@ -144,6 +149,22 @@ def is_valid_ip_addr(addr):
conf_value=addr)
+@validate(name=IP_PREFIX)
+def is_valid_ip_prefix(prefix):
+ if not (validation.is_valid_ipv4_prefix(prefix)
+ or validation.is_valid_ipv6_prefix(prefix)):
+ raise ConfigValueError(conf_name=IP_PREFIX,
+ conf_value=prefix)
+
+
+@validate(name=GW_IP_ADDR)
+def is_valid_gw_ip_addr(addr):
+ if not (validation.is_valid_ipv4(addr)
+ or validation.is_valid_ipv6(addr)):
+ raise ConfigValueError(conf_name=GW_IP_ADDR,
+ conf_value=addr)
+
+
@validate(name=MPLS_LABELS)
def is_valid_mpls_labels(labels):
if not validation.is_valid_mpls_labels(labels):
@@ -221,8 +242,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,
- PMSI_TUNNEL_TYPE])
+ 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*.
"""
@@ -249,7 +270,7 @@ def add_evpn_local(route_type, route_dist, next_hop, **kwargs):
@RegisterWithArgChecks(name='evpn_prefix.delete_local',
req_args=[EVPN_ROUTE_TYPE, ROUTE_DISTINGUISHER],
opt_args=[EVPN_ESI, EVPN_ETHERNET_TAG_ID, MAC_ADDR,
- IP_ADDR, EVPN_VNI])
+ IP_ADDR, IP_PREFIX, EVPN_VNI])
def delete_evpn_local(route_type, route_dist, **kwargs):
"""Deletes/withdraws EVPN route from VRF identified by *route_dist*.
"""
diff --git a/ryu/services/protocols/bgp/bgp_sample_conf.py b/ryu/services/protocols/bgp/bgp_sample_conf.py
index a15192da..de8caf02 100644
--- a/ryu/services/protocols/bgp/bgp_sample_conf.py
+++ b/ryu/services/protocols/bgp/bgp_sample_conf.py
@@ -5,6 +5,8 @@ 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_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_IP_PREFIX_ROUTE
# =============================================================================
@@ -100,6 +102,21 @@ BGP = {
'ip_addr': '10.30.1.1',
'next_hop': '172.17.0.1',
},
+ {
+ 'route_type': EVPN_MULTICAST_ETAG_ROUTE,
+ 'route_dist': '65001:200',
+ 'esi': 0,
+ 'ethernet_tag_id': 0,
+ 'ip_addr': '10.40.1.1',
+ },
+ {
+ 'route_type': EVPN_IP_PREFIX_ROUTE,
+ 'route_dist': '65001:200',
+ 'esi': 0,
+ 'ethernet_tag_id': 0,
+ 'ip_prefix': '10.50.1.0/24',
+ 'gw_ip_addr': '172.16.0.1',
+ },
],
}
diff --git a/ryu/services/protocols/bgp/bgpspeaker.py b/ryu/services/protocols/bgp/bgpspeaker.py
index 9e30d3e5..4a1ce977 100644
--- a/ryu/services/protocols/bgp/bgpspeaker.py
+++ b/ryu/services/protocols/bgp/bgpspeaker.py
@@ -29,6 +29,8 @@ from ryu.services.protocols.bgp.api.base import EVPN_ETHERNET_TAG_ID
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
+from ryu.services.protocols.bgp.api.base import IP_PREFIX
+from ryu.services.protocols.bgp.api.base import GW_IP_ADDR
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
@@ -36,6 +38,7 @@ 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 EVPN_IP_PREFIX_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 (
@@ -531,13 +534,14 @@ 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,
+ ip_prefix=None, gw_ip_addr=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
- supported route types are EVPN_MAC_IP_ADV_ROUTE and
- EVPN_MULTICAST_ETAG_ROUTE.
+ supported route types are EVPN_MAC_IP_ADV_ROUTE,
+ EVPN_MULTICAST_ETAG_ROUTE and EVPN_IP_PREFIX_ROUTE.
``route_dist`` specifies a route distinguisher value.
@@ -550,6 +554,11 @@ class BGPSpeaker(object):
``ip_addr`` specifies an IPv4 or IPv6 address to advertise.
+ ``ip_prefix`` specifies an IPv4 or IPv6 prefix to advertise.
+
+ ``gw_ip_addr`` specifies an IPv4 or IPv6 address of
+ gateway to advertise.
+
``vni`` specifies an Virtual Network Identifier for VXLAN
or Virtual Subnet Identifier for NVGRE.
If tunnel_type is not 'vxlan' or 'nvgre', this field is ignored.
@@ -605,13 +614,24 @@ class BGPSpeaker(object):
elif pmsi_tunnel_type is not None:
raise ValueError('Unsupported PMSI tunnel type: %s' %
pmsi_tunnel_type)
+ elif route_type == EVPN_IP_PREFIX_ROUTE:
+ kwargs.update({
+ EVPN_ESI: esi,
+ EVPN_ETHERNET_TAG_ID: ethernet_tag_id,
+ IP_PREFIX: ip_prefix,
+ GW_IP_ADDR: gw_ip_addr,
+ })
+ # Set tunnel type specific arguments
+ if tunnel_type in [TUNNEL_TYPE_VXLAN, TUNNEL_TYPE_NVGRE]:
+ kwargs[EVPN_VNI] = vni
else:
raise ValueError('Unsupported EVPN route type: %s' % route_type)
call(func_name, **kwargs)
def evpn_prefix_del(self, route_type, route_dist, esi=0,
- ethernet_tag_id=None, mac_addr=None, ip_addr=None):
+ ethernet_tag_id=None, mac_addr=None, ip_addr=None,
+ ip_prefix=None):
""" This method deletes an advertised EVPN route.
``route_type`` specifies one of the EVPN route type name.
@@ -626,6 +646,8 @@ class BGPSpeaker(object):
``mac_addr`` specifies a MAC address to advertise.
``ip_addr`` specifies an IPv4 or IPv6 address to advertise.
+
+ ``ip_prefix`` specifies an IPv4 or IPv6 prefix to advertise.
"""
func_name = 'evpn_prefix.delete_local'
@@ -646,6 +668,11 @@ class BGPSpeaker(object):
EVPN_ETHERNET_TAG_ID: ethernet_tag_id,
IP_ADDR: ip_addr,
})
+ elif route_type == EVPN_IP_PREFIX_ROUTE:
+ kwargs.update({
+ EVPN_ETHERNET_TAG_ID: ethernet_tag_id,
+ IP_PREFIX: ip_prefix,
+ })
else:
raise ValueError('Unsupported EVPN route type: %s' % route_type)
diff --git a/ryu/services/protocols/bgp/info_base/vrf.py b/ryu/services/protocols/bgp/info_base/vrf.py
index 96812976..f6b50bcc 100644
--- a/ryu/services/protocols/bgp/info_base/vrf.py
+++ b/ryu/services/protocols/bgp/info_base/vrf.py
@@ -36,6 +36,7 @@ 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
+from ryu.lib.packet.bgp import EvpnIpPrefixNLRI
from ryu.services.protocols.bgp.base import OrderedDict
from ryu.services.protocols.bgp.constants import VPN_TABLE
@@ -292,6 +293,8 @@ class VrfTable(Table):
# 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/tests/unit/services/protocols/bgp/test_bgpspeaker.py b/ryu/tests/unit/services/protocols/bgp/test_bgpspeaker.py
index 535f8a0e..d5e4908e 100644
--- a/ryu/tests/unit/services/protocols/bgp/test_bgpspeaker.py
+++ b/ryu/tests/unit/services/protocols/bgp/test_bgpspeaker.py
@@ -191,6 +191,88 @@ class Test_BGPSpeaker(unittest.TestCase):
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
+ route_dist = '65000:100'
+ esi = 0 # denotes single-homed
+ ethernet_tag_id = 200
+ ip_prefix = '192.168.0.0/24'
+ gw_ip_addr = '172.16.0.1'
+ next_hop = '0.0.0.0'
+ expected_kwargs = {
+ 'route_type': route_type,
+ 'route_dist': route_dist,
+ 'esi': esi,
+ 'ethernet_tag_id': ethernet_tag_id,
+ 'ip_prefix': ip_prefix,
+ 'gw_ip_addr': gw_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,
+ ethernet_tag_id=ethernet_tag_id,
+ ip_prefix=ip_prefix,
+ gw_ip_addr=gw_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_vni(self, mock_call):
+ # Prepare test data
+ route_type = bgpspeaker.EVPN_IP_PREFIX_ROUTE
+ route_dist = '65000:100'
+ esi = 0 # denotes single-homed
+ ethernet_tag_id = 200
+ ip_prefix = '192.168.0.0/24'
+ gw_ip_addr = '172.16.0.1'
+ vni = 500
+ tunnel_type = bgpspeaker.TUNNEL_TYPE_VXLAN
+ next_hop = '0.0.0.0'
+ expected_kwargs = {
+ 'route_type': route_type,
+ 'route_dist': route_dist,
+ 'esi': esi,
+ 'ethernet_tag_id': ethernet_tag_id,
+ 'ip_prefix': ip_prefix,
+ 'gw_ip_addr': gw_ip_addr,
+ 'tunnel_type': tunnel_type,
+ '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,
+ ip_prefix=ip_prefix,
+ gw_ip_addr=gw_ip_addr,
+ tunnel_type=tunnel_type,
+ vni=vni,
+ )
+
+ # 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))
@@ -319,6 +401,36 @@ class Test_BGPSpeaker(unittest.TestCase):
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_del_ip_prefix_route(self, mock_call):
+ # Prepare test data
+ route_type = bgpspeaker.EVPN_IP_PREFIX_ROUTE
+ route_dist = '65000:100'
+ ethernet_tag_id = 200
+ ip_prefix = '192.168.0.0/24'
+ expected_kwargs = {
+ 'route_type': route_type,
+ 'route_dist': route_dist,
+ 'ethernet_tag_id': ethernet_tag_id,
+ 'ip_prefix': ip_prefix,
+ }
+
+ # Test
+ speaker = bgpspeaker.BGPSpeaker(65000, '10.0.0.1')
+ speaker.evpn_prefix_del(
+ route_type=route_type,
+ route_dist=route_dist,
+ ethernet_tag_id=ethernet_tag_id,
+ ip_prefix=ip_prefix,
+ )
+
+ # 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')