--- /dev/null
+# Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+ Prefix related APIs.
+"""
+import logging
+
+from ryu.lib.packet.bgp import EvpnEsi
+from ryu.lib.packet.bgp import EvpnNLRI
+from ryu.lib.packet.bgp import EvpnEthernetAutoDiscoveryNLRI
+from ryu.lib.packet.bgp import EvpnMacIPAdvertisementNLRI
+from ryu.lib.packet.bgp import EvpnInclusiveMulticastEthernetTagNLRI
+from ryu.lib.packet.bgp import EvpnEthernetSegmentNLRI
+from ryu.lib.packet.bgp import EvpnIpPrefixNLRI
+from ryu.lib.packet.bgp import BGPPathAttributePmsiTunnel
+from ryu.lib.packet.bgp import FlowSpecIPv4NLRI
+from ryu.lib.packet.bgp import FlowSpecIPv6NLRI
+from ryu.lib.packet.bgp import FlowSpecVPNv4NLRI
+from ryu.lib.packet.bgp import FlowSpecVPNv6NLRI
+from ryu.lib.packet.bgp import FlowSpecL2VPNNLRI
+from ryu.lib.packet.bgp import BGPFlowSpecTrafficRateCommunity
+from ryu.lib.packet.bgp import BGPFlowSpecTrafficActionCommunity
+from ryu.lib.packet.bgp import BGPFlowSpecRedirectCommunity
+from ryu.lib.packet.bgp import BGPFlowSpecTrafficMarkingCommunity
+from ryu.lib.packet.bgp import BGPFlowSpecVlanActionCommunity
+from ryu.lib.packet.bgp import BGPFlowSpecTPIDActionCommunity
+
+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 REDUNDANCY_MODE
+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
+from ryu.services.protocols.bgp.api.base import RegisterWithArgChecks
+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.api.base import FLOWSPEC_FAMILY
+from ryu.services.protocols.bgp.api.base import FLOWSPEC_RULES
+from ryu.services.protocols.bgp.api.base import FLOWSPEC_ACTIONS
+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
+from ryu.services.protocols.bgp.core import BgpCoreError
+from ryu.services.protocols.bgp.core_manager import CORE_MANAGER
+from ryu.services.protocols.bgp.rtconf.base import ConfigValueError
+from ryu.services.protocols.bgp.rtconf.base import RuntimeConfigError
+from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF
+from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV4
+from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_L2_EVPN
+from ryu.services.protocols.bgp.utils import validation
+
+LOG = logging.getLogger('bgpspeaker.api.prefix')
+
+# Maximum value of the Ethernet Tag ID
+EVPN_MAX_ET = EvpnNLRI.MAX_ET
+
+# ESI Types
+ESI_TYPE_ARBITRARY = EvpnEsi.ARBITRARY
+ESI_TYPE_LACP = EvpnEsi.LACP
+ESI_TYPE_L2_BRIDGE = EvpnEsi.L2_BRIDGE
+ESI_TYPE_MAC_BASED = EvpnEsi.MAC_BASED
+ESI_TYPE_ROUTER_ID = EvpnEsi.ROUTER_ID
+ESI_TYPE_AS_BASED = EvpnEsi.AS_BASED
+SUPPORTED_ESI_TYPES = [
+ ESI_TYPE_ARBITRARY,
+ ESI_TYPE_LACP,
+ ESI_TYPE_L2_BRIDGE,
+ ESI_TYPE_MAC_BASED,
+ ESI_TYPE_ROUTER_ID,
+ ESI_TYPE_AS_BASED,
+]
+
+# Constants used in API calls for EVPN
+EVPN_ETH_AUTO_DISCOVERY = EvpnEthernetAutoDiscoveryNLRI.ROUTE_TYPE_NAME
+EVPN_MAC_IP_ADV_ROUTE = EvpnMacIPAdvertisementNLRI.ROUTE_TYPE_NAME
+EVPN_MULTICAST_ETAG_ROUTE = (
+ EvpnInclusiveMulticastEthernetTagNLRI.ROUTE_TYPE_NAME)
+EVPN_ETH_SEGMENT = EvpnEthernetSegmentNLRI.ROUTE_TYPE_NAME
+EVPN_IP_PREFIX_ROUTE = EvpnIpPrefixNLRI.ROUTE_TYPE_NAME
+SUPPORTED_EVPN_ROUTE_TYPES = [
+ EVPN_ETH_AUTO_DISCOVERY,
+ EVPN_MAC_IP_ADV_ROUTE,
+ EVPN_MULTICAST_ETAG_ROUTE,
+ EVPN_ETH_SEGMENT,
+ EVPN_IP_PREFIX_ROUTE,
+]
+
+# Constants used in API calls for Flow Specification
+FLOWSPEC_FAMILY_IPV4 = FlowSpecIPv4NLRI.FLOWSPEC_FAMILY
+FLOWSPEC_FAMILY_IPV6 = FlowSpecIPv6NLRI.FLOWSPEC_FAMILY
+FLOWSPEC_FAMILY_VPNV4 = FlowSpecVPNv4NLRI.FLOWSPEC_FAMILY
+FLOWSPEC_FAMILY_VPNV6 = FlowSpecVPNv6NLRI.FLOWSPEC_FAMILY
+FLOWSPEC_FAMILY_L2VPN = FlowSpecL2VPNNLRI.FLOWSPEC_FAMILY
+SUPPORTED_FLOWSPEC_FAMILIES = (
+ FLOWSPEC_FAMILY_IPV4,
+ FLOWSPEC_FAMILY_IPV6,
+ FLOWSPEC_FAMILY_VPNV4,
+ FLOWSPEC_FAMILY_VPNV6,
+ FLOWSPEC_FAMILY_L2VPN,
+)
+
+# Constants for the Traffic Filtering Actions of Flow Specification
+# Constants for the Traffic Filtering Actions of Flow Specification.
+FLOWSPEC_ACTION_TRAFFIC_RATE = BGPFlowSpecTrafficRateCommunity.ACTION_NAME
+FLOWSPEC_ACTION_TRAFFIC_ACTION = BGPFlowSpecTrafficActionCommunity.ACTION_NAME
+FLOWSPEC_ACTION_REDIRECT = BGPFlowSpecRedirectCommunity.ACTION_NAME
+FLOWSPEC_ACTION_TRAFFIC_MARKING = BGPFlowSpecTrafficMarkingCommunity.ACTION_NAME
+FLOWSPEC_ACTION_VLAN = BGPFlowSpecVlanActionCommunity.ACTION_NAME
+FLOWSPEC_ACTION_TPID = BGPFlowSpecTPIDActionCommunity.ACTION_NAME
+
+SUPPORTTED_FLOWSPEC_ACTIONS = (
+ FLOWSPEC_ACTION_TRAFFIC_RATE,
+ FLOWSPEC_ACTION_TRAFFIC_ACTION,
+ FLOWSPEC_ACTION_REDIRECT,
+ FLOWSPEC_ACTION_TRAFFIC_MARKING,
+ FLOWSPEC_ACTION_VLAN,
+ FLOWSPEC_ACTION_TPID,
+)
+
+
+# Constants for ESI Label extended community
+REDUNDANCY_MODE_ALL_ACTIVE = 'all_active'
+REDUNDANCY_MODE_SINGLE_ACTIVE = 'single_active'
+SUPPORTED_REDUNDANCY_MODES = [
+ REDUNDANCY_MODE_ALL_ACTIVE,
+ REDUNDANCY_MODE_SINGLE_ACTIVE,
+]
+
+# Constants for BGP Tunnel Encapsulation Attribute
+TUNNEL_TYPE_VXLAN = 'vxlan'
+TUNNEL_TYPE_NVGRE = 'nvgre'
+TUNNEL_TYPE_MPLS = 'mpls'
+TUNNEL_TYPE_MPLS_IN_GRE = 'mpls_in_gre'
+TUNNEL_TYPE_VXLAN_GRE = 'vxlan_gre'
+SUPPORTED_TUNNEL_TYPES = [
+ TUNNEL_TYPE_VXLAN,
+ TUNNEL_TYPE_NVGRE,
+ TUNNEL_TYPE_MPLS,
+ 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,
+ sub_code=1,
+ def_desc='Unknown error related to operation on '
+ 'prefixes')
+class PrefixError(RuntimeConfigError):
+ pass
+
+
+@validate(name=PREFIX)
+def is_valid_prefix(prefix):
+ if not (validation.is_valid_ipv4_prefix(prefix)
+ or validation.is_valid_ipv6_prefix(prefix)):
+ raise ConfigValueError(conf_name=PREFIX,
+ conf_value=prefix)
+
+
+@validate(name=NEXT_HOP)
+def is_valid_next_hop(next_hop):
+ if not (validation.is_valid_ipv4(next_hop)
+ or validation.is_valid_ipv6(next_hop)):
+ raise ConfigValueError(conf_name=NEXT_HOP,
+ conf_value=next_hop)
+
+
+@validate(name=EVPN_ROUTE_TYPE)
+def is_valid_evpn_route_type(route_type):
+ if route_type not in SUPPORTED_EVPN_ROUTE_TYPES:
+ raise ConfigValueError(conf_name=EVPN_ROUTE_TYPE,
+ conf_value=route_type)
+
+
+@validate(name=EVPN_ESI)
+def is_valid_esi(esi):
+ if not validation.is_valid_esi(esi):
+ raise ConfigValueError(conf_name=EVPN_ESI,
+ conf_value=esi)
+
+
+@validate(name=EVPN_ETHERNET_TAG_ID)
+def is_valid_ethernet_tag_id(ethernet_tag_id):
+ if not validation.is_valid_ethernet_tag_id(ethernet_tag_id):
+ raise ConfigValueError(conf_name=EVPN_ETHERNET_TAG_ID,
+ conf_value=ethernet_tag_id)
+
+
+@validate(name=REDUNDANCY_MODE)
+def is_valid_redundancy_mode(redundancy_mode):
+ if redundancy_mode not in SUPPORTED_REDUNDANCY_MODES:
+ raise ConfigValueError(conf_name=REDUNDANCY_MODE,
+ conf_value=redundancy_mode)
+
+
+@validate(name=MAC_ADDR)
+def is_valid_mac_addr(addr):
+ if not validation.is_valid_mac(addr):
+ raise ConfigValueError(conf_name=MAC_ADDR,
+ conf_value=addr)
+
+
+@validate(name=IP_ADDR)
+def is_valid_ip_addr(addr):
+ # Note: Allows empty IP Address (means length=0).
+ # e.g.) L2VPN MAC advertisement of Cisco NX-OS
+ if not (addr is None
+ or validation.is_valid_ipv4(addr)
+ or validation.is_valid_ipv6(addr)):
+ raise ConfigValueError(conf_name=IP_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):
+ raise ConfigValueError(conf_name=MPLS_LABELS,
+ conf_value=labels)
+
+
+@validate(name=EVPN_VNI)
+def is_valid_vni(vni):
+ if not validation.is_valid_vni(vni):
+ raise ConfigValueError(conf_name=EVPN_VNI,
+ conf_value=vni)
+
+
+@validate(name=TUNNEL_TYPE)
+def is_valid_tunnel_type(tunnel_type):
+ if tunnel_type not in SUPPORTED_TUNNEL_TYPES:
+ raise ConfigValueError(conf_name=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)
+
+
+@validate(name=FLOWSPEC_FAMILY)
+def is_valid_flowspec_family(flowspec_family):
+ if flowspec_family not in SUPPORTED_FLOWSPEC_FAMILIES:
+ raise ConfigValueError(conf_name=FLOWSPEC_FAMILY,
+ conf_value=flowspec_family)
+
+
+@validate(name=FLOWSPEC_RULES)
+def is_valid_flowspec_rules(rules):
+ if not isinstance(rules, dict):
+ raise ConfigValueError(conf_name=FLOWSPEC_RULES,
+ conf_value=rules)
+
+
+@validate(name=FLOWSPEC_ACTIONS)
+def is_valid_flowspec_actions(actions):
+ for k in actions:
+ if k not in SUPPORTTED_FLOWSPEC_ACTIONS:
+ raise ConfigValueError(conf_name=FLOWSPEC_ACTIONS,
+ conf_value=actions)
+
+
+@RegisterWithArgChecks(name='prefix.add_local',
+ req_args=[ROUTE_DISTINGUISHER, PREFIX, NEXT_HOP],
+ opt_args=[VRF_RF])
+def add_local(route_dist, prefix, next_hop, route_family=VRF_RF_IPV4):
+ """Adds *prefix* from VRF identified by *route_dist* and sets the source as
+ network controller.
+ """
+ try:
+ # Create new path and insert into appropriate VRF table.
+ tm = CORE_MANAGER.get_core_service().table_manager
+ label = tm.update_vrf_table(route_dist, prefix, next_hop, route_family)
+ # Currently we only allocate one label per local_prefix,
+ # so we share first label from the list.
+ if label:
+ label = label[0]
+
+ # Send success response with new label.
+ return [{ROUTE_DISTINGUISHER: route_dist, PREFIX: prefix,
+ VRF_RF: route_family, VPN_LABEL: label}]
+ except BgpCoreError as e:
+ raise PrefixError(desc=e)
+
+
+@RegisterWithArgChecks(name='prefix.delete_local',
+ req_args=[ROUTE_DISTINGUISHER, PREFIX],
+ opt_args=[VRF_RF])
+def delete_local(route_dist, prefix, route_family=VRF_RF_IPV4):
+ """Deletes/withdraws *prefix* from VRF identified by *route_dist* and
+ source as network controller.
+ """
+ try:
+ tm = CORE_MANAGER.get_core_service().table_manager
+ tm.update_vrf_table(route_dist, prefix,
+ route_family=route_family, is_withdraw=True)
+ # Send success response.
+ return [{ROUTE_DISTINGUISHER: route_dist, PREFIX: prefix,
+ VRF_RF: route_family}]
+ except BgpCoreError as e:
+ raise PrefixError(desc=e)
+
+
+# =============================================================================
+# BGP EVPN Routes related APIs
+# =============================================================================
+
+@RegisterWithArgChecks(name='evpn_prefix.add_local',
+ req_args=[EVPN_ROUTE_TYPE, ROUTE_DISTINGUISHER,
+ NEXT_HOP],
+ opt_args=[EVPN_ESI, EVPN_ETHERNET_TAG_ID,
+ REDUNDANCY_MODE, MAC_ADDR, 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*.
+ """
+
+ if(route_type in [EVPN_ETH_AUTO_DISCOVERY, EVPN_ETH_SEGMENT]
+ and kwargs['esi'] == 0):
+ raise ConfigValueError(conf_name=EVPN_ESI,
+ conf_value=kwargs['esi'])
+
+ try:
+ # Create new path and insert into appropriate VRF table.
+ tm = CORE_MANAGER.get_core_service().table_manager
+ label = tm.update_vrf_table(route_dist, next_hop=next_hop,
+ route_family=VRF_RF_L2_EVPN,
+ route_type=route_type, **kwargs)
+ # Currently we only allocate one label per local route,
+ # so we share first label from the list.
+ if label:
+ label = label[0]
+
+ # Send success response with new label.
+ return [{EVPN_ROUTE_TYPE: route_type,
+ ROUTE_DISTINGUISHER: route_dist,
+ VRF_RF: VRF_RF_L2_EVPN,
+ VPN_LABEL: label}.update(kwargs)]
+ except BgpCoreError as e:
+ raise PrefixError(desc=e)
+
+
+@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, IP_PREFIX, EVPN_VNI])
+def delete_evpn_local(route_type, route_dist, **kwargs):
+ """Deletes/withdraws EVPN route from VRF identified by *route_dist*.
+ """
+ try:
+ tm = CORE_MANAGER.get_core_service().table_manager
+ tm.update_vrf_table(route_dist,
+ route_family=VRF_RF_L2_EVPN,
+ route_type=route_type, is_withdraw=True, **kwargs)
+ # Send success response.
+ return [{EVPN_ROUTE_TYPE: route_type,
+ ROUTE_DISTINGUISHER: route_dist,
+ VRF_RF: VRF_RF_L2_EVPN}.update(kwargs)]
+ except BgpCoreError as e:
+ raise PrefixError(desc=e)
+
+
+# =============================================================================
+# BGP Flow Specification Routes related APIs
+# =============================================================================
+
+@RegisterWithArgChecks(
+ name='flowspec.add_local',
+ req_args=[FLOWSPEC_FAMILY, ROUTE_DISTINGUISHER, FLOWSPEC_RULES],
+ opt_args=[FLOWSPEC_ACTIONS])
+def add_flowspec_local(flowspec_family, route_dist, rules, **kwargs):
+ """Adds Flow Specification route from VRF identified by *route_dist*.
+ """
+ try:
+ # Create new path and insert into appropriate VRF table.
+ tm = CORE_MANAGER.get_core_service().table_manager
+ tm.update_flowspec_vrf_table(
+ flowspec_family=flowspec_family, route_dist=route_dist,
+ rules=rules, **kwargs)
+
+ # Send success response.
+ return [{FLOWSPEC_FAMILY: flowspec_family,
+ ROUTE_DISTINGUISHER: route_dist,
+ FLOWSPEC_RULES: rules}.update(kwargs)]
+
+ except BgpCoreError as e:
+ raise PrefixError(desc=e)
+
+
+@RegisterWithArgChecks(
+ name='flowspec.del_local',
+ req_args=[FLOWSPEC_FAMILY, ROUTE_DISTINGUISHER, FLOWSPEC_RULES])
+def del_flowspec_local(flowspec_family, route_dist, rules):
+ """Deletes/withdraws Flow Specification route from VRF identified
+ by *route_dist*.
+ """
+ try:
+ tm = CORE_MANAGER.get_core_service().table_manager
+ tm.update_flowspec_vrf_table(
+ flowspec_family=flowspec_family, route_dist=route_dist,
+ rules=rules, is_withdraw=True)
+
+ # Send success response.
+ return [{FLOWSPEC_FAMILY: flowspec_family,
+ ROUTE_DISTINGUISHER: route_dist,
+ FLOWSPEC_RULES: rules}]
+
+ except BgpCoreError as e:
+ raise PrefixError(desc=e)