diff options
author | Shinpei Muraoka <shinpei.muraoka@gmail.com> | 2016-11-09 10:44:05 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2016-11-14 13:21:08 +0900 |
commit | 444c29fd735bd5c08b0726a4a58f5880af122061 (patch) | |
tree | 52666a4e7f35cceb68403426fc6bfe16ffe8aa39 | |
parent | 972c31f0e6f32ae40417a79001353328b965728f (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.py | 2 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/api/prefix.py | 27 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/bgp_sample_conf.py | 17 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/bgpspeaker.py | 35 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/info_base/vrf.py | 3 | ||||
-rw-r--r-- | ryu/tests/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 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') |