diff options
| author | Donald Sharp <sharpd@cumulusnetworks.com> | 2020-07-10 10:54:59 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-07-10 10:54:59 -0400 |
| commit | 373edbbc9975f2ebeae7d019eb386f48b53d2696 (patch) | |
| tree | e0608f138ac01dc9d09557ea1123b8ffd40899a0 | |
| parent | f24db7598ef269f269992e8ab8e38d370d60dfd2 (diff) | |
| parent | c3753405d5bbe8890711dd1bb50d9965cfb46e65 (diff) | |
Merge pull request #6530 from mjstapp/backup_nhg_notify
lib, zebra: async notifications for backup routes and LSPs
| -rw-r--r-- | lib/nexthop.c | 22 | ||||
| -rw-r--r-- | lib/nexthop.h | 3 | ||||
| -rw-r--r-- | sharpd/sharp_zebra.c | 6 | ||||
| -rw-r--r-- | staticd/static_zebra.c | 1 | ||||
| -rw-r--r-- | tests/topotests/zebra_rib/r1/v4_route_1_static_override.json | 2 | ||||
| -rw-r--r-- | zebra/rib.h | 18 | ||||
| -rw-r--r-- | zebra/zapi_msg.c | 2 | ||||
| -rw-r--r-- | zebra/zebra_dplane.c | 69 | ||||
| -rw-r--r-- | zebra/zebra_dplane.h | 14 | ||||
| -rw-r--r-- | zebra/zebra_mpls.c | 504 | ||||
| -rw-r--r-- | zebra/zebra_mpls.h | 13 | ||||
| -rw-r--r-- | zebra/zebra_nhg.c | 68 | ||||
| -rw-r--r-- | zebra/zebra_pw.c | 4 | ||||
| -rw-r--r-- | zebra/zebra_rib.c | 282 | ||||
| -rw-r--r-- | zebra/zebra_rnh.c | 156 | ||||
| -rw-r--r-- | zebra/zebra_vty.c | 418 |
16 files changed, 973 insertions, 609 deletions
diff --git a/lib/nexthop.c b/lib/nexthop.c index 0d239e091b..3496081d47 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -187,35 +187,41 @@ int nexthop_cmp(const struct nexthop *next1, const struct nexthop *next2) return ret; } -int nexthop_same_firsthop(struct nexthop *next1, struct nexthop *next2) +bool nexthop_same_firsthop(const struct nexthop *next1, + const struct nexthop *next2) { + /* Map the TYPE_IPx types to TYPE_IPx_IFINDEX */ int type1 = NEXTHOP_FIRSTHOPTYPE(next1->type); int type2 = NEXTHOP_FIRSTHOPTYPE(next2->type); if (type1 != type2) - return 0; + return false; + + if (next1->vrf_id != next2->vrf_id) + return false; + switch (type1) { case NEXTHOP_TYPE_IPV4_IFINDEX: if (!IPV4_ADDR_SAME(&next1->gate.ipv4, &next2->gate.ipv4)) - return 0; + return false; if (next1->ifindex != next2->ifindex) - return 0; + return false; break; case NEXTHOP_TYPE_IFINDEX: if (next1->ifindex != next2->ifindex) - return 0; + return false; break; case NEXTHOP_TYPE_IPV6_IFINDEX: if (!IPV6_ADDR_SAME(&next1->gate.ipv6, &next2->gate.ipv6)) - return 0; + return false; if (next1->ifindex != next2->ifindex) - return 0; + return false; break; default: /* do nothing */ break; } - return 1; + return true; } /* diff --git a/lib/nexthop.h b/lib/nexthop.h index 9b71262589..eda88efc08 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -208,7 +208,8 @@ extern int nexthop_g_addr_cmp(enum nexthop_types_t type, extern const char *nexthop_type_to_str(enum nexthop_types_t nh_type); extern bool nexthop_labels_match(const struct nexthop *nh1, const struct nexthop *nh2); -extern int nexthop_same_firsthop(struct nexthop *next1, struct nexthop *next2); +extern bool nexthop_same_firsthop(const struct nexthop *next1, + const struct nexthop *next2); extern const char *nexthop2str(const struct nexthop *nexthop, char *str, int size); diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index c47f2105cb..7ab2d6ec22 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -435,6 +435,12 @@ static int sharp_debug_nexthops(struct zapi_route *api) int i; char buf[PREFIX_STRLEN]; + if (api->nexthop_num == 0) { + zlog_debug( + " Not installed"); + return 0; + } + for (i = 0; i < api->nexthop_num; i++) { struct zapi_nexthop *znh = &api->nexthops[i]; diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index 5cadf34365..c42f632ffb 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -363,6 +363,7 @@ extern void static_zebra_route_add(struct route_node *rn, memcpy(&api.src_prefix, src_pp, sizeof(api.src_prefix)); } SET_FLAG(api.flags, ZEBRA_FLAG_RR_USE_DISTANCE); + SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); if (si_changed->distance) { SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE); diff --git a/tests/topotests/zebra_rib/r1/v4_route_1_static_override.json b/tests/topotests/zebra_rib/r1/v4_route_1_static_override.json index aa9522aff6..22e199f9aa 100644 --- a/tests/topotests/zebra_rib/r1/v4_route_1_static_override.json +++ b/tests/topotests/zebra_rib/r1/v4_route_1_static_override.json @@ -10,7 +10,7 @@ "installed":true, "table":254, "internalStatus":16, - "internalFlags":72, + "internalFlags":73, "internalNextHopNum":1, "internalNextHopActiveNum":1, "nexthops":[ diff --git a/zebra/rib.h b/zebra/rib.h index a024b6dfaa..ec992974fa 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -94,9 +94,11 @@ struct route_entry { struct nhg_hash_entry *nhe; /* Nexthop group from FIB (optional), reflecting what is actually - * installed in the FIB if that differs. + * installed in the FIB if that differs. The 'backup' group is used + * when backup nexthops are present in the route's nhg. */ struct nexthop_group fib_ng; + struct nexthop_group fib_backup_ng; /* Nexthop group hash entry ID */ uint32_t nhe_id; @@ -526,7 +528,7 @@ DECLARE_HOOK(rib_update, (struct route_node * rn, const char *reason), /* * Access active nexthop-group, either RIB or FIB version */ -static inline struct nexthop_group *rib_active_nhg(struct route_entry *re) +static inline struct nexthop_group *rib_get_fib_nhg(struct route_entry *re) { if (re->fib_ng.nexthop) return &(re->fib_ng); @@ -534,6 +536,18 @@ static inline struct nexthop_group *rib_active_nhg(struct route_entry *re) return &(re->nhe->nhg); } +/* + * Access active nexthop-group, either RIB or FIB version + */ +static inline struct nexthop_group *rib_get_fib_backup_nhg( + struct route_entry *re) +{ + if (re->fib_backup_ng.nexthop) + return &(re->fib_backup_ng); + else + return zebra_nhg_get_backup_nhg(re->nhe); +} + extern void zebra_vty_init(void); extern pid_t pid; diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index a40aa8b643..dc7c595d26 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2485,7 +2485,7 @@ static void zread_vrf_label(ZAPI_HANDLER_ARGS) if (really_remove) mpls_lsp_uninstall(def_zvrf, ltype, zvrf->label[afi], NEXTHOP_TYPE_IFINDEX, NULL, - ifp->ifindex); + ifp->ifindex, false /*backup*/); } if (nlabel != MPLS_LABEL_NONE) { diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 64383fc81c..e34b6f23ff 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -1149,6 +1149,37 @@ void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh) nexthop_group_copy_nh_sorted(&(ctx->u.rinfo.zd_ng), nh); } +/* + * Set the list of backup nexthops; their ordering is preserved (they're not + * re-sorted.) + */ +void dplane_ctx_set_backup_nhg(struct zebra_dplane_ctx *ctx, + const struct nexthop_group *nhg) +{ + struct nexthop *nh, *last_nh, *nexthop; + + DPLANE_CTX_VALID(ctx); + + if (ctx->u.rinfo.backup_ng.nexthop) { + nexthops_free(ctx->u.rinfo.backup_ng.nexthop); + ctx->u.rinfo.backup_ng.nexthop = NULL; + } + + last_nh = NULL; + + /* Be careful to preserve the order of the backup list */ + for (nh = nhg->nexthop; nh; nh = nh->next) { + nexthop = nexthop_dup(nh, NULL); + + if (last_nh) + NEXTHOP_APPEND(last_nh, nexthop); + else + ctx->u.rinfo.backup_ng.nexthop = nexthop; + + last_nh = nexthop; + } +} + uint32_t dplane_ctx_get_nhg_id(const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); @@ -1303,7 +1334,7 @@ const struct nhlfe_list_head *dplane_ctx_get_backup_nhlfe_list( 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, + const union g_addr *gate, ifindex_t ifindex, uint8_t num_labels, mpls_label_t *out_labels) @@ -1322,7 +1353,7 @@ zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx, 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, + const union g_addr *gate, ifindex_t ifindex, uint8_t num_labels, mpls_label_t *out_labels) @@ -1921,18 +1952,12 @@ done: /* * Capture information for an LSP update in a dplane context. */ -static int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, - enum dplane_op_e op, - zebra_lsp_t *lsp) +int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, + zebra_lsp_t *lsp) { int ret = AOK; zebra_nhlfe_t *nhlfe, *new_nhlfe; - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) - zlog_debug("init dplane ctx %s: in-label %u ecmp# %d", - dplane_op2str(op), lsp->ile.in_label, - lsp->num_ecmp); - ctx->zd_op = op; ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; @@ -1944,6 +1969,20 @@ static int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, nhlfe_list_init(&(ctx->u.lsp.nhlfe_list)); nhlfe_list_init(&(ctx->u.lsp.backup_nhlfe_list)); + + /* This may be called to create/init a dplane context, not necessarily + * to copy an lsp object. + */ + if (lsp == NULL) { + ret = AOK; + goto done; + } + + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) + zlog_debug("init dplane ctx %s: in-label %u ecmp# %d", + dplane_op2str(op), lsp->ile.in_label, + lsp->num_ecmp); + ctx->u.lsp.ile = lsp->ile; ctx->u.lsp.addr_family = lsp->addr_family; ctx->u.lsp.num_ecmp = lsp->num_ecmp; @@ -2012,6 +2051,7 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx, struct route_table *table; struct route_node *rn; struct route_entry *re; + const struct nexthop_group *nhg; if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) zlog_debug("init dplane ctx %s: pw '%s', loc %u, rem %u", @@ -2062,10 +2102,11 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx, break; } - if (re) + if (re) { + nhg = rib_get_fib_nhg(re); copy_nexthops(&(ctx->u.pw.nhg.nexthop), - re->nhe->nhg.nexthop, NULL); - + nhg->nexthop, NULL); + } route_unlock_node(rn); } } @@ -2442,7 +2483,7 @@ dplane_route_notif_update(struct route_node *rn, new_ctx->u.rinfo.zd_ng.nexthop = NULL; copy_nexthops(&(new_ctx->u.rinfo.zd_ng.nexthop), - (rib_active_nhg(re))->nexthop, NULL); + (rib_get_fib_nhg(re))->nexthop, NULL); for (ALL_NEXTHOPS(new_ctx->u.rinfo.zd_ng, nexthop)) UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 9e07231fea..8e873886df 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -283,6 +283,8 @@ void dplane_ctx_set_distance(struct zebra_dplane_ctx *ctx, uint8_t distance); uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx); void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh); +void dplane_ctx_set_backup_nhg(struct zebra_dplane_ctx *ctx, + const struct nexthop_group *nhg); uint32_t dplane_ctx_get_nhg_id(const struct zebra_dplane_ctx *ctx); const struct nexthop_group *dplane_ctx_get_ng( @@ -308,6 +310,14 @@ dplane_ctx_get_nhe_nh_grp(const struct zebra_dplane_ctx *ctx); uint8_t dplane_ctx_get_nhe_nh_grp_count(const struct zebra_dplane_ctx *ctx); /* Accessors for LSP information */ + +/* Init the internal LSP data struct - necessary before adding to it. + * If 'lsp' is non-NULL, info will be copied from it to the internal + * context data area. + */ +int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, + zebra_lsp_t *lsp); + mpls_label_t dplane_ctx_get_in_label(const struct zebra_dplane_ctx *ctx); void dplane_ctx_set_in_label(struct zebra_dplane_ctx *ctx, mpls_label_t label); @@ -325,7 +335,7 @@ const struct nhlfe_list_head *dplane_ctx_get_backup_nhlfe_list( 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, + const union g_addr *gate, ifindex_t ifindex, uint8_t num_labels, mpls_label_t *out_labels); @@ -333,7 +343,7 @@ zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx, 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, + const union g_addr *gate, ifindex_t ifindex, uint8_t num_labels, mpls_label_t *out_labels); diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 8ee8601689..e741268ebb 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -110,17 +110,14 @@ static zebra_nhlfe_t *nhlfe_find(struct nhlfe_list_head *list, 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); + uint8_t num_labels, const mpls_label_t *labels, + bool is_backup); 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); @@ -167,6 +164,15 @@ static void clear_nhlfe_installed(zebra_lsp_t *lsp) UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); } + + frr_each_safe(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) { + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + + UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + } } /* @@ -240,7 +246,8 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label, nhlfe = nhlfe_add(lsp, lsp_type, nexthop->type, &nexthop->gate, nexthop->ifindex, nexthop->nh_label->num_labels, - nexthop->nh_label->label); + nexthop->nh_label->label, + false /*backup*/); if (!nhlfe) return -1; @@ -797,8 +804,7 @@ static void lsp_select_best_nhlfe(zebra_lsp_t *lsp) /* * First compute the best path, after checking nexthop status. We are - * only - * concerned with non-deleted NHLFEs. + * only concerned with non-deleted NHLFEs. */ frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { /* Clear selection flags. */ @@ -816,6 +822,14 @@ static void lsp_select_best_nhlfe(zebra_lsp_t *lsp) if (!lsp->best_nhlfe) return; + /* + * Check the active status of backup nhlfes also + */ + frr_each_safe(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) { + if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) + (void)nhlfe_nexthop_active(nhlfe); + } + /* Mark best NHLFE as selected. */ SET_FLAG(lsp->best_nhlfe->flags, NHLFE_FLAG_SELECTED); @@ -910,9 +924,9 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data) if (IS_ZEBRA_DEBUG_MPLS) { if (oldbest) - nhlfe2str(oldbest, buf, BUFSIZ); + nhlfe2str(oldbest, buf, sizeof(buf)); if (newbest) - nhlfe2str(newbest, buf2, BUFSIZ); + nhlfe2str(newbest, buf2, sizeof(buf2)); zlog_debug( "Process LSP in-label %u oldbest %s newbest %s " "flags 0x%x ecmp# %d", @@ -1310,13 +1324,14 @@ static zebra_nhlfe_t *nhlfe_alloc(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, } /* - * Add NHLFE. Base entry must have been created and duplicate - * check done. + * Add primary or backup 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) + uint8_t num_labels, const mpls_label_t *labels, + bool is_backup) { zebra_nhlfe_t *nhlfe; @@ -1327,36 +1342,12 @@ static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, nhlfe = nhlfe_alloc(lsp, lsp_type, gtype, gate, ifindex, num_labels, labels); - /* Enqueue to LSP, at head of list. */ - 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. */ - nhlfe_list_add_tail(&lsp->backup_nhlfe_list, nhlfe); + /* Enqueue to LSP: primaries at head of list, backups at tail */ + if (is_backup) { + SET_FLAG(nhlfe->flags, NHLFE_FLAG_IS_BACKUP); + nhlfe_list_add_tail(&lsp->backup_nhlfe_list, nhlfe); + } else + nhlfe_list_add_head(&lsp->nhlfe_list, nhlfe); return nhlfe; } @@ -1590,6 +1581,9 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty) break; } vty_out(vty, "%s", + CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_IS_BACKUP) ? " (backup)" + : ""); + vty_out(vty, "%s", CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED) ? " (installed)" : ""); vty_out(vty, "\n"); @@ -1616,6 +1610,7 @@ static void lsp_print(struct vty *vty, zebra_lsp_t *lsp) /* Find backup in backup list */ i = 0; + backup = NULL; frr_each(nhlfe_list, &lsp->backup_nhlfe_list, backup) { if (i == nhlfe->nexthop->backup_idx) break; @@ -1933,23 +1928,27 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) /* TODO -- Confirm that this result is still 'current' */ - if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) { - /* Update zebra object */ - SET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); - frr_each(nhlfe_list, &lsp->nhlfe_list, nhlfe) { - nexthop = nhlfe->nexthop; - if (!nexthop) - continue; - - SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - } - } else { + if (status != ZEBRA_DPLANE_REQUEST_SUCCESS) { UNSET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); clear_nhlfe_installed(lsp); flog_warn(EC_ZEBRA_LSP_INSTALL_FAILURE, "LSP Install Failure: in-label %u", lsp->ile.in_label); + break; + } + + /* Update zebra object */ + SET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); + frr_each(nhlfe_list, &lsp->nhlfe_list, nhlfe) { + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED) && + CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { + SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + } } break; @@ -1970,53 +1969,23 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) } /* - * Process async dplane notifications. + * Process LSP installation info from two sets of nhlfes: a set from + * a dplane notification, and a set from the zebra LSP object. Update + * counters of installed nexthops, and return whether the LSP has changed. */ -void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) +static bool compare_notif_nhlfes(const struct nhlfe_list_head *ctx_head, + struct nhlfe_list_head *nhlfe_head, + int *start_counter, int *end_counter) { - struct zebra_vrf *zvrf; - zebra_ile_t tmp_ile; - 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; - int start_count = 0, end_count = 0; /* Installed counts */ + int start_count = 0, end_count = 0; bool changed_p = false; bool is_debug = (IS_ZEBRA_DEBUG_DPLANE | IS_ZEBRA_DEBUG_MPLS); - if (is_debug) - zlog_debug("LSP dplane notif, in-label %u", - dplane_ctx_get_in_label(ctx)); - - /* Look for zebra LSP object */ - zvrf = vrf_info_lookup(VRF_DEFAULT); - if (zvrf == NULL) - goto done; - - lsp_table = zvrf->lsp_table; - - tmp_ile.in_label = dplane_ctx_get_in_label(ctx); - lsp = hash_lookup(lsp_table, &tmp_ile); - if (lsp == NULL) { - if (is_debug) - zlog_debug("dplane LSP notif: in-label %u not found", - dplane_ctx_get_in_label(ctx)); - goto done; - } - - /* - * The dataplane/forwarding plane is notifying zebra about the state - * of the nexthops associated with this LSP. First, we take a - * pre-scan pass to determine whether the LSP has transitioned - * from installed -> uninstalled. In that case, we need to have - * the existing state of the LSP objects available before making - * any changes. - */ - head = dplane_ctx_get_nhlfe_list(ctx); - frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { + frr_each_safe(nhlfe_list, nhlfe_head, nhlfe) { char buf[NEXTHOP_STRLEN]; nexthop = nhlfe->nexthop; @@ -2026,8 +1995,9 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) start_count++; + ctx_nhlfe = NULL; ctx_nexthop = NULL; - frr_each(nhlfe_list_const, head, ctx_nhlfe) { + frr_each(nhlfe_list_const, ctx_head, ctx_nhlfe) { ctx_nexthop = ctx_nhlfe->nexthop; if (!ctx_nexthop) continue; @@ -2085,32 +2055,39 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) } } - if (is_debug) - zlog_debug("LSP dplane notif: lfib start_count %d, end_count %d%s", - start_count, end_count, - changed_p ? ", changed" : ""); + if (start_counter) + *start_counter += start_count; + if (end_counter) + *end_counter += end_count; - /* - * Has the LSP become uninstalled? - */ - if (start_count > 0 && end_count == 0) { - /* Inform other lfibs */ - dplane_lsp_notif_update(lsp, DPLANE_OP_LSP_DELETE, ctx); - } + return changed_p; +} - /* - * Now we take a second pass and bring the zebra - * nexthop state into sync with the forwarding-plane state. - */ - frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { +/* + * Update an lsp nhlfe list from a dplane context, typically an async + * notification context. Update the LSP list to match the installed + * status from the context's list. + */ +static int update_nhlfes_from_ctx(struct nhlfe_list_head *nhlfe_head, + const struct nhlfe_list_head *ctx_head) +{ + int ret = 0; + zebra_nhlfe_t *nhlfe; + const zebra_nhlfe_t *ctx_nhlfe; + struct nexthop *nexthop; + const struct nexthop *ctx_nexthop; + bool is_debug = (IS_ZEBRA_DEBUG_DPLANE | IS_ZEBRA_DEBUG_MPLS); + + frr_each_safe(nhlfe_list, nhlfe_head, nhlfe) { char buf[NEXTHOP_STRLEN]; nexthop = nhlfe->nexthop; if (!nexthop) continue; + ctx_nhlfe = NULL; ctx_nexthop = NULL; - frr_each(nhlfe_list_const, head, ctx_nhlfe) { + frr_each(nhlfe_list_const, ctx_head, ctx_nhlfe) { ctx_nexthop = ctx_nhlfe->nexthop; if (!ctx_nexthop) continue; @@ -2130,10 +2107,16 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) /* Bring zebra nhlfe install state into sync */ if (CHECK_FLAG(ctx_nhlfe->flags, NHLFE_FLAG_INSTALLED)) { + if (is_debug) + zlog_debug("%s: matched lsp nhlfe %s (installed)", + __func__, buf); SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); } else { + if (is_debug) + zlog_debug("%s: matched lsp nhlfe %s (not installed)", + __func__, buf); UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); } @@ -2153,13 +2136,101 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) } else { /* Not mentioned in lfib set -> uninstalled */ - + if (is_debug) + zlog_debug("%s: no match for lsp nhlfe %s", + __func__, buf); UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); } } + return ret; +} + +/* + * Process async dplane notifications. + */ +void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) +{ + struct zebra_vrf *zvrf; + zebra_ile_t tmp_ile; + struct hash *lsp_table; + zebra_lsp_t *lsp; + const struct nhlfe_list_head *ctx_list; + int start_count = 0, end_count = 0; /* Installed counts */ + bool changed_p = false; + bool is_debug = (IS_ZEBRA_DEBUG_DPLANE | IS_ZEBRA_DEBUG_MPLS); + + if (is_debug) + zlog_debug("LSP dplane notif, in-label %u", + dplane_ctx_get_in_label(ctx)); + + /* Look for zebra LSP object */ + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (zvrf == NULL) + goto done; + + lsp_table = zvrf->lsp_table; + + tmp_ile.in_label = dplane_ctx_get_in_label(ctx); + lsp = hash_lookup(lsp_table, &tmp_ile); + if (lsp == NULL) { + if (is_debug) + zlog_debug("dplane LSP notif: in-label %u not found", + dplane_ctx_get_in_label(ctx)); + goto done; + } + + /* + * The dataplane/forwarding plane is notifying zebra about the state + * of the nexthops associated with this LSP. First, we take a + * pre-scan pass to determine whether the LSP has transitioned + * from installed -> uninstalled. In that case, we need to have + * the existing state of the LSP objects available before making + * any changes. + */ + ctx_list = dplane_ctx_get_nhlfe_list(ctx); + + changed_p = compare_notif_nhlfes(ctx_list, &lsp->nhlfe_list, + &start_count, &end_count); + + if (is_debug) + zlog_debug("LSP dplane notif: lfib start_count %d, end_count %d%s", + start_count, end_count, + changed_p ? ", changed" : ""); + + ctx_list = dplane_ctx_get_backup_nhlfe_list(ctx); + + if (compare_notif_nhlfes(ctx_list, &lsp->backup_nhlfe_list, + &start_count, &end_count)) + /* Avoid accidentally setting back to 'false' */ + changed_p = true; + + if (is_debug) + zlog_debug("LSP dplane notif: lfib backups, start_count %d, end_count %d%s", + start_count, end_count, + changed_p ? ", changed" : ""); + + /* + * Has the LSP become uninstalled? We need the existing state of the + * nexthops/nhlfes at this point so we know what to delete. + */ + if (start_count > 0 && end_count == 0) { + /* Inform other lfibs */ + dplane_lsp_notif_update(lsp, DPLANE_OP_LSP_DELETE, ctx); + } + + /* + * Now we take a second pass and bring the zebra + * nexthop state into sync with the forwarding-plane state. + */ + ctx_list = dplane_ctx_get_nhlfe_list(ctx); + update_nhlfes_from_ctx(&lsp->nhlfe_list, ctx_list); + + ctx_list = dplane_ctx_get_backup_nhlfe_list(ctx); + update_nhlfes_from_ctx(&lsp->backup_nhlfe_list, ctx_list); + if (end_count > 0) { SET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); @@ -2234,14 +2305,14 @@ int zebra_mpls_lsp_uninstall(struct zebra_vrf *zvrf, struct route_node *rn, zebra_nhlfe_t *zebra_mpls_lsp_add_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, enum nexthop_types_t gtype, - union g_addr *gate, + const 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_add(lsp, lsp_type, gtype, gate, ifindex, num_labels, - out_labels); + out_labels, false /*backup*/); } /* @@ -2252,14 +2323,14 @@ zebra_nhlfe_t *zebra_mpls_lsp_add_nhlfe(zebra_lsp_t *lsp, 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, + const 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); + return nhlfe_add(lsp, lsp_type, gtype, gate, ifindex, num_labels, + out_labels, true); } /* @@ -2275,7 +2346,8 @@ zebra_nhlfe_t *zebra_mpls_lsp_add_nh(zebra_lsp_t *lsp, return NULL; nhlfe = nhlfe_add(lsp, lsp_type, nh->type, &nh->gate, nh->ifindex, - nh->nh_label->num_labels, nh->nh_label->label); + nh->nh_label->num_labels, nh->nh_label->label, + false /*backup*/); return nhlfe; } @@ -2293,9 +2365,9 @@ zebra_nhlfe_t *zebra_mpls_lsp_add_backup_nh(zebra_lsp_t *lsp, if (nh->nh_label == NULL || nh->nh_label->num_labels == 0) return NULL; - nhlfe = nhlfe_backup_add(lsp, lsp_type, nh->type, &nh->gate, + nhlfe = nhlfe_add(lsp, lsp_type, nh->type, &nh->gate, nh->ifindex, nh->nh_label->num_labels, - nh->nh_label->label); + nh->nh_label->label, true); return nhlfe; } @@ -2846,6 +2918,9 @@ int mpls_ftn_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); SET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED); + /* This will create (or ref) a new nhe, so we will discard the local + * temporary nhe + */ mpls_zebra_nhe_update(re, afi, new_nhe); zebra_nhg_free(new_nhe); @@ -3013,7 +3088,8 @@ int mpls_zapi_labels_process(bool add_p, struct zebra_vrf *zvrf, else ret = mpls_lsp_uninstall(zvrf, zl->type, zl->local_label, znh->type, - &znh->gate, znh->ifindex); + &znh->gate, znh->ifindex, + false); if (ret < 0) { if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_MPLS) { zapi_nexthop2str(znh, buf, sizeof(buf)); @@ -3055,10 +3131,10 @@ int mpls_zapi_labels_process(bool add_p, struct zebra_vrf *zvrf, 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); + ret = mpls_lsp_uninstall(zvrf, zl->type, + zl->local_label, + znh->type, &znh->gate, + znh->ifindex, true); if (ret < 0) { if (IS_ZEBRA_DEBUG_RECV || @@ -3123,92 +3199,22 @@ 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) + ifindex_t ifindex, bool is_backup) { zebra_nhlfe_t *nhlfe; char buf[MPLS_LABEL_STRLEN]; + const char *backup_str; - nhlfe = nhlfe_find(&lsp->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 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); - } + if (is_backup) { + nhlfe = nhlfe_find(&lsp->backup_nhlfe_list, type, gtype, + gate, ifindex); + backup_str = "backup "; } else { - /* Add LSP entry to this nexthop */ - nhlfe = nhlfe_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 nexthop %s out-label(s) %s", - lsp->ile.in_label, type, buf, buf2); - } - - lsp->addr_family = NHLFE_FAMILY(nhlfe); + nhlfe = nhlfe_find(&lsp->nhlfe_list, type, gtype, gate, + ifindex); + backup_str = ""; } - /* 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; @@ -3234,8 +3240,9 @@ lsp_add_backup_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t type, 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); + zlog_debug("LSP in-label %u type %d %snexthop %s out-label(s) changed to %s (old %s)", + lsp->ile.in_label, type, backup_str, buf, + buf2, buf3); } /* Update out label(s), trigger processing. */ @@ -3249,8 +3256,8 @@ lsp_add_backup_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t type, } } else { /* Add LSP entry to this nexthop */ - nhlfe = nhlfe_backup_add(lsp, type, gtype, gate, ifindex, - num_out_labels, out_labels); + nhlfe = nhlfe_add(lsp, type, gtype, gate, ifindex, + num_out_labels, out_labels, is_backup); if (!nhlfe) return NULL; @@ -3261,8 +3268,9 @@ lsp_add_backup_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t type, 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); + zlog_debug("Add LSP in-label %u type %d %snexthop %s out-label(s) %s", + lsp->ile.in_label, type, backup_str, buf, + buf2); } lsp->addr_family = NHLFE_FAMILY(nhlfe); @@ -3300,7 +3308,7 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, return -1; nhlfe = lsp_add_nhlfe(lsp, type, num_out_labels, out_labels, gtype, - gate, ifindex); + gate, ifindex, false /*backup*/); if (nhlfe == NULL) return -1; @@ -3320,7 +3328,8 @@ static int lsp_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type, zebra_nhlfe_t *nhlfe; nhlfe = lsp_add_nhlfe(lsp, type, znh->label_num, znh->labels, - znh->type, &znh->gate, znh->ifindex); + znh->type, &znh->gate, znh->ifindex, + false /*backup*/); if (nhlfe == NULL) return -1; @@ -3345,9 +3354,9 @@ static int lsp_backup_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type, { zebra_nhlfe_t *nhlfe; - nhlfe = lsp_add_backup_nhlfe(lsp, type, znh->label_num, - znh->labels, znh->type, &znh->gate, - znh->ifindex); + nhlfe = lsp_add_nhlfe(lsp, type, znh->label_num, + znh->labels, znh->type, &znh->gate, + znh->ifindex, true /*backup*/); if (nhlfe == NULL) { if (IS_ZEBRA_DEBUG_MPLS) zlog_debug("%s: unable to add backup nhlfe, label: %u", @@ -3368,13 +3377,14 @@ static int lsp_backup_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type, */ int mpls_lsp_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) + const union g_addr *gate, ifindex_t ifindex, + bool backup_p) { struct hash *lsp_table; zebra_ile_t tmp_ile; zebra_lsp_t *lsp; zebra_nhlfe_t *nhlfe; - char buf[BUFSIZ]; + char buf[NEXTHOP_STRLEN]; bool schedule_lsp = false; /* Lookup table. */ @@ -3387,7 +3397,13 @@ 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->nhlfe_list, type, gtype, gate, ifindex); + + if (backup_p) + nhlfe = nhlfe_find(&lsp->backup_nhlfe_list, type, gtype, + gate, ifindex); + else + nhlfe = nhlfe_find(&lsp->nhlfe_list, type, gtype, gate, + ifindex); if (!nhlfe) return 0; @@ -3420,56 +3436,6 @@ int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, 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) { @@ -3754,7 +3720,7 @@ int zebra_mpls_static_lsp_del(struct zebra_vrf *zvrf, mpls_label_t in_label, /* Uninstall LSP from the main table. */ mpls_lsp_uninstall(zvrf, ZEBRA_LSP_STATIC, in_label, gtype, - gate, ifindex); + gate, ifindex, false); /* Delete static LSP NHLFE */ snhlfe_del(snhlfe); diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index 9b5fb39573..07a8efeb8b 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -213,7 +213,7 @@ int zebra_mpls_lsp_uninstall(struct zebra_vrf *zvrf, struct route_node *rn, zebra_nhlfe_t *zebra_mpls_lsp_add_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t lsp_type, enum nexthop_types_t gtype, - union g_addr *gate, + const union g_addr *gate, ifindex_t ifindex, uint8_t num_labels, const mpls_label_t *out_labels); @@ -222,7 +222,7 @@ zebra_nhlfe_t *zebra_mpls_lsp_add_nhlfe(zebra_lsp_t *lsp, 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, + const union g_addr *gate, ifindex_t ifindex, uint8_t num_labels, const mpls_label_t *out_labels); @@ -331,7 +331,8 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, */ int mpls_lsp_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); + const union g_addr *gate, ifindex_t ifindex, + bool backup_p); /* * Uninstall all NHLFEs for a particular LSP forwarding entry. @@ -339,12 +340,6 @@ int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, int mpls_lsp_uninstall_all_vrf(struct zebra_vrf *zvrf, enum lsp_types_t type, mpls_label_t in_label); -/* - * Uninstall all Segment Routing NHLFEs for a particular LSP forwarding entry. - * If no other NHLFEs exist, the entry would be deleted. - */ -void mpls_sr_lsp_uninstall_all(struct hash_bucket *bucket, void *ctxt); - #if defined(HAVE_CUMULUS) /* * Check that the label values used in LSP creation are consistent. The diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 02ba69bd4d..f5e4a4e79e 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -1734,6 +1734,10 @@ static bool nexthop_valid_resolve(const struct nexthop *nexthop, if (CHECK_FLAG(resolved->flags, NEXTHOP_FLAG_RECURSIVE)) return false; + /* Must be ACTIVE */ + if (!CHECK_FLAG(resolved->flags, NEXTHOP_FLAG_ACTIVE)) + return false; + switch (nexthop->type) { case NEXTHOP_TYPE_IPV4_IFINDEX: case NEXTHOP_TYPE_IPV6_IFINDEX: @@ -1926,11 +1930,23 @@ static int nexthop_active(afi_t afi, struct route_entry *re, return 1; } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) { + struct nexthop_group *nhg; + resolved = 0; - for (ALL_NEXTHOPS(match->nhe->nhg, newhop)) { - if (!CHECK_FLAG(match->status, - ROUTE_ENTRY_INSTALLED)) - continue; + + /* Only useful if installed */ + if (!CHECK_FLAG(match->status, ROUTE_ENTRY_INSTALLED)) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: match %p (%u) not installed", + __func__, match, + match->nhe->id); + + goto done_with_match; + } + + /* Examine installed nexthops */ + nhg = &match->nhe->nhg; + for (ALL_NEXTHOPS_PTR(nhg, newhop)) { if (!nexthop_valid_resolve(nexthop, newhop)) continue; @@ -1945,25 +1961,21 @@ static int nexthop_active(afi_t afi, struct route_entry *re, resolved = 1; } - if (resolved) - re->nexthop_mtu = match->mtu; - else if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - " %s: Recursion failed to find", - __func__); + /* Examine installed backup nexthops, if any. There + * are only installed backups *if* there is a + * dedicated fib list. + */ + nhg = rib_get_fib_backup_nhg(match); + if (nhg == NULL || + nhg == zebra_nhg_get_backup_nhg(match->nhe)) + goto done_with_match; - return resolved; - } else if (re->type == ZEBRA_ROUTE_STATIC) { - resolved = 0; - for (ALL_NEXTHOPS(match->nhe->nhg, newhop)) { - if (!CHECK_FLAG(match->status, - ROUTE_ENTRY_INSTALLED)) - continue; + for (ALL_NEXTHOPS_PTR(nhg, newhop)) { if (!nexthop_valid_resolve(nexthop, newhop)) continue; - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("%s: STATIC match %p (%u), newhop %pNHv", + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: RECURSIVE match backup %p (%u), newhop %pNHv", __func__, match, match->nhe->id, newhop); @@ -1972,13 +1984,14 @@ static int nexthop_active(afi_t afi, struct route_entry *re, nexthop_set_resolved(afi, newhop, nexthop); resolved = 1; } +done_with_match: if (resolved) re->nexthop_mtu = match->mtu; - - if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED) + else if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug( - " %s: Static route unable to resolve", + " %s: Recursion failed to find", __func__); + return resolved; } else { if (IS_ZEBRA_DEBUG_RIB_DETAILED) { @@ -2170,17 +2183,20 @@ done: } /* - * Process a list of nexthops, given the head of the list, determining + * Process a list of nexthops, given an nhg, determining * whether each one is ACTIVE/installable at this time. */ static uint32_t nexthop_list_active_update(struct route_node *rn, struct route_entry *re, - struct nexthop *nexthop) + struct nexthop_group *nhg) { union g_addr prev_src; unsigned int prev_active, new_active; ifindex_t prev_index; uint32_t counter = 0; + struct nexthop *nexthop; + + nexthop = nhg->nexthop; /* Process nexthops one-by-one */ for ( ; nexthop; nexthop = nexthop->next) { @@ -2263,7 +2279,7 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re) curr_nhe->id = 0; /* Process nexthops */ - curr_active = nexthop_list_active_update(rn, re, curr_nhe->nhg.nexthop); + curr_active = nexthop_list_active_update(rn, re, &curr_nhe->nhg); if (IS_ZEBRA_DEBUG_NHG_DETAIL) zlog_debug("%s: re %p curr_active %u", __func__, re, @@ -2274,7 +2290,7 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re) goto backups_done; backup_active = nexthop_list_active_update( - rn, re, zebra_nhg_get_backup_nhg(curr_nhe)->nexthop); + rn, re, zebra_nhg_get_backup_nhg(curr_nhe)); if (IS_ZEBRA_DEBUG_NHG_DETAIL) zlog_debug("%s: re %p backup_active %u", __func__, re, diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index 8f0c964c18..2328ab650a 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -550,7 +550,7 @@ static void vty_show_mpls_pseudowire_detail(struct vty *vty) re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, &pw->nexthop, NULL); if (re) { - for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) { + for (ALL_NEXTHOPS_PTR(rib_get_fib_nhg(re), nexthop)) { snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop); vty_out(vty, " Next Hop: %s\n", buf_nh); @@ -604,7 +604,7 @@ static void vty_show_mpls_pseudowire(struct zebra_pw *pw, json_object *json_pws) re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, &pw->nexthop, NULL); if (re) { - for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) { + for (ALL_NEXTHOPS_PTR(rib_get_fib_nhg(re), nexthop)) { json_nexthop = json_object_new_object(); snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop); json_object_string_add(json_nexthop, "nexthop", buf_nh); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 31582dcb3d..67b3812ed3 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1342,6 +1342,92 @@ static bool rib_compare_routes(const struct route_entry *re1, } /* + * Compare nexthop lists from a route and a dplane context; test whether + * the list installed in the FIB matches the route's list. + * Set 'changed_p' to 'true' if there were changes to the route's + * installed nexthops. + * + * Return 'false' if any ACTIVE route nexthops are not mentioned in the FIB + * list. + */ +static bool rib_update_nhg_from_ctx(struct nexthop_group *re_nhg, + const struct nexthop_group *ctx_nhg, + bool *changed_p) +{ + bool matched_p = true; + struct nexthop *nexthop, *ctx_nexthop; + + /* Get the first `installed` one to check against. + * If the dataplane doesn't set these to be what was actually installed, + * it will just be whatever was in re->nhe->nhg? + */ + ctx_nexthop = ctx_nhg->nexthop; + + if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_RECURSIVE) + || !CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + ctx_nexthop = nexthop_next_active_resolved(ctx_nexthop); + + for (ALL_NEXTHOPS_PTR(re_nhg, nexthop)) { + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + continue; + + /* Check for a FIB nexthop corresponding to the RIB nexthop */ + if (nexthop_same(ctx_nexthop, nexthop) == false) { + /* If the FIB doesn't know about the nexthop, + * it's not installed + */ + if (IS_ZEBRA_DEBUG_RIB_DETAILED || + IS_ZEBRA_DEBUG_NHG_DETAIL) { + zlog_debug("%s: no ctx match for rib nh %pNHv %s", + __func__, nexthop, + (CHECK_FLAG(nexthop->flags, + NEXTHOP_FLAG_FIB) ? + "(FIB)":"")); + } + matched_p = false; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + *changed_p = true; + + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + + /* Keep checking nexthops */ + continue; + } + + if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_FIB)) { + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: rib nh %pNHv -> installed", + __func__, nexthop); + + *changed_p = true; + } + + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + } else { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: rib nh %pNHv -> uninstalled", + __func__, nexthop); + + *changed_p = true; + } + + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + } + + ctx_nexthop = nexthop_next_active_resolved(ctx_nexthop); + } + + return matched_p; +} + +/* * Update a route from a dplane context. This consolidates common code * that can be used in processing of results from FIB updates, and in * async notification processing. @@ -1352,10 +1438,10 @@ static bool rib_update_re_from_ctx(struct route_entry *re, struct zebra_dplane_ctx *ctx) { char dest_str[PREFIX_STRLEN] = ""; - char nh_str[NEXTHOP_STRLEN]; - struct nexthop *nexthop, *ctx_nexthop; + struct nexthop *nexthop; bool matched; const struct nexthop_group *ctxnhg; + struct nexthop_group *re_nhg; bool is_selected = false; /* Is 're' currently the selected re? */ bool changed_p = false; /* Change to nexthops? */ rib_dest_t *dest; @@ -1386,10 +1472,13 @@ static bool rib_update_re_from_ctx(struct route_entry *re, matched = false; ctxnhg = dplane_ctx_get_ng(ctx); - /* Check both fib group and notif group for equivalence. + /* Check route's fib group and incoming notif group for equivalence. * * Let's assume the nexthops are ordered here to save time. */ + /* TODO -- this isn't testing or comparing the FIB flags; we should + * do a more explicit loop, checking the incoming notification's flags. + */ if (re->fib_ng.nexthop && ctxnhg->nexthop && nexthop_group_equal(&re->fib_ng, ctxnhg)) matched = true; @@ -1400,7 +1489,7 @@ static bool rib_update_re_from_ctx(struct route_entry *re, zlog_debug( "%s(%u):%s update_from_ctx(): existing fib nhg, no change", VRF_LOGNAME(vrf), re->vrf_id, dest_str); - goto done; + goto check_backups; } else if (re->fib_ng.nexthop) { /* @@ -1430,70 +1519,16 @@ static bool rib_update_re_from_ctx(struct route_entry *re, * * Assume nexthops are ordered here as well. */ - matched = true; - ctx_nexthop = ctxnhg->nexthop; - - /* Nothing installed - we can skip some of the checking/comparison + /* If nothing is installed, we can skip some of the checking/comparison * of nexthops. */ - if (ctx_nexthop == NULL) { + if (ctxnhg->nexthop == NULL) { changed_p = true; goto no_nexthops; } - /* Get the first `installed` one to check against. - * If the dataplane doesn't set these to be what was actually installed, - * it will just be whatever was in re->nhe->nhg? - */ - if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_RECURSIVE) - || !CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - ctx_nexthop = nexthop_next_active_resolved(ctx_nexthop); - - for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) { - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - continue; - - if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - continue; - - /* Check for a FIB nexthop corresponding to the RIB nexthop */ - if (nexthop_same(ctx_nexthop, nexthop) == false) { - /* If the FIB doesn't know about the nexthop, - * it's not installed - */ - if (IS_ZEBRA_DEBUG_RIB_DETAILED) { - nexthop2str(nexthop, nh_str, sizeof(nh_str)); - zlog_debug( - "update_from_ctx: no match for rib nh %s", - nh_str); - } - matched = false; - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) - changed_p = true; - - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - - /* Keep checking nexthops */ - continue; - } - - if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_FIB)) { - if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) - changed_p = true; - - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - } else { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) - changed_p = true; - - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - } - - ctx_nexthop = nexthop_next_active_resolved(ctx_nexthop); - } + matched = rib_update_nhg_from_ctx(&(re->nhe->nhg), ctxnhg, &changed_p); /* If all nexthops were processed, we're done */ if (matched) { @@ -1502,7 +1537,7 @@ static bool rib_update_re_from_ctx(struct route_entry *re, "%s(%u):%s update_from_ctx(): rib nhg matched, changed '%s'", VRF_LOGNAME(vrf), re->vrf_id, dest_str, (changed_p ? "true" : "false")); - goto done; + goto check_backups; } no_nexthops: @@ -1527,7 +1562,81 @@ no_nexthops: _nexthop_add(&(re->fib_ng.nexthop), nexthop); } +check_backups: + + /* + * Check the status of the route's backup nexthops, if any. + * The logic for backups is somewhat different: if any backup is + * installed, a new fib nhg will be attached to the route. + */ + re_nhg = zebra_nhg_get_backup_nhg(re->nhe); + if (re_nhg == NULL) + goto done; /* No backup nexthops */ + + /* First check the route's 'fib' list of backups, if it's present + * from some previous event. + */ + re_nhg = &re->fib_backup_ng; + ctxnhg = dplane_ctx_get_backup_ng(ctx); + + matched = false; + if (re_nhg->nexthop && ctxnhg && nexthop_group_equal(re_nhg, ctxnhg)) + matched = true; + + /* If the new FIB set matches an existing FIB set, we're done. */ + if (matched) { + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug( + "%s(%u):%s update_from_ctx(): existing fib backup nhg, no change", + VRF_LOGNAME(vrf), re->vrf_id, dest_str); + goto done; + + } else if (re->fib_backup_ng.nexthop) { + /* + * Free stale fib backup list and move on to check + * the route's backups. + */ + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug( + "%s(%u):%s update_from_ctx(): replacing fib backup nhg", + VRF_LOGNAME(vrf), re->vrf_id, dest_str); + nexthops_free(re->fib_backup_ng.nexthop); + re->fib_backup_ng.nexthop = NULL; + + /* Note that the installed nexthops have changed */ + changed_p = true; + } else { + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s(%u):%s update_from_ctx(): no fib backup nhg", + VRF_LOGNAME(vrf), re->vrf_id, dest_str); + } + + /* + * If a FIB backup nexthop set exists: attach a copy + * to the route if any backup is installed + */ + if (ctxnhg && ctxnhg->nexthop) { + + for (ALL_NEXTHOPS_PTR(ctxnhg, nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + break; + } + + /* If no installed backups, we're done */ + if (nexthop == NULL) + goto done; + + if (IS_ZEBRA_DEBUG_RIB) + zlog_debug("%s(%u):%s update_from_ctx(): changed %s, adding new backup fib nhg", + VRF_LOGNAME(vrf), re->vrf_id, dest_str, + (changed_p ? "true" : "false")); + + copy_nexthops(&(re->fib_backup_ng.nexthop), ctxnhg->nexthop, + NULL); + } + done: + return changed_p; } @@ -1814,6 +1923,38 @@ done: } /* + * Count installed/FIB nexthops + */ +static int rib_count_installed_nh(struct route_entry *re) +{ + int count = 0; + struct nexthop *nexthop; + struct nexthop_group *nhg; + + nhg = rib_get_fib_nhg(re); + + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + /* The meaningful flag depends on where the installed + * nexthops reside. + */ + if (nhg == &(re->fib_backup_ng)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + count++; + } else { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + count++; + } + } + + for (ALL_NEXTHOPS_PTR(rib_get_fib_backup_nhg(re), nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + count++; + } + + return count; +} + +/* * Handle notification from async dataplane: the dataplane has detected * some change to a route, and notifies zebra so that the control plane * can reflect that change. @@ -1930,12 +2071,8 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) */ start_count = 0; - if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) { - for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) - start_count++; - } - } + if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) + start_count = rib_count_installed_nh(re); /* Update zebra's nexthop FIB flags based on the context struct's * nexthops. @@ -1954,12 +2091,7 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) * Perform follow-up work if the actual status of the prefix * changed. */ - - end_count = 0; - for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) - end_count++; - } + end_count = rib_count_installed_nh(re); /* Various fib transitions: changed nexthops; from installed to * not-installed; or not-installed to installed. @@ -1988,7 +2120,7 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); /* Changed nexthops - update kernel/others */ - dplane_route_notif_update(rn, re, DPLANE_OP_ROUTE_INSTALL, ctx); + dplane_route_notif_update(rn, re, DPLANE_OP_ROUTE_UPDATE, ctx); /* Redistribute, lsp, and nht update */ redistribute_update(dest_pfx, src_pfx, re, NULL); diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 20af96a557..d1a5cf2a9d 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -419,7 +419,7 @@ static int zebra_rnh_apply_nht_rmap(afi_t afi, struct zebra_vrf *zvrf, at_least_one++; /* at least one valid NH */ else { SET_FLAG(nexthop->flags, - NEXTHOP_FLAG_RNH_FILTERED); + NEXTHOP_FLAG_RNH_FILTERED); } } } @@ -458,12 +458,12 @@ zebra_rnh_resolve_import_entry(struct zebra_vrf *zvrf, afi_t afi, if (IS_ZEBRA_DEBUG_NHT_DETAILED) { char buf[PREFIX_STRLEN]; - char buf1[PREFIX_STRLEN]; + char buf1[SRCDEST2STR_BUFFER]; zlog_debug("%s: %u:%s Resolved Import Entry to %s", __func__, rnh->vrf_id, prefix2str(&rnh->node->p, buf, sizeof(buf)), - srcdest_rnode2str(rn, buf1, sizeof(buf))); + srcdest_rnode2str(rn, buf1, sizeof(buf1))); } /* Identify appropriate route entry. */ @@ -974,12 +974,131 @@ static void copy_state(struct rnh *rnh, const struct route_entry *re, state->vrf_id = re->vrf_id; state->status = re->status; - state->nhe = zebra_nhg_alloc(); + state->nhe = zebra_nhe_copy(re->nhe, 0); + + /* Copy the 'fib' nexthops also, if present - we want to capture + * the true installed nexthops. + */ + if (re->fib_ng.nexthop) + nexthop_group_copy(&state->fib_ng, &re->fib_ng); + if (re->fib_backup_ng.nexthop) + nexthop_group_copy(&state->fib_backup_ng, &re->fib_backup_ng); - nexthop_group_copy(&(state->nhe->nhg), &(re->nhe->nhg)); rnh->state = state; } +/* + * Compare two route_entries' nexthops. + */ +static bool compare_valid_nexthops(struct route_entry *r1, + struct route_entry *r2) +{ + bool matched_p = false; + struct nexthop_group *nhg1, *nhg2; + struct nexthop *nh1, *nh2; + + /* Account for backup nexthops and for the 'fib' nexthop lists, + * if present. + */ + nhg1 = rib_get_fib_nhg(r1); + nhg2 = rib_get_fib_nhg(r2); + + nh1 = nhg1->nexthop; + nh2 = nhg2->nexthop; + + while (1) { + /* Find each list's next valid nexthop */ + while ((nh1 != NULL) && !rnh_nexthop_valid(r1, nh1)) + nh1 = nexthop_next(nh1); + + while ((nh2 != NULL) && !rnh_nexthop_valid(r2, nh2)) + nh2 = nexthop_next(nh2); + + if (nh1 && nh2) { + /* Any difference is a no-match */ + if (nexthop_cmp(nh1, nh2) != 0) { + if (IS_ZEBRA_DEBUG_NHT_DETAILED) + zlog_debug("%s: nh1, nh2 differ", + __func__); + goto done; + } + + nh1 = nexthop_next(nh1); + nh2 = nexthop_next(nh2); + } else if (nh1 || nh2) { + /* One list has more valid nexthops than the other */ + if (IS_ZEBRA_DEBUG_NHT_DETAILED) + zlog_debug("%s: nh1 %s, nh2 %s", __func__, + nh1 ? "non-NULL" : "NULL", + nh2 ? "non-NULL" : "NULL"); + goto done; + } else + break; /* Done with both lists */ + } + + /* The test for the backups is slightly different: the only installed + * backups will be in the 'fib' list. + */ + nhg1 = rib_get_fib_backup_nhg(r1); + if (nhg1 == zebra_nhg_get_backup_nhg(r1->nhe)) + nhg1 = NULL; + + nhg2 = rib_get_fib_backup_nhg(r2); + if (nhg2 == zebra_nhg_get_backup_nhg(r2->nhe)) + nhg2 = NULL; + + if (nhg1) + nh1 = nhg1->nexthop; + else + nh1 = NULL; + + if (nhg2) + nh2 = nhg2->nexthop; + else + nh2 = NULL; + + while (1) { + /* Find each backup list's next valid nexthop */ + while ((nh1 != NULL) && !rnh_nexthop_valid(r1, nh1)) + nh1 = nexthop_next(nh1); + + while ((nh2 != NULL) && !rnh_nexthop_valid(r2, nh2)) + nh2 = nexthop_next(nh2); + + if (nh1 && nh2) { + /* Any difference is a no-match */ + if (nexthop_cmp(nh1, nh2) != 0) { + if (IS_ZEBRA_DEBUG_NHT_DETAILED) + zlog_debug("%s: backup nh1, nh2 differ", + __func__); + goto done; + } + + nh1 = nexthop_next(nh1); + nh2 = nexthop_next(nh2); + } else if (nh1 || nh2) { + /* One list has more valid nexthops than the other */ + if (IS_ZEBRA_DEBUG_NHT_DETAILED) + zlog_debug("%s: backup nh1 %s, nh2 %s", + __func__, + nh1 ? "non-NULL" : "NULL", + nh2 ? "non-NULL" : "NULL"); + goto done; + } else + break; /* Done with both lists */ + } + + /* Well, it's a match */ + if (IS_ZEBRA_DEBUG_NHT_DETAILED) + zlog_debug("%s: matched", __func__); + + matched_p = true; + +done: + + return matched_p; +} + static int compare_state(struct route_entry *r1, struct route_entry *r2) { if (!r1 && !r2) @@ -994,12 +1113,7 @@ static int compare_state(struct route_entry *r1, struct route_entry *r2) if (r1->metric != r2->metric) return 1; - if (nexthop_group_nexthop_num(&(r1->nhe->nhg)) - != nexthop_group_nexthop_num(&(r2->nhe->nhg))) - return 1; - - if (nexthop_group_hash(&(r1->nhe->nhg)) != - nexthop_group_hash(&(r2->nhe->nhg))) + if (!compare_valid_nexthops(r1, r2)) return 1; return 0; @@ -1044,6 +1158,7 @@ static int send_client(struct rnh *rnh, struct zserv *client, } if (re) { struct zapi_nexthop znh; + struct nexthop_group *nhg; stream_putc(s, re->type); stream_putw(s, re->instance); @@ -1052,7 +1167,9 @@ static int send_client(struct rnh *rnh, struct zserv *client, num = 0; nump = stream_get_endp(s); stream_putc(s, 0); - for (ALL_NEXTHOPS(re->nhe->nhg, nh)) + + nhg = rib_get_fib_nhg(re); + for (ALL_NEXTHOPS_PTR(nhg, nh)) if (rnh_nexthop_valid(re, nh)) { zapi_nexthop_from_nexthop(&znh, nh); ret = zapi_nexthop_encode(s, &znh, 0/*flags*/); @@ -1061,6 +1178,21 @@ static int send_client(struct rnh *rnh, struct zserv *client, num++; } + + nhg = rib_get_fib_backup_nhg(re); + if (nhg == zebra_nhg_get_backup_nhg(re->nhe)) + nhg = NULL; + + if (nhg) { + for (ALL_NEXTHOPS_PTR(nhg, nh)) + if (rnh_nexthop_valid(re, nh)) { + zapi_nexthop_from_nexthop(&znh, nh); + zapi_nexthop_encode(s, &znh, + 0 /* flags */); + num++; + } + } + stream_putc_at(s, nump, num); } else { stream_putc(s, 0); // type diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 9718b40d9d..1da2660509 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -516,8 +516,189 @@ static void show_route_nexthop_helper(struct vty *vty, sizeof(buf), 1)); } - if ((re == NULL) && nexthop->weight) + if (nexthop->weight) vty_out(vty, ", weight %u", nexthop->weight); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) + vty_out(vty, ", backup %d", nexthop->backup_idx); +} + +/* + * Render a nexthop into a json object; the caller allocates and owns + * the json object memory. + */ +static void show_nexthop_json_helper(json_object *json_nexthop, + const struct nexthop *nexthop, + const struct route_entry *re) +{ + char buf[SRCDEST2STR_BUFFER]; + struct vrf *vrf = NULL; + json_object *json_labels = NULL; + + json_object_int_add(json_nexthop, "flags", + nexthop->flags); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) + json_object_boolean_true_add(json_nexthop, + "duplicate"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + json_object_boolean_true_add(json_nexthop, + "fib"); + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + json_object_string_add( + json_nexthop, "ip", + inet_ntoa(nexthop->gate.ipv4)); + json_object_string_add(json_nexthop, "afi", + "ipv4"); + + if (nexthop->ifindex) { + json_object_int_add(json_nexthop, + "interfaceIndex", + nexthop->ifindex); + json_object_string_add( + json_nexthop, "interfaceName", + ifindex2ifname( + nexthop->ifindex, + nexthop->vrf_id)); + } + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + json_object_string_add( + json_nexthop, "ip", + inet_ntop(AF_INET6, &nexthop->gate.ipv6, + buf, sizeof(buf))); + json_object_string_add(json_nexthop, "afi", + "ipv6"); + + if (nexthop->ifindex) { + json_object_int_add(json_nexthop, + "interfaceIndex", + nexthop->ifindex); + json_object_string_add( + json_nexthop, "interfaceName", + ifindex2ifname( + nexthop->ifindex, + nexthop->vrf_id)); + } + break; + + case NEXTHOP_TYPE_IFINDEX: + json_object_boolean_true_add( + json_nexthop, "directlyConnected"); + json_object_int_add(json_nexthop, + "interfaceIndex", + nexthop->ifindex); + json_object_string_add( + json_nexthop, "interfaceName", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + json_object_boolean_true_add(json_nexthop, + "unreachable"); + switch (nexthop->bh_type) { + case BLACKHOLE_REJECT: + json_object_boolean_true_add( + json_nexthop, "reject"); + break; + case BLACKHOLE_ADMINPROHIB: + json_object_boolean_true_add( + json_nexthop, + "admin-prohibited"); + break; + case BLACKHOLE_NULL: + json_object_boolean_true_add( + json_nexthop, "blackhole"); + break; + case BLACKHOLE_UNSPEC: + break; + } + break; + default: + break; + } + + if ((nexthop->vrf_id != re->vrf_id) + && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) { + vrf = vrf_lookup_by_id(nexthop->vrf_id); + json_object_string_add(json_nexthop, "vrf", + vrf->name); + } + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) + json_object_boolean_true_add(json_nexthop, + "duplicate"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + json_object_boolean_true_add(json_nexthop, + "active"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) + json_object_boolean_true_add(json_nexthop, + "onLink"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + json_object_boolean_true_add(json_nexthop, + "recursive"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) + json_object_int_add(json_nexthop, "backupIndex", + nexthop->backup_idx); + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + if (nexthop->src.ipv4.s_addr) { + if (inet_ntop(AF_INET, + &nexthop->src.ipv4, buf, + sizeof(buf))) + json_object_string_add( + json_nexthop, "source", + buf); + } + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, + &in6addr_any)) { + if (inet_ntop(AF_INET6, + &nexthop->src.ipv6, buf, + sizeof(buf))) + json_object_string_add( + json_nexthop, "source", + buf); + } + break; + default: + break; + } + + if (nexthop->nh_label + && nexthop->nh_label->num_labels) { + json_labels = json_object_new_array(); + + for (int label_index = 0; + label_index + < nexthop->nh_label->num_labels; + label_index++) + json_object_array_add( + json_labels, + json_object_new_int( + nexthop->nh_label->label + [label_index])); + + json_object_object_add(json_nexthop, "labels", + json_labels); + } + + if (nexthop->weight) + json_object_int_add(json_nexthop, "weight", + nexthop->weight); + } static void vty_show_ip_route(struct vty *vty, struct route_node *rn, @@ -530,12 +711,12 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, json_object *json_nexthops = NULL; json_object *json_nexthop = NULL; json_object *json_route = NULL; - json_object *json_labels = NULL; time_t uptime; struct vrf *vrf = NULL; rib_dest_t *dest = rib_dest_from_rnode(rn); struct nexthop_group *nhg; char up_str[MONOTIME_STRLEN]; + bool first_p; uptime = monotime(NULL); uptime -= re->uptime; @@ -546,7 +727,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, * nexthops. */ if (is_fib) - nhg = rib_active_nhg(re); + nhg = rib_get_fib_nhg(re); else nhg = &(re->nhe->nhg); @@ -611,177 +792,44 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { json_nexthop = json_object_new_object(); - json_object_int_add(json_nexthop, "flags", - nexthop->flags); + show_nexthop_json_helper(json_nexthop, nexthop, re); + json_object_array_add(json_nexthops, json_nexthop); + } - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) - json_object_boolean_true_add(json_nexthop, - "duplicate"); + json_object_object_add(json_route, "nexthops", json_nexthops); - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) - json_object_boolean_true_add(json_nexthop, - "fib"); + /* If there are backup nexthops, include them */ + if (is_fib) + nhg = rib_get_fib_backup_nhg(re); + else + nhg = zebra_nhg_get_backup_nhg(re->nhe); - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - json_object_string_add( - json_nexthop, "ip", - inet_ntoa(nexthop->gate.ipv4)); - json_object_string_add(json_nexthop, "afi", - "ipv4"); - - if (nexthop->ifindex) { - json_object_int_add(json_nexthop, - "interfaceIndex", - nexthop->ifindex); - json_object_string_add( - json_nexthop, "interfaceName", - ifindex2ifname( - nexthop->ifindex, - nexthop->vrf_id)); - } - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - json_object_string_add( - json_nexthop, "ip", - inet_ntop(AF_INET6, &nexthop->gate.ipv6, - buf, sizeof(buf))); - json_object_string_add(json_nexthop, "afi", - "ipv6"); - - if (nexthop->ifindex) { - json_object_int_add(json_nexthop, - "interfaceIndex", - nexthop->ifindex); - json_object_string_add( - json_nexthop, "interfaceName", - ifindex2ifname( - nexthop->ifindex, - nexthop->vrf_id)); - } - break; - - case NEXTHOP_TYPE_IFINDEX: - json_object_boolean_true_add( - json_nexthop, "directlyConnected"); - json_object_int_add(json_nexthop, - "interfaceIndex", - nexthop->ifindex); - json_object_string_add( - json_nexthop, "interfaceName", - ifindex2ifname(nexthop->ifindex, - nexthop->vrf_id)); - break; - case NEXTHOP_TYPE_BLACKHOLE: - json_object_boolean_true_add(json_nexthop, - "unreachable"); - switch (nexthop->bh_type) { - case BLACKHOLE_REJECT: - json_object_boolean_true_add( - json_nexthop, "reject"); - break; - case BLACKHOLE_ADMINPROHIB: - json_object_boolean_true_add( - json_nexthop, - "admin-prohibited"); - break; - case BLACKHOLE_NULL: - json_object_boolean_true_add( - json_nexthop, "blackhole"); - break; - case BLACKHOLE_UNSPEC: - break; - } - break; - default: - break; - } + if (nhg) { + json_nexthops = json_object_new_array(); - if ((nexthop->vrf_id != re->vrf_id) - && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) { - vrf = vrf_lookup_by_id(nexthop->vrf_id); - json_object_string_add(json_nexthop, "vrf", - vrf->name); - } - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) - json_object_boolean_true_add(json_nexthop, - "duplicate"); - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - json_object_boolean_true_add(json_nexthop, - "active"); - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) - json_object_boolean_true_add(json_nexthop, - "onLink"); - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - json_object_boolean_true_add(json_nexthop, - "recursive"); - - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - if (nexthop->src.ipv4.s_addr) { - if (inet_ntop(AF_INET, - &nexthop->src.ipv4, buf, - sizeof(buf))) - json_object_string_add( - json_nexthop, "source", - buf); - } - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, - &in6addr_any)) { - if (inet_ntop(AF_INET6, - &nexthop->src.ipv6, buf, - sizeof(buf))) - json_object_string_add( - json_nexthop, "source", - buf); - } - break; - default: - break; - } + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + json_nexthop = json_object_new_object(); - if (nexthop->nh_label - && nexthop->nh_label->num_labels) { - json_labels = json_object_new_array(); - - for (int label_index = 0; - label_index - < nexthop->nh_label->num_labels; - label_index++) - json_object_array_add( - json_labels, - json_object_new_int( - nexthop->nh_label->label - [label_index])); - - json_object_object_add(json_nexthop, "labels", - json_labels); + show_nexthop_json_helper(json_nexthop, + nexthop, re); + json_object_array_add(json_nexthops, + json_nexthop); } - if (nexthop->weight) - json_object_int_add(json_nexthop, "weight", - nexthop->weight); - - json_object_array_add(json_nexthops, json_nexthop); + json_object_object_add(json_route, "backupNexthops", + json_nexthops); } - json_object_object_add(json_route, "nexthops", json_nexthops); json_object_array_add(json, json_route); return; } /* Nexthop information. */ + first_p = true; for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { - if (nexthop == nhg->nexthop) { + if (first_p) { + first_p = false; + /* Prefix information. */ len = vty_out(vty, "%c", zebra_route_char(re->type)); if (re->instance) @@ -808,40 +856,36 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, show_route_nexthop_helper(vty, re, nexthop); - if (nexthop->weight) - vty_out(vty, ", weight %u", nexthop->weight); - vty_out(vty, ", %s\n", up_str); + } - /* Check for backup info */ - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { - struct nexthop *backup; - int i; + /* Check for backup info if present */ + if (is_fib) + nhg = rib_get_fib_backup_nhg(re); + else + nhg = zebra_nhg_get_backup_nhg(re->nhe); - if (re->nhe->backup_info == NULL || - re->nhe->backup_info->nhe == NULL) - continue; + if (nhg == NULL) + return; - i = 0; - for (ALL_NEXTHOPS(re->nhe->backup_info->nhe->nhg, - backup)) { - if (i == nexthop->backup_idx) - break; - i++; - } + /* Print backup info */ + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + bool star_p = false; - /* Print useful backup info */ - if (backup) { - /* TODO -- install state is not accurate */ - vty_out(vty, " %*c [backup %d]", - /*re_status_output_char(re, backup),*/ - len - 3 + (2 * nexthop_level(nexthop)), - ' ', nexthop->backup_idx); - show_route_nexthop_helper(vty, re, backup); - vty_out(vty, "\n"); - } - } + if (is_fib) + star_p = CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + + /* TODO -- it'd be nice to be able to include + * the entire list of backups, *and* include the + * real installation state. + */ + vty_out(vty, " b%c %*c", + (star_p ? '*' : ' '), + len - 3 + (2 * nexthop_level(nexthop)), ' '); + show_route_nexthop_helper(vty, re, nexthop); + vty_out(vty, "\n"); } + } static void vty_show_ip_route_detail_json(struct vty *vty, |
