diff options
author | Shinpei Muraoka <shinpei.muraoka@gmail.com> | 2017-03-24 17:10:48 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2017-03-28 10:00:21 +0900 |
commit | b3a83ef185970dfd1c3bfa895cf0eb2c40d2be4e (patch) | |
tree | 68e20e06d15cc62bcf7eefb431895725bf1f160e | |
parent | a486539400f5348f14826dab6b1a3f685d157e53 (diff) |
BGPSpeaker: Support Flow Specification update messages
This patch enables BGPSpeaker to store FlowSpec routes into
the global table and VRF tables and to provide the API
for advertising routes.
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 | 3 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/api/prefix.py | 105 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/api/rtconf.py | 26 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/application.py | 2 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/base.py | 4 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/bgp_sample_conf.py | 92 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/bgpspeaker.py | 146 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/core_managers/table_manager.py | 151 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/model.py | 4 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/net_ctrl.py | 7 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/peer.py | 23 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/rtconf/base.py | 20 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/rtconf/neighbors.py | 32 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/rtconf/vrfs.py | 13 | ||||
-rw-r--r-- | ryu/services/protocols/bgp/utils/bgp.py | 51 |
15 files changed, 666 insertions, 13 deletions
diff --git a/ryu/services/protocols/bgp/api/base.py b/ryu/services/protocols/bgp/api/base.py index 125fee93..87dc13b6 100644 --- a/ryu/services/protocols/bgp/api/base.py +++ b/ryu/services/protocols/bgp/api/base.py @@ -54,6 +54,9 @@ MPLS_LABELS = 'mpls_labels' TUNNEL_TYPE = 'tunnel_type' EVPN_VNI = 'vni' PMSI_TUNNEL_TYPE = 'pmsi_tunnel_type' +FLOWSPEC_FAMILY = 'flowspec_family' +FLOWSPEC_RULES = 'rules' +FLOWSPEC_ACTIONS = 'actions' # API call registry _CALL_REGISTRY = {} diff --git a/ryu/services/protocols/bgp/api/prefix.py b/ryu/services/protocols/bgp/api/prefix.py index a73a123c..0751f2f5 100644 --- a/ryu/services/protocols/bgp/api/prefix.py +++ b/ryu/services/protocols/bgp/api/prefix.py @@ -26,6 +26,13 @@ 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 FlowSpecVPNv4NLRI +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.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 @@ -43,6 +50,9 @@ 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 @@ -55,7 +65,6 @@ 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 @@ -92,6 +101,29 @@ SUPPORTED_EVPN_ROUTE_TYPES = [ EVPN_IP_PREFIX_ROUTE, ] +# Constants used in API calls for Flow Specification +FLOWSPEC_FAMILY_IPV4 = FlowSpecIPv4NLRI.FLOWSPEC_FAMILY +FLOWSPEC_FAMILY_VPNV4 = FlowSpecVPNv4NLRI.FLOWSPEC_FAMILY +SUPPORTED_FLOWSPEC_FAMILIES = ( + FLOWSPEC_FAMILY_IPV4, + FLOWSPEC_FAMILY_VPNV4, +) + +# 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 + +SUPPORTTED_FLOWSPEC_ACTIONS = ( + FLOWSPEC_ACTION_TRAFFIC_RATE, + FLOWSPEC_ACTION_TRAFFIC_ACTION, + FLOWSPEC_ACTION_REDIRECT, + FLOWSPEC_ACTION_TRAFFIC_MARKING, +) + + # Constants for ESI Label extended community REDUNDANCY_MODE_ALL_ACTIVE = 'all_active' REDUNDANCY_MODE_SINGLE_ACTIVE = 'single_active' @@ -241,6 +273,28 @@ def is_valid_pmsi_tunnel_type(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]) @@ -340,3 +394,52 @@ def delete_evpn_local(route_type, route_dist, **kwargs): 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) diff --git a/ryu/services/protocols/bgp/api/rtconf.py b/ryu/services/protocols/bgp/api/rtconf.py index 45c6420b..d981499b 100644 --- a/ryu/services/protocols/bgp/api/rtconf.py +++ b/ryu/services/protocols/bgp/api/rtconf.py @@ -20,6 +20,9 @@ import logging from ryu.services.protocols.bgp.api.base import register from ryu.services.protocols.bgp.api.base import RegisterWithArgChecks +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.core_manager import CORE_MANAGER from ryu.services.protocols.bgp.rtconf.base import ConfWithId from ryu.services.protocols.bgp.rtconf.base import RuntimeConfigError @@ -297,3 +300,26 @@ def bmp_start(host, port): def bmp_stop(host, port): core = CORE_MANAGER.get_core_service() return core.stop_bmp(host, port) + + +# ============================================================================= +# BGP Flow Specification Routes related APIs +# ============================================================================= + +@RegisterWithArgChecks( + name='flowspec.add', + req_args=[FLOWSPEC_FAMILY, FLOWSPEC_RULES], + opt_args=[FLOWSPEC_ACTIONS]) +def add_flowspec(flowspec_family, rules, **kwargs): + tm = CORE_MANAGER.get_core_service().table_manager + tm.update_flowspec_global_table(flowspec_family, rules, **kwargs) + return True + + +@RegisterWithArgChecks( + name='flowspec.del', + req_args=[FLOWSPEC_FAMILY, FLOWSPEC_RULES]) +def del_flowspec(flowspec_family, rules): + tm = CORE_MANAGER.get_core_service().table_manager + tm.update_flowspec_global_table(flowspec_family, rules, is_withdraw=True) + return True diff --git a/ryu/services/protocols/bgp/application.py b/ryu/services/protocols/bgp/application.py index a3e386bc..4663ddb8 100644 --- a/ryu/services/protocols/bgp/application.py +++ b/ryu/services/protocols/bgp/application.py @@ -331,6 +331,8 @@ class RyuBGPSpeaker(RyuApp): prefix_add = self.speaker.prefix_add elif 'route_type' in route_settings: prefix_add = self.speaker.evpn_prefix_add + elif 'flowspec_family' in route_settings: + prefix_add = self.speaker.flowspec_prefix_add else: LOG.debug('Skip invalid route settings: %s', route_settings) continue diff --git a/ryu/services/protocols/bgp/base.py b/ryu/services/protocols/bgp/base.py index 190118f5..e5469020 100644 --- a/ryu/services/protocols/bgp/base.py +++ b/ryu/services/protocols/bgp/base.py @@ -36,6 +36,8 @@ from ryu.lib.packet.bgp import RF_IPv6_UC from ryu.lib.packet.bgp import RF_IPv4_VPN from ryu.lib.packet.bgp import RF_IPv6_VPN from ryu.lib.packet.bgp import RF_L2_EVPN +from ryu.lib.packet.bgp import RF_IPv4_FLOWSPEC +from ryu.lib.packet.bgp import RF_VPNv4_FLOWSPEC from ryu.lib.packet.bgp import RF_RTC_UC from ryu.services.protocols.bgp.utils.circlist import CircularListType from ryu.services.protocols.bgp.utils.evtlet import LoopingCall @@ -56,6 +58,8 @@ SUPPORTED_GLOBAL_RF = { RF_RTC_UC, RF_IPv6_VPN, RF_L2_EVPN, + RF_IPv4_FLOWSPEC, + RF_VPNv4_FLOWSPEC, } diff --git a/ryu/services/protocols/bgp/bgp_sample_conf.py b/ryu/services/protocols/bgp/bgp_sample_conf.py index 9b9564c3..e77ec578 100644 --- a/ryu/services/protocols/bgp/bgp_sample_conf.py +++ b/ryu/services/protocols/bgp/bgp_sample_conf.py @@ -3,6 +3,7 @@ import os from ryu.services.protocols.bgp.bgpspeaker import RF_VPN_V4 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 RF_VPNV4_FLOWSPEC from ryu.services.protocols.bgp.bgpspeaker import EVPN_MAX_ET from ryu.services.protocols.bgp.bgpspeaker import ESI_TYPE_LACP from ryu.services.protocols.bgp.bgpspeaker import ESI_TYPE_MAC_BASED @@ -12,6 +13,10 @@ 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_ETH_SEGMENT from ryu.services.protocols.bgp.bgpspeaker import EVPN_IP_PREFIX_ROUTE +from ryu.services.protocols.bgp.bgpspeaker import FLOWSPEC_FAMILY_IPV4 +from ryu.services.protocols.bgp.bgpspeaker import FLOWSPEC_FAMILY_VPNV4 +from ryu.services.protocols.bgp.bgpspeaker import FLOWSPEC_TA_SAMPLE +from ryu.services.protocols.bgp.bgpspeaker import FLOWSPEC_TA_TERMINAL from ryu.services.protocols.bgp.bgpspeaker import REDUNDANCY_MODE_SINGLE_ACTIVE # ============================================================================= @@ -42,6 +47,12 @@ BGP = { 'remote_as': 65001, 'enable_evpn': True, }, + { + 'address': '172.17.0.4', + 'remote_as': 65001, + 'enable_ipv4fs': True, + 'enable_vpnv4fs': True, + }, ], # List of BGP VRF tables. @@ -69,11 +80,21 @@ BGP = { 'export_rts': ['65001:200'], 'route_family': RF_L2_EVPN, }, + # Example of VRF for FlowSpec + { + 'route_dist': '65001:250', + 'import_rts': ['65001:250'], + 'export_rts': ['65001:250'], + 'route_family': RF_VPNV4_FLOWSPEC, + }, ], # List of BGP routes. # The parameters for each route are the same as the arguments of - # BGPSpeaker.prefix_add() or BGPSpeaker.evpn_prefix_add() method. + # the following methods: + # - BGPSpeaker.prefix_add() + # - BGPSpeaker.evpn_prefix_add() + # - BGPSpeaker.flowspec_prefix_add() 'routes': [ # Example of IPv4 prefix { @@ -143,6 +164,75 @@ BGP = { 'ip_prefix': '10.50.1.0/24', 'gw_ip_addr': '172.16.0.1', }, + # Example of Flow Specification IPv4 prefix + { + 'flowspec_family': FLOWSPEC_FAMILY_IPV4, + 'rules': { + 'dst_prefix': '10.60.1.0/24', + 'src_prefix': '172.17.0.0/24', + 'ip_proto': 6, + 'port': '80 | 8000', + 'dst_port': '>9000 & <9050', + 'src_port': '>=8500 & <=9000', + 'icmp_type': 0, + 'icmp_code': 6, + 'tcp_flags': 'SYN+ACK & !=URGENT', + 'packet_len': 1000, + 'dscp': '22 | 24', + 'fragment': 'LF | ==FF', + }, + 'actions': { + 'traffic_rate': { + 'as_number': 0, + 'rate_info': 100.0, + }, + 'traffic_action': { + 'action': FLOWSPEC_TA_SAMPLE | FLOWSPEC_TA_TERMINAL, + }, + 'redirect': { + 'as_number': 10, + 'local_administrator': 100, + }, + 'traffic_marking': { + 'dscp': 24, + } + }, + }, + # Example of Flow Specification VPNv4 prefix + { + 'flowspec_family': FLOWSPEC_FAMILY_VPNV4, + 'route_dist': '65001:250', + 'rules': { + 'dst_prefix': '10.70.1.0/24', + 'src_prefix': '172.18.0.0/24', + 'ip_proto': 6, + 'port': '80 | 8000', + 'dst_port': '>9000 & <9050', + 'src_port': '>=8500 & <=9000', + 'icmp_type': 0, + 'icmp_code': 6, + 'tcp_flags': 'SYN+ACK & !=URGENT', + 'packet_len': 1000, + 'dscp': '22 | 24', + 'fragment': 'LF | ==FF', + }, + 'actions': { + 'traffic_rate': { + 'as_number': 0, + 'rate_info': 100.0, + }, + 'traffic_action': { + 'action': FLOWSPEC_TA_SAMPLE | FLOWSPEC_TA_TERMINAL, + }, + 'redirect': { + 'as_number': 10, + 'local_administrator': 100, + }, + 'traffic_marking': { + 'dscp': 24, + } + }, + }, ], } diff --git a/ryu/services/protocols/bgp/bgpspeaker.py b/ryu/services/protocols/bgp/bgpspeaker.py index f1784240..d943082a 100644 --- a/ryu/services/protocols/bgp/bgpspeaker.py +++ b/ryu/services/protocols/bgp/bgpspeaker.py @@ -18,6 +18,7 @@ import netaddr from ryu.lib import hub +from ryu.lib.packet.bgp import BGPFlowSpecTrafficActionCommunity from ryu.services.protocols.bgp.core_manager import CORE_MANAGER from ryu.services.protocols.bgp.signals.emit import BgpSignalBus @@ -53,6 +54,12 @@ 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.api.prefix import ( + FLOWSPEC_FAMILY, + FLOWSPEC_FAMILY_IPV4, + FLOWSPEC_FAMILY_VPNV4, + FLOWSPEC_RULES, + FLOWSPEC_ACTIONS) from ryu.services.protocols.bgp.rtconf.common import LOCAL_AS from ryu.services.protocols.bgp.rtconf.common import ROUTER_ID from ryu.services.protocols.bgp.rtconf.common import CLUSTER_ID @@ -72,6 +79,8 @@ from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_IPV6 from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_VPNV4 from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_VPNV6 from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_EVPN +from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_IPV4FS +from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_VPNV4FS from ryu.services.protocols.bgp.rtconf.base import CAP_ENHANCED_REFRESH from ryu.services.protocols.bgp.rtconf.base import CAP_FOUR_OCTET_AS_NUMBER from ryu.services.protocols.bgp.rtconf.base import MULTI_EXIT_DISC @@ -81,6 +90,8 @@ from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CAP_MBGP_IPV6 from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CAP_MBGP_VPNV4 from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CAP_MBGP_VPNV6 from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CAP_MBGP_EVPN +from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CAP_MBGP_IPV4FS +from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CAP_MBGP_VPNV4FS from ryu.services.protocols.bgp.rtconf.neighbors import ( DEFAULT_CAP_ENHANCED_REFRESH, DEFAULT_CAP_FOUR_OCTET_AS_NUMBER) from ryu.services.protocols.bgp.rtconf.neighbors import DEFAULT_CONNECT_MODE @@ -108,6 +119,11 @@ NEIGHBOR_CONF_MED = MULTI_EXIT_DISC # for backward compatibility RF_VPN_V4 = vrfs.VRF_RF_IPV4 RF_VPN_V6 = vrfs.VRF_RF_IPV6 RF_L2_EVPN = vrfs.VRF_RF_L2_EVPN +RF_VPNV4_FLOWSPEC = vrfs.VRF_RF_IPV4_FLOWSPEC + +# Constants for the Traffic Filtering Actions of Flow Specification. +FLOWSPEC_TA_SAMPLE = BGPFlowSpecTrafficActionCommunity.SAMPLE +FLOWSPEC_TA_TERMINAL = BGPFlowSpecTrafficActionCommunity.TERMINAL class EventPrefix(object): @@ -330,6 +346,8 @@ class BGPSpeaker(object): enable_vpnv4=DEFAULT_CAP_MBGP_VPNV4, enable_vpnv6=DEFAULT_CAP_MBGP_VPNV6, enable_evpn=DEFAULT_CAP_MBGP_EVPN, + enable_ipv4fs=DEFAULT_CAP_MBGP_IPV4FS, + enable_vpnv4fs=DEFAULT_CAP_MBGP_VPNV4FS, enable_enhanced_refresh=DEFAULT_CAP_ENHANCED_REFRESH, enable_four_octet_as_number=DEFAULT_CAP_FOUR_OCTET_AS_NUMBER, next_hop=None, password=None, multi_exit_disc=None, @@ -366,6 +384,12 @@ class BGPSpeaker(object): ``enable_evpn`` enables Ethernet VPN address family for this neighbor. The default is False. + ``enable_ipv4fs`` enables IPv4 Flow Specification address family + for this neighbor. The default is False. + + ``enable_vpnv4fs`` enables VPNv4 Flow Specification address family + for this neighbor. The default is False. + ``enable_enhanced_refresh`` enables Enhanced Route Refresh for this neighbor. The default is False. @@ -424,6 +448,8 @@ class BGPSpeaker(object): CAP_MBGP_VPNV4: enable_vpnv4, CAP_MBGP_VPNV6: enable_vpnv6, CAP_MBGP_EVPN: enable_evpn, + CAP_MBGP_IPV4FS: enable_ipv4fs, + CAP_MBGP_VPNV4FS: enable_vpnv4fs, } if multi_exit_disc: @@ -761,6 +787,126 @@ class BGPSpeaker(object): call(func_name, **kwargs) + def flowspec_prefix_add(self, flowspec_family, rules, route_dist=None, + actions=None): + """ This method adds a new Flow Specification prefix to be advertised. + + ``flowspec_family`` specifies one of the flowspec family name. + The supported flowspec families are FLOWSPEC_FAMILY_IPV4 and + FLOWSPEC_FAMILY_VPNV4. + + ``route_dist`` specifies a route distinguisher value. + This parameter is necessary for only VPNv4 Flow Specification + address family. + + ``rules`` specifies NLRIs of Flow Specification as + a dictionary type value. + For the supported NLRI types and arguments, + see `from_user()` method of the following classes. + + - :py:mod:`ryu.lib.packet.bgp.FlowSpecIPv4NLRI` + - :py:mod:`ryu.lib.packet.bgp.FlowSpecVPNv4NLRI` + + `` actions`` specifies Traffic Filtering Actions of + Flow Specification as a dictionary type value. + The keys are "ACTION_NAME" for each action class and + values are used for the arguments to that class. + For the supported "ACTION_NAME" and arguments, + see the following table. + + =============== =============================================================== + ACTION_NAME Action Class + =============== =============================================================== + traffic_rate :py:mod:`ryu.lib.packet.bgp.BGPFlowSpecTrafficRateCommunity` + traffic_action :py:mod:`ryu.lib.packet.bgp.BGPFlowSpecTrafficActionCommunity` + redirect :py:mod:`ryu.lib.packet.bgp.BGPFlowSpecRedirectCommunity` + traffic_marking :py:mod:`ryu.lib.packet.bgp.BGPFlowSpecTrafficMarkingCommunity` + =============== =============================================================== + + Example(IPv4):: + + >>> speaker = BGPSpeaker(as_number=65001, router_id='172.17.0.1') + >>> speaker.neighbor_add(address='172.17.0.2', + ... remote_as=65002, + ... enable_ipv4fs=True) + >>> speaker.flowspec_prefix_add( + ... flowspec_family=FLOWSPEC_FAMILY_IPV4, + ... rules={ + ... 'dst_prefix': '10.60.1.0/24' + ... }, + ... actions={ + ... 'traffic_marking': { + ... 'dscp': 24 + ... } + ... } + ... ) + + Example(VPNv4):: + + >>> speaker = BGPSpeaker(as_number=65001, router_id='172.17.0.1') + >>> speaker.neighbor_add(address='172.17.0.2', + ... remote_as=65002, + ... enable_vpnv4fs=True) + >>> speaker.vrf_add(route_dist='65001:100', + ... import_rts=['65001:100'], + ... export_rts=['65001:100'], + ... route_family=RF_VPNV4_FLOWSPEC) + >>> speaker.flowspec_prefix_add( + ... flowspec_family=FLOWSPEC_FAMILY_VPNV4, + ... route_dist='65000:100', + ... rules={ + ... 'dst_prefix': '10.60.1.0/24' + ... }, + ... actions={ + ... 'traffic_marking': { + ... 'dscp': 24 + ... } + ... } + ... ) + """ + func_name = 'flowspec.add' + + # Set required arguments + kwargs = { + FLOWSPEC_FAMILY: flowspec_family, + FLOWSPEC_RULES: rules, + FLOWSPEC_ACTIONS: actions or {}, + } + + if flowspec_family == FLOWSPEC_FAMILY_VPNV4: + func_name = 'flowspec.add_local' + kwargs.update({ROUTE_DISTINGUISHER: route_dist}) + + call(func_name, **kwargs) + + def flowspec_prefix_del(self, flowspec_family, rules, route_dist=None): + """ This method deletes an advertised Flow Specification route. + + ``flowspec_family`` specifies one of the flowspec family name. + + ``route_dist`` specifies a route distinguisher value. + + ``rules`` specifies NLRIs of Flow Specification as + a dictionary type value. + + See the following method for details of each parameter and usages. + + - :py:mod:`ryu.services.protocols.bgp.bgpspeaker.BGPSpeaker.flowspec_prefix_add` + """ + func_name = 'flowspec.del' + + # Set required arguments + kwargs = { + FLOWSPEC_FAMILY: flowspec_family, + FLOWSPEC_RULES: rules, + } + + if flowspec_family == FLOWSPEC_FAMILY_VPNV4: + func_name = 'flowspec.del_local' + kwargs.update({ROUTE_DISTINGUISHER: route_dist}) + + call(func_name, **kwargs) + def vrf_add(self, route_dist, import_rts, export_rts, site_of_origins=None, route_family=RF_VPN_V4, multi_exit_disc=None): """ This method adds a new vrf used for VPN. diff --git a/ryu/services/protocols/bgp/core_managers/table_manager.py b/ryu/services/protocols/bgp/core_managers/table_manager.py index 5aa9454a..297b7a41 100644 --- a/ryu/services/protocols/bgp/core_managers/table_manager.py +++ b/ryu/services/protocols/bgp/core_managers/table_manager.py @@ -15,10 +15,16 @@ from ryu.services.protocols.bgp.info_base.vrf4 import Vrf4Table from ryu.services.protocols.bgp.info_base.vrf6 import Vrf6Table from ryu.services.protocols.bgp.info_base.vrfevpn import VrfEvpnTable from ryu.services.protocols.bgp.info_base.evpn import EvpnTable +from ryu.services.protocols.bgp.info_base.ipv4fs import IPv4FlowSpecPath +from ryu.services.protocols.bgp.info_base.ipv4fs import IPv4FlowSpecTable +from ryu.services.protocols.bgp.info_base.vpnv4fs import VPNv4FlowSpecTable +from ryu.services.protocols.bgp.info_base.vrf4fs import Vrf4FlowSpecTable from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV4 from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV6 from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_L2_EVPN +from ryu.services.protocols.bgp.rtconf.vrfs import VRF_RF_IPV4_FLOWSPEC from ryu.services.protocols.bgp.rtconf.vrfs import SUPPORTED_VRF_RF +from ryu.services.protocols.bgp.utils.bgp import create_v4flowspec_actions from ryu.lib import type_desc from ryu.lib.packet.bgp import RF_IPv4_UC @@ -26,12 +32,16 @@ from ryu.lib.packet.bgp import RF_IPv6_UC from ryu.lib.packet.bgp import RF_IPv4_VPN from ryu.lib.packet.bgp import RF_IPv6_VPN from ryu.lib.packet.bgp import RF_L2_EVPN +from ryu.lib.packet.bgp import RF_IPv4_FLOWSPEC +from ryu.lib.packet.bgp import RF_VPNv4_FLOWSPEC from ryu.lib.packet.bgp import RF_RTC_UC 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 BGP_ATTR_TYPE_ORIGIN from ryu.lib.packet.bgp import BGP_ATTR_TYPE_AS_PATH from ryu.lib.packet.bgp import BGP_ATTR_ORIGIN_IGP +from ryu.lib.packet.bgp import BGP_ATTR_TYPE_EXTENDED_COMMUNITIES from ryu.lib.packet.bgp import EvpnEsi from ryu.lib.packet.bgp import EvpnArbitraryEsi from ryu.lib.packet.bgp import EvpnNLRI @@ -39,6 +49,7 @@ from ryu.lib.packet.bgp import EvpnMacIPAdvertisementNLRI from ryu.lib.packet.bgp import EvpnInclusiveMulticastEthernetTagNLRI from ryu.lib.packet.bgp import IPAddrPrefix from ryu.lib.packet.bgp import IP6AddrPrefix +from ryu.lib.packet.bgp import FlowSpecIPv4NLRI from ryu.services.protocols.bgp.utils.validation import is_valid_ipv4 from ryu.services.protocols.bgp.utils.validation import is_valid_ipv4_prefix @@ -119,6 +130,8 @@ class TableCoreManager(object): vpn_table = self.get_vpn6_table() elif vrf_table.route_family == VrfEvpnTable.ROUTE_FAMILY: vpn_table = self.get_evpn_table() + elif vrf_table.route_family == Vrf4FlowSpecTable.ROUTE_FAMILY: + vpn_table = self.get_vpnv4fs_table() else: raise ValueError('Invalid VRF table route family: %s' % vrf_table.route_family) @@ -185,6 +198,10 @@ class TableCoreManager(object): global_table = self.get_vpn6_table() elif route_family == RF_L2_EVPN: global_table = self.get_evpn_table() + elif route_family == RF_IPv4_FLOWSPEC: + global_table = self.get_ipv4fs_table() + elif route_family == RF_VPNv4_FLOWSPEC: + global_table = self.get_vpnv4fs_table() elif route_family == RF_RTC_UC: global_table = self.get_rtc_table() @@ -295,6 +312,36 @@ class TableCoreManager(object): self._next_vpnv4_label += 1 return lbl + def get_ipv4fs_table(self): + """Returns global IPv4 Flow Specification table. + + Creates the table if it does not exist. + """ + ipv4fs_table = self._global_tables.get(RF_IPv4_FLOWSPEC) + # Lazy initialization of the table. + if not ipv4fs_table: + ipv4fs_table = IPv4FlowSpecTable(self._core_service, + self._signal_bus) + self._global_tables[RF_IPv4_FLOWSPEC] = ipv4fs_table + self._tables[(None, RF_IPv4_FLOWSPEC)] = ipv4fs_table + + return ipv4fs_table + + def get_vpnv4fs_table(self): + """Returns global VPNv4 Flow Specification table. + + Creates the table if it does not exist. + """ + vpnv4fs_table = self._global_tables.get(RF_VPNv4_FLOWSPEC) + # Lazy initialization of the table. + if not vpnv4fs_table: + vpnv4fs_table = VPNv4FlowSpecTable(self._core_service, + self._signal_bus) + self._global_tables[RF_VPNv4_FLOWSPEC] = vpnv4fs_table + self._tables[(None, RF_VPNv4_FLOWSPEC)] = vpnv4fs_table + + return vpnv4fs_table + def get_nexthop_label(self, label_key): return self._next_hop_label.get(label_key, None) @@ -374,6 +421,8 @@ class TableCoreManager(object): vrf_table = Vrf6Table elif route_family == VRF_RF_L2_EVPN: vrf_table = VrfEvpnTable + elif route_family == VRF_RF_IPV4_FLOWSPEC: + vrf_table = Vrf4FlowSpecTable else: raise ValueError('Unsupported route family for VRF: %s' % route_family) @@ -457,6 +506,8 @@ class TableCoreManager(object): route_family = RF_IPv6_UC elif vpn_path.route_family == RF_L2_EVPN: route_family = RF_L2_EVPN + elif vpn_path.route_family == RF_VPNv4_FLOWSPEC: + route_family = RF_IPv4_FLOWSPEC else: raise ValueError('Unsupported route family for VRF: %s' % vpn_path.route_family) @@ -580,6 +631,49 @@ class TableCoreManager(object): vni=vni, tunnel_type=tunnel_type, pmsi_tunnel_type=pmsi_tunnel_type) + def update_flowspec_vrf_table(self, flowspec_family, route_dist, rules, + actions=None, is_withdraw=False): + """Update a BGP route in the VRF table for Flow Specification. + + ``flowspec_family`` specifies one of the flowspec family name. + + ``route_dist`` specifies a route distinguisher value. + + ``rules`` specifies NLRIs of Flow Specification as + a dictionary type value. + + `` actions`` specifies Traffic Filtering Actions of + Flow Specification as a dictionary type value. + + If `is_withdraw` is False, which is the default, add a BGP route + to the VRF table identified by `route_dist`. + If `is_withdraw` is True, remove a BGP route from the VRF table. + """ + from ryu.services.protocols.bgp.core import BgpCoreError + from ryu.services.protocols.bgp.api.prefix import FLOWSPEC_FAMILY_VPNV4 + + if flowspec_family == FLOWSPEC_FAMILY_VPNV4: + vrf_table = self._tables.get((route_dist, VRF_RF_IPV4_FLOWSPEC)) + prefix = FlowSpecIPv4NLRI.from_user(**rules) + try: + communities = create_v4flowspec_actions(actions) + except ValueError as e: + raise BgpCoreError(desc=str(e)) + else: + raise BgpCoreError( + desc='Unsupported flowspec_family %s' % flowspec_family) + + if vrf_table is None: + raise BgpCoreError( + desc='VRF table does not exist: route_dist=%s, ' + 'flowspec_family=%s' % (route_dist, flowspec_family)) + + # We do not check if we have a path to given prefix, we issue + # withdrawal. Hence multiple withdrawals have not side effect. + vrf_table.insert_vrffs_path( + nlri=prefix, communities=communities, + is_withdraw=is_withdraw) + def update_global_table(self, prefix, next_hop=None, is_withdraw=False): """Update a BGP route in the Global table for the given `prefix` with the given `next_hop`. @@ -616,7 +710,62 @@ class TableCoreManager(object): pattrs=pathattrs, nexthop=next_hop, is_withdraw=is_withdraw) - # add to global ipv4 table and propagates to neighbors + # add to global table and propagates to neighbors + self.learn_path(new_path) + + def update_flowspec_global_table(self, flowspec_family, rules, + actions=None, is_withdraw=False): + """Update a BGP route in the Global table for Flow Specification. + + ``flowspec_family`` specifies one of the Flow Specification + family name. + + ``rules`` specifies NLRIs of Flow Specification as + a dictionary type value. + + `` actions`` specifies Traffic Filtering Actions of + Flow Specification as a dictionary type value. + + If `is_withdraw` is False, which is the default, add a BGP route + to the Global table. + If `is_withdraw` is True, remove a BGP route from the Global table. + """ + + from ryu.services.protocols.bgp.core import BgpCoreError + from ryu.services.protocols.bgp.api.prefix import FLOWSPEC_FAMILY_IPV4 + + src_ver_num = 1 + peer = None + + # set mandatory path attributes + origin = BGPPathAttributeOrigin(BGP_ATTR_ORIGIN_IGP) + aspath = BGPPathAttributeAsPath([[]]) + + pathattrs = OrderedDict() + pathattrs[BGP_ATTR_TYPE_ORIGIN] = origin + pathattrs[BGP_ATTR_TYPE_AS_PATH] = aspath + + if flowspec_family == FLOWSPEC_FAMILY_IPV4: + _nlri = FlowSpecIPv4NLRI.from_user(**rules) + p = IPv4FlowSpecPath + + try: + communities = create_v4flowspec_actions(actions) + except ValueError as e: + raise BgpCoreError(desc=str(e)) + + if communities: + pathattrs[BGP_ATTR_TYPE_EXTENDED_COMMUNITIES] = ( + BGPPathAttributeExtendedCommunities( + communities=communities)) + else: + raise BgpCoreError( + desc='Unsupported flowspec family %s' % flowspec_family) + + new_path = p(peer, _nlri, src_ver_num, + pattrs=pathattrs, is_withdraw=is_withdraw) + + # add to global table and propagates to neighbors self.learn_path(new_path) def clean_stale_routes(self, peer, route_family=None): diff --git a/ryu/services/protocols/bgp/model.py b/ryu/services/protocols/bgp/model.py index c945754a..67391397 100644 --- a/ryu/services/protocols/bgp/model.py +++ b/ryu/services/protocols/bgp/model.py @@ -97,9 +97,11 @@ class FlexinetOutgoingRoute(object): from ryu.services.protocols.bgp.info_base.vrf4 import Vrf4Path from ryu.services.protocols.bgp.info_base.vrf6 import Vrf6Path from ryu.services.protocols.bgp.info_base.vrfevpn import VrfEvpnPath + from ryu.services.protocols.bgp.info_base.vrf4fs import Vrf4FlowSpecPath assert path.route_family in (Vrf4Path.ROUTE_FAMILY, Vrf6Path.ROUTE_FAMILY, - VrfEvpnPath.ROUTE_FAMILY) + VrfEvpnPath.ROUTE_FAMILY, + Vrf4FlowSpecPath.ROUTE_FAMILY) self.sink = None self._path = path diff --git a/ryu/services/protocols/bgp/net_ctrl.py b/ryu/services/protocols/bgp/net_ctrl.py index 972d0b98..7944ac20 100644 --- a/ryu/services/protocols/bgp/net_ctrl.py +++ b/ryu/services/protocols/bgp/net_ctrl.py @@ -25,6 +25,8 @@ import traceback import msgpack +from ryu.lib.packet import safi as subaddr_family + from ryu.services.protocols.bgp import api from ryu.services.protocols.bgp.api.base import ApiException from ryu.services.protocols.bgp.api.base import NEXT_HOP @@ -268,8 +270,11 @@ def _create_prefix_notification(outgoing_msg, rpc_session): params = [{ROUTE_DISTINGUISHER: outgoing_msg.route_dist, PREFIX: vpn_nlri.prefix, NEXT_HOP: path.nexthop, - VPN_LABEL: path.label_list[0], VRF_RF: VrfConf.rf_2_vrf_rf(path.route_family)}] + if path.nlri.ROUTE_FAMILY.safi not in (subaddr_family.IP_FLOWSPEC, + subaddr_family.VPN_FLOWSPEC): + params[VPN_LABEL] = path.label_list[0] + if not path.is_withdraw: # Create notification to NetworkController. rpc_msg = rpc_session.create_notification( diff --git a/ryu/services/protocols/bgp/peer.py b/ryu/services/protocols/bgp/peer.py index 37027395..dbd04545 100644 --- a/ryu/services/protocols/bgp/peer.py +++ b/ryu/services/protocols/bgp/peer.py @@ -54,6 +54,8 @@ from ryu.lib.packet.bgp import RF_IPv4_UC from ryu.lib.packet.bgp import RF_IPv6_UC from ryu.lib.packet.bgp import RF_IPv4_VPN from ryu.lib.packet.bgp import RF_IPv6_VPN +from ryu.lib.packet.bgp import RF_IPv4_FLOWSPEC +from ryu.lib.packet.bgp import RF_VPNv4_FLOWSPEC from ryu.lib.packet.bgp import RF_RTC_UC from ryu.lib.packet.bgp import get_rf @@ -100,6 +102,8 @@ 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 +from ryu.lib.packet import safi as subaddr_family + LOG = logging.getLogger('bgpspeaker.peer') @@ -1030,9 +1034,11 @@ class Peer(Source, Sink, NeighborConfListener, Activity): unknown_opttrans_attrs = None nlri_list = [path.nlri] - # By default, we use BGPSpeaker's interface IP with this peer - # as next_hop. - if self.is_ebgp_peer(): + if path.route_family.safi in (subaddr_family.IP_FLOWSPEC, + subaddr_family.VPN_FLOWSPEC): + # Flow Specification does not have next_hop. + next_hop = [] + elif self.is_ebgp_peer(): next_hop = self._session_next_hop(path) if path.is_local() and path.has_nexthop(): next_hop = path.nexthop @@ -1518,9 +1524,14 @@ class Peer(Source, Sink, NeighborConfListener, Activity): raise bgp.MissingWellKnown(BGP_ATTR_TYPE_ORIGIN) # Validate Next hop. - # TODO(PH): Currently ignore other cases. - if (not mp_reach_attr.next_hop or - (mp_reach_attr.next_hop == self.host_bind_ip)): + if mp_reach_attr.route_family.safi in ( + subaddr_family.IP_FLOWSPEC, + subaddr_family.VPN_FLOWSPEC): + # Because the Flow Specification does not have nexthop, + # skips check. + pass + elif (not mp_reach_attr.next_hop or + mp_reach_attr.next_hop == self.host_bind_ip): LOG.error('Nexthop of received UPDATE msg. (%s) same as local' ' interface address %s.', mp_reach_attr.next_hop, diff --git a/ryu/services/protocols/bgp/rtconf/base.py b/ryu/services/protocols/bgp/rtconf/base.py index fdccbd62..bdd43091 100644 --- a/ryu/services/protocols/bgp/rtconf/base.py +++ b/ryu/services/protocols/bgp/rtconf/base.py @@ -46,6 +46,8 @@ CAP_MBGP_IPV6 = 'cap_mbgp_ipv6' CAP_MBGP_VPNV4 = 'cap_mbgp_vpnv4' CAP_MBGP_VPNV6 = 'cap_mbgp_vpnv6' CAP_MBGP_EVPN = 'cap_mbgp_evpn' +CAP_MBGP_IPV4FS = 'cap_mbgp_ipv4fs' +CAP_MBGP_VPNV4FS = 'cap_mbgp_vpnv4fs' CAP_RTC = 'cap_rtc' RTC_AS = 'rtc_as' HOLD_TIME = 'hold_time' @@ -648,6 +650,24 @@ def validate_cap_mbgp_evpn(cmevpn): return cmevpn +@validate(name=CAP_MBGP_IPV4FS) +def validate_cap_mbgp_ipv4fs(cmv4fs): + if not isinstance(cmv4fs, bool): + raise ConfigTypeError(desc='Invalid MP-BGP ' + 'IPv4 Flow Specification capability ' + 'settings: %s. Boolean value expected' % cmv4fs) + return cmv4fs + + +@validate(name=CAP_MBGP_VPNV4FS) +def validate_cap_mbgp_vpnv4fs(cmv4fs): + if not isinstance(cmv4fs, bool): + raise ConfigTypeError(desc='Invalid MP-BGP ' + 'VPNv4 Flow Specification capability ' + 'settings: %s. Boolean value expected' % cmv4fs) + return cmv4fs + + @validate(name=CAP_RTC) def validate_cap_rtc(cap_rtc): if not isinstance(cap_rtc, bool): diff --git a/ryu/services/protocols/bgp/rtconf/neighbors.py b/ryu/services/protocols/bgp/rtconf/neighbors.py index 2276c242..987d4da0 100644 --- a/ryu/services/protocols/bgp/rtconf/neighbors.py +++ b/ryu/services/protocols/bgp/rtconf/neighbors.py @@ -27,6 +27,8 @@ from ryu.lib.packet.bgp import RF_IPv6_UC from ryu.lib.packet.bgp import RF_IPv4_VPN from ryu.lib.packet.bgp import RF_IPv6_VPN from ryu.lib.packet.bgp import RF_L2_EVPN +from ryu.lib.packet.bgp import RF_IPv4_FLOWSPEC +from ryu.lib.packet.bgp import RF_VPNv4_FLOWSPEC from ryu.lib.packet.bgp import RF_RTC_UC from ryu.lib.packet.bgp import BGPOptParamCapabilityFourOctetAsNumber from ryu.lib.packet.bgp import BGPOptParamCapabilityEnhancedRouteRefresh @@ -48,6 +50,8 @@ from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_IPV6 from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_VPNV4 from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_VPNV6 from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_EVPN +from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_IPV4FS +from ryu.services.protocols.bgp.rtconf.base import CAP_MBGP_VPNV4FS from ryu.services.protocols.bgp.rtconf.base import CAP_REFRESH from ryu.services.protocols.bgp.rtconf.base import CAP_RTC from ryu.services.protocols.bgp.rtconf.base import compute_optional_conf @@ -105,6 +109,8 @@ DEFAULT_CAP_MBGP_IPV6 = False DEFAULT_CAP_MBGP_VPNV4 = False DEFAULT_CAP_MBGP_VPNV6 = False DEFAULT_CAP_MBGP_EVPN = False +DEFAULT_CAP_MBGP_IPV4FS = False +DEFAULT_CAP_MBGP_VPNV4FS = False DEFAULT_HOLD_TIME = 40 DEFAULT_ENABLED = True DEFAULT_CAP_RTC = False @@ -317,7 +323,9 @@ class NeighborConf(ConfWithId, ConfWithStats): CAP_FOUR_OCTET_AS_NUMBER, CAP_MBGP_IPV4, CAP_MBGP_IPV6, CAP_MBGP_VPNV4, CAP_MBGP_VPNV6, - CAP_RTC, CAP_MBGP_EVPN, RTC_AS, HOLD_TIME, + CAP_RTC, CAP_MBGP_EVPN, + CAP_MBGP_IPV4FS, CAP_MBGP_VPNV4FS, + RTC_AS, HOLD_TIME, ENABLED, MULTI_EXIT_DISC, MAX_PREFIXES, ADVERTISE_PEER_AS, SITE_OF_ORIGINS, LOCAL_ADDRESS, LOCAL_PORT, LOCAL_AS, @@ -349,6 +357,10 @@ class NeighborConf(ConfWithId, ConfWithStats): CAP_MBGP_EVPN, DEFAULT_CAP_MBGP_EVPN, **kwargs) self._settings[CAP_MBGP_VPNV6] = compute_optional_conf( CAP_MBGP_VPNV6, DEFAULT_CAP_MBGP_VPNV6, **kwargs) + self._settings[CAP_MBGP_IPV4FS] = compute_optional_conf( + CAP_MBGP_IPV4FS, DEFAULT_CAP_MBGP_IPV4FS, **kwargs) + self._settings[CAP_MBGP_VPNV4FS] = compute_optional_conf( + CAP_MBGP_VPNV4FS, DEFAULT_CAP_MBGP_VPNV4FS, **kwargs) self._settings[HOLD_TIME] = compute_optional_conf( HOLD_TIME, DEFAULT_HOLD_TIME, **kwargs) self._settings[ENABLED] = compute_optional_conf( @@ -519,6 +531,14 @@ class NeighborConf(ConfWithId, ConfWithStats): return self._settings[CAP_MBGP_EVPN] @property + def cap_mbgp_ipv4fs(self): + return self._settings[CAP_MBGP_IPV4FS] + + @property + def cap_mbgp_vpnv4fs(self): + return self._settings[CAP_MBGP_VPNV4FS] + + @property def cap_rtc(self): return self._settings[CAP_RTC] @@ -642,6 +662,16 @@ class NeighborConf(ConfWithId, ConfWithStats): BGPOptParamCapabilityMultiprotocol( RF_L2_EVPN.afi, RF_L2_EVPN.safi)) + if self.cap_mbgp_ipv4fs: + mbgp_caps.append( + BGPOptParamCapabilityMultiprotocol( + RF_IPv4_FLOWSPEC.afi, RF_IPv4_FLOWSPEC.safi)) + + if self.cap_mbgp_vpnv4fs: + mbgp_caps.append( + BGPOptParamCapabilityMultiprotocol( + RF_VPNv4_FLOWSPEC.afi, RF_VPNv4_FLOWSPEC.safi)) + if mbgp_caps: capabilities[BGP_CAP_MULTIPROTOCOL] = mbgp_caps diff --git a/ryu/services/protocols/bgp/rtconf/vrfs.py b/ryu/services/protocols/bgp/rtconf/vrfs.py index 78d20f22..9106a0de 100644 --- a/ryu/services/protocols/bgp/rtconf/vrfs.py +++ b/ryu/services/protocols/bgp/rtconf/vrfs.py @@ -23,6 +23,7 @@ import logging from ryu.lib.packet.bgp import RF_IPv4_UC from ryu.lib.packet.bgp import RF_IPv6_UC from ryu.lib.packet.bgp import RF_L2_EVPN +from ryu.lib.packet.bgp import RF_IPv4_FLOWSPEC from ryu.services.protocols.bgp.utils import validation from ryu.services.protocols.bgp.base import get_validator @@ -59,7 +60,13 @@ IMPORT_MAPS = 'import_maps' VRF_RF_IPV4 = 'ipv4' VRF_RF_IPV6 = 'ipv6' VRF_RF_L2_EVPN = 'evpn' -SUPPORTED_VRF_RF = (VRF_RF_IPV4, VRF_RF_IPV6, VRF_RF_L2_EVPN) +VRF_RF_IPV4_FLOWSPEC = 'ipv4fs' +SUPPORTED_VRF_RF = ( + VRF_RF_IPV4, + VRF_RF_IPV6, + VRF_RF_L2_EVPN, + VRF_RF_IPV4_FLOWSPEC, +) # Default configuration values. @@ -226,6 +233,8 @@ class VrfConf(ConfWithId, ConfWithStats): return RF_IPv6_UC elif vrf_rf == VRF_RF_L2_EVPN: return RF_L2_EVPN + elif vrf_rf == VRF_RF_IPV4_FLOWSPEC: + return RF_IPv4_FLOWSPEC else: raise ValueError('Unsupported VRF route family given %s' % vrf_rf) @@ -237,6 +246,8 @@ class VrfConf(ConfWithId, ConfWithStats): return VRF_RF_IPV6 elif route_family == RF_L2_EVPN: return VRF_RF_L2_EVPN + elif route_family == RF_IPv4_FLOWSPEC: + return VRF_RF_IPV4_FLOWSPEC else: raise ValueError('No supported mapping for route family ' 'to vrf_route_family exists for %s' % diff --git a/ryu/services/protocols/bgp/utils/bgp.py b/ryu/services/protocols/bgp/utils/bgp.py index 4bdedf00..5aa2012d 100644 --- a/ryu/services/protocols/bgp/utils/bgp.py +++ b/ryu/services/protocols/bgp/utils/bgp.py @@ -28,6 +28,8 @@ from ryu.lib.packet.bgp import ( RF_IPv4_VPN, RF_IPv6_VPN, RF_L2_EVPN, + RF_IPv4_FLOWSPEC, + RF_VPNv4_FLOWSPEC, RF_RTC_UC, RouteTargetMembershipNLRI, BGP_ATTR_TYPE_MULTI_EXIT_DISC, @@ -41,6 +43,10 @@ from ryu.lib.packet.bgp import ( BGPTwoOctetAsSpecificExtendedCommunity, BGPIPv4AddressSpecificExtendedCommunity, BGPFourOctetAsSpecificExtendedCommunity, + BGPFlowSpecTrafficRateCommunity, + BGPFlowSpecTrafficActionCommunity, + BGPFlowSpecRedirectCommunity, + BGPFlowSpecTrafficMarkingCommunity, ) from ryu.services.protocols.bgp.info_base.rtc import RtcPath from ryu.services.protocols.bgp.info_base.ipv4 import Ipv4Path @@ -48,6 +54,8 @@ from ryu.services.protocols.bgp.info_base.ipv6 import Ipv6Path from ryu.services.protocols.bgp.info_base.vpnv4 import Vpnv4Path from ryu.services.protocols.bgp.info_base.vpnv6 import Vpnv6Path from ryu.services.protocols.bgp.info_base.evpn import EvpnPath +from ryu.services.protocols.bgp.info_base.ipv4fs import IPv4FlowSpecPath +from ryu.services.protocols.bgp.info_base.vpnv4fs import VPNv4FlowSpecPath LOG = logging.getLogger('utils.bgp') @@ -58,6 +66,8 @@ _ROUTE_FAMILY_TO_PATH_MAP = {RF_IPv4_UC: Ipv4Path, RF_IPv4_VPN: Vpnv4Path, RF_IPv6_VPN: Vpnv6Path, RF_L2_EVPN: EvpnPath, + RF_IPv4_FLOWSPEC: IPv4FlowSpecPath, + RF_VPNv4_FLOWSPEC: VPNv4FlowSpecPath, RF_RTC_UC: RtcPath} @@ -178,3 +188,44 @@ def create_rt_extended_community(value, subtype=2): 'Invalid Route Target or Route Origin value: %s' % value) return ext_com + + +def create_v4flowspec_actions(actions=None): + """ + Create list of traffic filtering actions + for Ipv4 Flow Specification and VPNv4 Flow Specification. + + `` actions`` specifies Traffic Filtering Actions of + Flow Specification as a dictionary type value. + + Returns a list of extended community values. + """ + from ryu.services.protocols.bgp.api.prefix import ( + FLOWSPEC_ACTION_TRAFFIC_RATE, + FLOWSPEC_ACTION_TRAFFIC_ACTION, + FLOWSPEC_ACTION_REDIRECT, + FLOWSPEC_ACTION_TRAFFIC_MARKING, + ) + + # Supported action type for IPv4 and VPNv4. + action_types = { + FLOWSPEC_ACTION_TRAFFIC_RATE: BGPFlowSpecTrafficRateCommunity, + FLOWSPEC_ACTION_TRAFFIC_ACTION: BGPFlowSpecTrafficActionCommunity, + FLOWSPEC_ACTION_REDIRECT: BGPFlowSpecRedirectCommunity, + FLOWSPEC_ACTION_TRAFFIC_MARKING: BGPFlowSpecTrafficMarkingCommunity, + } + + communities = [] + + if actions is None: + return communities + + for name, action in actions.items(): + cls_ = action_types.get(name, None) + if cls_: + communities.append(cls_(**action)) + else: + raise ValueError( + 'Unsupported flowspec action %s' % name) + + return communities |