Signed-off-by: Shinpei Muraoka <[email protected]>
---
 ryu/services/protocols/bgp/api/base.py             |   2 +
 ryu/services/protocols/bgp/api/prefix.py           |  27 ++++-
 ryu/services/protocols/bgp/bgp_sample_conf.py      |  17 ++++
 ryu/services/protocols/bgp/bgpspeaker.py           |  35 ++++++-
 ryu/services/protocols/bgp/info_base/vrf.py        |   3 +
 .../unit/services/protocols/bgp/test_bgpspeaker.py | 112 +++++++++++++++++++++
 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 11fd747..3dd252f 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 43ddc67..3cc61af 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 a15192d..de8caf0 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 9e30d3e..4a1ce97 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 9681297..f6b50bc 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 535f8a0..d5e4908 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')
-- 
2.7.4


------------------------------------------------------------------------------
Developer Access Program for Intel Xeon Phi Processors
Access to Intel Xeon Phi processor-based developer platforms.
With one year of Intel Parallel Studio XE.
Training and support from Colfax.
Order your platform today. http://sdm.link/xeonphi
_______________________________________________
Ryu-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ryu-devel

Reply via email to