diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index f1ffa7b840a3..33cc836dacae 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -360,7 +360,7 @@ void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi) * leaked to VPN. Zebra should install this srv6-function in the kernel with * an action of "End.DT4/6's IP FIB to route the PDU." */ -void vpn_leak_zebra_vrf_sid_update(struct bgp *bgp, afi_t afi) +void vpn_leak_zebra_vrf_sid_update_per_af(struct bgp *bgp, afi_t afi) { int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); enum seg6local_action_t act; @@ -406,11 +406,78 @@ void vpn_leak_zebra_vrf_sid_update(struct bgp *bgp, afi_t afi) bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent = tovpn_sid_ls; } +/* + * This function informs zebra of the srv6-function this vrf sets on routes + * leaked to VPN. Zebra should install this srv6-function in the kernel with + * an action of "End.DT46's IP FIB to route the PDU." + */ +void vpn_leak_zebra_vrf_sid_update_per_vrf(struct bgp *bgp) +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + enum seg6local_action_t act; + struct seg6local_context ctx = {}; + struct in6_addr *tovpn_sid = NULL; + struct in6_addr *tovpn_sid_ls = NULL; + struct vrf *vrf; + + if (bgp->vrf_id == VRF_UNKNOWN) { + if (debug) + zlog_debug( + "%s: vrf %s: vrf_id not set, can't set zebra vrf label", + __func__, bgp->name_pretty); + return; + } + + tovpn_sid = bgp->tovpn_sid; + if (!tovpn_sid) { + if (debug) + zlog_debug("%s: vrf %s: sid not set", __func__, + bgp->name_pretty); + return; + } + + if (debug) + zlog_debug("%s: vrf %s: setting sid %pI6 for vrf id %d", + __func__, bgp->name_pretty, tovpn_sid, bgp->vrf_id); + + vrf = vrf_lookup_by_id(bgp->vrf_id); + if (!vrf) + return; + + ctx.table = vrf->data.l.table_id; + act = ZEBRA_SEG6_LOCAL_ACTION_END_DT46; + zclient_send_localsid(zclient, tovpn_sid, bgp->vrf_id, act, &ctx); + + tovpn_sid_ls = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr)); + *tovpn_sid_ls = *tovpn_sid; + bgp->tovpn_zebra_vrf_sid_last_sent = tovpn_sid_ls; +} + +/* + * This function informs zebra of the srv6-function this vrf sets on routes + * leaked to VPN. Zebra should install this srv6-function in the kernel with + * an action of "End.DT4/6/46's IP FIB to route the PDU." + */ +void vpn_leak_zebra_vrf_sid_update(struct bgp *bgp, afi_t afi) +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + + if (bgp->vpn_policy[afi].tovpn_sid) + return vpn_leak_zebra_vrf_sid_update_per_af(bgp, afi); + + if (bgp->tovpn_sid) + return vpn_leak_zebra_vrf_sid_update_per_vrf(bgp); + + if (debug) + zlog_debug("%s: vrf %s: afi %s: sid not set", __func__, + bgp->name_pretty, afi2str(afi)); +} + /* * If zebra tells us vrf has become unconfigured, tell zebra not to * use this srv6-function to forward to the vrf anymore */ -void vpn_leak_zebra_vrf_sid_withdraw(struct bgp *bgp, afi_t afi) +void vpn_leak_zebra_vrf_sid_withdraw_per_af(struct bgp *bgp, afi_t afi) { int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); @@ -432,6 +499,45 @@ void vpn_leak_zebra_vrf_sid_withdraw(struct bgp *bgp, afi_t afi) bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent); } +/* + * If zebra tells us vrf has become unconfigured, tell zebra not to + * use this srv6-function to forward to the vrf anymore + */ +void vpn_leak_zebra_vrf_sid_withdraw_per_vrf(struct bgp *bgp) +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + + if (bgp->vrf_id == VRF_UNKNOWN) { + if (debug) + zlog_debug( + "%s: vrf %s: vrf_id not set, can't set zebra vrf label", + __func__, bgp->name_pretty); + return; + } + + if (debug) + zlog_debug("%s: deleting sid for vrf %s (id=%d)", __func__, + bgp->name_pretty, bgp->vrf_id); + + zclient_send_localsid(zclient, bgp->tovpn_zebra_vrf_sid_last_sent, + bgp->vrf_id, ZEBRA_SEG6_LOCAL_ACTION_UNSPEC, + NULL); + XFREE(MTYPE_BGP_SRV6_SID, bgp->tovpn_zebra_vrf_sid_last_sent); +} + +/* + * If zebra tells us vrf has become unconfigured, tell zebra not to + * use this srv6-function to forward to the vrf anymore + */ +void vpn_leak_zebra_vrf_sid_withdraw(struct bgp *bgp, afi_t afi) +{ + if (bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent) + vpn_leak_zebra_vrf_sid_withdraw_per_af(bgp, afi); + + if (bgp->tovpn_zebra_vrf_sid_last_sent) + vpn_leak_zebra_vrf_sid_withdraw_per_vrf(bgp); +} + int vpn_leak_label_callback( mpls_label_t label, void *labelid, @@ -610,7 +716,8 @@ static uint32_t alloc_new_sid(struct bgp *bgp, uint32_t index, return label; } -void ensure_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi) +void ensure_vrf_tovpn_sid_per_af(struct bgp *bgp_vpn, struct bgp *bgp_vrf, + afi_t afi) { int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); char buf[256]; @@ -678,6 +785,81 @@ void ensure_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi) tovpn_sid_transpose_label; } +void ensure_vrf_tovpn_sid_per_vrf(struct bgp *bgp_vpn, struct bgp *bgp_vrf) +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); + struct srv6_locator_chunk *tovpn_sid_locator; + struct in6_addr *tovpn_sid; + uint32_t tovpn_sid_index = 0, tovpn_sid_transpose_label; + bool tovpn_sid_auto = false; + + if (debug) + zlog_debug("%s: try to allocate new SID for vrf %s", __func__, + bgp_vrf->name_pretty); + + /* skip when tovpn sid is already allocated on vrf instance */ + if (bgp_vrf->tovpn_sid) + return; + + /* + * skip when bgp vpn instance ins't allocated + * or srv6 locator chunk isn't allocated + */ + if (!bgp_vpn || !bgp_vpn->srv6_locator_chunks) + return; + + tovpn_sid_index = bgp_vrf->tovpn_sid_index; + tovpn_sid_auto = CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_TOVPN_SID_AUTO); + + /* skip when VPN isn't configured on vrf-instance */ + if (tovpn_sid_index == 0 && !tovpn_sid_auto) + return; + + /* check invalid case both configured index and auto */ + if (tovpn_sid_index != 0 && tovpn_sid_auto) { + zlog_err("%s: index-mode and auto-mode both selected. ignored.", + __func__); + return; + } + + tovpn_sid_locator = srv6_locator_chunk_alloc(); + tovpn_sid = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr)); + + tovpn_sid_transpose_label = alloc_new_sid(bgp_vpn, tovpn_sid_index, + tovpn_sid_locator, tovpn_sid); + + if (tovpn_sid_transpose_label == 0) { + if (debug) + zlog_debug("%s: not allocated new sid for vrf %s", + __func__, bgp_vrf->name_pretty); + srv6_locator_chunk_free(tovpn_sid_locator); + XFREE(MTYPE_BGP_SRV6_SID, tovpn_sid); + return; + } + + if (debug) + zlog_debug("%s: new sid %pI6 allocated for vrf %s", __func__, + tovpn_sid, bgp_vrf->name_pretty); + + bgp_vrf->tovpn_sid = tovpn_sid; + bgp_vrf->tovpn_sid_locator = tovpn_sid_locator; + bgp_vrf->tovpn_sid_transpose_label = tovpn_sid_transpose_label; +} + +void ensure_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi) +{ + /* per-af sid */ + if (bgp_vrf->vpn_policy[afi].tovpn_sid_index != 0 || + CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_SID_AUTO)) + return ensure_vrf_tovpn_sid_per_af(bgp_vpn, bgp_vrf, afi); + + /* per-vrf sid */ + if (bgp_vrf->tovpn_sid_index != 0 || + CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_TOVPN_SID_AUTO)) + return ensure_vrf_tovpn_sid_per_vrf(bgp_vpn, bgp_vrf); +} + /* * This function embeds upper `len` bits of `label` in `sid`, * starting at offset `offset` as seen from the MSB of `sid`. @@ -1337,6 +1519,29 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */ &from_bgp->vpn_policy[afi] .tovpn_sid_locator->prefix.prefix, sizeof(struct in6_addr)); + } else if (from_bgp->tovpn_sid_locator) { + encode_label(from_bgp->tovpn_sid_transpose_label, &label); + static_attr.srv6_l3vpn = + XCALLOC(MTYPE_BGP_SRV6_L3VPN, + sizeof(struct bgp_attr_srv6_l3vpn)); + static_attr.srv6_l3vpn->sid_flags = 0x00; + static_attr.srv6_l3vpn->endpoint_behavior = 0xffff; + static_attr.srv6_l3vpn->loc_block_len = + from_bgp->tovpn_sid_locator->block_bits_length; + static_attr.srv6_l3vpn->loc_node_len = + from_bgp->tovpn_sid_locator->node_bits_length; + static_attr.srv6_l3vpn->func_len = + from_bgp->tovpn_sid_locator->function_bits_length; + static_attr.srv6_l3vpn->arg_len = + from_bgp->tovpn_sid_locator->argument_bits_length; + static_attr.srv6_l3vpn->transposition_len = + from_bgp->tovpn_sid_locator->function_bits_length; + static_attr.srv6_l3vpn->transposition_offset = + from_bgp->tovpn_sid_locator->block_bits_length + + from_bgp->tovpn_sid_locator->node_bits_length; + memcpy(&static_attr.srv6_l3vpn->sid, + &from_bgp->tovpn_sid_locator->prefix.prefix, + sizeof(struct in6_addr)); } diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index 9af4cdf3a258..03d7a0dd2ce1 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -81,9 +81,16 @@ extern void vpn_leak_to_vrf_withdraw(struct bgp *from_bgp, extern void vpn_leak_zebra_vrf_label_update(struct bgp *bgp, afi_t afi); extern void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi); extern void vpn_leak_zebra_vrf_sid_update(struct bgp *bgp, afi_t afi); +extern void vpn_leak_zebra_vrf_sid_update_per_af(struct bgp *bgp, afi_t afi); +extern void vpn_leak_zebra_vrf_sid_update_per_vrf(struct bgp *bgp); extern void vpn_leak_zebra_vrf_sid_withdraw(struct bgp *bgp, afi_t afi); +extern void vpn_leak_zebra_vrf_sid_withdraw_per_af(struct bgp *bgp, afi_t afi); +extern void vpn_leak_zebra_vrf_sid_withdraw_per_vrf(struct bgp *bgp); extern int vpn_leak_label_callback(mpls_label_t label, void *lblid, bool alloc); extern void ensure_vrf_tovpn_sid(struct bgp *vpn, struct bgp *vrf, afi_t afi); +extern void ensure_vrf_tovpn_sid_per_af(struct bgp *vpn, struct bgp *vrf, + afi_t afi); +extern void ensure_vrf_tovpn_sid_per_vrf(struct bgp *vpn, struct bgp *vrf); extern void transpose_sid(struct in6_addr *sid, uint32_t label, uint8_t offset, uint8_t size); extern void vrf_import_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp, @@ -251,17 +258,26 @@ static inline void vpn_leak_postchange(enum vpn_policy_direction direction, vpn_leak_zebra_vrf_label_update(bgp_vrf, afi); } - if (!bgp_vrf->vpn_policy[afi].tovpn_sid) + if (!bgp_vrf->vpn_policy[afi].tovpn_sid && !bgp_vrf->tovpn_sid) ensure_vrf_tovpn_sid(bgp_vpn, bgp_vrf, afi); - if (!bgp_vrf->vpn_policy[afi].tovpn_sid - && bgp_vrf->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent) + if ((!bgp_vrf->vpn_policy[afi].tovpn_sid && + bgp_vrf->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent) || + (!bgp_vrf->tovpn_sid && + bgp_vrf->tovpn_zebra_vrf_sid_last_sent)) vpn_leak_zebra_vrf_sid_withdraw(bgp_vrf, afi); - if (sid_diff(bgp_vrf->vpn_policy[afi].tovpn_sid, - bgp_vrf->vpn_policy[afi] - .tovpn_zebra_vrf_sid_last_sent)) { - vpn_leak_zebra_vrf_sid_update(bgp_vrf, afi); + if (bgp_vrf->vpn_policy[afi].tovpn_sid) { + if (sid_diff(bgp_vrf->vpn_policy[afi].tovpn_sid, + bgp_vrf->vpn_policy[afi] + .tovpn_zebra_vrf_sid_last_sent)) { + vpn_leak_zebra_vrf_sid_update(bgp_vrf, afi); + } + } else if (bgp_vrf->tovpn_sid) { + if (sid_diff(bgp_vrf->tovpn_sid, + bgp_vrf->tovpn_zebra_vrf_sid_last_sent)) { + vpn_leak_zebra_vrf_sid_update(bgp_vrf, afi); + } } vpn_leak_from_vrf_update_all(bgp_vpn, bgp_vrf, afi); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index e574ae780519..187e29f0c565 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -331,6 +331,9 @@ static int bgp_srv6_locator_unset(struct bgp *bgp) /* refresh vpnv6 tovpn_sid */ XFREE(MTYPE_BGP_SRV6_SID, bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid); + + /* refresh per-vrf tovpn_sid */ + XFREE(MTYPE_BGP_SRV6_SID, bgp_vrf->tovpn_sid); } /* update vpn bgp processes */ @@ -356,6 +359,9 @@ static int bgp_srv6_locator_unset(struct bgp *bgp) srv6_locator_chunk_free(tovpn_sid_locator); bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_locator = NULL; } + + /* refresh per-vrf tovpn_sid_locator */ + srv6_locator_chunk_free(bgp_vrf->tovpn_sid_locator); } /* clear locator name */ @@ -8995,6 +9001,14 @@ DEFPY (af_sid_vpn_export, return CMD_WARNING_CONFIG_FAILED; } + if (bgp->tovpn_sid_index != 0 || + CHECK_FLAG(bgp->vrf_flags, BGP_VRF_TOVPN_SID_AUTO)) { + vty_out(vty, + "per-vrf sid and per-af sid are mutually exclusive\n" + "Failed: per-vrf sid is configured. Remove per-vrf sid before configuring per-af sid\n"); + return CMD_WARNING_CONFIG_FAILED; + } + /* skip when it's already configured */ if ((sid_idx != 0 && bgp->vpn_policy[afi].tovpn_sid_index != 0) || (sid_auto && CHECK_FLAG(bgp->vpn_policy[afi].flags, @@ -9036,6 +9050,86 @@ DEFPY (af_sid_vpn_export, return CMD_SUCCESS; } +DEFPY (bgp_sid_vpn_export, + bgp_sid_vpn_export_cmd, + "[no] sid vpn per-vrf export <(1-255)$sid_idx|auto$sid_auto>", + NO_STR + "sid value for VRF\n" + "Between current vrf and vpn\n" + "sid per-VRF (both IPv4 and IPv6 address families)\n" + "For routes leaked from current vrf to vpn\n" + "Sid allocation index\n" + "Automatically assign a label\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int debug; + + debug = (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF) | + BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF)); + + if (no) { + /* implement me */ + vty_out(vty, "It's not implemented\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (bgp->vpn_policy[AFI_IP].tovpn_sid_index != 0 || + CHECK_FLAG(bgp->vpn_policy[AFI_IP].flags, + BGP_VPN_POLICY_TOVPN_SID_AUTO) || + bgp->vpn_policy[AFI_IP6].tovpn_sid_index != 0 || + CHECK_FLAG(bgp->vpn_policy[AFI_IP6].flags, + BGP_VPN_POLICY_TOVPN_SID_AUTO)) { + vty_out(vty, + "per-vrf sid and per-af sid are mutually exclusive\n" + "Failed: per-af sid is configured. Remove per-af sid before configuring per-vrf sid\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* skip when it's already configured */ + if ((sid_idx != 0 && bgp->tovpn_sid_index != 0) || + (sid_auto && CHECK_FLAG(bgp->vrf_flags, BGP_VRF_TOVPN_SID_AUTO))) + return CMD_SUCCESS; + + /* + * mode change between sid_idx and sid_auto isn't supported. + * user must negate sid vpn export when they want to change the mode + */ + if ((sid_auto && bgp->tovpn_sid_index != 0) || + (sid_idx != 0 && + CHECK_FLAG(bgp->vrf_flags, BGP_VRF_TOVPN_SID_AUTO))) { + vty_out(vty, "it's already configured as %s.\n", + sid_auto ? "auto-mode" : "idx-mode"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* pre-change */ + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP, bgp_get_default(), + bgp); + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP6, bgp_get_default(), + bgp); + + if (sid_auto) { + /* SID allocation auto-mode */ + if (debug) + zlog_debug("%s: auto per-vrf sid alloc.", __func__); + SET_FLAG(bgp->vrf_flags, BGP_VRF_TOVPN_SID_AUTO); + } else { + /* SID allocation index-mode */ + if (debug) + zlog_debug("%s: idx %ld per-vrf sid alloc.", __func__, + sid_idx); + bgp->tovpn_sid_index = sid_idx; + } + + /* post-change */ + vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP, bgp_get_default(), + bgp); + vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP6, + bgp_get_default(), bgp); + + return CMD_SUCCESS; +} + ALIAS (af_label_vpn_export, af_no_label_vpn_export_cmd, "no label vpn export", @@ -19803,6 +19897,7 @@ void bgp_vty_init(void) install_element(BGP_SRV6_NODE, &no_bgp_srv6_locator_cmd); install_element(BGP_IPV4_NODE, &af_sid_vpn_export_cmd); install_element(BGP_IPV6_NODE, &af_sid_vpn_export_cmd); + install_element(BGP_NODE, &bgp_sid_vpn_export_cmd); bgp_vty_if_init(); } diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index b5c13fddd053..f7dda74cce2b 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -3315,6 +3315,17 @@ static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) XFREE(MTYPE_BGP_SRV6_SID, bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid); } + + /* refresh per-vrf tovpn_sid */ + tovpn_sid = bgp_vrf->tovpn_sid; + if (tovpn_sid) { + tmp_prefi.family = AF_INET6; + tmp_prefi.prefixlen = IPV6_MAX_BITLEN; + tmp_prefi.prefix = *tovpn_sid; + if (prefix_match((struct prefix *)&loc.prefix, + (struct prefix *)&tmp_prefi)) + XFREE(MTYPE_BGP_SRV6_SID, bgp_vrf->tovpn_sid); + } } vpn_leak_postchange_all(); @@ -3353,6 +3364,17 @@ static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS) NULL; } } + + /* refresh per-vrf tovpn_sid_locator */ + tovpn_sid_locator = bgp_vrf->tovpn_sid_locator; + if (tovpn_sid_locator) { + tmp_prefi.family = AF_INET6; + 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(tovpn_sid_locator); + } } return 0; diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 64d87f946c69..c844f1530f5c 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -759,6 +759,8 @@ struct bgp { #define BGP_VRF_EXPORT_AUTO_RT_CFGD (1 << 4) /* retain auto when cfgd */ #define BGP_VRF_RD_CFGD (1 << 5) #define BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY (1 << 6) +/* per-VRF toVPN SID */ +#define BGP_VRF_TOVPN_SID_AUTO (1 << 7) /* unique ID for auto derivation of RD for this vrf */ uint16_t vrf_rd_id; @@ -806,6 +808,12 @@ struct bgp { char srv6_locator_name[SRV6_LOCNAME_SIZE]; struct list *srv6_locator_chunks; struct list *srv6_functions; + uint32_t tovpn_sid_index; /* unset => set to 0 */ + struct in6_addr *tovpn_sid; + struct srv6_locator_chunk *tovpn_sid_locator; + uint32_t tovpn_sid_transpose_label; + struct in6_addr *tovpn_zebra_vrf_sid_last_sent; + /* TCP keepalive parameters for BGP connection */ uint16_t tcp_keepalive_idle; uint16_t tcp_keepalive_intvl;