diff --git a/ryu/services/protocols/bgp/api/base.py b/ryu/services/protocols/bgp/api/base.py index 6c4d0fa1..9cd8328b 100644 --- a/ryu/services/protocols/bgp/api/base.py +++ b/ryu/services/protocols/bgp/api/base.py @@ -54,6 +54,8 @@ MPLS_LABELS = 'mpls_labels' TUNNEL_TYPE = 'tunnel_type' EVPN_VNI = 'vni' PMSI_TUNNEL_TYPE = 'pmsi_tunnel_type' +TUNNEL_ENDPOINT_IP = 'tunnel_endpoint_ip' +MAC_MOBILITY = 'mac_mobility' FLOWSPEC_FAMILY = 'flowspec_family' FLOWSPEC_RULES = 'rules' FLOWSPEC_ACTIONS = 'actions' diff --git a/ryu/services/protocols/bgp/api/prefix.py b/ryu/services/protocols/bgp/api/prefix.py index c3e42622..8827c1bc 100644 --- a/ryu/services/protocols/bgp/api/prefix.py +++ b/ryu/services/protocols/bgp/api/prefix.py @@ -55,6 +55,8 @@ 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 MAC_MOBILITY +from ryu.services.protocols.bgp.api.base import TUNNEL_ENDPOINT_IP 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 @@ -360,7 +362,8 @@ def delete_local(route_dist, prefix, route_family=VRF_RF_IPV4): 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]) + PMSI_TUNNEL_TYPE, TUNNEL_ENDPOINT_IP, + MAC_MOBILITY]) def add_evpn_local(route_type, route_dist, next_hop, **kwargs): """Adds EVPN route from VRF identified by *route_dist*. """ diff --git a/ryu/services/protocols/bgp/bgpspeaker.py b/ryu/services/protocols/bgp/bgpspeaker.py index 2f53f636..5e4382a7 100644 --- a/ryu/services/protocols/bgp/bgpspeaker.py +++ b/ryu/services/protocols/bgp/bgpspeaker.py @@ -43,6 +43,8 @@ from ryu.services.protocols.bgp.api.base import ROUTE_FAMILY 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 MAC_MOBILITY +from ryu.services.protocols.bgp.api.base import TUNNEL_ENDPOINT_IP from ryu.services.protocols.bgp.api.prefix import EVPN_MAX_ET from ryu.services.protocols.bgp.api.prefix import ESI_TYPE_LACP from ryu.services.protocols.bgp.api.prefix import ESI_TYPE_L2_BRIDGE @@ -689,7 +691,7 @@ class BGPSpeaker(object): ethernet_tag_id=None, mac_addr=None, ip_addr=None, ip_prefix=None, gw_ip_addr=None, vni=None, next_hop=None, tunnel_type=None, pmsi_tunnel_type=None, - redundancy_mode=None): + redundancy_mode=None, tunnel_endpoint_ip=None, mac_mobility=None): """ This method adds a new EVPN route to be advertised. ``route_type`` specifies one of the EVPN route type name. @@ -755,6 +757,15 @@ class BGPSpeaker(object): - REDUNDANCY_MODE_ALL_ACTIVE = 'all_active' - REDUNDANCY_MODE_SINGLE_ACTIVE = 'single_active' + + ``tunnel_endpoint_ip`` specifies a VTEP IP address other than the + local router ID. This attribute is advertised only if route_type is + EVPN_MULTICAST_ETAG_ROUTE, and defaults to the local router ID. + + ``mac_mobility`` specifies an optional integer sequence number to use + in a MAC Mobility extended community field. The special value '-1' can + be used to set the STATIC flag with a 0-value sequence number. + """ func_name = 'evpn_prefix.add_local' @@ -799,6 +810,10 @@ class BGPSpeaker(object): # Set tunnel type specific arguments if tunnel_type in [TUNNEL_TYPE_VXLAN, TUNNEL_TYPE_NVGRE]: kwargs[EVPN_VNI] = vni + + # Pass on mac_mobility, must be integer value + if mac_mobility is not None: + kwargs[MAC_MOBILITY] = int(mac_mobility) elif route_type == EVPN_MULTICAST_ETAG_ROUTE: kwargs.update({ EVPN_ETHERNET_TAG_ID: ethernet_tag_id, @@ -815,6 +830,9 @@ class BGPSpeaker(object): elif pmsi_tunnel_type is not None: raise ValueError('Unsupported PMSI tunnel type: %s' % pmsi_tunnel_type) + # Set non-default tunnel endpoint IP, if provided + if tunnel_endpoint_ip is not None: + kwargs[TUNNEL_ENDPOINT_IP] = tunnel_endpoint_ip elif route_type == EVPN_ETH_SEGMENT: kwargs.update({ EVPN_ESI: esi, @@ -830,6 +848,10 @@ class BGPSpeaker(object): # Set tunnel type specific arguments if tunnel_type in [TUNNEL_TYPE_VXLAN, TUNNEL_TYPE_NVGRE]: kwargs[EVPN_VNI] = vni + + # Add mac_mobility + if mac_mobility is not None: + kwargs[MAC_MOBILITY] = int(mac_mobility) else: raise ValueError('Unsupported EVPN route type: %s' % route_type) diff --git a/ryu/services/protocols/bgp/core_managers/table_manager.py b/ryu/services/protocols/bgp/core_managers/table_manager.py index 432a1a46..43b30f52 100644 --- a/ryu/services/protocols/bgp/core_managers/table_manager.py +++ b/ryu/services/protocols/bgp/core_managers/table_manager.py @@ -617,7 +617,8 @@ class TableCoreManager(object): def update_vrf_table(self, route_dist, prefix=None, next_hop=None, route_family=None, route_type=None, tunnel_type=None, is_withdraw=False, redundancy_mode=None, - pmsi_tunnel_type=None, **kwargs): + pmsi_tunnel_type=None, tunnel_endpoint_ip=None, + mac_mobility=None, **kwargs): """Update a BGP route in the VRF table identified by `route_dist` with the given `next_hop`. @@ -637,6 +638,13 @@ class TableCoreManager(object): This field is advertised only if route_type is EVPN_MULTICAST_ETAG_ROUTE. + `tunnel_endpoint_ip` specifies a tunnel endpoint IP other than the + default local router ID; only used in EVPN_MULTICAST_ETAG_ROUTE + + `mac_mobility` specifies an optional integer sequence number to insert + as a MAC Mobility extended community; special value `-1` is used for + static MACs (MAC Mobility sequence 0 with STATIC flag set) + Returns assigned VPN label. """ from ryu.services.protocols.bgp.core import BgpCoreError @@ -708,6 +716,7 @@ class TableCoreManager(object): return vrf_table.insert_vrf_path( nlri=prefix, next_hop=next_hop, gen_lbl=gen_lbl, is_withdraw=is_withdraw, redundancy_mode=redundancy_mode, + mac_mobility=mac_mobility, vni=vni, tunnel_type=tunnel_type, pmsi_tunnel_type=pmsi_tunnel_type) diff --git a/ryu/services/protocols/bgp/info_base/vrf.py b/ryu/services/protocols/bgp/info_base/vrf.py index 8b06c6a1..ec43fe58 100644 --- a/ryu/services/protocols/bgp/info_base/vrf.py +++ b/ryu/services/protocols/bgp/info_base/vrf.py @@ -306,6 +306,17 @@ class VrfTable(Table): flags=flags, mpls_label=label_list[0])) + # Add MAC Move functionality + # see https://datatracker.ietf.org/doc/html/rfc7432#section-7.7 + mac_mobility_seq = kwargs.get('mac_mobility', None) + if mac_mobility_seq is not None: + from ryu.lib.packet.bgp import BGPEvpnMacMobilityExtendedCommunity + is_static = (mac_mobility_seq == -1) # Magic value for static MACs + communities.append(BGPEvpnMacMobilityExtendedCommunity( + subtype=0, + flags=1 if is_static else 0, # 0x1 = sticky (static) MAC + sequence_number=mac_mobility_seq if not is_static else 0)) + pattrs[BGP_ATTR_TYPE_EXTENDED_COMMUNITIES] = \ BGPPathAttributeExtendedCommunities(communities=communities) if vrf_conf.multi_exit_disc: @@ -318,8 +329,9 @@ class VrfTable(Table): from ryu.services.protocols.bgp.api.prefix import ( PMSI_TYPE_INGRESS_REP) if pmsi_tunnel_type == PMSI_TYPE_INGRESS_REP: - tunnel_id = PmsiTunnelIdIngressReplication( - tunnel_endpoint_ip=self._core_service.router_id) + # Support other VTEP IP than local router_id + vtep = kwargs.get('tunnel_endpoint_ip', self._core_service.router_id) + tunnel_id = PmsiTunnelIdIngressReplication(tunnel_endpoint_ip=vtep) else: # pmsi_tunnel_type == PMSI_TYPE_NO_TUNNEL_INFO tunnel_id = None pattrs[BGP_ATTR_TYEP_PMSI_TUNNEL_ATTRIBUTE] = \