diff --git a/lib/zclient.c b/lib/zclient.c index be2c4e54a0..02532e7069 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1542,6 +1542,39 @@ int zapi_backup_nexthop_from_nexthop(struct zapi_nexthop *znh, return ret; } +/* + * Format some info about a zapi nexthop, for debug or logging. + */ +const char *zapi_nexthop2str(const struct zapi_nexthop *znh, char *buf, + int bufsize) +{ + char tmp[INET6_ADDRSTRLEN]; + + switch (znh->type) { + case NEXTHOP_TYPE_IFINDEX: + snprintf(buf, bufsize, "if %u", znh->ifindex); + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + inet_ntop(AF_INET, &znh->gate.ipv4, tmp, sizeof(tmp)); + snprintf(buf, bufsize, "%s if %u", tmp, znh->ifindex); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + inet_ntop(AF_INET6, &znh->gate.ipv6, tmp, sizeof(tmp)); + snprintf(buf, bufsize, "%s if %u", tmp, znh->ifindex); + break; + case NEXTHOP_TYPE_BLACKHOLE: + snprintf(buf, bufsize, "blackhole"); + break; + default: + snprintf(buf, bufsize, "unknown"); + break; + } + + return buf; +} + /* * Decode the nexthop-tracking update message */ @@ -2811,6 +2844,27 @@ int zapi_labels_encode(struct stream *s, int cmd, struct zapi_labels *zl) return -1; } + if (CHECK_FLAG(zl->message, ZAPI_LABELS_HAS_BACKUPS)) { + + if (zl->backup_nexthop_num > MULTIPATH_NUM) { + flog_err( + EC_LIB_ZAPI_ENCODE, + "%s: label %u: can't encode %u nexthops (maximum is %u)", + __func__, zl->local_label, zl->nexthop_num, + MULTIPATH_NUM); + return -1; + } + stream_putw(s, zl->backup_nexthop_num); + + for (int i = 0; i < zl->backup_nexthop_num; i++) { + znh = &zl->backup_nexthops[i]; + + if (zapi_nexthop_encode(s, znh, 0) < 0) + return -1; + } + + } + /* Put length at the first point of the stream. */ stream_putw_at(s, 0, stream_get_endp(s)); @@ -2885,6 +2939,28 @@ int zapi_labels_decode(struct stream *s, struct zapi_labels *zl) return -1; } + if (CHECK_FLAG(zl->message, ZAPI_LABELS_HAS_BACKUPS)) { + STREAM_GETW(s, zl->backup_nexthop_num); + + if (zl->backup_nexthop_num > MULTIPATH_NUM) { + flog_warn( + EC_LIB_ZAPI_ENCODE, + "%s: Prefix %pFX has %d backup nexthops, but we can only use the first %d", + __func__, &zl->route.prefix, + zl->backup_nexthop_num, MULTIPATH_NUM); + } + + zl->backup_nexthop_num = MIN(MULTIPATH_NUM, + zl->backup_nexthop_num); + + for (int i = 0; i < zl->backup_nexthop_num; i++) { + znh = &zl->backup_nexthops[i]; + + if (zapi_nexthop_decode(s, znh, 0) < 0) + return -1; + } + } + return 0; stream_failure: return -1; diff --git a/lib/zclient.h b/lib/zclient.h index 4ada064623..5b3b4635a4 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -484,7 +484,8 @@ struct zapi_route { struct zapi_labels { uint8_t message; -#define ZAPI_LABELS_FTN 0x01 +#define ZAPI_LABELS_FTN 0x01 +#define ZAPI_LABELS_HAS_BACKUPS 0x02 enum lsp_types_t type; mpls_label_t local_label; struct { @@ -495,6 +496,10 @@ struct zapi_labels { uint16_t nexthop_num; struct zapi_nexthop nexthops[MULTIPATH_NUM]; + + /* Backup nexthops, if present */ + uint16_t backup_nexthop_num; + struct zapi_nexthop backup_nexthops[MULTIPATH_NUM]; }; struct zapi_pw { @@ -799,6 +804,8 @@ int zapi_backup_nexthop_from_nexthop(struct zapi_nexthop *znh, const struct nexthop *nh); extern bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr); +const char *zapi_nexthop2str(const struct zapi_nexthop *znh, char *buf, + int bufsize); /* Decode the zebra error message */ extern bool zapi_error_decode(struct stream *s, enum zebra_error_types *error); diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index e2ea773055..987588947e 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -410,6 +410,8 @@ DEFPY(sharp_lsp_prefix_v4, sharp_lsp_prefix_v4_cmd, "Instance\n") { struct nexthop_group_cmd *nhgc = NULL; + struct nexthop_group_cmd *backup_nhgc = NULL; + struct nexthop_group *backup_nhg = NULL; struct prefix p = {}; int type = 0; @@ -441,9 +443,23 @@ DEFPY(sharp_lsp_prefix_v4, sharp_lsp_prefix_v4_cmd, return CMD_WARNING; } + /* Use group's backup nexthop info if present */ + if (nhgc->backup_list_name[0]) { + backup_nhgc = nhgc_find(nhgc->backup_list_name); + + if (!backup_nhgc) { + vty_out(vty, + "%% Backup group %s not found for group %s\n", + nhgc->backup_list_name, + nhgname); + return CMD_WARNING; + } + backup_nhg = &(backup_nhgc->nhg); + } + if (sharp_install_lsps_helper(true, pfx->family > 0 ? &p : NULL, type, instance, inlabel, - &(nhgc->nhg)) == 0) + &(nhgc->nhg), backup_nhg) == 0) return CMD_SUCCESS; else { vty_out(vty, "%% LSP install failed!\n"); @@ -454,7 +470,7 @@ DEFPY(sharp_lsp_prefix_v4, sharp_lsp_prefix_v4_cmd, DEFPY(sharp_remove_lsp_prefix_v4, sharp_remove_lsp_prefix_v4_cmd, "sharp remove lsp \ (0-100000)$inlabel\ - nexthop-group NHGNAME$nhgname\ + [nexthop-group NHGNAME$nhgname] \ [prefix A.B.C.D/M$pfx\ " FRR_IP_REDIST_STR_SHARPD "$type_str [instance (0-255)$instance]]", "Sharp Routing Protocol\n" @@ -472,6 +488,7 @@ DEFPY(sharp_remove_lsp_prefix_v4, sharp_remove_lsp_prefix_v4_cmd, struct nexthop_group_cmd *nhgc = NULL; struct prefix p = {}; int type = 0; + struct nexthop_group *nhg = NULL; /* We're offered a v4 prefix */ if (pfx->family > 0 && type_str) { @@ -489,21 +506,24 @@ DEFPY(sharp_remove_lsp_prefix_v4, sharp_remove_lsp_prefix_v4_cmd, return CMD_WARNING; } - nhgc = nhgc_find(nhgname); - if (!nhgc) { - vty_out(vty, "%% Nexthop-group '%s' does not exist\n", - nhgname); - return CMD_WARNING; - } + if (nhgname) { + nhgc = nhgc_find(nhgname); + if (!nhgc) { + vty_out(vty, "%% Nexthop-group '%s' does not exist\n", + nhgname); + return CMD_WARNING; + } - if (nhgc->nhg.nexthop == NULL) { - vty_out(vty, "%% Nexthop-group '%s' is empty\n", nhgname); - return CMD_WARNING; + if (nhgc->nhg.nexthop == NULL) { + vty_out(vty, "%% Nexthop-group '%s' is empty\n", + nhgname); + return CMD_WARNING; + } + nhg = &(nhgc->nhg); } if (sharp_install_lsps_helper(false, pfx->family > 0 ? &p : NULL, - type, instance, inlabel, - &(nhgc->nhg)) == 0) + type, instance, inlabel, nhg, NULL) == 0) return CMD_SUCCESS; else { vty_out(vty, "%% LSP remove failed!\n"); diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index e1bd6f5722..0795096440 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -89,7 +89,8 @@ static int sharp_ifp_down(struct interface *ifp) int sharp_install_lsps_helper(bool install_p, const struct prefix *p, uint8_t type, int instance, uint32_t in_label, - const struct nexthop_group *nhg) + const struct nexthop_group *nhg, + const struct nexthop_group *backup_nhg) { struct zapi_labels zl = {}; struct zapi_nexthop *znh; @@ -106,32 +107,68 @@ int sharp_install_lsps_helper(bool install_p, const struct prefix *p, zl.route.instance = instance; } + /* List of nexthops is optional for delete */ i = 0; - for (ALL_NEXTHOPS_PTR(nhg, nh)) { - znh = &zl.nexthops[i]; + if (nhg) { + for (ALL_NEXTHOPS_PTR(nhg, nh)) { + znh = &zl.nexthops[i]; - /* Must have labels to be useful */ - if (nh->nh_label == NULL || nh->nh_label->num_labels == 0) - continue; + /* Must have labels to be useful */ + if (nh->nh_label == NULL || + nh->nh_label->num_labels == 0) + continue; - if (nh->type == NEXTHOP_TYPE_IFINDEX || - nh->type == NEXTHOP_TYPE_BLACKHOLE) - /* Hmm - can't really deal with these types */ - continue; + if (nh->type == NEXTHOP_TYPE_IFINDEX || + nh->type == NEXTHOP_TYPE_BLACKHOLE) + /* Hmm - can't really deal with these types */ + continue; - ret = zapi_nexthop_from_nexthop(znh, nh); - if (ret < 0) - return -1; + ret = zapi_nexthop_from_nexthop(znh, nh); + if (ret < 0) + return -1; - i++; + i++; + } } - /* Whoops - no nexthops isn't very useful */ - if (i == 0) + /* Whoops - no nexthops isn't very useful for install */ + if (i == 0 && install_p) return -1; zl.nexthop_num = i; + /* Add optional backup nexthop info. Since these are used by index, + * we can't just skip over an invalid backup nexthop: we will + * invalidate the entire operation. + */ + if (backup_nhg != NULL) { + i = 0; + for (ALL_NEXTHOPS_PTR(backup_nhg, nh)) { + znh = &zl.backup_nexthops[i]; + + /* Must have labels to be useful */ + if (nh->nh_label == NULL || + nh->nh_label->num_labels == 0) + return -1; + + if (nh->type == NEXTHOP_TYPE_IFINDEX || + nh->type == NEXTHOP_TYPE_BLACKHOLE) + /* Hmm - can't really deal with these types */ + return -1; + + ret = zapi_nexthop_from_nexthop(znh, nh); + if (ret < 0) + return -1; + + i++; + } + + if (i > 0) + SET_FLAG(zl.message, ZAPI_LABELS_HAS_BACKUPS); + + zl.backup_nexthop_num = i; + } + if (install_p) ret = zebra_send_mpls_labels(zclient, ZEBRA_MPLS_LABELS_ADD, &zl); diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h index 926bff676b..2b8e19dd97 100644 --- a/sharpd/sharp_zebra.h +++ b/sharpd/sharp_zebra.h @@ -42,5 +42,7 @@ extern void sharp_remove_routes_helper(struct prefix *p, vrf_id_t vrf_id, int sharp_install_lsps_helper(bool install_p, const struct prefix *p, uint8_t type, int instance, uint32_t in_label, - const struct nexthop_group *nhg); + const struct nexthop_group *nhg, + const struct nexthop_group *backup_nhg); + #endif diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 2e6cc7cd06..095c5570a2 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -3504,6 +3504,7 @@ enum zebra_dplane_result kernel_neigh_update_ctx(struct zebra_dplane_ctx *ctx) int netlink_mpls_multipath(int cmd, struct zebra_dplane_ctx *ctx) { mpls_lse_t lse; + const struct nhlfe_list_head *head; const zebra_nhlfe_t *nhlfe; struct nexthop *nexthop = NULL; unsigned int nexthop_num; @@ -3524,7 +3525,8 @@ int netlink_mpls_multipath(int cmd, struct zebra_dplane_ctx *ctx) * or multipath case. */ nexthop_num = 0; - for (nhlfe = dplane_ctx_get_nhlfe(ctx); nhlfe; nhlfe = nhlfe->next) { + head = dplane_ctx_get_nhlfe_list(ctx); + frr_each(nhlfe_list_const, head, nhlfe) { nexthop = nhlfe->nexthop; if (!nexthop) continue; @@ -3579,8 +3581,7 @@ int netlink_mpls_multipath(int cmd, struct zebra_dplane_ctx *ctx) routedesc); nexthop_num = 0; - for (nhlfe = dplane_ctx_get_nhlfe(ctx); - nhlfe; nhlfe = nhlfe->next) { + frr_each(nhlfe_list_const, head, nhlfe) { nexthop = nhlfe->nexthop; if (!nexthop) continue; @@ -3618,8 +3619,7 @@ int netlink_mpls_multipath(int cmd, struct zebra_dplane_ctx *ctx) routedesc); nexthop_num = 0; - for (nhlfe = dplane_ctx_get_nhlfe(ctx); - nhlfe; nhlfe = nhlfe->next) { + frr_each(nhlfe_list_const, head, nhlfe) { nexthop = nhlfe->nexthop; if (!nexthop) continue; diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index ffe5ca4845..16714acc6e 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1998,6 +1998,7 @@ static void zread_mpls_labels_add(ZAPI_HANDLER_ARGS) { struct stream *s; struct zapi_labels zl; + int ret; /* Get input stream. */ s = msg; @@ -2011,20 +2012,11 @@ static void zread_mpls_labels_add(ZAPI_HANDLER_ARGS) if (!mpls_enabled) return; - for (int i = 0; i < zl.nexthop_num; i++) { - struct zapi_nexthop *znh; - - znh = &zl.nexthops[i]; - - mpls_lsp_install(zvrf, zl.type, zl.local_label, - znh->label_num, znh->labels, - znh->type, &znh->gate, znh->ifindex); - - if (CHECK_FLAG(zl.message, ZAPI_LABELS_FTN)) - mpls_ftn_update(1, zvrf, zl.type, &zl.route.prefix, - znh->type, &znh->gate, znh->ifindex, - zl.route.type, zl.route.instance, - znh->labels[0]); + ret = mpls_zapi_labels_process(true, zvrf, &zl); + if (ret < 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Error processing zapi request", + __func__); } } @@ -2042,6 +2034,7 @@ static void zread_mpls_labels_delete(ZAPI_HANDLER_ARGS) { struct stream *s; struct zapi_labels zl; + int ret; /* Get input stream. */ s = msg; @@ -2056,21 +2049,11 @@ static void zread_mpls_labels_delete(ZAPI_HANDLER_ARGS) return; if (zl.nexthop_num > 0) { - for (int i = 0; i < zl.nexthop_num; i++) { - struct zapi_nexthop *znh; - - znh = &zl.nexthops[i]; - mpls_lsp_uninstall(zvrf, zl.type, zl.local_label, - znh->type, &znh->gate, - znh->ifindex); - - if (CHECK_FLAG(zl.message, ZAPI_LABELS_FTN)) - mpls_ftn_update(0, zvrf, zl.type, - &zl.route.prefix, znh->type, - &znh->gate, znh->ifindex, - zl.route.type, - zl.route.instance, - znh->labels[0]); + ret = mpls_zapi_labels_process(false /*delete*/, zvrf, &zl); + if (ret < 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Error processing zapi request", + __func__); } } else { mpls_lsp_uninstall_all_vrf(zvrf, zl.type, zl.local_label); @@ -2110,26 +2093,16 @@ static void zread_mpls_labels_replace(ZAPI_HANDLER_ARGS) if (!mpls_enabled) return; + /* This removes everything, then re-adds from the client's + * zapi message. Since the LSP will be processed later, on this + * this same pthread, all of the changes will 'appear' at once. + */ mpls_lsp_uninstall_all_vrf(zvrf, zl.type, zl.local_label); if (CHECK_FLAG(zl.message, ZAPI_LABELS_FTN)) mpls_ftn_uninstall(zvrf, zl.type, &zl.route.prefix, zl.route.type, zl.route.instance); - for (int i = 0; i < zl.nexthop_num; i++) { - struct zapi_nexthop *znh; - - znh = &zl.nexthops[i]; - mpls_lsp_install(zvrf, zl.type, zl.local_label, - znh->label_num, znh->labels, znh->type, - &znh->gate, znh->ifindex); - - if (CHECK_FLAG(zl.message, ZAPI_LABELS_FTN)) { - mpls_ftn_update(1, zvrf, zl.type, &zl.route.prefix, - znh->type, &znh->gate, znh->ifindex, - zl.route.type, zl.route.instance, - znh->labels[0]); - } - } + mpls_zapi_labels_process(true, zvrf, &zl); } /* Send response to a table manager connect request to client */ diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 278e894d06..63d55d061d 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -33,6 +33,7 @@ #include "zebra/zebra_router.h" #include "zebra/zebra_dplane.h" #include "zebra/zebra_vxlan_private.h" +#include "zebra/zebra_mpls.h" #include "zebra/rt.h" #include "zebra/debug.h" @@ -510,20 +511,28 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx) case DPLANE_OP_LSP_DELETE: case DPLANE_OP_LSP_NOTIFY: { - zebra_nhlfe_t *nhlfe, *next; + zebra_nhlfe_t *nhlfe; - /* Free allocated NHLFEs */ - for (nhlfe = ctx->u.lsp.nhlfe_list; nhlfe; nhlfe = next) { - next = nhlfe->next; - - zebra_mpls_nhlfe_del(nhlfe); + /* Unlink and free allocated NHLFEs */ + frr_each_safe(nhlfe_list, &ctx->u.lsp.nhlfe_list, nhlfe) { + nhlfe_list_del(&ctx->u.lsp.nhlfe_list, nhlfe); + zebra_mpls_nhlfe_free(nhlfe); } - /* Clear pointers in lsp struct, in case we're cacheing + /* Unlink and free allocated backup NHLFEs, if present */ + frr_each_safe(nhlfe_list, + &(ctx->u.lsp.backup_nhlfe_list), nhlfe) { + nhlfe_list_del(&ctx->u.lsp.backup_nhlfe_list, + nhlfe); + zebra_mpls_nhlfe_free(nhlfe); + } + + /* Clear pointers in lsp struct, in case we're caching * free context structs. */ - ctx->u.lsp.nhlfe_list = NULL; + nhlfe_list_init(&ctx->u.lsp.nhlfe_list); ctx->u.lsp.best_nhlfe = NULL; + nhlfe_list_init(&ctx->u.lsp.backup_nhlfe_list); break; } @@ -1217,11 +1226,18 @@ void dplane_ctx_set_lsp_flags(struct zebra_dplane_ctx *ctx, ctx->u.lsp.flags = flags; } -const zebra_nhlfe_t *dplane_ctx_get_nhlfe(const struct zebra_dplane_ctx *ctx) +const struct nhlfe_list_head *dplane_ctx_get_nhlfe_list( + const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); + return &(ctx->u.lsp.nhlfe_list); +} - return ctx->u.lsp.nhlfe_list; +const struct nhlfe_list_head *dplane_ctx_get_backup_nhlfe_list( + const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return &(ctx->u.lsp.backup_nhlfe_list); } zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx, @@ -1230,7 +1246,7 @@ zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx, union g_addr *gate, ifindex_t ifindex, uint8_t num_labels, - mpls_label_t out_labels[]) + mpls_label_t *out_labels) { zebra_nhlfe_t *nhlfe; @@ -1243,6 +1259,26 @@ zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx, return nhlfe; } +zebra_nhlfe_t *dplane_ctx_add_backup_nhlfe(struct zebra_dplane_ctx *ctx, + enum lsp_types_t lsp_type, + enum nexthop_types_t nh_type, + union g_addr *gate, + ifindex_t ifindex, + uint8_t num_labels, + mpls_label_t *out_labels) +{ + zebra_nhlfe_t *nhlfe; + + DPLANE_CTX_VALID(ctx); + + nhlfe = zebra_mpls_lsp_add_backup_nhlfe(&(ctx->u.lsp), + lsp_type, nh_type, gate, + ifindex, num_labels, + out_labels); + + return nhlfe; +} + const zebra_nhlfe_t * dplane_ctx_get_best_nhlfe(const struct zebra_dplane_ctx *ctx) { @@ -1729,27 +1765,21 @@ static int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, memset(&ctx->u.lsp, 0, sizeof(ctx->u.lsp)); + nhlfe_list_init(&(ctx->u.lsp.nhlfe_list)); + nhlfe_list_init(&(ctx->u.lsp.backup_nhlfe_list)); ctx->u.lsp.ile = lsp->ile; ctx->u.lsp.addr_family = lsp->addr_family; ctx->u.lsp.num_ecmp = lsp->num_ecmp; ctx->u.lsp.flags = lsp->flags; /* Copy source LSP's nhlfes, and capture 'best' nhlfe */ - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) { + frr_each(nhlfe_list, &lsp->nhlfe_list, nhlfe) { /* Not sure if this is meaningful... */ if (nhlfe->nexthop == NULL) continue; - new_nhlfe = - zebra_mpls_lsp_add_nhlfe( - &(ctx->u.lsp), - nhlfe->type, - nhlfe->nexthop->type, - &(nhlfe->nexthop->gate), - nhlfe->nexthop->ifindex, - nhlfe->nexthop->nh_label->num_labels, - nhlfe->nexthop->nh_label->label); - + new_nhlfe = zebra_mpls_lsp_add_nh(&(ctx->u.lsp), nhlfe->type, + nhlfe->nexthop); if (new_nhlfe == NULL || new_nhlfe->nexthop == NULL) { ret = ENOMEM; break; @@ -1763,9 +1793,32 @@ static int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, ctx->u.lsp.best_nhlfe = new_nhlfe; } + if (ret != AOK) + goto done; + + /* Capture backup nhlfes/nexthops */ + frr_each(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) { + /* Not sure if this is meaningful... */ + if (nhlfe->nexthop == NULL) + continue; + + new_nhlfe = zebra_mpls_lsp_add_backup_nh(&(ctx->u.lsp), + nhlfe->type, + nhlfe->nexthop); + if (new_nhlfe == NULL || new_nhlfe->nexthop == NULL) { + ret = ENOMEM; + break; + } + + /* Need to copy flags too */ + new_nhlfe->flags = nhlfe->flags; + new_nhlfe->nexthop->flags = nhlfe->nexthop->flags; + } + /* On error the ctx will be cleaned-up, so we don't need to * deal with any allocated nhlfe or nexthop structs here. */ +done: return ret; } diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 9ce542944d..35ce338959 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -310,14 +310,26 @@ void dplane_ctx_set_addr_family(struct zebra_dplane_ctx *ctx, uint32_t dplane_ctx_get_lsp_flags(const struct zebra_dplane_ctx *ctx); void dplane_ctx_set_lsp_flags(struct zebra_dplane_ctx *ctx, uint32_t flags); -const zebra_nhlfe_t *dplane_ctx_get_nhlfe(const struct zebra_dplane_ctx *ctx); +const struct nhlfe_list_head *dplane_ctx_get_nhlfe_list( + const struct zebra_dplane_ctx *ctx); +const struct nhlfe_list_head *dplane_ctx_get_backup_nhlfe_list( + const struct zebra_dplane_ctx *ctx); + zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx, enum lsp_types_t lsp_type, enum nexthop_types_t nh_type, union g_addr *gate, ifindex_t ifindex, uint8_t num_labels, - mpls_label_t out_labels[]); + mpls_label_t *out_labels); + +zebra_nhlfe_t *dplane_ctx_add_backup_nhlfe(struct zebra_dplane_ctx *ctx, + enum lsp_types_t lsp_type, + enum nexthop_types_t nh_type, + union g_addr *gate, + ifindex_t ifindex, + uint8_t num_labels, + mpls_label_t *out_labels); const zebra_nhlfe_t *dplane_ctx_get_best_nhlfe( const struct zebra_dplane_ctx *ctx); diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 0aaede4507..1210430b06 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -93,28 +93,38 @@ static void lsp_processq_del(struct work_queue *wq, void *data); static void lsp_processq_complete(struct work_queue *wq); static int lsp_processq_add(zebra_lsp_t *lsp); static void *lsp_alloc(void *p); + +/* Check whether lsp can be freed - no nhlfes, e.g., and call free api */ +static void lsp_check_free(struct hash *lsp_table, zebra_lsp_t **plsp); + /* Free lsp; sets caller's pointer to NULL */ static void lsp_free(struct hash *lsp_table, zebra_lsp_t **plsp); static char *nhlfe2str(zebra_nhlfe_t *nhlfe, char *buf, int size); static int nhlfe_nhop_match(zebra_nhlfe_t *nhlfe, enum nexthop_types_t gtype, const union g_addr *gate, ifindex_t ifindex); -static zebra_nhlfe_t *nhlfe_find(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, +static zebra_nhlfe_t *nhlfe_find(struct nhlfe_list_head *list, + enum lsp_types_t lsp_type, enum nexthop_types_t gtype, const union g_addr *gate, ifindex_t ifindex); static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, enum nexthop_types_t gtype, const union g_addr *gate, ifindex_t ifindex, - uint8_t num_labels, mpls_label_t *labels); -static int nhlfe_del(zebra_nhlfe_t *snhlfe); + uint8_t num_labels, const mpls_label_t *labels); +static int nhlfe_del(zebra_nhlfe_t *nhlfe); +static void nhlfe_free(zebra_nhlfe_t *nhlfe); static void nhlfe_out_label_update(zebra_nhlfe_t *nhlfe, struct mpls_label_stack *nh_label); static int mpls_lsp_uninstall_all(struct hash *lsp_table, zebra_lsp_t *lsp, enum lsp_types_t type); +static int lsp_backup_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, + mpls_label_t in_label, + enum nexthop_types_t gtype, + const union g_addr *gate, ifindex_t ifindex); static int mpls_static_lsp_uninstall_all(struct zebra_vrf *zvrf, mpls_label_t in_label); static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty); -static void lsp_print(zebra_lsp_t *lsp, void *ctxt); +static void lsp_print(struct vty *vty, zebra_lsp_t *lsp); static void *slsp_alloc(void *p); static int snhlfe_match(zebra_snhlfe_t *snhlfe, enum nexthop_types_t gtype, const union g_addr *gate, ifindex_t ifindex); @@ -131,7 +141,13 @@ static char *snhlfe2str(zebra_snhlfe_t *snhlfe, char *buf, int size); static void mpls_lsp_uninstall_all_type(struct hash_bucket *bucket, void *ctxt); static void mpls_ftn_uninstall_all(struct zebra_vrf *zvrf, int afi, enum lsp_types_t lsp_type); +static int lsp_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type, + const struct zapi_nexthop *znh); +static int lsp_backup_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type, + const struct zapi_nexthop *znh); +/* List implementations - declare internal linkage */ +DECLARE_DLIST(snhlfe_list, struct zebra_snhlfe_t_, list); /* Static functions */ @@ -143,7 +159,7 @@ static void clear_nhlfe_installed(zebra_lsp_t *lsp) zebra_nhlfe_t *nhlfe; struct nexthop *nexthop; - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) { + frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { nexthop = nhlfe->nexthop; if (!nexthop) continue; @@ -196,7 +212,8 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label, if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) continue; - nhlfe = nhlfe_find(lsp, lsp_type, nexthop->type, &nexthop->gate, + nhlfe = nhlfe_find(&lsp->nhlfe_list, lsp_type, + nexthop->type, &nexthop->gate, nexthop->ifindex); if (nhlfe) { /* Clear deleted flag (in case it was set) */ @@ -251,9 +268,9 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label, if (added || changed) { if (lsp_processq_add(lsp)) return -1; - } else if (!lsp->nhlfe_list - && !CHECK_FLAG(lsp->flags, LSP_FLAG_SCHEDULED)) - lsp_free(lsp_table, &lsp); + } else { + lsp_check_free(lsp_table, &lsp); + } return 0; } @@ -267,7 +284,7 @@ static int lsp_uninstall(struct zebra_vrf *zvrf, mpls_label_t label) struct hash *lsp_table; zebra_ile_t tmp_ile; zebra_lsp_t *lsp; - zebra_nhlfe_t *nhlfe, *nhlfe_next; + zebra_nhlfe_t *nhlfe; char buf[BUFSIZ]; /* Lookup table. */ @@ -278,12 +295,11 @@ static int lsp_uninstall(struct zebra_vrf *zvrf, mpls_label_t label) /* If entry is not present, exit. */ tmp_ile.in_label = label; lsp = hash_lookup(lsp_table, &tmp_ile); - if (!lsp || !lsp->nhlfe_list) + if (!lsp || (nhlfe_list_first(&lsp->nhlfe_list) == NULL)) return 0; /* Mark NHLFEs for delete or directly delete, as appropriate. */ - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe_next) { - nhlfe_next = nhlfe->next; + frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { /* Skip static NHLFEs */ if (nhlfe->type == ZEBRA_LSP_STATIC) @@ -308,9 +324,9 @@ static int lsp_uninstall(struct zebra_vrf *zvrf, mpls_label_t label) if (CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED)) { if (lsp_processq_add(lsp)) return -1; - } else if (!lsp->nhlfe_list - && !CHECK_FLAG(lsp->flags, LSP_FLAG_SCHEDULED)) - lsp_free(lsp_table, &lsp); + } else { + lsp_check_free(lsp_table, &lsp); + } return 0; } @@ -784,7 +800,7 @@ static void lsp_select_best_nhlfe(zebra_lsp_t *lsp) * only * concerned with non-deleted NHLFEs. */ - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) { + frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { /* Clear selection flags. */ UNSET_FLAG(nhlfe->flags, (NHLFE_FLAG_SELECTED | NHLFE_FLAG_MULTIPATH)); @@ -809,7 +825,7 @@ static void lsp_select_best_nhlfe(zebra_lsp_t *lsp) * new (uninstalled) NHLFE has been selected, an installed entry that is * still selected has a change or an installed entry is to be removed. */ - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) { + frr_each(nhlfe_list, &lsp->nhlfe_list, nhlfe) { int nh_chg, nh_sel, nh_inst; nexthop = nhlfe->nexthop; @@ -967,8 +983,7 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data) * Any NHLFE that was installed but is not * selected now needs to have its flags updated. */ - for (nhlfe = lsp->nhlfe_list; nhlfe; - nhlfe = nhlfe->next) { + frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { nexthop = nhlfe->nexthop; if (!nexthop) continue; @@ -1012,7 +1027,7 @@ static void lsp_processq_del(struct work_queue *wq, void *data) struct zebra_vrf *zvrf; zebra_lsp_t *lsp; struct hash *lsp_table; - zebra_nhlfe_t *nhlfe, *nhlfe_next; + zebra_nhlfe_t *nhlfe; zvrf = vrf_info_lookup(VRF_DEFAULT); assert(zvrf); @@ -1031,14 +1046,17 @@ static void lsp_processq_del(struct work_queue *wq, void *data) */ UNSET_FLAG(lsp->flags, LSP_FLAG_SCHEDULED); - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe_next) { - nhlfe_next = nhlfe->next; + frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) nhlfe_del(nhlfe); } - if (!lsp->nhlfe_list) - lsp_free(lsp_table, &lsp); + frr_each_safe(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) { + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) + nhlfe_del(nhlfe); + } + + lsp_check_free(lsp_table, &lsp); } /* @@ -1080,6 +1098,8 @@ static void *lsp_alloc(void *p) lsp = XCALLOC(MTYPE_LSP, sizeof(zebra_lsp_t)); lsp->ile = *ile; + nhlfe_list_init(&lsp->nhlfe_list); + nhlfe_list_init(&lsp->backup_nhlfe_list); if (IS_ZEBRA_DEBUG_MPLS) zlog_debug("Alloc LSP in-label %u", lsp->ile.in_label); @@ -1087,6 +1107,24 @@ static void *lsp_alloc(void *p) return ((void *)lsp); } +/* + * Check whether lsp can be freed - no nhlfes, e.g., and call free api + */ +static void lsp_check_free(struct hash *lsp_table, zebra_lsp_t **plsp) +{ + zebra_lsp_t *lsp; + + if (plsp == NULL || *plsp == NULL) + return; + + lsp = *plsp; + + if ((nhlfe_list_first(&lsp->nhlfe_list) == NULL) && + (nhlfe_list_first(&lsp->backup_nhlfe_list) == NULL) && + !CHECK_FLAG(lsp->flags, LSP_FLAG_SCHEDULED)) + lsp_free(lsp_table, plsp); +} + /* * Dtor for an LSP: remove from ile hash, release any internal allocations, * free LSP object. @@ -1094,7 +1132,7 @@ static void *lsp_alloc(void *p) static void lsp_free(struct hash *lsp_table, zebra_lsp_t **plsp) { zebra_lsp_t *lsp; - zebra_nhlfe_t *nhlfe, *nhlfe_next; + zebra_nhlfe_t *nhlfe; if (plsp == NULL || *plsp == NULL) return; @@ -1106,11 +1144,12 @@ static void lsp_free(struct hash *lsp_table, zebra_lsp_t **plsp) lsp->ile.in_label, lsp->flags); /* Free nhlfes, if any. */ - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe_next) { - nhlfe_next = nhlfe->next; - + frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) + nhlfe_del(nhlfe); + + /* Free backup nhlfes, if any. */ + frr_each_safe(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) nhlfe_del(nhlfe); - } hash_release(lsp_table, &lsp->ile); XFREE(MTYPE_LSP, lsp); @@ -1190,16 +1229,14 @@ static int nhlfe_nhop_match(zebra_nhlfe_t *nhlfe, enum nexthop_types_t gtype, /* * Locate NHLFE that matches with passed info. */ -static zebra_nhlfe_t *nhlfe_find(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, +static zebra_nhlfe_t *nhlfe_find(struct nhlfe_list_head *list, + enum lsp_types_t lsp_type, enum nexthop_types_t gtype, const union g_addr *gate, ifindex_t ifindex) { zebra_nhlfe_t *nhlfe; - if (!lsp) - return NULL; - - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) { + frr_each_safe(nhlfe_list, list, nhlfe) { if (nhlfe->type != lsp_type) continue; if (!nhlfe_nhop_match(nhlfe, gtype, gate, ifindex)) @@ -1210,13 +1247,13 @@ static zebra_nhlfe_t *nhlfe_find(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, } /* - * Add NHLFE. Base entry must have been created and duplicate - * check done. + * Allocate and init new NHLFE. */ -static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, - enum nexthop_types_t gtype, - const union g_addr *gate, ifindex_t ifindex, - uint8_t num_labels, mpls_label_t labels[]) +static zebra_nhlfe_t *nhlfe_alloc(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, + enum nexthop_types_t gtype, + const union g_addr *gate, ifindex_t ifindex, + uint8_t num_labels, + const mpls_label_t *labels) { zebra_nhlfe_t *nhlfe; struct nexthop *nexthop; @@ -1235,6 +1272,7 @@ static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, XFREE(MTYPE_NHLFE, nhlfe); return NULL; } + nexthop_add_labels(nexthop, lsp_type, num_labels, labels); nexthop->vrf_id = VRF_DEFAULT; @@ -1260,18 +1298,85 @@ static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, XFREE(MTYPE_NHLFE, nhlfe); return NULL; } - nhlfe->nexthop = nexthop; - if (lsp->nhlfe_list) - lsp->nhlfe_list->prev = nhlfe; - nhlfe->next = lsp->nhlfe_list; - lsp->nhlfe_list = nhlfe; return nhlfe; } /* - * Delete NHLFE. Entry must be present on list. + * Add NHLFE. Base entry must have been created and duplicate + * check done. + */ +static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, + enum nexthop_types_t gtype, + const union g_addr *gate, ifindex_t ifindex, + uint8_t num_labels, const mpls_label_t *labels) +{ + zebra_nhlfe_t *nhlfe; + + if (!lsp) + return NULL; + + /* Allocate new object */ + nhlfe = nhlfe_alloc(lsp, lsp_type, gtype, gate, ifindex, num_labels, + labels); + + /* Enqueue to LSP, at head of list. */ + if (nhlfe) + nhlfe_list_add_head(&lsp->nhlfe_list, nhlfe); + + return nhlfe; +} + +/* + * Add backup NHLFE. Base entry must have been created and duplicate + * check done. + */ +static zebra_nhlfe_t *nhlfe_backup_add(zebra_lsp_t *lsp, + enum lsp_types_t lsp_type, + enum nexthop_types_t gtype, + const union g_addr *gate, + ifindex_t ifindex, uint8_t num_labels, + const mpls_label_t *labels) +{ + zebra_nhlfe_t *nhlfe; + + if (!lsp) + return NULL; + + /* Allocate new object */ + nhlfe = nhlfe_alloc(lsp, lsp_type, gtype, gate, ifindex, num_labels, + labels); + + SET_FLAG(nhlfe->flags, NHLFE_FLAG_IS_BACKUP); + + /* Enqueue to LSP, at tail of list. */ + if (nhlfe) + nhlfe_list_add_tail(&lsp->backup_nhlfe_list, nhlfe); + + return nhlfe; +} + +/* + * Common delete for NHLFEs. + */ +static void nhlfe_free(zebra_nhlfe_t *nhlfe) +{ + if (!nhlfe) + return; + + /* Free nexthop. */ + if (nhlfe->nexthop) + nexthop_free(nhlfe->nexthop); + + nhlfe->nexthop = NULL; + + XFREE(MTYPE_NHLFE, nhlfe); +} + + +/* + * Disconnect NHLFE from LSP, and free. Entry must be present on LSP's list. */ static int nhlfe_del(zebra_nhlfe_t *nhlfe) { @@ -1284,22 +1389,18 @@ static int nhlfe_del(zebra_nhlfe_t *nhlfe) if (!lsp) return -1; - /* Free nexthop. */ - if (nhlfe->nexthop) - nexthop_free(nhlfe->nexthop); - - /* Unlink from LSP */ - if (nhlfe->next) - nhlfe->next->prev = nhlfe->prev; - if (nhlfe->prev) - nhlfe->prev->next = nhlfe->next; - else - lsp->nhlfe_list = nhlfe->next; - if (nhlfe == lsp->best_nhlfe) lsp->best_nhlfe = NULL; - XFREE(MTYPE_NHLFE, nhlfe); + /* Unlink from LSP */ + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_IS_BACKUP)) + nhlfe_list_del(&lsp->backup_nhlfe_list, nhlfe); + else + nhlfe_list_del(&lsp->nhlfe_list, nhlfe); + + nhlfe->lsp = NULL; + + nhlfe_free(nhlfe); return 0; } @@ -1316,14 +1417,12 @@ static void nhlfe_out_label_update(zebra_nhlfe_t *nhlfe, static int mpls_lsp_uninstall_all(struct hash *lsp_table, zebra_lsp_t *lsp, enum lsp_types_t type) { - zebra_nhlfe_t *nhlfe, *nhlfe_next; + zebra_nhlfe_t *nhlfe; int schedule_lsp = 0; char buf[BUFSIZ]; /* Mark NHLFEs for delete or directly delete, as appropriate. */ - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe_next) { - nhlfe_next = nhlfe->next; - + frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { /* Skip non-static NHLFEs */ if (nhlfe->type != type) continue; @@ -1344,13 +1443,34 @@ static int mpls_lsp_uninstall_all(struct hash *lsp_table, zebra_lsp_t *lsp, } } + frr_each_safe(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) { + /* Skip non-static NHLFEs */ + if (nhlfe->type != type) + continue; + + if (IS_ZEBRA_DEBUG_MPLS) { + nhlfe2str(nhlfe, buf, BUFSIZ); + zlog_debug( + "Del backup LSP in-label %u type %d nexthop %s flags 0x%x", + lsp->ile.in_label, type, buf, nhlfe->flags); + } + + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) { + UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); + SET_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED); + schedule_lsp = 1; + } else { + nhlfe_del(nhlfe); + } + } + /* Queue LSP for processing, if needed, else delete. */ if (schedule_lsp) { if (lsp_processq_add(lsp)) return -1; - } else if (!lsp->nhlfe_list - && !CHECK_FLAG(lsp->flags, LSP_FLAG_SCHEDULED)) - lsp_free(lsp_table, &lsp); + } else { + lsp_check_free(lsp_table, &lsp); + } return 0; } @@ -1374,7 +1494,7 @@ static int mpls_static_lsp_uninstall_all(struct zebra_vrf *zvrf, /* If entry is not present, exit. */ tmp_ile.in_label = in_label; lsp = hash_lookup(lsp_table, &tmp_ile); - if (!lsp || !lsp->nhlfe_list) + if (!lsp || (nhlfe_list_first(&lsp->nhlfe_list) == NULL)) return 0; return mpls_lsp_uninstall_all(lsp_table, lsp, ZEBRA_LSP_STATIC); @@ -1424,7 +1544,7 @@ static json_object *nhlfe_json(zebra_nhlfe_t *nhlfe) static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty) { struct nexthop *nexthop; - char buf[BUFSIZ]; + char buf[MPLS_LABEL_STRLEN]; nexthop = nhlfe->nexthop; if (!nexthop || !nexthop->nh_label) // unexpected @@ -1432,7 +1552,9 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty) vty_out(vty, " type: %s remote label: %s distance: %d\n", nhlfe_type2str(nhlfe->type), - label2str(nexthop->nh_label->label[0], buf, BUFSIZ), + mpls_label2str(nexthop->nh_label->num_labels, + nexthop->nh_label->label, + buf, sizeof(buf), 0), nhlfe->distance); switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: @@ -1464,19 +1586,36 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty) /* * Print an LSP forwarding entry. */ -static void lsp_print(zebra_lsp_t *lsp, void *ctxt) +static void lsp_print(struct vty *vty, zebra_lsp_t *lsp) { - zebra_nhlfe_t *nhlfe; - struct vty *vty; - - vty = (struct vty *)ctxt; + zebra_nhlfe_t *nhlfe, *backup; + int i; vty_out(vty, "Local label: %u%s\n", lsp->ile.in_label, CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED) ? " (installed)" : ""); - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + frr_each(nhlfe_list, &lsp->nhlfe_list, nhlfe) { nhlfe_print(nhlfe, vty); + + if (nhlfe->nexthop && + CHECK_FLAG(nhlfe->nexthop->flags, + NEXTHOP_FLAG_HAS_BACKUP)) { + /* Find backup in backup list */ + + i = 0; + frr_each(nhlfe_list, &lsp->backup_nhlfe_list, backup) { + if (i == nhlfe->nexthop->backup_idx) + break; + i++; + } + + if (backup) { + vty_out(vty, " [backup %d]", i); + nhlfe_print(backup, vty); + } + } + } } /* @@ -1493,7 +1632,7 @@ static json_object *lsp_json(zebra_lsp_t *lsp) if (CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED)) json_object_boolean_true_add(json, "installed"); - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + frr_each(nhlfe_list, &lsp->nhlfe_list, nhlfe) json_object_array_add(json_nhlfe_list, nhlfe_json(nhlfe)); json_object_object_add(json, "nexthops", json_nhlfe_list); @@ -1541,6 +1680,8 @@ static void *slsp_alloc(void *p) slsp = XCALLOC(MTYPE_SLSP, sizeof(zebra_slsp_t)); slsp->ile = *ile; + snhlfe_list_init(&slsp->snhlfe_list); + return ((void *)slsp); } @@ -1600,7 +1741,7 @@ static zebra_snhlfe_t *snhlfe_find(zebra_slsp_t *slsp, if (!slsp) return NULL; - for (snhlfe = slsp->snhlfe_list; snhlfe; snhlfe = snhlfe->next) { + frr_each_safe(snhlfe_list, &slsp->snhlfe_list, snhlfe) { if (!snhlfe_match(snhlfe, gtype, gate, ifindex)) break; } @@ -1642,10 +1783,7 @@ static zebra_snhlfe_t *snhlfe_add(zebra_slsp_t *slsp, return NULL; } - if (slsp->snhlfe_list) - slsp->snhlfe_list->prev = snhlfe; - snhlfe->next = slsp->snhlfe_list; - slsp->snhlfe_list = snhlfe; + snhlfe_list_add_head(&slsp->snhlfe_list, snhlfe); return snhlfe; } @@ -1664,14 +1802,8 @@ static int snhlfe_del(zebra_snhlfe_t *snhlfe) if (!slsp) return -1; - if (snhlfe->next) - snhlfe->next->prev = snhlfe->prev; - if (snhlfe->prev) - snhlfe->prev->next = snhlfe->next; - else - slsp->snhlfe_list = snhlfe->next; + snhlfe_list_del(&slsp->snhlfe_list, snhlfe); - snhlfe->prev = snhlfe->next = NULL; XFREE(MTYPE_SNHLFE_IFNAME, snhlfe->ifname); XFREE(MTYPE_SNHLFE, snhlfe); @@ -1683,13 +1815,12 @@ static int snhlfe_del(zebra_snhlfe_t *snhlfe) */ static int snhlfe_del_all(zebra_slsp_t *slsp) { - zebra_snhlfe_t *snhlfe, *snhlfe_next; + zebra_snhlfe_t *snhlfe; if (!slsp) return -1; - for (snhlfe = slsp->snhlfe_list; snhlfe; snhlfe = snhlfe_next) { - snhlfe_next = snhlfe->next; + frr_each_safe(snhlfe_list, &slsp->snhlfe_list, snhlfe) { snhlfe_del(snhlfe); } @@ -1793,8 +1924,7 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) { /* Update zebra object */ SET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); - for (nhlfe = lsp->nhlfe_list; nhlfe; - nhlfe = nhlfe->next) { + frr_each(nhlfe_list, &lsp->nhlfe_list, nhlfe) { nexthop = nhlfe->nexthop; if (!nexthop) continue; @@ -1837,6 +1967,7 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) struct hash *lsp_table; zebra_lsp_t *lsp; zebra_nhlfe_t *nhlfe; + const struct nhlfe_list_head *head; const zebra_nhlfe_t *ctx_nhlfe; struct nexthop *nexthop; const struct nexthop *ctx_nexthop; @@ -1872,7 +2003,8 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) * the existing state of the LSP objects available before making * any changes. */ - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) { + head = dplane_ctx_get_nhlfe_list(ctx); + frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { char buf[NEXTHOP_STRLEN]; nexthop = nhlfe->nexthop; @@ -1883,9 +2015,7 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) start_count++; ctx_nexthop = NULL; - for (ctx_nhlfe = dplane_ctx_get_nhlfe(ctx); - ctx_nhlfe; ctx_nhlfe = ctx_nhlfe->next) { - + frr_each(nhlfe_list_const, head, ctx_nhlfe) { ctx_nexthop = ctx_nhlfe->nexthop; if (!ctx_nexthop) continue; @@ -1960,7 +2090,7 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) * Now we take a second pass and bring the zebra * nexthop state into sync with the forwarding-plane state. */ - for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) { + frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { char buf[NEXTHOP_STRLEN]; nexthop = nhlfe->nexthop; @@ -1968,9 +2098,7 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) continue; ctx_nexthop = NULL; - for (ctx_nhlfe = dplane_ctx_get_nhlfe(ctx); - ctx_nhlfe; ctx_nhlfe = ctx_nhlfe->next) { - + frr_each(nhlfe_list_const, head, ctx_nhlfe) { ctx_nexthop = ctx_nhlfe->nexthop; if (!ctx_nexthop) continue; @@ -2088,7 +2216,8 @@ int zebra_mpls_lsp_uninstall(struct zebra_vrf *zvrf, struct route_node *rn, } /* - * Add an NHLFE to an LSP, return the newly-added object + * Add an NHLFE to an LSP, return the newly-added object. This path only changes + * the LSP object - nothing is scheduled for processing, for example. */ zebra_nhlfe_t *zebra_mpls_lsp_add_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, @@ -2096,20 +2225,76 @@ zebra_nhlfe_t *zebra_mpls_lsp_add_nhlfe(zebra_lsp_t *lsp, union g_addr *gate, ifindex_t ifindex, uint8_t num_labels, - mpls_label_t out_labels[]) + const mpls_label_t *out_labels) { /* Just a public pass-through to the internal implementation */ return nhlfe_add(lsp, lsp_type, gtype, gate, ifindex, num_labels, out_labels); } +/* + * Add a backup NHLFE to an LSP, return the newly-added object. + * This path only changes the LSP object - nothing is scheduled for + * processing, for example. + */ +zebra_nhlfe_t *zebra_mpls_lsp_add_backup_nhlfe(zebra_lsp_t *lsp, + enum lsp_types_t lsp_type, + enum nexthop_types_t gtype, + union g_addr *gate, + ifindex_t ifindex, + uint8_t num_labels, + const mpls_label_t *out_labels) +{ + /* Just a public pass-through to the internal implementation */ + return nhlfe_backup_add(lsp, lsp_type, gtype, gate, ifindex, num_labels, + out_labels); +} + +/* + * Add an NHLFE to an LSP based on a nexthop; return the newly-added object + */ +zebra_nhlfe_t *zebra_mpls_lsp_add_nh(zebra_lsp_t *lsp, + enum lsp_types_t lsp_type, + const struct nexthop *nh) +{ + zebra_nhlfe_t *nhlfe; + + if (nh->nh_label == NULL || nh->nh_label->num_labels == 0) + return NULL; + + nhlfe = nhlfe_add(lsp, lsp_type, nh->type, &nh->gate, nh->ifindex, + nh->nh_label->num_labels, nh->nh_label->label); + + return nhlfe; +} + +/* + * Add a backup NHLFE to an LSP based on a nexthop; + * return the newly-added object. + */ +zebra_nhlfe_t *zebra_mpls_lsp_add_backup_nh(zebra_lsp_t *lsp, + enum lsp_types_t lsp_type, + const struct nexthop *nh) +{ + zebra_nhlfe_t *nhlfe; + + if (nh->nh_label == NULL || nh->nh_label->num_labels == 0) + return NULL; + + nhlfe = nhlfe_backup_add(lsp, lsp_type, nh->type, &nh->gate, + nh->ifindex, nh->nh_label->num_labels, + nh->nh_label->label); + + return nhlfe; +} + /* * Free an allocated NHLFE */ -void zebra_mpls_nhlfe_del(zebra_nhlfe_t *nhlfe) +void zebra_mpls_nhlfe_free(zebra_nhlfe_t *nhlfe) { /* Just a pass-through to the internal implementation */ - nhlfe_del(nhlfe); + nhlfe_free(nhlfe); } /* @@ -2579,22 +2764,23 @@ void zebra_mpls_print_fec(struct vty *vty, struct zebra_vrf *zvrf, fec_print(rn->info, vty); } -static void mpls_zebra_nhg_update(struct route_entry *re, afi_t afi, - struct nexthop_group *new_grp) +static void mpls_zebra_nhe_update(struct route_entry *re, afi_t afi, + struct nhg_hash_entry *new_nhe) { struct nhg_hash_entry *nhe; - nhe = zebra_nhg_rib_find(0, new_grp, afi); + nhe = zebra_nhg_rib_find_nhe(new_nhe, afi); route_entry_update_nhe(re, nhe); } -static bool mpls_ftn_update_nexthop(int add, struct nexthop *nexthop, - enum lsp_types_t type, mpls_label_t label) +static bool ftn_update_nexthop(bool add_p, struct nexthop *nexthop, + enum lsp_types_t type, + const struct zapi_nexthop *znh) { - if (add && nexthop->nh_label_type == ZEBRA_LSP_NONE) - nexthop_add_labels(nexthop, type, 1, &label); - else if (!add && nexthop->nh_label_type == type) + if (add_p && nexthop->nh_label_type == ZEBRA_LSP_NONE) + nexthop_add_labels(nexthop, type, znh->label_num, znh->labels); + else if (!add_p && nexthop->nh_label_type == type) nexthop_del_labels(nexthop); else return false; @@ -2602,98 +2788,6 @@ static bool mpls_ftn_update_nexthop(int add, struct nexthop *nexthop, return true; } -/* - * Install/uninstall a FEC-To-NHLFE (FTN) binding. - */ -int mpls_ftn_update(int add, struct zebra_vrf *zvrf, enum lsp_types_t type, - struct prefix *prefix, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex, uint8_t route_type, - unsigned short route_instance, mpls_label_t out_label) -{ - struct route_table *table; - struct route_node *rn; - struct route_entry *re; - struct nexthop *nexthop; - struct nexthop_group new_grp = {}; - bool found; - afi_t afi = family2afi(prefix->family); - - /* Lookup table. */ - table = zebra_vrf_table(afi, SAFI_UNICAST, zvrf_id(zvrf)); - if (!table) - return -1; - - /* Lookup existing route */ - rn = route_node_get(table, prefix); - RNODE_FOREACH_RE (rn, re) { - if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) - continue; - if (re->type == route_type && re->instance == route_instance) - break; - } - - if (re == NULL) - return -1; - - /* - * Copy over current nexthops into a temporary group. - * We can't just change the values here since we are hashing - * on labels. We need to create a whole new group - */ - nexthop_group_copy(&new_grp, &(re->nhe->nhg)); - - found = false; - for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next) { - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - if (gtype != NEXTHOP_TYPE_IPV4 - && gtype != NEXTHOP_TYPE_IPV4_IFINDEX) - continue; - if (!IPV4_ADDR_SAME(&nexthop->gate.ipv4, &gate->ipv4)) - continue; - if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX - && nexthop->ifindex != ifindex) - continue; - if (!mpls_ftn_update_nexthop(add, nexthop, type, - out_label)) - break; - found = true; - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - if (gtype != NEXTHOP_TYPE_IPV6 - && gtype != NEXTHOP_TYPE_IPV6_IFINDEX) - continue; - if (!IPV6_ADDR_SAME(&nexthop->gate.ipv6, &gate->ipv6)) - continue; - if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX - && nexthop->ifindex != ifindex) - continue; - if (!mpls_ftn_update_nexthop(add, nexthop, type, - out_label)) - break; - found = true; - break; - default: - break; - } - } - - if (found) { - SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); - SET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED); - - mpls_zebra_nhg_update(re, afi, &new_grp); - - rib_queue_add(rn); - } - - nexthops_free(new_grp.nexthop); - - return found ? 0 : -1; -} - int mpls_ftn_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, struct prefix *prefix, uint8_t route_type, unsigned short route_instance) @@ -2702,7 +2796,7 @@ int mpls_ftn_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, struct route_node *rn; struct route_entry *re; struct nexthop *nexthop; - struct nexthop_group new_grp = {}; + struct nhg_hash_entry *new_nhe; afi_t afi = family2afi(prefix->family); /* Lookup table. */ @@ -2721,51 +2815,308 @@ int mpls_ftn_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, if (re == NULL) return -1; - nexthop_group_copy(&new_grp, &(re->nhe->nhg)); + /* + * Nexthops are now shared by multiple routes, so we have to make + * a local copy, modify the copy, then update the route. + */ + new_nhe = zebra_nhe_copy(re->nhe, 0); - for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next) + for (nexthop = new_nhe->nhg.nexthop; nexthop; nexthop = nexthop->next) nexthop_del_labels(nexthop); + /* Update backup routes/nexthops also, if present. */ + if (zebra_nhg_get_backup_nhg(new_nhe) != NULL) { + for (nexthop = new_nhe->backup_info->nhe->nhg.nexthop; nexthop; + nexthop = nexthop->next) + nexthop_del_labels(nexthop); + } + SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); SET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED); - mpls_zebra_nhg_update(re, afi, &new_grp); + mpls_zebra_nhe_update(re, afi, new_nhe); - nexthops_free(new_grp.nexthop); + zebra_nhg_free(new_nhe); rib_queue_add(rn); return 0; } +/* + * Iterate through a list of nexthops, for a match for 'znh'. If found, + * update its labels according to 'add_p', and return 'true' if successful. + */ +static bool ftn_update_znh(bool add_p, enum lsp_types_t type, + struct nexthop *head, const struct zapi_nexthop *znh) +{ + bool found = false, success = false; + struct nexthop *nexthop; + + for (nexthop = head; nexthop; nexthop = nexthop->next) { + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + if (znh->type != NEXTHOP_TYPE_IPV4 + && znh->type != NEXTHOP_TYPE_IPV4_IFINDEX) + continue; + if (!IPV4_ADDR_SAME(&nexthop->gate.ipv4, + &znh->gate.ipv4)) + continue; + if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX + && nexthop->ifindex != znh->ifindex) + continue; + + found = true; + + if (!ftn_update_nexthop(add_p, nexthop, type, znh)) + break; + + success = true; + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (znh->type != NEXTHOP_TYPE_IPV6 + && znh->type != NEXTHOP_TYPE_IPV6_IFINDEX) + continue; + if (!IPV6_ADDR_SAME(&nexthop->gate.ipv6, + &znh->gate.ipv6)) + continue; + if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX + && nexthop->ifindex != znh->ifindex) + continue; + + found = true; + + if (!ftn_update_nexthop(add_p, nexthop, type, znh)) + break; + success = true; + break; + default: + break; + } + + if (found) + break; + } + + return success; +} + +/* + * Install/uninstall LSP and (optionally) FEC-To-NHLFE (FTN) bindings, + * using zapi message info. + * There are several changes that need to be made, in several zebra + * data structures, so we want to do all the work required at once. + */ +int mpls_zapi_labels_process(bool add_p, struct zebra_vrf *zvrf, + const struct zapi_labels *zl) +{ + int i, counter, ret = 0; + char buf[NEXTHOP_STRLEN], prefix_buf[PREFIX_STRLEN]; + const struct zapi_nexthop *znh; + struct route_table *table; + struct route_node *rn = NULL; + struct route_entry *re = NULL; + struct nhg_hash_entry *new_nhe = NULL; + bool found; + afi_t afi = AFI_IP; + const struct prefix *prefix = NULL; + struct hash *lsp_table; + zebra_ile_t tmp_ile; + zebra_lsp_t *lsp = NULL; + + /* Prep LSP for add case */ + if (add_p) { + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return -1; + + /* Find or create LSP object */ + tmp_ile.in_label = zl->local_label; + lsp = hash_get(lsp_table, &tmp_ile, lsp_alloc); + if (!lsp) + return -1; + } + + /* Prep for route/FEC update if requested */ + if (CHECK_FLAG(zl->message, ZAPI_LABELS_FTN)) { + prefix = &zl->route.prefix; + + afi = family2afi(prefix->family); + + /* Lookup table. */ + table = zebra_vrf_table(afi, SAFI_UNICAST, zvrf_id(zvrf)); + if (table) { + /* Lookup existing route */ + rn = route_node_get(table, prefix); + RNODE_FOREACH_RE(rn, re) { + if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) + continue; + if (re->type == zl->route.type && + re->instance == zl->route.instance) + break; + } + } + + if (re) { + /* + * Copy over current nexthops into a temporary group. + * We can't just change the values here since the nhgs + * are shared and if the labels change, we'll need + * to find or create a new nhg. We need to create + * a whole temporary group, make changes to it, + * then attach that to the route. + */ + new_nhe = zebra_nhe_copy(re->nhe, 0); + + } else { + /* + * The old version of the zapi code + * attempted to manage LSPs before trying to + * find a route/FEC, so we'll continue that way. + */ + if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_MPLS) { + prefix2str(prefix, prefix_buf, + sizeof(prefix_buf)); + zlog_debug("%s: FTN update requested: no route for prefix %s", + __func__, prefix_buf); + } + } + } + + /* + * Use info from the zapi nexthops to add/replace/remove LSP/FECs + */ + + counter = 0; + for (i = 0; i < zl->nexthop_num; i++) { + + znh = &zl->nexthops[i]; + + /* Attempt LSP update */ + if (add_p) + ret = lsp_znh_install(lsp, zl->type, znh); + else + ret = mpls_lsp_uninstall(zvrf, zl->type, + zl->local_label, znh->type, + &znh->gate, znh->ifindex); + if (ret < 0) { + if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_MPLS) { + zapi_nexthop2str(znh, buf, sizeof(buf)); + zlog_debug("%s: Unable to %sinstall LSP: label %u, znh %s", + __func__, (add_p ? "" : "un"), + zl->local_label, buf); + } + continue; + } + + /* Attempt route/FEC update if requested */ + if (re == NULL) + continue; + + /* Search the route's nexthops for a match, and update it. */ + found = ftn_update_znh(add_p, zl->type, new_nhe->nhg.nexthop, + znh); + if (found) { + counter++; + } else if (IS_ZEBRA_DEBUG_RECV | IS_ZEBRA_DEBUG_MPLS) { + zapi_nexthop2str(znh, buf, sizeof(buf)); + prefix2str(prefix, prefix_buf, sizeof(prefix_buf)); + zlog_debug("%s: Unable to update FEC: prefix %s, label %u, znh %s", + __func__, prefix_buf, zl->local_label, buf); + } + } + + /* + * Process backup LSPs/nexthop entries also. We associate backup + * LSP info with backup nexthops. + */ + if (!CHECK_FLAG(zl->message, ZAPI_LABELS_HAS_BACKUPS)) + goto znh_done; + + for (i = 0; i < zl->backup_nexthop_num; i++) { + + znh = &zl->backup_nexthops[i]; + + if (add_p) + ret = lsp_backup_znh_install(lsp, zl->type, znh); + else + ret = lsp_backup_uninstall(zvrf, zl->type, + zl->local_label, + znh->type, &znh->gate, + znh->ifindex); + + if (ret < 0) { + if (IS_ZEBRA_DEBUG_RECV || + IS_ZEBRA_DEBUG_MPLS) { + zapi_nexthop2str(znh, buf, sizeof(buf)); + zlog_debug("%s: Unable to %sinstall backup LSP: label %u, znh %s", + __func__, (add_p ? "" : "un"), + zl->local_label, buf); + } + continue; + } + + /* Attempt backup nexthop/FEC update if requested */ + if (re == NULL || zebra_nhg_get_backup_nhg(new_nhe) == NULL) + continue; + + /* Search the route's backup nexthops for a match + * and update it. + */ + found = ftn_update_znh(add_p, zl->type, + new_nhe->backup_info->nhe->nhg.nexthop, + znh); + if (found) { + counter++; + } else if (IS_ZEBRA_DEBUG_RECV | IS_ZEBRA_DEBUG_MPLS) { + zapi_nexthop2str(znh, buf, sizeof(buf)); + prefix2str(prefix, prefix_buf, sizeof(prefix_buf)); + zlog_debug("%s: Unable to update backup FEC: prefix %s, label %u, znh %s", + __func__, prefix_buf, zl->local_label, buf); + } + } + +znh_done: + + /* + * If we made changes, update the route, and schedule it + * for rib processing + */ + if (re != NULL && counter > 0) { + assert(rn != NULL); + + SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); + SET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED); + + mpls_zebra_nhe_update(re, afi, new_nhe); + + rib_queue_add(rn); + } + + if (new_nhe) + zebra_nhg_free(new_nhe); + + return ret; +} + /* * Install/update a NHLFE for an LSP in the forwarding table. This may be * a new LSP entry or a new NHLFE for an existing in-label or an update of * the out-label for an existing NHLFE (update case). */ -int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, - mpls_label_t in_label, uint8_t num_out_labels, - mpls_label_t out_labels[], enum nexthop_types_t gtype, - const union g_addr *gate, ifindex_t ifindex) +static zebra_nhlfe_t * +lsp_add_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t type, + uint8_t num_out_labels, const mpls_label_t *out_labels, + enum nexthop_types_t gtype, const union g_addr *gate, + ifindex_t ifindex) { - struct hash *lsp_table; - zebra_ile_t tmp_ile; - zebra_lsp_t *lsp; zebra_nhlfe_t *nhlfe; - char buf[BUFSIZ]; + char buf[MPLS_LABEL_STRLEN]; - /* Lookup table. */ - lsp_table = zvrf->lsp_table; - if (!lsp_table) - return -1; - - /* Find or create LSP object */ - tmp_ile.in_label = in_label; - lsp = hash_get(lsp_table, &tmp_ile, lsp_alloc); - if (!lsp) - return -1; - - nhlfe = nhlfe_find(lsp, type, gtype, gate, ifindex); + nhlfe = nhlfe_find(&lsp->nhlfe_list, type, gtype, gate, ifindex); if (nhlfe) { struct nexthop *nh = nhlfe->nexthop; @@ -2778,23 +3129,21 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, && !memcmp(nh->nh_label->label, out_labels, sizeof(mpls_label_t) * num_out_labels)) /* No change */ - return 0; + return nhlfe; if (IS_ZEBRA_DEBUG_MPLS) { char buf2[MPLS_LABEL_STRLEN]; char buf3[MPLS_LABEL_STRLEN]; - nhlfe2str(nhlfe, buf, BUFSIZ); + nhlfe2str(nhlfe, buf, sizeof(buf)); mpls_label2str(num_out_labels, out_labels, buf2, sizeof(buf2), 0); mpls_label2str(nh->nh_label->num_labels, nh->nh_label->label, buf3, sizeof(buf3), 0); - zlog_debug( - "LSP in-label %u type %d nexthop %s " - "out-label(s) changed to %s (old %s)", - in_label, type, buf, buf2, buf3); + zlog_debug("LSP in-label %u type %d nexthop %s out-label(s) changed to %s (old %s)", + lsp->ile.in_label, type, buf, buf2, buf3); } /* Update out label(s), trigger processing. */ @@ -2811,19 +3160,17 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, nhlfe = nhlfe_add(lsp, type, gtype, gate, ifindex, num_out_labels, out_labels); if (!nhlfe) - return -1; + return NULL; if (IS_ZEBRA_DEBUG_MPLS) { - char buf2[BUFSIZ]; + char buf2[MPLS_LABEL_STRLEN]; - nhlfe2str(nhlfe, buf, BUFSIZ); + nhlfe2str(nhlfe, buf, sizeof(buf)); mpls_label2str(num_out_labels, out_labels, buf2, sizeof(buf2), 0); - zlog_debug( - "Add LSP in-label %u type %d nexthop %s " - "out-label(s) %s", - in_label, type, buf, buf2); + zlog_debug("Add LSP in-label %u type %d nexthop %s out-label(s) %s", + lsp->ile.in_label, type, buf, buf2); } lsp->addr_family = NHLFE_FAMILY(nhlfe); @@ -2831,6 +3178,172 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, /* Mark NHLFE, queue LSP for processing. */ SET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); + + return nhlfe; +} + +/* + * Install/update a NHLFE for an LSP in the forwarding table. This may be + * a new LSP entry or a new NHLFE for an existing in-label or an update of + * the out-label for an existing NHLFE (update case). + */ +static zebra_nhlfe_t * +lsp_add_backup_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t type, + uint8_t num_out_labels, const mpls_label_t *out_labels, + enum nexthop_types_t gtype, const union g_addr *gate, + ifindex_t ifindex) +{ + zebra_nhlfe_t *nhlfe; + char buf[MPLS_LABEL_STRLEN]; + + nhlfe = nhlfe_find(&lsp->backup_nhlfe_list, type, gtype, gate, ifindex); + if (nhlfe) { + struct nexthop *nh = nhlfe->nexthop; + + assert(nh); + assert(nh->nh_label); + + /* Clear deleted flag (in case it was set) */ + UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED); + if (nh->nh_label->num_labels == num_out_labels + && !memcmp(nh->nh_label->label, out_labels, + sizeof(mpls_label_t) * num_out_labels)) + /* No change */ + return nhlfe; + + if (IS_ZEBRA_DEBUG_MPLS) { + char buf2[MPLS_LABEL_STRLEN]; + char buf3[MPLS_LABEL_STRLEN]; + + nhlfe2str(nhlfe, buf, sizeof(buf)); + mpls_label2str(num_out_labels, out_labels, buf2, + sizeof(buf2), 0); + mpls_label2str(nh->nh_label->num_labels, + nh->nh_label->label, buf3, sizeof(buf3), + 0); + + zlog_debug("LSP in-label %u type %d backup nexthop %s out-label(s) changed to %s (old %s)", + lsp->ile.in_label, type, buf, buf2, buf3); + } + + /* Update out label(s), trigger processing. */ + if (nh->nh_label->num_labels == num_out_labels) + memcpy(nh->nh_label->label, out_labels, + sizeof(mpls_label_t) * num_out_labels); + else { + nexthop_del_labels(nh); + nexthop_add_labels(nh, type, num_out_labels, + out_labels); + } + } else { + /* Add LSP entry to this nexthop */ + nhlfe = nhlfe_backup_add(lsp, type, gtype, gate, ifindex, + num_out_labels, out_labels); + if (!nhlfe) + return NULL; + + if (IS_ZEBRA_DEBUG_MPLS) { + char buf2[MPLS_LABEL_STRLEN]; + + nhlfe2str(nhlfe, buf, sizeof(buf)); + mpls_label2str(num_out_labels, out_labels, buf2, + sizeof(buf2), 0); + + zlog_debug("Add LSP in-label %u type %d backup nexthop %s out-label(s) %s", + lsp->ile.in_label, type, buf, buf2); + } + + lsp->addr_family = NHLFE_FAMILY(nhlfe); + } + + /* Mark NHLFE, queue LSP for processing. */ + SET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); + + return nhlfe; +} + +/* + * Install an LSP and forwarding entry; used primarily + * from zapi message processing. + */ +int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, + mpls_label_t in_label, uint8_t num_out_labels, + const mpls_label_t *out_labels, enum nexthop_types_t gtype, + const union g_addr *gate, ifindex_t ifindex) +{ + struct hash *lsp_table; + zebra_ile_t tmp_ile; + zebra_lsp_t *lsp; + zebra_nhlfe_t *nhlfe; + + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return -1; + + /* Find or create LSP object */ + tmp_ile.in_label = in_label; + lsp = hash_get(lsp_table, &tmp_ile, lsp_alloc); + if (!lsp) + return -1; + + nhlfe = lsp_add_nhlfe(lsp, type, num_out_labels, out_labels, gtype, + gate, ifindex); + if (nhlfe == NULL) + return -1; + + /* Queue LSP for processing. */ + if (lsp_processq_add(lsp)) + return -1; + + return 0; +} + +/* + * Install or replace NHLFE, using info from zapi nexthop + */ +static int lsp_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type, + const struct zapi_nexthop *znh) +{ + zebra_nhlfe_t *nhlfe; + + nhlfe = lsp_add_nhlfe(lsp, type, znh->label_num, znh->labels, + znh->type, &znh->gate, znh->ifindex); + if (nhlfe == NULL) + return -1; + + /* Update backup info if present */ + if (CHECK_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) { + nhlfe->nexthop->backup_idx = znh->backup_idx; + SET_FLAG(nhlfe->nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP); + } + + /* Queue LSP for processing. */ + if (lsp_processq_add(lsp)) + return -1; + + return 0; +} + +/* + * Install/update backup NHLFE for an LSP, using info from a zapi message. + */ +static int lsp_backup_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type, + const struct zapi_nexthop *znh) +{ + zebra_nhlfe_t *nhlfe; + + nhlfe = lsp_add_backup_nhlfe(lsp, type, znh->label_num, + znh->labels, znh->type, &znh->gate, + znh->ifindex); + if (nhlfe == NULL) { + if (IS_ZEBRA_DEBUG_MPLS) + zlog_debug("%s: unable to add backup nhlfe, label: %u", + __func__, lsp->ile.in_label); + return -1; + } + + /* Queue LSP for processing. */ if (lsp_processq_add(lsp)) return -1; @@ -2861,7 +3374,7 @@ int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, lsp = hash_lookup(lsp_table, &tmp_ile); if (!lsp) return 0; - nhlfe = nhlfe_find(lsp, type, gtype, gate, ifindex); + nhlfe = nhlfe_find(&lsp->nhlfe_list, type, gtype, gate, ifindex); if (!nhlfe) return 0; @@ -2881,14 +3394,62 @@ int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, nhlfe_del(nhlfe); /* Free LSP entry if no other NHLFEs and not scheduled. */ - if (!lsp->nhlfe_list - && !CHECK_FLAG(lsp->flags, LSP_FLAG_SCHEDULED)) - lsp_free(lsp_table, &lsp); + lsp_check_free(lsp_table, &lsp); } return 0; } +/* + * Uninstall a particular NHLFE in the forwarding table. If this is + * the only NHLFE, the entire LSP forwarding entry has to be deleted. + */ +static int lsp_backup_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, + mpls_label_t in_label, + enum nexthop_types_t gtype, + const union g_addr *gate, ifindex_t ifindex) +{ + struct hash *lsp_table; + zebra_ile_t tmp_ile; + zebra_lsp_t *lsp; + zebra_nhlfe_t *nhlfe; + char buf[BUFSIZ]; + + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return -1; + + /* If entry is not present, exit. */ + tmp_ile.in_label = in_label; + lsp = hash_lookup(lsp_table, &tmp_ile); + if (!lsp) + return 0; + nhlfe = nhlfe_find(&lsp->backup_nhlfe_list, type, gtype, gate, ifindex); + if (!nhlfe) + return 0; + + if (IS_ZEBRA_DEBUG_MPLS) { + nhlfe2str(nhlfe, buf, BUFSIZ); + zlog_debug("Del backup LSP in-label %u type %d nexthop %s flags 0x%x", + in_label, type, buf, nhlfe->flags); + } + + /* Mark NHLFE for delete or directly delete, as appropriate. */ + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) { + UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); + SET_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED); + if (lsp_processq_add(lsp)) + return -1; + } else { + nhlfe_del(nhlfe); + + /* Free LSP entry if no other NHLFEs and not scheduled. */ + lsp_check_free(lsp_table, &lsp); + } + return 0; +} + int mpls_lsp_uninstall_all_vrf(struct zebra_vrf *zvrf, enum lsp_types_t type, mpls_label_t in_label) { @@ -2921,7 +3482,7 @@ static void mpls_lsp_uninstall_all_type(struct hash_bucket *bucket, void *ctxt) struct hash *lsp_table; lsp = (zebra_lsp_t *)bucket->data; - if (!lsp->nhlfe_list) + if (nhlfe_list_first(&lsp->nhlfe_list) == NULL) return; lsp_table = args->lsp_table; @@ -2942,7 +3503,8 @@ static void mpls_ftn_uninstall_all(struct zebra_vrf *zvrf, struct route_node *rn; struct route_entry *re; struct nexthop *nexthop; - int update; + struct nexthop_group *nhg; + bool update; /* Process routes of interested address-families. */ table = zebra_vrf_table(afi, SAFI_UNICAST, zvrf_id(zvrf)); @@ -2950,13 +3512,15 @@ static void mpls_ftn_uninstall_all(struct zebra_vrf *zvrf, return; for (rn = route_top(table); rn; rn = route_next(rn)) { - update = 0; + update = false; + RNODE_FOREACH_RE (rn, re) { - struct nexthop_group new_grp = {}; + struct nhg_hash_entry *new_nhe; - nexthop_group_copy(&new_grp, &(re->nhe->nhg)); + new_nhe = zebra_nhe_copy(re->nhe, 0); - for (nexthop = new_grp.nexthop; nexthop; + nhg = &new_nhe->nhg; + for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) { if (nexthop->nh_label_type != lsp_type) continue; @@ -2965,13 +3529,30 @@ static void mpls_ftn_uninstall_all(struct zebra_vrf *zvrf, SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); SET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED); - update = 1; + update = true; + } + + /* Check for backup info and update that also */ + nhg = zebra_nhg_get_backup_nhg(new_nhe); + if (nhg != NULL) { + for (nexthop = nhg->nexthop; nexthop; + nexthop = nexthop->next) { + if (nexthop->nh_label_type != lsp_type) + continue; + + nexthop_del_labels(nexthop); + SET_FLAG(re->status, + ROUTE_ENTRY_CHANGED); + SET_FLAG(re->status, + ROUTE_ENTRY_LABELS_CHANGED); + update = true; + } } if (CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED)) - mpls_zebra_nhg_update(re, afi, &new_grp); + mpls_zebra_nhe_update(re, afi, new_nhe); - nexthops_free(new_grp.nexthop); + zebra_nhg_free(new_nhe); } if (update) @@ -3014,15 +3595,17 @@ int zebra_mpls_lsp_label_consistent(struct zebra_vrf *zvrf, return 1; /* If not only NHLFE, cannot allow label change. */ - if (snhlfe != slsp->snhlfe_list || snhlfe->next) + if (snhlfe != snhlfe_list_first(&slsp->snhlfe_list) || + snhlfe_list_next(&slsp->snhlfe_list, snhlfe) != NULL) return 0; } else { /* If other NHLFEs exist, label operation must match. */ - if (slsp->snhlfe_list) { + snhlfe = snhlfe_list_first(&slsp->snhlfe_list); + if (snhlfe != NULL) { int cur_op, new_op; - cur_op = (slsp->snhlfe_list->out_label - == MPLS_LABEL_IMPLICIT_NULL); + cur_op = (snhlfe->out_label == + MPLS_LABEL_IMPLICIT_NULL); new_op = (out_label == MPLS_LABEL_IMPLICIT_NULL); if (cur_op != new_op) return 0; @@ -3159,7 +3742,7 @@ int zebra_mpls_static_lsp_del(struct zebra_vrf *zvrf, mpls_label_t in_label, /* Remove entire static LSP entry if no NHLFE - valid in either case * above. */ - if (!slsp->snhlfe_list) { + if (snhlfe_list_first(&slsp->snhlfe_list) == NULL) { slsp = hash_release(slsp_table, &tmp_ile); XFREE(MTYPE_SLSP, slsp); } @@ -3208,7 +3791,7 @@ void zebra_mpls_print_lsp(struct vty *vty, struct zebra_vrf *zvrf, json, JSON_C_TO_STRING_PRETTY)); json_object_free(json); } else - lsp_print(lsp, (void *)vty); + lsp_print(vty, lsp); } /* @@ -3247,8 +3830,7 @@ void zebra_mpls_print_lsp_table(struct vty *vty, struct zebra_vrf *zvrf, ttable_rowseps(tt, 0, BOTTOM, true, '-'); for (ALL_LIST_ELEMENTS_RO(lsp_list, node, lsp)) { - for (nhlfe = lsp->nhlfe_list; nhlfe; - nhlfe = nhlfe->next) { + frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { struct nexthop *nexthop; const char *out_label_str; char nh_buf[NEXTHOP_STRLEN]; @@ -3320,8 +3902,7 @@ int zebra_mpls_write_lsp_config(struct vty *vty, struct zebra_vrf *zvrf) hash_get_sorted_list(zvrf->slsp_table, slsp_cmp); for (ALL_LIST_ELEMENTS_RO(slsp_list, node, slsp)) { - for (snhlfe = slsp->snhlfe_list; snhlfe; - snhlfe = snhlfe->next) { + frr_each(snhlfe_list, &slsp->snhlfe_list, snhlfe) { char buf[BUFSIZ]; char lstr[30]; diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index e468fb9c1b..9b5fb39573 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -55,6 +55,10 @@ typedef struct zebra_nhlfe_t_ zebra_nhlfe_t; typedef struct zebra_lsp_t_ zebra_lsp_t; typedef struct zebra_fec_t_ zebra_fec_t; +/* Declare LSP nexthop list types */ +PREDECL_DLIST(snhlfe_list); +PREDECL_DLIST(nhlfe_list); + /* * (Outgoing) nexthop label forwarding entry configuration */ @@ -71,9 +75,8 @@ struct zebra_snhlfe_t_ { /* Backpointer to base entry. */ zebra_slsp_t *slsp; - /* Pointers to more outgoing information for same in-label */ - zebra_snhlfe_t *next; - zebra_snhlfe_t *prev; + /* Linkage for LSPs' lists */ + struct snhlfe_list_item list; }; /* @@ -96,10 +99,12 @@ struct zebra_nhlfe_t_ { #define NHLFE_FLAG_MULTIPATH (1 << 2) #define NHLFE_FLAG_DELETED (1 << 3) #define NHLFE_FLAG_INSTALLED (1 << 4) +#define NHLFE_FLAG_IS_BACKUP (1 << 5) - zebra_nhlfe_t *next; - zebra_nhlfe_t *prev; uint8_t distance; + + /* Linkage for LSPs' lists */ + struct nhlfe_list_item list; }; /* @@ -117,7 +122,7 @@ struct zebra_slsp_t_ { zebra_ile_t ile; /* List of outgoing nexthop static configuration */ - zebra_snhlfe_t *snhlfe_list; + struct snhlfe_list_head snhlfe_list; }; /* @@ -127,11 +132,18 @@ struct zebra_lsp_t_ { /* Incoming label */ zebra_ile_t ile; - /* List of NHLFE, pointer to best and num equal-cost. */ - zebra_nhlfe_t *nhlfe_list; + /* List of NHLFEs, pointer to best, and num equal-cost. */ + struct nhlfe_list_head nhlfe_list; + zebra_nhlfe_t *best_nhlfe; uint32_t num_ecmp; + /* Backup nhlfes, if present. The nexthop in a primary/active nhlfe + * refers to its backup (if any) by index, so the order of this list + * is significant. + */ + struct nhlfe_list_head backup_nhlfe_list; + /* Flags */ uint32_t flags; #define LSP_FLAG_SCHEDULED (1 << 0) @@ -164,6 +176,9 @@ struct zebra_fec_t_ { struct list *client_list; }; +/* Declare typesafe list apis/macros */ +DECLARE_DLIST(nhlfe_list, struct zebra_nhlfe_t_, list); + /* Function declarations. */ /* @@ -201,10 +216,31 @@ zebra_nhlfe_t *zebra_mpls_lsp_add_nhlfe(zebra_lsp_t *lsp, union g_addr *gate, ifindex_t ifindex, uint8_t num_labels, - mpls_label_t out_labels[]); + const mpls_label_t *out_labels); + +/* Add or update a backup NHLFE for an LSP; return the object */ +zebra_nhlfe_t *zebra_mpls_lsp_add_backup_nhlfe(zebra_lsp_t *lsp, + enum lsp_types_t lsp_type, + enum nexthop_types_t gtype, + union g_addr *gate, + ifindex_t ifindex, + uint8_t num_labels, + const mpls_label_t *out_labels); + +/* + * Add NHLFE or backup NHLFE to an LSP based on a nexthop. These just maintain + * the LSP and NHLFE objects; nothing is scheduled for processing. + * Return: the newly-added object + */ +zebra_nhlfe_t *zebra_mpls_lsp_add_nh(zebra_lsp_t *lsp, + enum lsp_types_t lsp_type, + const struct nexthop *nh); +zebra_nhlfe_t *zebra_mpls_lsp_add_backup_nh(zebra_lsp_t *lsp, + enum lsp_types_t lsp_type, + const struct nexthop *nh); /* Free an allocated NHLFE */ -void zebra_mpls_nhlfe_del(zebra_nhlfe_t *nhlfe); +void zebra_mpls_nhlfe_free(zebra_nhlfe_t *nhlfe); int zebra_mpls_fec_register(struct zebra_vrf *zvrf, struct prefix *p, uint32_t label, uint32_t label_index, @@ -266,12 +302,11 @@ void zebra_mpls_print_fec(struct vty *vty, struct zebra_vrf *zvrf, struct prefix *p); /* - * Install/uninstall a FEC-To-NHLFE (FTN) binding. + * Handle zapi request to install/uninstall LSP and + * (optionally) FEC-To-NHLFE (FTN) bindings. */ -int mpls_ftn_update(int add, struct zebra_vrf *zvrf, enum lsp_types_t type, - struct prefix *prefix, enum nexthop_types_t gtype, - union g_addr *gate, ifindex_t ifindex, uint8_t route_type, - unsigned short route_instance, mpls_label_t out_label); +int mpls_zapi_labels_process(bool add_p, struct zebra_vrf *zvrf, + const struct zapi_labels *zl); /* * Uninstall all NHLFEs bound to a single FEC. @@ -287,7 +322,7 @@ int mpls_ftn_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, */ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, mpls_label_t in_label, uint8_t num_out_labels, - mpls_label_t out_labels[], enum nexthop_types_t gtype, + const mpls_label_t *out_labels, enum nexthop_types_t gtype, const union g_addr *gate, ifindex_t ifindex); /* diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c index 5e18414985..c8a3cbbbce 100644 --- a/zebra/zebra_mpls_openbsd.c +++ b/zebra/zebra_mpls_openbsd.c @@ -239,8 +239,9 @@ static int kernel_send_rtmsg_v6(int action, mpls_label_t in_label, static int kernel_lsp_cmd(struct zebra_dplane_ctx *ctx) { + const struct nhlfe_list_head *head; const zebra_nhlfe_t *nhlfe; - struct nexthop *nexthop = NULL; + const struct nexthop *nexthop = NULL; unsigned int nexthop_num = 0; int action; @@ -258,7 +259,8 @@ static int kernel_lsp_cmd(struct zebra_dplane_ctx *ctx) return -1; } - for (nhlfe = dplane_ctx_get_nhlfe(ctx); nhlfe; nhlfe = nhlfe->next) { + head = dplane_ctx_get_nhlfe_list(ctx); + frr_each(nhlfe_list_const, head, nhlfe) { nexthop = nhlfe->nexthop; if (!nexthop) continue; diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 1ac18c6fdd..d07ceb652c 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -371,8 +371,12 @@ struct nhg_hash_entry *zebra_nhg_alloc(void) return nhe; } -static struct nhg_hash_entry *zebra_nhg_copy(const struct nhg_hash_entry *orig, - uint32_t id) +/* + * Allocate new nhe and make shallow copy of 'orig'; no + * recursive info is copied. + */ +struct nhg_hash_entry *zebra_nhe_copy(const struct nhg_hash_entry *orig, + uint32_t id) { struct nhg_hash_entry *nhe; @@ -401,7 +405,7 @@ static void *zebra_nhg_hash_alloc(void *arg) struct nhg_hash_entry *nhe = NULL; struct nhg_hash_entry *copy = arg; - nhe = zebra_nhg_copy(copy, copy->id); + nhe = zebra_nhe_copy(copy, copy->id); /* Mark duplicate nexthops in a group at creation time. */ nexthop_group_mark_duplicates(&(nhe->nhg)); @@ -1140,7 +1144,7 @@ static int nhg_ctx_process_new(struct nhg_ctx *ctx) * their attributes are unhashable. */ - kernel_nhe = zebra_nhg_copy(nhe, id); + kernel_nhe = zebra_nhe_copy(nhe, id); if (IS_ZEBRA_DEBUG_NHG_DETAIL) zlog_debug("%s: copying kernel nhe (%u), dup of %u", @@ -2245,7 +2249,7 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re) /* Make a local copy of the existing nhe, so we don't work on/modify * the shared nhe. */ - curr_nhe = zebra_nhg_copy(re->nhe, re->nhe->id); + curr_nhe = zebra_nhe_copy(re->nhe, re->nhe->id); if (IS_ZEBRA_DEBUG_NHG_DETAIL) zlog_debug("%s: re %p nhe %p (%u), curr_nhe %p", diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h index 785ce20b75..de5f097472 100644 --- a/zebra/zebra_nhg.h +++ b/zebra/zebra_nhg.h @@ -202,6 +202,12 @@ void zebra_nhg_hash_free(void *p); void zebra_nhe_init(struct nhg_hash_entry *nhe, afi_t afi, const struct nexthop *nh); +/* + * Shallow copy of 'orig', into new/allocated nhe. + */ +struct nhg_hash_entry *zebra_nhe_copy(const struct nhg_hash_entry *orig, + uint32_t id); + /* Allocate, free backup nexthop info objects */ struct nhg_backup_info *zebra_nhg_backup_alloc(void); void zebra_nhg_backup_free(struct nhg_backup_info **p); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index d491982d62..71f802f182 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -2276,6 +2276,7 @@ rib_dest_t *zebra_rib_create_dest(struct route_node *rn) dest = XCALLOC(MTYPE_RIB_DEST, sizeof(rib_dest_t)); rnh_list_init(&dest->nht); + re_list_init(&dest->routes); route_lock_node(rn); /* rn route table reference */ rn->info = dest; dest->rnode = rn;