diff --git a/lib/nexthop.c b/lib/nexthop.c index 4ddb53cd9690..376371185a73 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -1158,3 +1158,331 @@ bool nexthop_is_ifindex_type(const struct nexthop *nh) return true; return false; } + +/* + * Render a nexthop into a json object; the caller allocates and owns + * the json object memory. + */ +void nexthop_json_helper(json_object *json_nexthop, + const struct nexthop *nexthop, + bool display_vrfid) +{ + json_object *json_labels = NULL; + json_object *json_backups = NULL; + json_object *json_seg6local = NULL; + json_object *json_seg6 = NULL; + json_object *json_segs = NULL; + int i; + + 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_addf(json_nexthop, "ip", "%pI4", + &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_addf(json_nexthop, "ip", "%pI6", + &nexthop->gate.ipv6); + 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, + "adminProhibited"); + break; + case BLACKHOLE_NULL: + json_object_boolean_true_add(json_nexthop, "blackhole"); + break; + case BLACKHOLE_UNSPEC: + break; + } + break; + } + + /* This nexthop is a resolver for the parent nexthop. + * Set resolver flag for better clarity and delimiter + * in flat list of nexthops in json. + */ + if (nexthop->rparent) + json_object_boolean_true_add(json_nexthop, "resolver"); + + if (display_vrfid) + json_object_string_add(json_nexthop, "vrf", + vrf_id_to_name(nexthop->vrf_id)); + 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_LINKDOWN)) + json_object_boolean_true_add(json_nexthop, "linkDown"); + + 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_backups = json_object_new_array(); + for (i = 0; i < nexthop->backup_num; i++) { + json_object_array_add( + json_backups, + json_object_new_int(nexthop->backup_idx[i])); + } + + json_object_object_add(json_nexthop, "backupIndex", + json_backups); + } + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + if (nexthop->src.ipv4.s_addr) + json_object_string_addf(json_nexthop, "source", "%pI4", + &nexthop->src.ipv4); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any)) + json_object_string_addf(json_nexthop, "source", "%pI6", + &nexthop->src.ipv6); + break; + case NEXTHOP_TYPE_IFINDEX: + case NEXTHOP_TYPE_BLACKHOLE: + 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_type == + ZEBRA_LSP_EVPN) + ? label2vni( + &nexthop->nh_label->label + [label_index]) + : 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); + + if (nexthop->srte_color) + json_object_int_add(json_nexthop, "srteColor", + nexthop->srte_color); + + if (nexthop->nh_srv6) { + json_seg6local = json_object_new_object(); + json_object_string_add( + json_seg6local, "action", + seg6local_action2str( + nexthop->nh_srv6->seg6local_action)); + json_object_object_add(json_nexthop, "seg6local", + json_seg6local); + if (nexthop->nh_srv6->seg6_segs && + nexthop->nh_srv6->seg6_segs->num_segs == 1) { + json_seg6 = json_object_new_object(); + json_object_string_addf(json_seg6, "segs", "%pI6", + &nexthop->nh_srv6->seg6_segs + ->seg[0]); + json_object_object_add(json_nexthop, "seg6", json_seg6); + } else { + if (nexthop->nh_srv6->seg6_segs) { + json_segs = json_object_new_array(); + for (int seg_idx = 0; + seg_idx < + nexthop->nh_srv6->seg6_segs->num_segs; + seg_idx++) + json_object_array_add( + json_segs, + json_object_new_stringf( + "%pI6", + &nexthop->nh_srv6 + ->seg6_segs + ->seg[seg_idx])); + json_object_object_add(json_nexthop, "seg6", + json_segs); + } + } + } +} + +/* + * Helper for nexthop output + */ +void nexthop_vty_helper(struct vty *vty, const struct nexthop *nexthop, + bool display_vrfid) +{ + char buf[MPLS_LABEL_STRLEN]; + char seg_buf[SRV6_SEG_STRLEN]; + struct seg6_segs segs; + uint8_t i; + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + vty_out(vty, " via %pI4", &nexthop->gate.ipv4); + if (nexthop->ifindex) + vty_out(vty, ", %s", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + vty_out(vty, " via %s", + inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, + sizeof(buf))); + if (nexthop->ifindex) + vty_out(vty, ", %s", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + break; + + case NEXTHOP_TYPE_IFINDEX: + vty_out(vty, " is directly connected, %s", + ifindex2ifname(nexthop->ifindex, nexthop->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + vty_out(vty, " unreachable"); + switch (nexthop->bh_type) { + case BLACKHOLE_REJECT: + vty_out(vty, " (ICMP unreachable)"); + break; + case BLACKHOLE_ADMINPROHIB: + vty_out(vty, " (ICMP admin-prohibited)"); + break; + case BLACKHOLE_NULL: + vty_out(vty, " (blackhole)"); + break; + case BLACKHOLE_UNSPEC: + break; + } + break; + } + + if (display_vrfid) + vty_out(vty, " (vrf %s)", vrf_id_to_name(nexthop->vrf_id)); + + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + vty_out(vty, " inactive"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) + vty_out(vty, " onlink"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_LINKDOWN)) + vty_out(vty, " linkdown"); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + vty_out(vty, " (recursive)"); + + switch (nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + if (nexthop->src.ipv4.s_addr) { + vty_out(vty, ", src %pI4", &nexthop->src.ipv4); + /* SR-TE information */ + if (nexthop->srte_color) + vty_out(vty, ", SR-TE color %u", + nexthop->srte_color); + } + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any)) + vty_out(vty, ", src %pI6", &nexthop->src.ipv6); + break; + case NEXTHOP_TYPE_IFINDEX: + case NEXTHOP_TYPE_BLACKHOLE: + break; + } + + /* Label information */ + if (nexthop->nh_label && nexthop->nh_label->num_labels) { + vty_out(vty, ", label %s", + mpls_label2str(nexthop->nh_label->num_labels, + nexthop->nh_label->label, buf, + sizeof(buf), nexthop->nh_label_type, 1)); + } + + if (nexthop->nh_srv6) { + seg6local_context2str(buf, sizeof(buf), + &nexthop->nh_srv6->seg6local_ctx, + nexthop->nh_srv6->seg6local_action); + if (nexthop->nh_srv6->seg6local_action != + ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) + vty_out(vty, ", seg6local %s %s", + seg6local_action2str( + nexthop->nh_srv6->seg6local_action), + buf); + if (nexthop->nh_srv6->seg6_segs && + IPV6_ADDR_CMP(&nexthop->nh_srv6->seg6_segs->seg[0], + &in6addr_any)) { + segs.num_segs = nexthop->nh_srv6->seg6_segs->num_segs; + for (i = 0; i < segs.num_segs; i++) + memcpy(&segs.segs[i], + &nexthop->nh_srv6->seg6_segs->seg[i], + sizeof(struct in6_addr)); + snprintf_seg6_segs(seg_buf, SRV6_SEG_STRLEN, &segs); + vty_out(vty, ", seg6 %s", seg_buf); + } + } + + 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[0]); + + for (i = 1; i < nexthop->backup_num; i++) + vty_out(vty, ",%d", nexthop->backup_idx[i]); + } +} diff --git a/lib/nexthop.h b/lib/nexthop.h index bed6447d49ed..9ec0ac4bc04a 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -252,6 +252,12 @@ extern bool nexthop_is_ifindex_type(const struct nexthop *nh); int nexthop_str2backups(const char *str, int *num_backups, uint8_t *backups); +void nexthop_json_helper(json_object *json_nexthop, + const struct nexthop *nexthop, + bool display_vrfid); +void nexthop_vty_helper(struct vty *vty, const struct nexthop *nexthop, + bool display_vrfid); + #ifdef _FRR_ATTRIBUTE_PRINTFRR #pragma FRR printfrr_ext "%pNH" (struct nexthop *) #endif diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 76cabd1bf09b..6466f6e27085 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -509,13 +509,48 @@ int zsend_interface_update(int cmd, struct zserv *client, struct interface *ifp) return zserv_send_message(client, s); } +static int zsend_redistribute_route_nhg(const struct nexthop_group *nhg, + struct zapi_route *api) +{ + struct nexthop *nexthop; + struct zapi_nexthop *api_nh; + int count = 0; + + for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) { + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + continue; + + api_nh = &(api->nexthops[count]); + api_nh->vrf_id = nexthop->vrf_id; + api_nh->type = nexthop->type; + api_nh->weight = nexthop->weight; + switch (nexthop->type) { + case NEXTHOP_TYPE_BLACKHOLE: + api_nh->bh_type = nexthop->bh_type; + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + api_nh->gate.ipv4 = nexthop->gate.ipv4; + api_nh->ifindex = nexthop->ifindex; + break; + case NEXTHOP_TYPE_IFINDEX: + api_nh->ifindex = nexthop->ifindex; + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + api_nh->gate.ipv6 = nexthop->gate.ipv6; + api_nh->ifindex = nexthop->ifindex; + } + count++; + } + return count; +} + int zsend_redistribute_route(int cmd, struct zserv *client, const struct route_node *rn, const struct route_entry *re, bool is_table_direct) { struct zapi_route api; - struct zapi_nexthop *api_nh; - struct nexthop *nexthop; const struct prefix *p, *src_p; uint8_t count = 0; afi_t afi; @@ -561,34 +596,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client, memcpy(&api.src_prefix, src_p, sizeof(api.src_prefix)); } - for (nexthop = re->nhe->nhg.nexthop; - nexthop; nexthop = nexthop->next) { - if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - continue; - - api_nh = &api.nexthops[count]; - api_nh->vrf_id = nexthop->vrf_id; - api_nh->type = nexthop->type; - api_nh->weight = nexthop->weight; - switch (nexthop->type) { - case NEXTHOP_TYPE_BLACKHOLE: - api_nh->bh_type = nexthop->bh_type; - break; - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - api_nh->gate.ipv4 = nexthop->gate.ipv4; - api_nh->ifindex = nexthop->ifindex; - break; - case NEXTHOP_TYPE_IFINDEX: - api_nh->ifindex = nexthop->ifindex; - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - api_nh->gate.ipv6 = nexthop->gate.ipv6; - api_nh->ifindex = nexthop->ifindex; - } - count++; - } + count = zsend_redistribute_route_nhg(&re->nhe->nhg, &api); /* Nexthops. */ if (count) { diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 15e36acda8c3..b0f620b5269a 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -3357,6 +3357,23 @@ static void mpls_lsp_uninstall_all_type(struct hash_bucket *bucket, void *ctxt) mpls_lsp_uninstall_all(lsp_table, lsp, args->type); } +static void mpls_ftn_uninstall_nhg(struct route_entry *re, + struct nexthop_group *nhg, + enum lsp_types_t lsp_type, bool *update) +{ + struct nexthop *nexthop; + + 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; + } +} + /* * Uninstall all FEC-To-NHLFE (FTN) bindings of the given address-family and * LSP type. @@ -3367,7 +3384,6 @@ static void mpls_ftn_uninstall_all(struct zebra_vrf *zvrf, struct route_table *table; struct route_node *rn; struct route_entry *re; - struct nexthop *nexthop; struct nexthop_group *nhg; bool update; @@ -3385,34 +3401,13 @@ static void mpls_ftn_uninstall_all(struct zebra_vrf *zvrf, new_nhe = zebra_nhe_copy(re->nhe, 0); nhg = &new_nhe->nhg; - 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; - } + mpls_ftn_uninstall_nhg(re, nhg, lsp_type, &update); /* 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 (nhg != NULL) + mpls_ftn_uninstall_nhg(re, nhg, lsp_type, + &update); if (CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED)) mpls_zebra_nhe_update(re, afi, new_nhe); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 5b95d8668af0..56a8b2521c15 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -2698,6 +2698,33 @@ static void early_route_memory_free(struct zebra_early_route *ere) XFREE(MTYPE_WQ_WRAPPER, ere); } +static void +zebra_rib_queue_early_evpn_route_handle(struct zebra_early_route *ere, + struct nexthop_group *nhg, bool add) +{ + struct route_entry *re = ere->re; + struct ipaddr vtep_ip = {}; + struct nexthop *tmp_nh; + + for (ALL_NEXTHOPS_PTR(nhg, tmp_nh)) { + if (!CHECK_FLAG(tmp_nh->flags, NEXTHOP_FLAG_EVPN)) + continue; + if (ere->afi == AFI_IP) { + vtep_ip.ipa_type = IPADDR_V4; + vtep_ip.ipaddr_v4 = tmp_nh->gate.ipv4; + } else { + vtep_ip.ipa_type = IPADDR_V6; + vtep_ip.ipaddr_v6 = tmp_nh->gate.ipv6; + } + if (add) + zebra_rib_queue_evpn_route_add(re->vrf_id, &tmp_nh->rmac, + &vtep_ip, &ere->p); + else + zebra_rib_queue_evpn_route_del(re->vrf_id, &vtep_ip, + &ere->p); + } +} + static void process_subq_early_route_add(struct zebra_early_route *ere) { struct route_entry *re = ere->re; @@ -2740,8 +2767,6 @@ static void process_subq_early_route_add(struct zebra_early_route *ere) return; } } else { - struct nexthop *tmp_nh; - /* Lookup nhe from route information */ nhe = zebra_nhg_rib_find_nhe(ere->re_nhe, ere->afi); if (!nhe) { @@ -2759,22 +2784,7 @@ static void process_subq_early_route_add(struct zebra_early_route *ere) early_route_memory_free(ere); return; } - for (ALL_NEXTHOPS(nhe->nhg, tmp_nh)) { - if (CHECK_FLAG(tmp_nh->flags, NEXTHOP_FLAG_EVPN)) { - struct ipaddr vtep_ip = {}; - - if (ere->afi == AFI_IP) { - vtep_ip.ipa_type = IPADDR_V4; - vtep_ip.ipaddr_v4 = tmp_nh->gate.ipv4; - } else { - vtep_ip.ipa_type = IPADDR_V6; - vtep_ip.ipaddr_v6 = tmp_nh->gate.ipv6; - } - zebra_rib_queue_evpn_route_add( - re->vrf_id, &tmp_nh->rmac, &vtep_ip, - &ere->p); - } - } + zebra_rib_queue_early_evpn_route_handle(ere, &nhe->nhg, true); } /* @@ -3088,8 +3098,6 @@ static void process_subq_early_route_delete(struct zebra_early_route *ere) } if (same) { - struct nexthop *tmp_nh; - if (ere->fromkernel && CHECK_FLAG(ere->re->flags, ZEBRA_FLAG_SELFROUTE) && !zrouter.allow_delete) { @@ -3104,26 +3112,8 @@ static void process_subq_early_route_delete(struct zebra_early_route *ere) * EVPN - the nexthop (and associated MAC) need to be * uninstalled if no more refs. */ - for (ALL_NEXTHOPS(re->nhe->nhg, tmp_nh)) { - struct ipaddr vtep_ip; - - if (CHECK_FLAG(tmp_nh->flags, NEXTHOP_FLAG_EVPN)) { - memset(&vtep_ip, 0, sizeof(struct ipaddr)); - if (ere->afi == AFI_IP) { - vtep_ip.ipa_type = IPADDR_V4; - memcpy(&(vtep_ip.ipaddr_v4), - &(tmp_nh->gate.ipv4), - sizeof(struct in_addr)); - } else { - vtep_ip.ipa_type = IPADDR_V6; - memcpy(&(vtep_ip.ipaddr_v6), - &(tmp_nh->gate.ipv6), - sizeof(struct in6_addr)); - } - zebra_rib_queue_evpn_route_del( - re->vrf_id, &vtep_ip, &ere->p); - } - } + zebra_rib_queue_early_evpn_route_handle(ere, &re->nhe->nhg, + false); /* Notify dplane if system route changes */ if (RIB_SYSTEM_ROUTE(re)) @@ -4185,6 +4175,16 @@ static void _route_entry_dump_nh(const struct route_entry *re, } +static void _route_entry_dump_nhg(const struct route_entry *re, + const char *straddr, + const struct nexthop_group *nhg) +{ + struct nexthop *nhop; + + for (ALL_NEXTHOPS_PTR(nhg, nhop)) + _route_entry_dump_nh(re, straddr, nhop); +} + /* This function dumps the contents of a given RE entry into * standard debug log. Calling function name and IP prefix in * question are passed as 1st and 2nd arguments. @@ -4199,7 +4199,6 @@ void _route_entry_dump(const char *func, union prefixconstptr pp, char srcaddr[PREFIX_STRLEN]; char flags_buf[128]; char status_buf[128]; - struct nexthop *nexthop; struct vrf *vrf = vrf_lookup_by_id(re->vrf_id); struct nexthop_group *nhg; @@ -4225,15 +4224,13 @@ void _route_entry_dump(const char *func, union prefixconstptr pp, nexthop_group_active_nexthop_num(&(re->nhe->nhg))); /* Dump nexthops */ - for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) - _route_entry_dump_nh(re, straddr, nexthop); + _route_entry_dump_nhg(re, straddr, &re->nhe->nhg); if (zebra_nhg_get_backup_nhg(re->nhe)) { zlog_debug("%s: backup nexthops:", straddr); nhg = zebra_nhg_get_backup_nhg(re->nhe); - for (ALL_NEXTHOPS_PTR(nhg, nexthop)) - _route_entry_dump_nh(re, straddr, nexthop); + _route_entry_dump_nhg(re, straddr, nhg); } zlog_debug("%s: dump complete", straddr); diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index b387e9949bb0..2f764756622c 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -1268,194 +1268,12 @@ void show_nexthop_json_helper(json_object *json_nexthop, const struct nexthop *nexthop, const struct route_entry *re) { - json_object *json_labels = NULL; - json_object *json_backups = NULL; - json_object *json_seg6local = NULL; - json_object *json_seg6 = NULL; - json_object *json_segs = NULL; - int i; - - 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_addf(json_nexthop, "ip", "%pI4", - &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_addf(json_nexthop, "ip", "%pI6", - &nexthop->gate.ipv6); - 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, - "adminProhibited"); - break; - case BLACKHOLE_NULL: - json_object_boolean_true_add(json_nexthop, "blackhole"); - break; - case BLACKHOLE_UNSPEC: - break; - } - break; - } - - /* This nexthop is a resolver for the parent nexthop. - * Set resolver flag for better clarity and delimiter - * in flat list of nexthops in json. - */ - if (nexthop->rparent) - json_object_boolean_true_add(json_nexthop, "resolver"); - - if ((re == NULL || (nexthop->vrf_id != re->vrf_id))) - json_object_string_add(json_nexthop, "vrf", - vrf_id_to_name(nexthop->vrf_id)); + bool display_vrfid = false; - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) - json_object_boolean_true_add(json_nexthop, "duplicate"); + if (re == NULL || nexthop->vrf_id != re->vrf_id) + display_vrfid = true; - 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_LINKDOWN)) - json_object_boolean_true_add(json_nexthop, "linkDown"); - - 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_backups = json_object_new_array(); - for (i = 0; i < nexthop->backup_num; i++) { - json_object_array_add( - json_backups, - json_object_new_int(nexthop->backup_idx[i])); - } - - json_object_object_add(json_nexthop, "backupIndex", - json_backups); - } - - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - if (nexthop->src.ipv4.s_addr) - json_object_string_addf(json_nexthop, "source", "%pI4", - &nexthop->src.ipv4); - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any)) - json_object_string_addf(json_nexthop, "source", "%pI6", - &nexthop->src.ipv6); - break; - case NEXTHOP_TYPE_IFINDEX: - case NEXTHOP_TYPE_BLACKHOLE: - 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_type == - ZEBRA_LSP_EVPN) - ? label2vni( - &nexthop->nh_label->label - [label_index]) - : 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); - - if (nexthop->srte_color) - json_object_int_add(json_nexthop, "srteColor", - nexthop->srte_color); - - if (nexthop->nh_srv6) { - json_seg6local = json_object_new_object(); - json_object_string_add( - json_seg6local, "action", - seg6local_action2str( - nexthop->nh_srv6->seg6local_action)); - json_object_object_add(json_nexthop, "seg6local", - json_seg6local); - if (nexthop->nh_srv6->seg6_segs && - nexthop->nh_srv6->seg6_segs->num_segs == 1) { - json_seg6 = json_object_new_object(); - json_object_string_addf(json_seg6, "segs", "%pI6", - &nexthop->nh_srv6->seg6_segs - ->seg[0]); - json_object_object_add(json_nexthop, "seg6", json_seg6); - } else { - if (nexthop->nh_srv6->seg6_segs) { - json_segs = json_object_new_array(); - for (int seg_idx = 0; - seg_idx < - nexthop->nh_srv6->seg6_segs->num_segs; - seg_idx++) - json_object_array_add( - json_segs, - json_object_new_stringf( - "%pI6", - &nexthop->nh_srv6 - ->seg6_segs - ->seg[seg_idx])); - json_object_object_add(json_nexthop, "seg6", - json_segs); - } - } - } + nexthop_json_helper(json_nexthop, nexthop, display_vrfid); } /* @@ -1464,129 +1282,12 @@ void show_nexthop_json_helper(json_object *json_nexthop, void show_route_nexthop_helper(struct vty *vty, const struct route_entry *re, const struct nexthop *nexthop) { - char buf[MPLS_LABEL_STRLEN]; - char seg_buf[SRV6_SEG_STRLEN]; - struct seg6_segs segs; - uint8_t i; - - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - vty_out(vty, " via %pI4", &nexthop->gate.ipv4); - if (nexthop->ifindex) - vty_out(vty, ", %s", - ifindex2ifname(nexthop->ifindex, - nexthop->vrf_id)); - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - vty_out(vty, " via %s", - inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, - sizeof(buf))); - if (nexthop->ifindex) - vty_out(vty, ", %s", - ifindex2ifname(nexthop->ifindex, - nexthop->vrf_id)); - break; - - case NEXTHOP_TYPE_IFINDEX: - vty_out(vty, " is directly connected, %s", - ifindex2ifname(nexthop->ifindex, nexthop->vrf_id)); - break; - case NEXTHOP_TYPE_BLACKHOLE: - vty_out(vty, " unreachable"); - switch (nexthop->bh_type) { - case BLACKHOLE_REJECT: - vty_out(vty, " (ICMP unreachable)"); - break; - case BLACKHOLE_ADMINPROHIB: - vty_out(vty, " (ICMP admin-prohibited)"); - break; - case BLACKHOLE_NULL: - vty_out(vty, " (blackhole)"); - break; - case BLACKHOLE_UNSPEC: - break; - } - break; - } - - if ((re == NULL || (nexthop->vrf_id != re->vrf_id))) - vty_out(vty, " (vrf %s)", vrf_id_to_name(nexthop->vrf_id)); - - if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - vty_out(vty, " inactive"); + bool display_vrfid = false; - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) - vty_out(vty, " onlink"); + if (re == NULL || nexthop->vrf_id != re->vrf_id) + display_vrfid = true; - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_LINKDOWN)) - vty_out(vty, " linkdown"); - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - vty_out(vty, " (recursive)"); - - switch (nexthop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - if (nexthop->src.ipv4.s_addr) { - vty_out(vty, ", src %pI4", &nexthop->src.ipv4); - /* SR-TE information */ - if (nexthop->srte_color) - vty_out(vty, ", SR-TE color %u", - nexthop->srte_color); - } - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any)) - vty_out(vty, ", src %pI6", &nexthop->src.ipv6); - break; - case NEXTHOP_TYPE_IFINDEX: - case NEXTHOP_TYPE_BLACKHOLE: - break; - } - - /* Label information */ - if (nexthop->nh_label && nexthop->nh_label->num_labels) { - vty_out(vty, ", label %s", - mpls_label2str(nexthop->nh_label->num_labels, - nexthop->nh_label->label, buf, - sizeof(buf), nexthop->nh_label_type, 1)); - } - - if (nexthop->nh_srv6) { - seg6local_context2str(buf, sizeof(buf), - &nexthop->nh_srv6->seg6local_ctx, - nexthop->nh_srv6->seg6local_action); - if (nexthop->nh_srv6->seg6local_action != - ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) - vty_out(vty, ", seg6local %s %s", - seg6local_action2str( - nexthop->nh_srv6->seg6local_action), - buf); - if (nexthop->nh_srv6->seg6_segs && - IPV6_ADDR_CMP(&nexthop->nh_srv6->seg6_segs->seg[0], - &in6addr_any)) { - segs.num_segs = nexthop->nh_srv6->seg6_segs->num_segs; - for (i = 0; i < segs.num_segs; i++) - memcpy(&segs.segs[i], - &nexthop->nh_srv6->seg6_segs->seg[i], - sizeof(struct in6_addr)); - snprintf_seg6_segs(seg_buf, SRV6_SEG_STRLEN, &segs); - vty_out(vty, ", seg6 %s", seg_buf); - } - } - - 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[0]); - - for (i = 1; i < nexthop->backup_num; i++) - vty_out(vty, ",%d", nexthop->backup_idx[i]); - } + nexthop_vty_helper(vty, nexthop, display_vrfid); } static void print_rnh(struct route_node *rn, struct vty *vty, json_object *json) diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index da6e2069ff28..1ddf3bd8a905 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -521,12 +521,29 @@ static void uptime2str(time_t uptime, char *buf, size_t bufsize) frrtime_to_interval(cur, buf, bufsize); } +static void show_nexthop_detail_group_helper(struct vty *vty, + struct route_entry *re, + struct nexthop_group *nhg) +{ + struct nexthop *nexthop; + + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + /* Use helper to format each nexthop */ + show_nexthop_detail_helper(vty, re, nexthop, + false /*not backup*/); + vty_out(vty, "\n"); + + /* Include backup(s), if present */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) + show_nh_backup_helper(vty, re, nexthop); + } +} + /* New RIB. Detailed information for IPv4 route. */ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, int mcast, bool use_fib, bool show_ng) { struct route_entry *re; - struct nexthop *nexthop; char buf[SRCDEST2STR_BUFFER]; struct zebra_vrf *zvrf; rib_dest_t *dest; @@ -588,31 +605,79 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, re->nhe_installed_id); } - for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) { - /* Use helper to format each nexthop */ - show_nexthop_detail_helper(vty, re, nexthop, - false /*not backup*/); - vty_out(vty, "\n"); - - /* Include backup(s), if present */ - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) - show_nh_backup_helper(vty, re, nexthop); - } + show_nexthop_detail_group_helper(vty, re, &re->nhe->nhg); zebra_show_ip_route_opaque(vty, re, NULL); vty_out(vty, "\n"); } } +static void show_route_nexthop_helper_specific( + struct vty *vty, struct route_entry *re, const struct nexthop *nexthop, + bool *first_p, bool is_fib, bool nhg_from_backup, int len, char *up_str) +{ + if (*first_p) { + *first_p = false; + } else if (nhg_from_backup) { + vty_out(vty, " b%c%*c", + re_status_output_char(re, nexthop, is_fib), + len - 3 + (2 * nexthop_level(nexthop)), ' '); + } else { + vty_out(vty, " %c%*c", + re_status_output_char(re, nexthop, is_fib), + len - 3 + (2 * nexthop_level(nexthop)), ' '); + } + + show_route_nexthop_helper(vty, re, nexthop); + vty_out(vty, ", %s\n", up_str); +} + +static void show_route_nexthop_group_helper(struct vty *vty, + struct route_entry *re, + const struct nexthop_group *nhg, + bool is_fib, int len) +{ + const struct nexthop *nexthop; + bool star_p = false; + + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + 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 show_nexthop_group_json_helper(json_object *json_nexthops, + const struct nexthop_group *nhg, + struct route_entry *re) +{ + const struct nexthop *nexthop; + json_object *json_nexthop = NULL; + + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + json_nexthop = json_object_new_object(); + show_nexthop_json_helper(json_nexthop, nexthop, re); + + json_object_array_add(json_nexthops, json_nexthop); + } +} + static void vty_show_ip_route(struct vty *vty, struct route_node *rn, struct route_entry *re, json_object *json, bool is_fib, bool show_ng) { - const struct nexthop *nexthop; + struct nexthop *nexthop; int len = 0; char buf[SRCDEST2STR_BUFFER]; json_object *json_nexthops = NULL; - json_object *json_nexthop = NULL; json_object *json_route = NULL; const rib_dest_t *dest = rib_dest_from_rnode(rn); const struct nexthop_group *nhg; @@ -701,14 +766,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, json_object_string_add(json_route, "uptime", up_str); - for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { - json_nexthop = json_object_new_object(); - show_nexthop_json_helper(json_nexthop, - nexthop, re); - - json_object_array_add(json_nexthops, - json_nexthop); - } + show_nexthop_group_json_helper(json_nexthops, nhg, re); json_object_object_add(json_route, "nexthops", json_nexthops); @@ -721,14 +779,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, if (nhg && nhg->nexthop) { json_nexthops = json_object_new_array(); - for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { - json_nexthop = json_object_new_object(); - - show_nexthop_json_helper(json_nexthop, - nexthop, re); - json_object_array_add(json_nexthops, - json_nexthop); - } + show_nexthop_group_json_helper(json_nexthops, nhg, re); json_object_object_add(json_route, "backupNexthops", json_nexthops); @@ -777,22 +828,10 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, len += vty_out(vty, " (%u)", re->nhe_id); /* Nexthop information. */ - for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { - if (first_p) { - first_p = false; - } else if (nhg_from_backup) { - vty_out(vty, " b%c%*c", - re_status_output_char(re, nexthop, is_fib), - len - 3 + (2 * nexthop_level(nexthop)), ' '); - } else { - vty_out(vty, " %c%*c", - re_status_output_char(re, nexthop, is_fib), - len - 3 + (2 * nexthop_level(nexthop)), ' '); - } - - show_route_nexthop_helper(vty, re, nexthop); - vty_out(vty, ", %s\n", up_str); - } + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) + show_route_nexthop_helper_specific(vty, re, nexthop, &first_p, + is_fib, nhg_from_backup, len, + up_str); /* If we only had backup nexthops, we're done */ if (nhg_from_backup) @@ -808,23 +847,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, return; /* Print backup info */ - for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { - bool star_p = false; - - 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"); - } - + show_route_nexthop_group_helper(vty, re, nhg, is_fib, len); } static void vty_show_ip_route_detail_json(struct vty *vty, @@ -1164,10 +1187,76 @@ DEFPY (show_ip_nht, return CMD_SUCCESS; } +static void show_nexthop_group_out_nexthop_specific( + struct vty *vty, struct json_object *json_nexthop_array, + int display_backup_info, struct nexthop *nexthop) +{ + json_object *json_nexthops = NULL; + + if (json_nexthop_array) { + json_nexthops = json_object_new_object(); + show_nexthop_json_helper(json_nexthops, nexthop, NULL); + } else { + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + vty_out(vty, " "); + else + /* Make recursive nexthops a bit more clear */ + vty_out(vty, " "); + show_route_nexthop_helper(vty, NULL, nexthop); + } + + if (display_backup_info) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + if (json_nexthop_array) + json_object_int_add(json_nexthops, "backup", + nexthop->backup_idx[0]); + else + vty_out(vty, " [backup %d]", + nexthop->backup_idx[0]); + } + + if (!json_nexthop_array) + vty_out(vty, "\n"); + else + json_object_array_add(json_nexthop_array, json_nexthops); + + return; + } + + if (!json_nexthop_array) { + /* TODO -- print more useful backup info */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + int i; + + vty_out(vty, "[backup"); + for (i = 0; i < nexthop->backup_num; i++) + vty_out(vty, " %d", nexthop->backup_idx[i]); + vty_out(vty, "]"); + } + vty_out(vty, "\n"); + } else { + json_object_array_add(json_nexthop_array, json_nexthops); + } +} + +static void +show_nexthop_group_out_nexthop(struct vty *vty, struct nexthop_group *nhg, + bool display_backup_info, + struct json_object *json_nexthop_array) +{ + struct nexthop *nexthop = NULL; + + + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + show_nexthop_group_out_nexthop_specific(vty, json_nexthop_array, + display_backup_info, + nexthop); + } +} + static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe, json_object *json_nhe_hdr) { - struct nexthop *nexthop = NULL; struct nhg_connected *rb_node_dep = NULL; struct nexthop_group *backup_nhg; char up_str[MONOTIME_STRLEN]; @@ -1175,10 +1264,8 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe, json_object *json_dependants = NULL; json_object *json_depends = NULL; json_object *json_nexthop_array = NULL; - json_object *json_nexthops = NULL; json_object *json = NULL; json_object *json_backup_nexthop_array = NULL; - json_object *json_backup_nexthops = NULL; uptime2str(nhe->uptime, up_str, sizeof(up_str)); @@ -1269,58 +1356,12 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe, json_nexthop_array = json_object_new_array(); - for (ALL_NEXTHOPS(nhe->nhg, nexthop)) { - if (json_nexthop_array) { - json_nexthops = json_object_new_object(); - show_nexthop_json_helper(json_nexthops, nexthop, NULL); - } else { - if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - vty_out(vty, " "); - else - /* Make recursive nexthops a bit more clear */ - vty_out(vty, " "); - show_route_nexthop_helper(vty, NULL, nexthop); - } - - if (nhe->backup_info == NULL || nhe->backup_info->nhe == NULL) { - if (CHECK_FLAG(nexthop->flags, - NEXTHOP_FLAG_HAS_BACKUP)) { - if (json) - json_object_int_add( - json_nexthops, "backup", - nexthop->backup_idx[0]); - else - vty_out(vty, " [backup %d]", - nexthop->backup_idx[0]); - } - - if (!json) - vty_out(vty, "\n"); - else - json_object_array_add(json_nexthop_array, - json_nexthops); - - continue; - } - - if (!json) { - /* TODO -- print more useful backup info */ - if (CHECK_FLAG(nexthop->flags, - NEXTHOP_FLAG_HAS_BACKUP)) { - int i; - - vty_out(vty, "[backup"); - for (i = 0; i < nexthop->backup_num; i++) - vty_out(vty, " %d", - nexthop->backup_idx[i]); - vty_out(vty, "]"); - } - vty_out(vty, "\n"); - } else { - json_object_array_add(json_nexthop_array, - json_nexthops); - } - } + if (nhe->backup_info == NULL || nhe->backup_info->nhe == NULL) + show_nexthop_group_out_nexthop(vty, &nhe->nhg, true, + json_nexthop_array); + else + show_nexthop_group_out_nexthop(vty, &nhe->nhg, false, + json_nexthop_array); if (json) json_object_object_add(json, "nexthops", json_nexthop_array); @@ -1333,28 +1374,8 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe, else vty_out(vty, " Backups:\n"); - for (ALL_NEXTHOPS_PTR(backup_nhg, nexthop)) { - if (json_backup_nexthop_array) { - json_backup_nexthops = json_object_new_object(); - show_nexthop_json_helper(json_backup_nexthops, - nexthop, NULL); - json_object_array_add(json_backup_nexthop_array, - json_backup_nexthops); - } else { - - if (!CHECK_FLAG(nexthop->flags, - NEXTHOP_FLAG_RECURSIVE)) - vty_out(vty, " "); - else - /* Make recursive nexthops a bit more - * clear - */ - vty_out(vty, " "); - show_route_nexthop_helper(vty, NULL, nexthop); - vty_out(vty, "\n"); - } - } - + show_nexthop_group_out_nexthop(vty, backup_nhg, false, + json_backup_nexthop_array); if (json) json_object_object_add(json, "backupNexthops", json_backup_nexthop_array);