diff options
| author | Russ White <russ@riw.us> | 2024-09-05 11:40:48 -0400 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-09-05 11:40:48 -0400 | 
| commit | 5d9ddcce7c0560366df52dd26d2f591d6c2e0d9e (patch) | |
| tree | b104291f59178cfd65ef2e5e99e1c9d32c587970 /bgpd/bgp_zebra.c | |
| parent | 7fef8d39b55cc205ee9a9f0c31dfa3d03c8532a0 (diff) | |
| parent | f6866093860b1069cac94d67618a11f589a61c89 (diff) | |
Merge pull request #15676 from cscarpitta/bgp-srv6-sid-manager
bgpd: Extend BGP to communicate with the SRv6 SID Manager to allocate/release SRv6 SIDs
Diffstat (limited to 'bgpd/bgp_zebra.c')
| -rw-r--r-- | bgpd/bgp_zebra.c | 394 | 
1 files changed, 377 insertions, 17 deletions
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 718dc2de06..19f26e9c75 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -3379,11 +3379,278 @@ static int bgp_zebra_process_srv6_locator_chunk(ZAPI_CALLBACK_ARGS)  	return 0;  } +/** + * Internal function to process an SRv6 locator + * + * @param locator The locator to be processed + */ +static int bgp_zebra_process_srv6_locator_internal(struct srv6_locator *locator) +{ +	struct bgp *bgp = bgp_get_default(); + +	if (!bgp || !bgp->srv6_enabled || !locator) +		return -1; + +	/* +	 * Check if the main BGP instance is configured to use the received +	 * locator +	 */ +	if (strcmp(bgp->srv6_locator_name, locator->name) != 0) { +		zlog_err("%s: SRv6 Locator name unmatch %s:%s", __func__, +			 bgp->srv6_locator_name, locator->name); +		return 0; +	} + +	zlog_info("%s: Received SRv6 locator %s %pFX, loc-block-len=%u, loc-node-len=%u func-len=%u, arg-len=%u", +		  __func__, locator->name, &locator->prefix, +		  locator->block_bits_length, locator->node_bits_length, +		  locator->function_bits_length, locator->argument_bits_length); + +	/* Store the locator in the main BGP instance */ +	bgp->srv6_locator = srv6_locator_alloc(locator->name); +	srv6_locator_copy(bgp->srv6_locator, locator); + +	/* +	 * Process VPN-to-VRF and VRF-to-VPN leaks to advertise new locator +	 * and SIDs. +	 */ +	vpn_leak_postchange_all(); + +	return 0; +} + +static int bgp_zebra_srv6_sid_notify(ZAPI_CALLBACK_ARGS) +{ +	struct bgp *bgp = bgp_get_default(); +	struct srv6_locator *locator; +	struct srv6_sid_ctx ctx; +	struct in6_addr sid_addr; +	enum zapi_srv6_sid_notify note; +	struct bgp *bgp_vrf; +	struct vrf *vrf; +	struct listnode *node, *nnode; +	char buf[256]; +	struct in6_addr *tovpn_sid; +	struct prefix_ipv6 tmp_prefix; +	uint32_t sid_func; +	bool found = false; + +	if (!bgp || !bgp->srv6_enabled) +		return -1; + +	if (!bgp->srv6_locator) { +		if (BGP_DEBUG(zebra, ZEBRA)) +			zlog_debug("%s: ignoring SRv6 SID notify: locator not set", +				   __func__); +		return -1; +	} + +	/* Decode the received notification message */ +	if (!zapi_srv6_sid_notify_decode(zclient->ibuf, &ctx, &sid_addr, +					 &sid_func, NULL, ¬e, NULL)) { +		zlog_err("%s : error in msg decode", __func__); +		return -1; +	} + +	if (BGP_DEBUG(zebra, ZEBRA)) +		zlog_debug("%s: received SRv6 SID notify: ctx %s sid_value %pI6 %s", +			   __func__, srv6_sid_ctx2str(buf, sizeof(buf), &ctx), +			   &sid_addr, zapi_srv6_sid_notify2str(note)); + +	/* Get the BGP instance for which the SID has been requested, if any */ +	for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp_vrf)) { +		vrf = vrf_lookup_by_id(bgp_vrf->vrf_id); +		if (!vrf) +			continue; + +		if (vrf->vrf_id == ctx.vrf_id) { +			found = true; +			break; +		} +	} + +	if (!found) { +		if (BGP_DEBUG(zebra, ZEBRA)) +			zlog_debug("%s: ignoring SRv6 SID notify: No VRF suitable for received SID ctx %s sid_value %pI6", +				   __func__, +				   srv6_sid_ctx2str(buf, sizeof(buf), &ctx), +				   &sid_addr); +		return -1; +	} + +	/* Handle notification */ +	switch (note) { +	case ZAPI_SRV6_SID_ALLOCATED: +		if (BGP_DEBUG(zebra, ZEBRA)) +			zlog_debug("SRv6 SID %pI6 %s : ALLOCATED", &sid_addr, +				   srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); + +		/* Verify that the received SID belongs to the configured locator */ +		tmp_prefix.family = AF_INET6; +		tmp_prefix.prefixlen = IPV6_MAX_BITLEN; +		tmp_prefix.prefix = sid_addr; + +		if (!prefix_match((struct prefix *)&bgp->srv6_locator->prefix, +				  (struct prefix *)&tmp_prefix)) +			return -1; + +		/* Get label */ +		uint8_t func_len = bgp->srv6_locator->function_bits_length; +		uint8_t shift_len = BGP_PREFIX_SID_SRV6_MAX_FUNCTION_LENGTH - +				    func_len; + +		int label = sid_func << shift_len; + +		/* Un-export VPN to VRF routes */ +		vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP, bgp, +				   bgp_vrf); +		vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP6, bgp, +				   bgp_vrf); + +		locator = srv6_locator_alloc(bgp->srv6_locator_name); +		srv6_locator_copy(locator, bgp->srv6_locator); + +		/* Store SID, locator, and label */ +		tovpn_sid = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr)); +		*tovpn_sid = sid_addr; +		if (ctx.behavior == ZEBRA_SEG6_LOCAL_ACTION_END_DT6) { +			XFREE(MTYPE_BGP_SRV6_SID, +			      bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid); +			srv6_locator_free( +				bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_locator); +			sid_unregister(bgp, +				       bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid); + +			bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid = tovpn_sid; +			bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_locator = locator; +			bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_transpose_label = +				label; +		} else if (ctx.behavior == ZEBRA_SEG6_LOCAL_ACTION_END_DT4) { +			XFREE(MTYPE_BGP_SRV6_SID, +			      bgp_vrf->vpn_policy[AFI_IP].tovpn_sid); +			srv6_locator_free( +				bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_locator); +			sid_unregister(bgp, +				       bgp_vrf->vpn_policy[AFI_IP].tovpn_sid); + +			bgp_vrf->vpn_policy[AFI_IP].tovpn_sid = tovpn_sid; +			bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_locator = locator; +			bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_transpose_label = +				label; +		} else if (ctx.behavior == ZEBRA_SEG6_LOCAL_ACTION_END_DT46) { +			XFREE(MTYPE_BGP_SRV6_SID, bgp_vrf->tovpn_sid); +			srv6_locator_free(bgp_vrf->tovpn_sid_locator); +			sid_unregister(bgp, bgp_vrf->tovpn_sid); + +			bgp_vrf->tovpn_sid = tovpn_sid; +			bgp_vrf->tovpn_sid_locator = locator; +			bgp_vrf->tovpn_sid_transpose_label = label; +		} else { +			srv6_locator_free(locator); +			if (BGP_DEBUG(zebra, ZEBRA)) +				zlog_debug("Unsupported behavior. Not assigned SRv6 SID: %s %pI6", +					   srv6_sid_ctx2str(buf, sizeof(buf), +							    &ctx), +					   &sid_addr); +			return -1; +		} + +		/* Register the new SID */ +		sid_register(bgp, tovpn_sid, bgp->srv6_locator_name); + +		/* Export VPN to VRF routes */ +		vpn_leak_postchange_all(); + +		break; +	case ZAPI_SRV6_SID_RELEASED: +		if (BGP_DEBUG(zebra, ZEBRA)) +			zlog_debug("SRv6 SID %pI6 %s: RELEASED", &sid_addr, +				   srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); + +		/* Un-export VPN to VRF routes */ +		vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP, bgp, +				   bgp_vrf); +		vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP6, bgp, +				   bgp_vrf); + +		/* Remove SID, locator, and label */ +		if (ctx.behavior == ZEBRA_SEG6_LOCAL_ACTION_END_DT6) { +			XFREE(MTYPE_BGP_SRV6_SID, +			      bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid); +			if (bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_locator) { +				srv6_locator_free(bgp->vpn_policy[AFI_IP6] +							  .tovpn_sid_locator); +				bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_locator = +					NULL; +			} +			bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_transpose_label = +				0; + +			/* Unregister the SID */ +			sid_unregister(bgp, +				       bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid); +		} else if (ctx.behavior == ZEBRA_SEG6_LOCAL_ACTION_END_DT4) { +			XFREE(MTYPE_BGP_SRV6_SID, +			      bgp_vrf->vpn_policy[AFI_IP].tovpn_sid); +			if (bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_locator) { +				srv6_locator_free(bgp->vpn_policy[AFI_IP] +							  .tovpn_sid_locator); +				bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_locator = +					NULL; +			} +			bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_transpose_label = +				0; + +			/* Unregister the SID */ +			sid_unregister(bgp, +				       bgp_vrf->vpn_policy[AFI_IP].tovpn_sid); +		} else if (ctx.behavior == ZEBRA_SEG6_LOCAL_ACTION_END_DT46) { +			XFREE(MTYPE_BGP_SRV6_SID, bgp_vrf->tovpn_sid); +			if (bgp_vrf->tovpn_sid_locator) { +				srv6_locator_free(bgp_vrf->tovpn_sid_locator); +				bgp_vrf->tovpn_sid_locator = NULL; +			} +			bgp_vrf->tovpn_sid_transpose_label = 0; + +			/* Unregister the SID */ +			sid_unregister(bgp, bgp_vrf->tovpn_sid); +		} else { +			if (BGP_DEBUG(zebra, ZEBRA)) +				zlog_debug("Unsupported behavior. Not assigned SRv6 SID: %s %pI6", +					   srv6_sid_ctx2str(buf, sizeof(buf), +							    &ctx), +					   &sid_addr); +			return -1; +		} + +		/* Export VPN to VRF routes*/ +		vpn_leak_postchange_all(); +		break; +	case ZAPI_SRV6_SID_FAIL_ALLOC: +		if (BGP_DEBUG(zebra, ZEBRA)) +			zlog_debug("SRv6 SID %pI6 %s: Failed to allocate", +				   &sid_addr, +				   srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); + +		/* Error will be logged by zebra module */ +		break; +	case ZAPI_SRV6_SID_FAIL_RELEASE: +		if (BGP_DEBUG(zebra, ZEBRA)) +			zlog_debug("%s: SRv6 SID %pI6 %s failure to release", +				   __func__, &sid_addr, +				   srv6_sid_ctx2str(buf, sizeof(buf), &ctx)); + +		/* Error will be logged by zebra module */ +		break; +	} + +	return 0; +} +  static int bgp_zebra_process_srv6_locator_add(ZAPI_CALLBACK_ARGS)  {  	struct srv6_locator loc = {};  	struct bgp *bgp = bgp_get_default(); -	const char *loc_name = bgp->srv6_locator_name;  	if (!bgp || !bgp->srv6_enabled)  		return 0; @@ -3391,10 +3658,7 @@ static int bgp_zebra_process_srv6_locator_add(ZAPI_CALLBACK_ARGS)  	if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0)  		return -1; -	if (bgp_zebra_srv6_manager_get_locator_chunk(loc_name) < 0) -		return -1; - -	return 0; +	return bgp_zebra_process_srv6_locator_internal(&loc);  }  static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) @@ -3402,7 +3666,8 @@ static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS)  	struct srv6_locator loc = {};  	struct bgp *bgp = bgp_get_default();  	struct listnode *node, *nnode; -	struct srv6_locator_chunk *chunk, *tovpn_sid_locator; +	struct srv6_locator_chunk *chunk; +	struct srv6_locator *tovpn_sid_locator;  	struct bgp_srv6_function *func;  	struct bgp *bgp_vrf;  	struct in6_addr *tovpn_sid; @@ -3414,6 +3679,12 @@ static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS)  	if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0)  		return -1; +	// clear SRv6 locator +	if (bgp->srv6_locator) { +		srv6_locator_free(bgp->srv6_locator); +		bgp->srv6_locator = NULL; +	} +  	// refresh chunks  	for (ALL_LIST_ELEMENTS(bgp->srv6_locator_chunks, node, nnode, chunk))  		if (prefix_match((struct prefix *)&loc.prefix, @@ -3490,10 +3761,12 @@ static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS)  			tmp_prefi.prefixlen = IPV6_MAX_BITLEN;  			tmp_prefi.prefix = tovpn_sid_locator->prefix.prefix;  			if (prefix_match((struct prefix *)&loc.prefix, -					 (struct prefix *)&tmp_prefi)) -				srv6_locator_chunk_free( -					&bgp_vrf->vpn_policy[AFI_IP] -						 .tovpn_sid_locator); +					 (struct prefix *)&tmp_prefi)) { +				srv6_locator_free(bgp_vrf->vpn_policy[AFI_IP] +							  .tovpn_sid_locator); +				bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_locator = +					NULL; +			}  		}  		/* refresh vpnv6 tovpn_sid_locator */ @@ -3504,10 +3777,12 @@ static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS)  			tmp_prefi.prefixlen = IPV6_MAX_BITLEN;  			tmp_prefi.prefix = tovpn_sid_locator->prefix.prefix;  			if (prefix_match((struct prefix *)&loc.prefix, -					 (struct prefix *)&tmp_prefi)) -				srv6_locator_chunk_free( -					&bgp_vrf->vpn_policy[AFI_IP6] -						 .tovpn_sid_locator); +					 (struct prefix *)&tmp_prefi)) { +				srv6_locator_free(bgp_vrf->vpn_policy[AFI_IP6] +							  .tovpn_sid_locator); +				bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_locator = +					NULL; +			}  		}  		/* refresh per-vrf tovpn_sid_locator */ @@ -3517,9 +3792,10 @@ static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS)  			tmp_prefi.prefixlen = IPV6_MAX_BITLEN;  			tmp_prefi.prefix = tovpn_sid_locator->prefix.prefix;  			if (prefix_match((struct prefix *)&loc.prefix, -					 (struct prefix *)&tmp_prefi)) -				srv6_locator_chunk_free( -					&bgp_vrf->tovpn_sid_locator); +					 (struct prefix *)&tmp_prefi)) { +				srv6_locator_free(bgp_vrf->tovpn_sid_locator); +				bgp_vrf->tovpn_sid_locator = NULL; +			}  		}  	} @@ -3556,6 +3832,7 @@ static zclient_handler *const bgp_handlers[] = {  	[ZEBRA_SRV6_LOCATOR_DELETE] = bgp_zebra_process_srv6_locator_delete,  	[ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK] =  		bgp_zebra_process_srv6_locator_chunk, +	[ZEBRA_SRV6_SID_NOTIFY] = bgp_zebra_srv6_sid_notify,  };  static int bgp_if_new_hook(struct interface *ifp) @@ -4093,6 +4370,89 @@ int bgp_zebra_srv6_manager_release_locator_chunk(const char *name)  	return srv6_manager_release_locator_chunk(zclient, name);  } +/** + * Ask the SRv6 Manager (zebra) about a specific locator + * + * @param name Locator name + * @return 0 on success, -1 otherwise + */ +int bgp_zebra_srv6_manager_get_locator(const char *name) +{ +	if (!name) +		return -1; + +	/* +	 * Send the Get Locator request to the SRv6 Manager and return the +	 * result +	 */ +	return srv6_manager_get_locator(zclient, name); +} + +/** + * Ask the SRv6 Manager (zebra) to allocate a SID. + * + * Optionally, it is possible to provide an IPv6 address (sid_value parameter). + * + * When sid_value is provided, the SRv6 Manager allocates the requested SID + * address, if the request can be satisfied (explicit allocation). + * + * When sid_value is not provided, the SRv6 Manager allocates any available SID + * from the provided locator (dynamic allocation). + * + * @param ctx Context to be associated with the request SID + * @param sid_value IPv6 address to be associated with the requested SID (optional) + * @param locator_name Name of the locator from which the SID must be allocated + * @param sid_func SID Function allocated by the SRv6 Manager. + */ +bool bgp_zebra_request_srv6_sid(const struct srv6_sid_ctx *ctx, +				struct in6_addr *sid_value, +				const char *locator_name, uint32_t *sid_func) +{ +	int ret; + +	if (!ctx || !locator_name) +		return false; + +	/* +	 * Send the Get SRv6 SID request to the SRv6 Manager and check the +	 * result +	 */ +	ret = srv6_manager_get_sid(zclient, ctx, sid_value, locator_name, +				   sid_func); +	if (ret < 0) { +		zlog_warn("%s: error getting SRv6 SID!", __func__); +		return false; +	} + +	return true; +} + +/** + * Ask the SRv6 Manager (zebra) to release a previously allocated SID. + * + * This function is used to tell the SRv6 Manager that BGP no longer intends + * to use the SID. + * + * @param ctx Context to be associated with the SID to be released + */ +void bgp_zebra_release_srv6_sid(const struct srv6_sid_ctx *ctx) +{ +	int ret; + +	if (!ctx) +		return; + +	/* +	 * Send the Release SRv6 SID request to the SRv6 Manager and check the +	 * result +	 */ +	ret = srv6_manager_release_sid(zclient, ctx); +	if (ret < 0) { +		zlog_warn("%s: error releasing SRv6 SID!", __func__); +		return; +	} +} +  void bgp_zebra_send_nexthop_label(int cmd, mpls_label_t label,  				  ifindex_t ifindex, vrf_id_t vrf_id,  				  enum lsp_types_t ltype, struct prefix *p,  | 
