]> git.puffer.fish Git - mirror/frr.git/commitdiff
zebra: fix JSON fields for "show ip/ipv6 nht" 12660/head
authorPooja Jagadeesh Doijode <pdoijode@nvidia.com>
Wed, 25 Jan 2023 02:15:36 +0000 (18:15 -0800)
committerPooja Jagadeesh Doijode <pdoijode@nvidia.com>
Wed, 25 Jan 2023 02:15:36 +0000 (18:15 -0800)
1. Renamed "gates" to "nexthops"
2. Displaying afi of the nexthops being dispalyed in place of
   "nexthops" JSON object in the old JSON output
3. Calling show_route_nexthop_helper() and show_nexthop_json_helper()
   instead of print_nh() inorder to keeps the fields in "nexthops"
   JSON object in sync with "nexthops" JSON object of
   "show nexthop-group rib json".

Updated vtysh:
    r1# show ip nht
    192.168.0.2
     resolved via connected
     is directly connected, r1-eth0 (vrf default)
     Client list: static(fd 28)
    192.168.0.4
     resolved via connected
     is directly connected, r1-eth0 (vrf default)
     Client list: static(fd 28)

Updated JSON:
    r1# show ip nht json
    {
      "default":{
        "ipv4":{
          "192.168.0.2":{
            "nhtConnected":false,
            "clientList":[
              {
                "protocol":"static",
                "socket":28,
                "protocolFiltered":"none"
              }
            ],
            "nexthops":[
              {
                "flags":3,
                "fib":true,
                "directlyConnected":true,
                "interfaceIndex":2,
                "interfaceName":"r1-eth0",
                "vrf":"default",
                "active":true
              }
            ],
            "resolvedProtocol":"connected"
          }
        }
      }
    }

Signed-off-by: Pooja Jagadeesh Doijode <pdoijode@nvidia.com>
tests/topotests/all_protocol_startup/r1/ip_nht.ref
tests/topotests/all_protocol_startup/r1/ipv6_nht.ref
zebra/zebra_rnh.c
zebra/zebra_rnh.h
zebra/zebra_vty.c

index 1da4da4df513b77f1a67f96df469cf4ccdd3bd83..0ef3f4b67535222a9ecdfe4587c4eb08e2d3bfd8 100644 (file)
@@ -1,34 +1,34 @@
 1.1.1.1
  resolved via static
- is directly connected, r1-eth1
+ is directly connected, r1-eth1 (vrf default), weight 1
  Client list: pbr(fd XX)
 1.1.1.2
  resolved via static
- is directly connected, r1-eth2
+ is directly connected, r1-eth2 (vrf default), weight 1
  Client list: pbr(fd XX)
 1.1.1.3
  resolved via static
- is directly connected, r1-eth3
+ is directly connected, r1-eth3 (vrf default), weight 1
  Client list: pbr(fd XX)
 1.1.1.4
  resolved via static
- is directly connected, r1-eth4
+ is directly connected, r1-eth4 (vrf default), weight 1
  Client list: pbr(fd XX)
 1.1.1.5
  resolved via static
- is directly connected, r1-eth5
+ is directly connected, r1-eth5 (vrf default), weight 1
  Client list: pbr(fd XX)
 1.1.1.6
  resolved via static
- is directly connected, r1-eth6
+ is directly connected, r1-eth6 (vrf default), weight 1
  Client list: pbr(fd XX)
 1.1.1.7
  resolved via static
- is directly connected, r1-eth7
+ is directly connected, r1-eth7 (vrf default), weight 1
  Client list: pbr(fd XX)
 1.1.1.8
  resolved via static
- is directly connected, r1-eth8
+ is directly connected, r1-eth8 (vrf default), weight 1
  Client list: pbr(fd XX)
 2.2.2.1
  unresolved
  Client list: pbr(fd XX)
 192.168.0.2
  resolved via connected
- is directly connected, r1-eth0
+ is directly connected, r1-eth0 (vrf default)
  Client list: static(fd XX)
 192.168.0.4
  resolved via connected
- is directly connected, r1-eth0
+ is directly connected, r1-eth0 (vrf default)
  Client list: static(fd XX)
 192.168.7.10
  resolved via connected
- is directly connected, r1-eth7
+ is directly connected, r1-eth7 (vrf default)
  Client list: bgp(fd XX)
 192.168.7.20(Connected)
  resolved via connected
- is directly connected, r1-eth7
+ is directly connected, r1-eth7 (vrf default)
  Client list: bgp(fd XX)
 192.168.161.4
  unresolved
index 0255ecdee80e863ea34b2525be94855b125cb1f4..8c937280075845dbbd6a84418cb1d35088506cdb 100644 (file)
@@ -1,13 +1,13 @@
 fc00::2
  resolved via connected
- is directly connected, r1-eth0
+ is directly connected, r1-eth0 (vrf default)
  Client list: static(fd XX)
 fc00:0:0:8::1000
  resolved via connected
- is directly connected, r1-eth8
+ is directly connected, r1-eth8 (vrf default)
  Client list: bgp(fd XX)
 fc00:0:0:8::2000(Connected)
  resolved via connected
- is directly connected, r1-eth8
+ is directly connected, r1-eth8 (vrf default)
  Client list: bgp(fd XX)
\ No newline at end of file
index 599c679864e908221afbfdd31b4634f4f21f8f60..e24556122b8862a75e1031309d60d95d2a83924c 100644 (file)
@@ -1270,73 +1270,291 @@ failure:
        return -1;
 }
 
-static void print_nh(struct nexthop *nexthop, struct vty *vty,
-                    json_object *json)
+
+/*
+ * Render a nexthop into a json object; the caller allocates and owns
+ * the json object memory.
+ */
+void show_nexthop_json_helper(json_object *json_nexthop,
+                             const struct nexthop *nexthop,
+                             const struct route_entry *re)
 {
-       struct zebra_ns *zns = zebra_ns_lookup(nexthop->vrf_id);
+       json_object *json_labels = NULL;
+       json_object *json_backups = NULL;
+       json_object *json_seg6local = NULL;
+       json_object *json_seg6 = 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:
-               if (json) {
-                       json_object_string_addf(json, "ip", "%pI4",
-                                               &nexthop->gate.ipv4);
-                       if (nexthop->ifindex)
-                               json_object_string_add(
-                                       json, "interface",
-                                       ifindex2ifname_per_ns(
-                                               zns, nexthop->ifindex));
-               } else {
-                       vty_out(vty, " via %pI4", &nexthop->gate.ipv4);
-                       if (nexthop->ifindex)
-                               vty_out(vty, ", %s",
-                                       ifindex2ifname_per_ns(
-                                               zns, nexthop->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:
-               if (json) {
-                       json_object_string_addf(json, "ip", "%pI6",
-                                               &nexthop->gate.ipv6);
-                       if (nexthop->ifindex)
-                               json_object_string_add(
-                                       json, "interface",
-                                       ifindex2ifname_per_ns(
-                                               zns, nexthop->ifindex));
-               } else {
-                       vty_out(vty, " %pI6", &nexthop->gate.ipv6);
-                       if (nexthop->ifindex)
-                               vty_out(vty, ", via %s",
-                                       ifindex2ifname_per_ns(
-                                               zns, nexthop->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:
-               if (json) {
-                       json_object_string_add(
-                               json, "interface",
-                               ifindex2ifname_per_ns(zns, nexthop->ifindex));
-                       json_object_boolean_true_add(json, "directlyConnected");
-               } else {
-                       vty_out(vty, " is directly connected, %s",
-                               ifindex2ifname_per_ns(zns, nexthop->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));
+
+       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;
+       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);
+
+       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);
+
+               json_seg6 = json_object_new_object();
+               json_object_string_addf(json_seg6, "segs", "%pI6",
+                                       &nexthop->nh_srv6->seg6_segs);
+               json_object_object_add(json_nexthop, "seg6", json_seg6);
+       }
+}
+
+/*
+ * Helper for nexthop output, used in the 'show ip route' path
+ */
+void show_route_nexthop_helper(struct vty *vty, const struct route_entry *re,
+                              const struct nexthop *nexthop)
+{
+       char buf[MPLS_LABEL_STRLEN];
+       int 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:
-               if (json) {
-                       json_object_string_add(json, "interface", "Null0");
-                       json_object_boolean_true_add(json, "directlyConnected");
-               } else {
-                       vty_out(vty, " is directly connected, Null0");
+               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");
+
+       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;
        default:
                break;
        }
 
-       if (!json)
-               vty_out(vty, "\n");
+       /* 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), 1));
+       }
+
+       if (nexthop->nh_srv6) {
+               seg6local_context2str(buf, sizeof(buf),
+                                     &nexthop->nh_srv6->seg6local_ctx,
+                                     nexthop->nh_srv6->seg6local_action);
+               vty_out(vty, ", seg6local %s %s",
+                       seg6local_action2str(
+                               nexthop->nh_srv6->seg6local_action),
+                       buf);
+               vty_out(vty, ", seg6 %pI6", &nexthop->nh_srv6->seg6_segs);
+       }
+
+       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]);
+       }
 }
 
 static void print_rnh(struct route_node *rn, struct vty *vty, json_object *json)
@@ -1368,7 +1586,8 @@ static void print_rnh(struct route_node *rn, struct vty *vty, json_object *json)
                        CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED));
                json_object_object_add(json_nht, "clientList",
                                       json_client_array);
-               json_object_object_add(json_nht, "gates", json_nexthop_array);
+               json_object_object_add(json_nht, "nexthops",
+                                      json_nexthop_array);
        } else {
                vty_out(vty, "%s%s\n",
                        inet_ntop(rn->p.family, &rn->p.u.prefix, buf, BUFSIZ),
@@ -1392,8 +1611,12 @@ static void print_rnh(struct route_node *rn, struct vty *vty, json_object *json)
                                json_nexthop = json_object_new_object();
                                json_object_array_add(json_nexthop_array,
                                                      json_nexthop);
+                               show_nexthop_json_helper(json_nexthop, nexthop,
+                                                        NULL);
+                       } else {
+                               show_route_nexthop_helper(vty, NULL, nexthop);
+                               vty_out(vty, "\n");
                        }
-                       print_nh(nexthop, vty, json_nexthop);
                }
        } else {
                if (json)
index 44ce65b4b650a31a839c47f5e1ff5ce99ca17702..88be69a81db31dc8fbc180a865806a3ec9c2496d 100644 (file)
@@ -58,6 +58,12 @@ extern bool rnh_nexthop_valid(const struct route_entry *re,
 void rnh_set_hide_backups(bool hide_p);
 bool rnh_get_hide_backups(void);
 
+void show_nexthop_json_helper(json_object *json_nexthop,
+                             const struct nexthop *nexthop,
+                             const struct route_entry *re);
+void show_route_nexthop_helper(struct vty *vty, const struct route_entry *re,
+                              const struct nexthop *nexthop);
+
 #ifdef __cplusplus
 }
 #endif
index ccd6eb263140c51a6a0001f07f4229696c75513b..8a73ae3d281e8c53c320853042c7ec94012651b3 100644 (file)
@@ -598,340 +598,6 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
        }
 }
 
-/*
- * Helper for nexthop output, used in the 'show ip route' path
- */
-static void show_route_nexthop_helper(struct vty *vty,
-                                     const struct route_entry *re,
-                                     const struct nexthop *nexthop)
-{
-       char buf[MPLS_LABEL_STRLEN];
-       int 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");
-
-       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) {
-                       if (inet_ntop(AF_INET, &nexthop->src.ipv4, buf,
-                                     sizeof(buf)))
-                               vty_out(vty, ", src %s", buf);
-                       /* 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)) {
-                       if (inet_ntop(AF_INET6, &nexthop->src.ipv6, buf,
-                                     sizeof(buf)))
-                               vty_out(vty, ", src %s", buf);
-               }
-               break;
-       default:
-               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), 1));
-       }
-
-       if (nexthop->nh_srv6) {
-               seg6local_context2str(buf, sizeof(buf),
-                                     &nexthop->nh_srv6->seg6local_ctx,
-                                     nexthop->nh_srv6->seg6local_action);
-               vty_out(vty, ", seg6local %s %s",
-                       seg6local_action2str(
-                               nexthop->nh_srv6->seg6local_action),
-                       buf);
-
-               inet_ntop(AF_INET6, &nexthop->nh_srv6->seg6_segs, buf,
-                         sizeof(buf));
-               vty_out(vty, ", seg6 %s", 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]);
-       }
-}
-
-
-/*
- * 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];
-       json_object *json_labels = NULL;
-       json_object *json_backups = NULL;
-       json_object *json_seg6local = NULL;
-       json_object *json_seg6 = 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));
-
-       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) {
-                       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);
-
-       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);
-
-               json_seg6 = json_object_new_object();
-               inet_ntop(AF_INET6, &nexthop->nh_srv6->seg6_segs, buf,
-                         sizeof(buf));
-               json_object_string_add(json_seg6, "segs", buf);
-               json_object_object_add(json_nexthop, "seg6", json_seg6);
-       }
-}
-
 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)
@@ -1410,7 +1076,9 @@ DEFPY (show_ip_nht,
                                                               zvrf_name(zvrf),
                                                               json_vrf);
                                        json_object_object_add(json_vrf,
-                                                              "nexthops",
+                                                              (afi == AFI_IP)
+                                                                      ? "ipv4"
+                                                                      : "ipv6",
                                                               json_nexthop);
                                } else {
                                        vty_out(vty, "\nVRF %s:\n",
@@ -1447,7 +1115,9 @@ DEFPY (show_ip_nht,
                else
                        json_object_object_add(json, "default", json_vrf);
 
-               json_object_object_add(json_vrf, "nexthops", json_nexthop);
+               json_object_object_add(json_vrf,
+                                      (afi == AFI_IP) ? "ipv4" : "ipv6",
+                                      json_nexthop);
        }
 
        zebra_print_rnh_table(vrf_id, afi, safi, vty, p, json_nexthop);