]> git.proxmox.com Git - mirror_frr.git/blobdiff - zebra/zebra_vty.c
Merge pull request #1847 from msablic/pim_auto_mtrace
[mirror_frr.git] / zebra / zebra_vty.c
index 82b0157ad377ae8cca95fff71f6af55549d49c73..9fe3c707bb1367f5e68daa940c12e79ecffb7869 100644 (file)
@@ -69,22 +69,20 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty,
 /*
  * special macro to allow us to get the correct zebra_vrf
  */
-#define ZEBRA_DECLVAR_CONTEXT(A, B)                    \
-       struct vrf *A = VTY_GET_CONTEXT(vrf);           \
-       struct zebra_vrf *B =                           \
-               (vrf) ? vrf->info : NULL;               \
+#define ZEBRA_DECLVAR_CONTEXT(A, B)                                            \
+       struct vrf *A = VTY_GET_CONTEXT(vrf);                                  \
+       struct zebra_vrf *B = (vrf) ? vrf->info : NULL;
 
 /* VNI range as per RFC 7432 */
 #define CMD_VNI_RANGE "(1-16777215)"
 
 /* General function for static route. */
-static int zebra_static_route(struct vty *vty, afi_t afi, safi_t safi,
-                             const char *negate, const char *dest_str,
-                             const char *mask_str, const char *src_str,
-                             const char *gate_str, const char *ifname,
-                             const char *flag_str, const char *tag_str,
-                             const char *distance_str, const char *vrf_id_str,
-                             const char *label_str)
+static int zebra_static_route_leak(
+       struct vty *vty, struct zebra_vrf *zvrf, struct zebra_vrf *nh_zvrf,
+       afi_t afi, safi_t safi, const char *negate, const char *dest_str,
+       const char *mask_str, const char *src_str, const char *gate_str,
+       const char *ifname, const char *flag_str, const char *tag_str,
+       const char *distance_str, const char *label_str)
 {
        int ret;
        u_char distance;
@@ -95,7 +93,6 @@ static int zebra_static_route(struct vty *vty, afi_t afi, safi_t safi,
        struct in_addr mask;
        enum static_blackhole_type bh_type = 0;
        route_tag_t tag = 0;
-       struct zebra_vrf *zvrf;
        u_char type;
        struct static_nh_label snh_label;
 
@@ -145,14 +142,6 @@ static int zebra_static_route(struct vty *vty, afi_t afi, safi_t safi,
        if (tag_str)
                tag = strtoul(tag_str, NULL, 10);
 
-       /* VRF id */
-       zvrf = zebra_vrf_lookup_by_name(vrf_id_str);
-
-       if (!zvrf) {
-               vty_out(vty, "%% vrf %s is not defined\n", vrf_id_str);
-               return CMD_WARNING_CONFIG_FAILED;
-       }
-
        /* Labels */
        memset(&snh_label, 0, sizeof(struct static_nh_label));
        if (label_str) {
@@ -171,8 +160,8 @@ static int zebra_static_route(struct vty *vty, afi_t afi, safi_t safi,
                        case -2:
                                vty_out(vty,
                                        "%% Cannot use reserved label(s) (%d-%d)\n",
-                                       MPLS_MIN_RESERVED_LABEL,
-                                       MPLS_MAX_RESERVED_LABEL);
+                                       MPLS_LABEL_RESERVED_MIN,
+                                       MPLS_LABEL_RESERVED_MAX);
                                break;
                        case -3:
                                vty_out(vty,
@@ -186,10 +175,11 @@ static int zebra_static_route(struct vty *vty, afi_t afi, safi_t safi,
 
        /* Null0 static route.  */
        if (ifname != NULL) {
-               if (strncasecmp(ifname, "Null0", strlen(ifname)) == 0 ||
-                   strncasecmp(ifname, "reject", strlen(ifname)) == 0 ||
-                   strncasecmp(ifname, "blackhole", strlen(ifname)) == 0) {
-                       vty_out(vty, "%% Nexthop interface cannot be Null0, reject or blackhole\n");
+               if (strncasecmp(ifname, "Null0", strlen(ifname)) == 0
+                   || strncasecmp(ifname, "reject", strlen(ifname)) == 0
+                   || strncasecmp(ifname, "blackhole", strlen(ifname)) == 0) {
+                       vty_out(vty,
+                               "%% Nexthop interface cannot be Null0, reject or blackhole\n");
                        return CMD_WARNING_CONFIG_FAILED;
                }
        }
@@ -237,16 +227,67 @@ static int zebra_static_route(struct vty *vty, afi_t afi, safi_t safi,
                        type = STATIC_IPV6_GATEWAY;
        }
 
-       if (!negate)
+       if (!negate) {
                static_add_route(afi, safi, type, &p, src_p, gatep, ifname,
-                                bh_type, tag, distance, zvrf, &snh_label);
-       else
+                                bh_type, tag, distance, zvrf, nh_zvrf,
+                                &snh_label);
+               /* Mark as having FRR configuration */
+               vrf_set_user_cfged(zvrf->vrf);
+       } else {
                static_delete_route(afi, safi, type, &p, src_p, gatep, ifname,
                                    tag, distance, zvrf, &snh_label);
+               /* If no other FRR config for this VRF, mark accordingly. */
+               if (!zebra_vrf_has_config(zvrf))
+                       vrf_reset_user_cfged(zvrf->vrf);
+       }
 
        return CMD_SUCCESS;
 }
 
+static int zebra_static_route(struct vty *vty, afi_t afi, safi_t safi,
+                             const char *negate, const char *dest_str,
+                             const char *mask_str, const char *src_str,
+                             const char *gate_str, const char *ifname,
+                             const char *flag_str, const char *tag_str,
+                             const char *distance_str, const char *vrf_name,
+                             const char *label_str)
+{
+       struct zebra_vrf *zvrf;
+       struct vrf *vrf;
+
+       /* VRF id */
+       zvrf = zebra_vrf_lookup_by_name(vrf_name);
+
+       /* When trying to delete, the VRF must exist. */
+       if (negate && !zvrf) {
+               vty_out(vty, "%% vrf %s is not defined\n", vrf_name);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       /* When trying to create, create the VRF if it doesn't exist.
+        * Note: The VRF isn't active until we hear about it from the kernel.
+        */
+       if (!zvrf) {
+               vrf = vrf_get(VRF_UNKNOWN, vrf_name);
+               if (!vrf) {
+                       vty_out(vty, "%% Could not create vrf %s\n", vrf_name);
+                       return CMD_WARNING_CONFIG_FAILED;
+               }
+               zvrf = vrf->info;
+               if (!zvrf) {
+                       vty_out(vty, "%% Could not create vrf-info %s\n",
+                               vrf_name);
+                       return CMD_WARNING_CONFIG_FAILED;
+               }
+               /* Mark as having FRR configuration */
+               vrf_set_user_cfged(vrf);
+       }
+       return zebra_static_route_leak(
+               vty, zvrf, zvrf, afi, safi, negate, dest_str, mask_str, src_str,
+               gate_str, ifname, flag_str, tag_str, distance_str, label_str);
+}
+
+
 /* Static unicast routes for multicast RPF lookup. */
 DEFPY (ip_mroute_dist,
        ip_mroute_dist_cmd,
@@ -383,8 +424,44 @@ DEFPY(ip_route_blackhole,
       MPLS_LABEL_HELPSTR)
 {
        return zebra_static_route(vty, AFI_IP, SAFI_UNICAST, no, prefix,
-                                 mask_str, NULL, NULL, NULL, flag,
-                                 tag_str, distance_str, vrf, label);
+                                 mask_str, NULL, NULL, NULL, flag, tag_str,
+                                 distance_str, vrf, label);
+}
+
+DEFPY(ip_route_blackhole_vrf,
+      ip_route_blackhole_vrf_cmd,
+      "[no] ip route\
+       <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask>                        \
+       <reject|blackhole>$flag                                               \
+       [{                                                                    \
+         tag (1-4294967295)                                                  \
+         |(1-255)$distance                                                   \
+         |label WORD                                                         \
+          }]",
+      NO_STR IP_STR
+      "Establish static routes\n"
+      "IP destination prefix (e.g. 10.0.0.0/8)\n"
+      "IP destination prefix\n"
+      "IP destination prefix mask\n"
+      "Emit an ICMP unreachable when matched\n"
+      "Silently discard pkts when matched\n"
+      "Set tag for this route\n"
+      "Tag value\n"
+      "Distance value for this route\n"
+      MPLS_LABEL_HELPSTR)
+{
+       VTY_DECLVAR_CONTEXT(vrf, vrf);
+       struct zebra_vrf *zvrf = vrf->info;
+
+       /*
+        * Coverity is complaining that prefix could
+        * be dereferenced, but we know that prefix will
+        * valid.  Add an assert to make it happy
+        */
+       assert(prefix);
+       return zebra_static_route_leak(vty, zvrf, zvrf, AFI_IP, SAFI_UNICAST,
+                                      no, prefix, mask_str, NULL, NULL, NULL,
+                                      flag, tag_str, distance_str, label);
 }
 
 DEFPY(ip_route_address_interface,
@@ -398,6 +475,7 @@ DEFPY(ip_route_address_interface,
          |(1-255)$distance                            \
          |vrf NAME                                    \
          |label WORD                                  \
+         |nexthop-vrf NAME                            \
           }]",
       NO_STR IP_STR
       "Establish static routes\n"
@@ -411,16 +489,88 @@ DEFPY(ip_route_address_interface,
       "Tag value\n"
       "Distance value for this route\n"
       VRF_CMD_HELP_STR
-      MPLS_LABEL_HELPSTR)
+      MPLS_LABEL_HELPSTR
+      VRF_CMD_HELP_STR)
 {
+       struct zebra_vrf *zvrf;
+       struct zebra_vrf *nh_zvrf;
+
        const char *flag = NULL;
        if (ifname && !strncasecmp(ifname, "Null0", 5)) {
                flag = "Null0";
                ifname = NULL;
        }
-       return zebra_static_route(vty, AFI_IP, SAFI_UNICAST, no, prefix,
-                                 mask_str, NULL, gate_str, ifname, flag,
-                                 tag_str, distance_str, vrf, label);
+
+       zvrf = zebra_vrf_lookup_by_name(vrf);
+       if (!zvrf) {
+               vty_out(vty, "%% vrf %s is not defined\n", vrf);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       if (nexthop_vrf)
+               nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
+       else
+               nh_zvrf = zvrf;
+
+       if (!nh_zvrf) {
+               vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       return zebra_static_route_leak(
+               vty, zvrf, nh_zvrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str,
+               NULL, gate_str, ifname, flag, tag_str, distance_str, label);
+}
+
+DEFPY(ip_route_address_interface_vrf,
+      ip_route_address_interface_vrf_cmd,
+      "[no] ip route\
+       <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+       A.B.C.D$gate                                   \
+       INTERFACE$ifname                               \
+       [{                                             \
+         tag (1-4294967295)                           \
+         |(1-255)$distance                            \
+         |label WORD                                  \
+         |nexthop-vrf NAME                            \
+          }]",
+      NO_STR IP_STR
+      "Establish static routes\n"
+      "IP destination prefix (e.g. 10.0.0.0/8)\n"
+      "IP destination prefix\n"
+      "IP destination prefix mask\n"
+      "IP gateway address\n"
+      "IP gateway interface name. Specify 'Null0' (case-insensitive) for a \
+      null route.\n"
+      "Set tag for this route\n"
+      "Tag value\n"
+      "Distance value for this route\n"
+      MPLS_LABEL_HELPSTR
+      VRF_CMD_HELP_STR)
+{
+       VTY_DECLVAR_CONTEXT(vrf, vrf);
+       const char *flag = NULL;
+       struct zebra_vrf *zvrf = vrf->info;
+       struct zebra_vrf *nh_zvrf;
+
+       if (ifname && !strncasecmp(ifname, "Null0", 5)) {
+               flag = "Null0";
+               ifname = NULL;
+       }
+
+       if (nexthop_vrf)
+               nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
+       else
+               nh_zvrf = zvrf;
+
+       if (!nh_zvrf) {
+               vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       return zebra_static_route_leak(
+               vty, zvrf, nh_zvrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str,
+               NULL, gate_str, ifname, flag, tag_str, distance_str, label);
 }
 
 DEFPY(ip_route,
@@ -433,6 +583,7 @@ DEFPY(ip_route,
          |(1-255)$distance                            \
          |vrf NAME                                    \
          |label WORD                                  \
+         |nexthop-vrf NAME                            \
           }]",
       NO_STR IP_STR
       "Establish static routes\n"
@@ -445,16 +596,87 @@ DEFPY(ip_route,
       "Tag value\n"
       "Distance value for this route\n"
       VRF_CMD_HELP_STR
-      MPLS_LABEL_HELPSTR)
+      MPLS_LABEL_HELPSTR
+      VRF_CMD_HELP_STR)
 {
+       struct zebra_vrf *zvrf;
+       struct zebra_vrf *nh_zvrf;
        const char *flag = NULL;
+
        if (ifname && !strncasecmp(ifname, "Null0", 5)) {
                flag = "Null0";
                ifname = NULL;
        }
-       return zebra_static_route(vty, AFI_IP, SAFI_UNICAST, no, prefix,
-                                 mask_str, NULL, gate_str, ifname, flag,
-                                 tag_str, distance_str, vrf, label);
+
+       zvrf = zebra_vrf_lookup_by_name(vrf);
+       if (!zvrf) {
+               vty_out(vty, "%% vrf %s is not defined\n", vrf);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       if (nexthop_vrf)
+               nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
+       else
+               nh_zvrf = zvrf;
+
+       if (!nh_zvrf) {
+               vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+
+       return zebra_static_route_leak(
+               vty, zvrf, nh_zvrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str,
+               NULL, gate_str, ifname, flag, tag_str, distance_str, label);
+}
+
+DEFPY(ip_route_vrf,
+      ip_route_vrf_cmd,
+      "[no] ip route\
+       <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+       <A.B.C.D$gate|INTERFACE$ifname>                \
+       [{                                             \
+         tag (1-4294967295)                           \
+         |(1-255)$distance                            \
+         |label WORD                                  \
+         |nexthop-vrf NAME                            \
+          }]",
+      NO_STR IP_STR
+      "Establish static routes\n"
+      "IP destination prefix (e.g. 10.0.0.0/8)\n"
+      "IP destination prefix\n"
+      "IP destination prefix mask\n"
+      "IP gateway address\n"
+      "IP gateway interface name\n"
+      "Set tag for this route\n"
+      "Tag value\n"
+      "Distance value for this route\n"
+      MPLS_LABEL_HELPSTR
+      VRF_CMD_HELP_STR)
+{
+       VTY_DECLVAR_CONTEXT(vrf, vrf);
+       struct zebra_vrf *zvrf = vrf->info;
+       struct zebra_vrf *nh_zvrf;
+
+       const char *flag = NULL;
+       if (ifname && !strncasecmp(ifname, "Null0", 5)) {
+               flag = "Null0";
+               ifname = NULL;
+       }
+
+       if (nexthop_vrf)
+               nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
+       else
+               nh_zvrf = zvrf;
+
+       if (!nh_zvrf) {
+               vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       return zebra_static_route_leak(
+               vty, zvrf, nh_zvrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str,
+               NULL, gate_str, ifname, flag, tag_str, distance_str, label);
 }
 
 /* New RIB.  Detailed information for IPv4 route. */
@@ -510,24 +732,26 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
                vty_out(vty, "  Last update ");
 
                if (uptime < ONE_DAY_SECOND)
-                       vty_out(vty, "%02d:%02d:%02d", tm->tm_hour,
-                               tm->tm_min, tm->tm_sec);
+                       vty_out(vty, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min,
+                               tm->tm_sec);
                else if (uptime < ONE_WEEK_SECOND)
-                       vty_out(vty, "%dd%02dh%02dm", tm->tm_yday,
-                               tm->tm_hour, tm->tm_min);
+                       vty_out(vty, "%dd%02dh%02dm", tm->tm_yday, tm->tm_hour,
+                               tm->tm_min);
                else
                        vty_out(vty, "%02dw%dd%02dh", tm->tm_yday / 7,
                                tm->tm_yday - ((tm->tm_yday / 7) * 7),
                                tm->tm_hour);
                vty_out(vty, " ago\n");
 
-               for (ALL_NEXTHOPS(re->nexthop, nexthop)) {
+               for (ALL_NEXTHOPS(re->ng, nexthop)) {
                        char addrstr[32];
 
                        vty_out(vty, "  %c%s",
                                CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)
-                                       ? CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)
-                                               ? ' ' : '*'
+                                       ? CHECK_FLAG(nexthop->flags,
+                                                    NEXTHOP_FLAG_DUPLICATE)
+                                                 ? ' '
+                                                 : '*'
                                        : ' ',
                                nexthop->rparent ? "  " : "");
 
@@ -538,8 +762,9 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
                                        inet_ntoa(nexthop->gate.ipv4));
                                if (nexthop->ifindex)
                                        vty_out(vty, ", via %s",
-                                               ifindex2ifname(nexthop->ifindex,
-                                                              re->vrf_id));
+                                               ifindex2ifname(
+                                                       nexthop->ifindex,
+                                                       nexthop->vrf_id));
                                break;
                        case NEXTHOP_TYPE_IPV6:
                        case NEXTHOP_TYPE_IPV6_IFINDEX:
@@ -548,13 +773,14 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
                                                  buf, sizeof buf));
                                if (nexthop->ifindex)
                                        vty_out(vty, ", via %s",
-                                               ifindex2ifname(nexthop->ifindex,
-                                                              re->vrf_id));
+                                               ifindex2ifname(
+                                                       nexthop->ifindex,
+                                                       nexthop->vrf_id));
                                break;
                        case NEXTHOP_TYPE_IFINDEX:
                                vty_out(vty, " directly connected, %s",
                                        ifindex2ifname(nexthop->ifindex,
-                                                      re->vrf_id));
+                                                      nexthop->vrf_id));
                                break;
                        case NEXTHOP_TYPE_BLACKHOLE:
                                vty_out(vty, " unreachable");
@@ -576,6 +802,14 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn,
                        default:
                                break;
                        }
+
+                       if (re->vrf_id != nexthop->vrf_id) {
+                               struct vrf *vrf =
+                                       vrf_lookup_by_id(nexthop->vrf_id);
+
+                               vty_out(vty, "(vrf %s)", vrf->name);
+                       }
+
                        if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE))
                                vty_out(vty, " (duplicate nexthop removed)");
 
@@ -676,11 +910,11 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
                }
 
                if (uptime < ONE_DAY_SECOND)
-                       sprintf(buf, "%02d:%02d:%02d", tm->tm_hour,
-                               tm->tm_min, tm->tm_sec);
+                       sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min,
+                               tm->tm_sec);
                else if (uptime < ONE_WEEK_SECOND)
-                       sprintf(buf, "%dd%02dh%02dm", tm->tm_yday,
-                               tm->tm_hour, tm->tm_min);
+                       sprintf(buf, "%dd%02dh%02dm", tm->tm_yday, tm->tm_hour,
+                               tm->tm_min);
                else
                        sprintf(buf, "%02dw%dd%02dh", tm->tm_yday / 7,
                                tm->tm_yday - ((tm->tm_yday / 7) * 7),
@@ -688,7 +922,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
 
                json_object_string_add(json_route, "uptime", buf);
 
-               for (ALL_NEXTHOPS(re->nexthop, nexthop)) {
+               for (ALL_NEXTHOPS(re->ng, nexthop)) {
                        json_nexthop = json_object_new_object();
 
                        if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE))
@@ -714,8 +948,9 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
                                                            nexthop->ifindex);
                                        json_object_string_add(
                                                json_nexthop, "interfaceName",
-                                               ifindex2ifname(nexthop->ifindex,
-                                                              re->vrf_id));
+                                               ifindex2ifname(
+                                                       nexthop->ifindex,
+                                                       nexthop->vrf_id));
                                }
                                break;
                        case NEXTHOP_TYPE_IPV6:
@@ -733,8 +968,9 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
                                                            nexthop->ifindex);
                                        json_object_string_add(
                                                json_nexthop, "interfaceName",
-                                               ifindex2ifname(nexthop->ifindex,
-                                                              re->vrf_id));
+                                               ifindex2ifname(
+                                                       nexthop->ifindex,
+                                                       nexthop->vrf_id));
                                }
                                break;
 
@@ -747,7 +983,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
                                json_object_string_add(
                                        json_nexthop, "interfaceName",
                                        ifindex2ifname(nexthop->ifindex,
-                                                      re->vrf_id));
+                                                      nexthop->vrf_id));
                                break;
                        case NEXTHOP_TYPE_BLACKHOLE:
                                json_object_boolean_true_add(json_nexthop,
@@ -774,6 +1010,13 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
                                break;
                        }
 
+                       if (nexthop->vrf_id != re->vrf_id) {
+                               struct vrf *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");
@@ -845,8 +1088,8 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
        }
 
        /* Nexthop information. */
-       for (ALL_NEXTHOPS(re->nexthop, nexthop)) {
-               if (nexthop == re->nexthop) {
+       for (ALL_NEXTHOPS(re->ng, nexthop)) {
+               if (nexthop == re->ng.nexthop) {
                        /* Prefix information. */
                        len = vty_out(vty, "%c", zebra_route_char(re->type));
                        if (re->instance)
@@ -868,8 +1111,10 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
                } else {
                        vty_out(vty, "  %c%*c",
                                CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)
-                                       ? CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)
-                                               ? ' ' : '*'
+                                       ? CHECK_FLAG(nexthop->flags,
+                                                    NEXTHOP_FLAG_DUPLICATE)
+                                                 ? ' '
+                                                 : '*'
                                        : ' ',
                                len - 3 + (2 * nexthop_level(nexthop)), ' ');
                }
@@ -881,7 +1126,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
                        if (nexthop->ifindex)
                                vty_out(vty, ", %s",
                                        ifindex2ifname(nexthop->ifindex,
-                                                      re->vrf_id));
+                                                      nexthop->vrf_id));
                        break;
                case NEXTHOP_TYPE_IPV6:
                case NEXTHOP_TYPE_IPV6_IFINDEX:
@@ -891,12 +1136,13 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
                        if (nexthop->ifindex)
                                vty_out(vty, ", %s",
                                        ifindex2ifname(nexthop->ifindex,
-                                                      re->vrf_id));
+                                                      nexthop->vrf_id));
                        break;
 
                case NEXTHOP_TYPE_IFINDEX:
                        vty_out(vty, " is directly connected, %s",
-                               ifindex2ifname(nexthop->ifindex, re->vrf_id));
+                               ifindex2ifname(nexthop->ifindex,
+                                              nexthop->vrf_id));
                        break;
                case NEXTHOP_TYPE_BLACKHOLE:
                        vty_out(vty, " unreachable");
@@ -917,6 +1163,13 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
                default:
                        break;
                }
+
+               if (nexthop->vrf_id != re->vrf_id) {
+                       struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id);
+
+                       vty_out(vty, "(vrf %s)", vrf->name);
+               }
+
                if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
                        vty_out(vty, " inactive");
 
@@ -969,46 +1222,21 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
        }
 }
 
-static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
-                           safi_t safi, bool use_fib, u_char use_json,
-                           route_tag_t tag,
-                           const struct prefix *longer_prefix_p,
-                           bool supernets_only, int type,
-                           u_short ospf_instance_id)
+static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf,
+                                struct route_table *table, afi_t afi,
+                                bool use_fib, route_tag_t tag,
+                                const struct prefix *longer_prefix_p,
+                                bool supernets_only, int type,
+                                u_short ospf_instance_id, u_char use_json)
 {
-       struct route_table *table;
-       rib_dest_t *dest;
        struct route_node *rn;
        struct route_entry *re;
        int first = 1;
-       struct zebra_vrf *zvrf = NULL;
-       char buf[BUFSIZ];
+       rib_dest_t *dest;
        json_object *json = NULL;
        json_object *json_prefix = NULL;
-       u_int32_t addr;
-
-       if (!(zvrf = zebra_vrf_lookup_by_name(vrf_name))) {
-               if (use_json)
-                       vty_out(vty, "{}\n");
-               else
-                       vty_out(vty, "vrf %s not defined\n", vrf_name);
-               return CMD_SUCCESS;
-       }
-
-       if (zvrf_id(zvrf) == VRF_UNKNOWN) {
-               if (use_json)
-                       vty_out(vty, "{}\n");
-               else
-                       vty_out(vty, "vrf %s inactive\n", vrf_name);
-               return CMD_SUCCESS;
-       }
-
-       table = zebra_vrf_table(afi, safi, zvrf_id(zvrf));
-       if (!table) {
-               if (use_json)
-                       vty_out(vty, "{}\n");
-               return CMD_SUCCESS;
-       }
+       uint32_t addr;
+       char buf[BUFSIZ];
 
        if (use_json)
                json = json_object_new_object();
@@ -1018,8 +1246,7 @@ static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
                dest = rib_dest_from_rnode(rn);
 
                RNODE_FOREACH_RE (rn, re) {
-                       if (use_fib
-                           && re != dest->selected_fib)
+                       if (use_fib && re != dest->selected_fib)
                                continue;
 
                        if (tag && re->tag != tag)
@@ -1086,6 +1313,67 @@ static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
                                             json, JSON_C_TO_STRING_PRETTY));
                json_object_free(json);
        }
+}
+
+static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
+                           safi_t safi, bool use_fib, u_char use_json,
+                           route_tag_t tag,
+                           const struct prefix *longer_prefix_p,
+                           bool supernets_only, int type,
+                           u_short ospf_instance_id)
+{
+       struct route_table *table;
+       struct zebra_vrf *zvrf = NULL;
+
+       if (!(zvrf = zebra_vrf_lookup_by_name(vrf_name))) {
+               if (use_json)
+                       vty_out(vty, "{}\n");
+               else
+                       vty_out(vty, "vrf %s not defined\n", vrf_name);
+               return CMD_SUCCESS;
+       }
+
+       if (zvrf_id(zvrf) == VRF_UNKNOWN) {
+               if (use_json)
+                       vty_out(vty, "{}\n");
+               else
+                       vty_out(vty, "vrf %s inactive\n", vrf_name);
+               return CMD_SUCCESS;
+       }
+
+       table = zebra_vrf_table(afi, safi, zvrf_id(zvrf));
+       if (!table) {
+               if (use_json)
+                       vty_out(vty, "{}\n");
+               return CMD_SUCCESS;
+       }
+
+       do_show_route_helper(vty, zvrf, table, afi, use_fib, tag,
+                            longer_prefix_p, supernets_only, type,
+                            ospf_instance_id, use_json);
+
+       return CMD_SUCCESS;
+}
+
+DEFPY (show_route_table,
+       show_route_table_cmd,
+       "show <ip$ipv4|ipv6$ipv6> route table (1-4294967295)$table [json$json]",
+       SHOW_STR
+       IP_STR
+       IP6_STR
+       "IP routing table\n"
+       "Table to display\n"
+       "The table number to display, if available\n"
+       JSON_STR)
+{
+       afi_t afi = ipv4 ? AFI_IP : AFI_IP6;
+       struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
+       struct route_table *t;
+
+       t = zebra_ns_find_table(zvrf->zns, table, afi);
+       if (t)
+               do_show_route_helper(vty, zvrf, t, afi, false, 0, false, false,
+                                    0, 0, !!json);
 
        return CMD_SUCCESS;
 }
@@ -1181,7 +1469,7 @@ DEFUN (ip_nht_default_route,
                return CMD_SUCCESS;
 
        zebra_rnh_ip_default_route = 1;
-       zebra_evaluate_rnh(0, AF_INET, 1, RNH_NEXTHOP_TYPE, NULL);
+       zebra_evaluate_rnh(VRF_DEFAULT, AF_INET, 1, RNH_NEXTHOP_TYPE, NULL);
        return CMD_SUCCESS;
 }
 
@@ -1197,7 +1485,7 @@ DEFUN (no_ip_nht_default_route,
                return CMD_SUCCESS;
 
        zebra_rnh_ip_default_route = 0;
-       zebra_evaluate_rnh(0, AF_INET, 1, RNH_NEXTHOP_TYPE, NULL);
+       zebra_evaluate_rnh(VRF_DEFAULT, AF_INET, 1, RNH_NEXTHOP_TYPE, NULL);
        return CMD_SUCCESS;
 }
 
@@ -1212,7 +1500,7 @@ DEFUN (ipv6_nht_default_route,
                return CMD_SUCCESS;
 
        zebra_rnh_ipv6_default_route = 1;
-       zebra_evaluate_rnh(0, AF_INET6, 1, RNH_NEXTHOP_TYPE, NULL);
+       zebra_evaluate_rnh(VRF_DEFAULT, AF_INET6, 1, RNH_NEXTHOP_TYPE, NULL);
        return CMD_SUCCESS;
 }
 
@@ -1228,7 +1516,7 @@ DEFUN (no_ipv6_nht_default_route,
                return CMD_SUCCESS;
 
        zebra_rnh_ipv6_default_route = 0;
-       zebra_evaluate_rnh(0, AF_INET6, 1, RNH_NEXTHOP_TYPE, NULL);
+       zebra_evaluate_rnh(VRF_DEFAULT, AF_INET6, 1, RNH_NEXTHOP_TYPE, NULL);
        return CMD_SUCCESS;
 }
 
@@ -1361,7 +1649,7 @@ DEFPY (show_route_detail,
                struct vrf *vrf;
                struct zebra_vrf *zvrf;
 
-               RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
+               RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
                        if ((zvrf = vrf->info) == NULL
                            || (table = zvrf->table[afi][SAFI_UNICAST]) == NULL)
                                continue;
@@ -1435,7 +1723,7 @@ DEFPY (show_route_summary,
                struct vrf *vrf;
                struct zebra_vrf *zvrf;
 
-               RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
+               RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
                        if ((zvrf = vrf->info) == NULL
                            || (table = zvrf->table[afi][SAFI_UNICAST]) == NULL)
                                continue;
@@ -1554,7 +1842,7 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty,
                         * In case of ECMP, count only once.
                         */
                        cnt = 0;
-                       for (nexthop = re->nexthop; (!cnt && nexthop);
+                       for (nexthop = re->ng.nexthop; (!cnt && nexthop);
                             nexthop = nexthop->next) {
                                cnt++;
                                rib_cnt[ZEBRA_ROUTE_TOTAL]++;
@@ -1603,97 +1891,91 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty,
 }
 
 /* Write static route configuration. */
-static int static_config(struct vty *vty, afi_t afi, safi_t safi,
-                        const char *cmd)
+int static_config(struct vty *vty, struct zebra_vrf *zvrf, afi_t afi,
+                 safi_t safi, const char *cmd)
 {
+       char spacing[100];
        struct route_node *rn;
        struct static_route *si;
        struct route_table *stable;
-       struct vrf *vrf;
-       struct zebra_vrf *zvrf;
        char buf[SRCDEST2STR_BUFFER];
        int write = 0;
 
-       RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
-               if (!(zvrf = vrf->info))
-                       continue;
-               if ((stable = zvrf->stable[afi][safi]) == NULL)
-                       continue;
+       if ((stable = zvrf->stable[afi][safi]) == NULL)
+               return write;
 
-               for (rn = route_top(stable); rn; rn = srcdest_route_next(rn))
-                       for (si = rn->info; si; si = si->next) {
-                               vty_out(vty, "%s %s", cmd,
-                                       srcdest_rnode2str(rn, buf, sizeof buf));
+       sprintf(spacing, "%s%s", (zvrf->vrf->vrf_id == VRF_DEFAULT) ? "" : " ",
+               cmd);
 
-                               switch (si->type) {
-                               case STATIC_IPV4_GATEWAY:
-                                       vty_out(vty, " %s",
-                                               inet_ntoa(si->addr.ipv4));
-                                       break;
-                               case STATIC_IPV6_GATEWAY:
-                                       vty_out(vty, " %s",
-                                               inet_ntop(AF_INET6,
-                                                         &si->addr.ipv6, buf,
-                                                         sizeof buf));
-                                       break;
-                               case STATIC_IFNAME:
-                                       vty_out(vty, " %s", si->ifname);
-                                       break;
-                               case STATIC_BLACKHOLE:
-                                       switch (si->bh_type) {
-                                       case STATIC_BLACKHOLE_DROP:
-                                               vty_out(vty, " blackhole");
-                                               break;
-                                       case STATIC_BLACKHOLE_NULL:
-                                               vty_out(vty, " Null0");
-                                               break;
-                                       case STATIC_BLACKHOLE_REJECT:
-                                               vty_out(vty, " reject");
-                                               break;
-                                       }
+       for (rn = route_top(stable); rn; rn = srcdest_route_next(rn))
+               for (si = rn->info; si; si = si->next) {
+                       vty_out(vty, "%s %s", spacing,
+                               srcdest_rnode2str(rn, buf, sizeof buf));
+
+                       switch (si->type) {
+                       case STATIC_IPV4_GATEWAY:
+                               vty_out(vty, " %s", inet_ntoa(si->addr.ipv4));
+                               break;
+                       case STATIC_IPV6_GATEWAY:
+                               vty_out(vty, " %s",
+                                       inet_ntop(AF_INET6, &si->addr.ipv6, buf,
+                                                 sizeof buf));
+                               break;
+                       case STATIC_IFNAME:
+                               vty_out(vty, " %s", si->ifname);
+                               break;
+                       case STATIC_BLACKHOLE:
+                               switch (si->bh_type) {
+                               case STATIC_BLACKHOLE_DROP:
+                                       vty_out(vty, " blackhole");
                                        break;
-                               case STATIC_IPV4_GATEWAY_IFNAME:
-                                       vty_out(vty, " %s %s",
-                                               inet_ntop(AF_INET,
-                                                         &si->addr.ipv4, buf,
-                                                         sizeof buf),
-                                               si->ifname);
+                               case STATIC_BLACKHOLE_NULL:
+                                       vty_out(vty, " Null0");
                                        break;
-                               case STATIC_IPV6_GATEWAY_IFNAME:
-                                       vty_out(vty, " %s %s",
-                                               inet_ntop(AF_INET6,
-                                                         &si->addr.ipv6, buf,
-                                                         sizeof buf),
-                                               si->ifname);
+                               case STATIC_BLACKHOLE_REJECT:
+                                       vty_out(vty, " reject");
                                        break;
                                }
+                               break;
+                       case STATIC_IPV4_GATEWAY_IFNAME:
+                               vty_out(vty, " %s %s",
+                                       inet_ntop(AF_INET, &si->addr.ipv4, buf,
+                                                 sizeof buf),
+                                       si->ifname);
+                               break;
+                       case STATIC_IPV6_GATEWAY_IFNAME:
+                               vty_out(vty, " %s %s",
+                                       inet_ntop(AF_INET6, &si->addr.ipv6, buf,
+                                                 sizeof buf),
+                                       si->ifname);
+                               break;
+                       }
 
-                               if (si->tag)
-                                       vty_out(vty, " tag %" ROUTE_TAG_PRI,
-                                               si->tag);
+                       if (si->tag)
+                               vty_out(vty, " tag %" ROUTE_TAG_PRI, si->tag);
 
-                               if (si->distance
-                                   != ZEBRA_STATIC_DISTANCE_DEFAULT)
-                                       vty_out(vty, " %d", si->distance);
+                       if (si->distance != ZEBRA_STATIC_DISTANCE_DEFAULT)
+                               vty_out(vty, " %d", si->distance);
 
-                               if (si->vrf_id != VRF_DEFAULT)
-                                       vty_out(vty, " vrf %s",
-                                               zvrf_name(zvrf));
+                       if (si->nh_vrf_id != si->vrf_id) {
+                               struct vrf *vrf;
 
-                               /* Label information */
-                               if (si->snh_label.num_labels)
-                                       vty_out(vty, " label %s",
-                                               mpls_label2str(
-                                                       si->snh_label
-                                                               .num_labels,
-                                                       si->snh_label.label,
-                                                       buf, sizeof buf, 0));
+                               vrf = vrf_lookup_by_id(si->nh_vrf_id);
+                               vty_out(vty, " nexthop-vrf %s",
+                                       (vrf) ? vrf->name : "Unknown");
+                       }
 
-                               vty_out(vty, "\n");
+                       /* Label information */
+                       if (si->snh_label.num_labels)
+                               vty_out(vty, " label %s",
+                                       mpls_label2str(si->snh_label.num_labels,
+                                                      si->snh_label.label, buf,
+                                                      sizeof buf, 0));
 
-                               write = 1;
-                       }
-       }
+                       vty_out(vty, "\n");
+
+                       write = 1;
+               }
        return write;
 }
 
@@ -1723,8 +2005,45 @@ DEFPY(ipv6_route_blackhole,
       MPLS_LABEL_HELPSTR)
 {
        return zebra_static_route(vty, AFI_IP6, SAFI_UNICAST, no, prefix_str,
-                                 NULL, from_str, NULL, NULL, flag,
-                                 tag_str, distance_str, vrf, label);
+                                 NULL, from_str, NULL, NULL, flag, tag_str,
+                                 distance_str, vrf, label);
+}
+
+DEFPY(ipv6_route_blackhole_vrf,
+      ipv6_route_blackhole_vrf_cmd,
+      "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+          <Null0|reject|blackhole>$flag                    \
+          [{                                               \
+            tag (1-4294967295)                             \
+            |(1-255)$distance                              \
+            |label WORD                                    \
+          }]",
+      NO_STR
+      IPV6_STR
+      "Establish static routes\n"
+      "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+      "IPv6 source-dest route\n"
+      "IPv6 source prefix\n"
+      "Null interface\n"
+      "Emit an ICMP unreachable when matched\n"
+      "Silently discard pkts when matched\n"
+      "Set tag for this route\n"
+      "Tag value\n"
+      "Distance value for this prefix\n"
+      MPLS_LABEL_HELPSTR)
+{
+       VTY_DECLVAR_CONTEXT(vrf, vrf);
+       struct zebra_vrf *zvrf = vrf->info;
+
+       /*
+        * Coverity is complaining that prefix could
+        * be dereferenced, but we know that prefix will
+        * valid.  Add an assert to make it happy
+        */
+       assert(prefix);
+       return zebra_static_route_leak(
+               vty, zvrf, zvrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL,
+               from_str, NULL, NULL, flag, tag_str, distance_str, label);
 }
 
 DEFPY(ipv6_route_address_interface,
@@ -1737,6 +2056,7 @@ DEFPY(ipv6_route_address_interface,
             |(1-255)$distance                              \
             |vrf NAME                                      \
             |label WORD                                    \
+            |nexthop-vrf NAME                              \
           }]",
       NO_STR
       IPV6_STR
@@ -1750,11 +2070,75 @@ DEFPY(ipv6_route_address_interface,
       "Tag value\n"
       "Distance value for this prefix\n"
       VRF_CMD_HELP_STR
-      MPLS_LABEL_HELPSTR)
+      MPLS_LABEL_HELPSTR
+      VRF_CMD_HELP_STR)
 {
-       return zebra_static_route(vty, AFI_IP6, SAFI_UNICAST, no, prefix_str,
-                                 NULL, from_str, gate_str, ifname, NULL,
-                                 tag_str, distance_str, vrf, label);
+       struct zebra_vrf *zvrf;
+       struct zebra_vrf *nh_zvrf;
+
+       zvrf = zebra_vrf_lookup_by_name(vrf);
+       if (!zvrf) {
+               vty_out(vty, "%% vrf %s is not defined\n", vrf);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       if (nexthop_vrf)
+               nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
+       else
+               nh_zvrf = zvrf;
+
+       if (!nh_zvrf) {
+               vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       return zebra_static_route_leak(
+               vty, zvrf, nh_zvrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL,
+               from_str, gate_str, ifname, NULL, tag_str, distance_str, label);
+}
+
+DEFPY(ipv6_route_address_interface_vrf,
+      ipv6_route_address_interface_vrf_cmd,
+      "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+          X:X::X:X$gate                                    \
+          INTERFACE$ifname                                 \
+          [{                                               \
+            tag (1-4294967295)                             \
+            |(1-255)$distance                              \
+            |label WORD                                    \
+            |nexthop-vrf NAME                              \
+          }]",
+      NO_STR
+      IPV6_STR
+      "Establish static routes\n"
+      "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+      "IPv6 source-dest route\n"
+      "IPv6 source prefix\n"
+      "IPv6 gateway address\n"
+      "IPv6 gateway interface name\n"
+      "Set tag for this route\n"
+      "Tag value\n"
+      "Distance value for this prefix\n"
+      MPLS_LABEL_HELPSTR
+      VRF_CMD_HELP_STR)
+{
+       VTY_DECLVAR_CONTEXT(vrf, vrf);
+       struct zebra_vrf *zvrf = vrf->info;
+       struct zebra_vrf *nh_zvrf;
+
+       if (nexthop_vrf)
+               nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
+       else
+               nh_zvrf = zvrf;
+
+       if (!nh_zvrf) {
+               vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       return zebra_static_route_leak(
+               vty, zvrf, nh_zvrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL,
+               from_str, gate_str, ifname, NULL, tag_str, distance_str, label);
 }
 
 DEFPY(ipv6_route,
@@ -1766,6 +2150,7 @@ DEFPY(ipv6_route,
             |(1-255)$distance                              \
             |vrf NAME                                      \
             |label WORD                                    \
+            |nexthop-vrf NAME                              \
           }]",
       NO_STR
       IPV6_STR
@@ -1779,11 +2164,74 @@ DEFPY(ipv6_route,
       "Tag value\n"
       "Distance value for this prefix\n"
       VRF_CMD_HELP_STR
-      MPLS_LABEL_HELPSTR)
+      MPLS_LABEL_HELPSTR
+      VRF_CMD_HELP_STR)
 {
-       return zebra_static_route(vty, AFI_IP6, SAFI_UNICAST, no, prefix_str,
-                                 NULL, from_str, gate_str, ifname, NULL,
-                                 tag_str, distance_str, vrf, label);
+       struct zebra_vrf *zvrf;
+       struct zebra_vrf *nh_zvrf;
+
+       zvrf = zebra_vrf_lookup_by_name(vrf);
+       if (!zvrf) {
+               vty_out(vty, "%% vrf %s is not defined\n", vrf);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       if (nexthop_vrf)
+               nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
+       else
+               nh_zvrf = zvrf;
+
+       if (!nh_zvrf) {
+               vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       return zebra_static_route_leak(
+               vty, zvrf, nh_zvrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL,
+               from_str, gate_str, ifname, NULL, tag_str, distance_str, label);
+}
+
+DEFPY(ipv6_route_vrf,
+      ipv6_route_vrf_cmd,
+      "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+          <X:X::X:X$gate|INTERFACE$ifname>                 \
+          [{                                               \
+            tag (1-4294967295)                             \
+            |(1-255)$distance                              \
+            |label WORD                                    \
+            |nexthop-vrf NAME                              \
+          }]",
+      NO_STR
+      IPV6_STR
+      "Establish static routes\n"
+      "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+      "IPv6 source-dest route\n"
+      "IPv6 source prefix\n"
+      "IPv6 gateway address\n"
+      "IPv6 gateway interface name\n"
+      "Set tag for this route\n"
+      "Tag value\n"
+      "Distance value for this prefix\n"
+      MPLS_LABEL_HELPSTR
+      VRF_CMD_HELP_STR)
+{
+       VTY_DECLVAR_CONTEXT(vrf, vrf);
+       struct zebra_vrf *zvrf = vrf->info;
+       struct zebra_vrf *nh_zvrf;
+
+       if (nexthop_vrf)
+               nh_zvrf = zebra_vrf_lookup_by_name(nexthop_vrf);
+       else
+               nh_zvrf = zvrf;
+
+       if (!nh_zvrf) {
+               vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       return zebra_static_route_leak(
+               vty, zvrf, nh_zvrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL,
+               from_str, gate_str, ifname, NULL, tag_str, distance_str, label);
 }
 
 /*
@@ -1890,28 +2338,90 @@ DEFUN (show_vrf,
        RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
                if (!(zvrf = vrf->info))
                        continue;
-               if (!zvrf_id(zvrf))
+               if (zvrf_id(zvrf) == VRF_DEFAULT)
                        continue;
 
                vty_out(vty, "vrf %s ", zvrf_name(zvrf));
-               if (zvrf_id(zvrf) == VRF_UNKNOWN)
+               if (zvrf_id(zvrf) == VRF_UNKNOWN || !zvrf_is_active(zvrf))
                        vty_out(vty, "inactive");
+               else if (zvrf_ns_name(zvrf))
+                       vty_out(vty, "id %u netns %s", zvrf_id(zvrf),
+                               zvrf_ns_name(zvrf));
                else
                        vty_out(vty, "id %u table %u", zvrf_id(zvrf),
                                zvrf->table_id);
+               if (vrf_is_user_cfged(vrf))
+                       vty_out(vty, " (configured)");
                vty_out(vty, "\n");
        }
 
        return CMD_SUCCESS;
 }
 
+DEFUN (default_vrf_vni_mapping,
+       default_vrf_vni_mapping_cmd,
+       "vni " CMD_VNI_RANGE "[prefix-routes-only]",
+       "VNI corresponding to the DEFAULT VRF\n"
+       "VNI-ID\n"
+       "Prefix routes only \n")
+{
+       int ret = 0;
+       char err[ERR_STR_SZ];
+       struct zebra_vrf *zvrf = NULL;
+       vni_t vni = strtoul(argv[1]->arg, NULL, 10);
+       int filter = 0;
+
+       zvrf = vrf_info_lookup(VRF_DEFAULT);
+       if (!zvrf)
+               return CMD_WARNING;
+
+       if (argc == 3)
+               filter = 1;
+
+       ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ,
+                                             filter, 1);
+       if (ret != 0) {
+               vty_out(vty, "%s\n", err);
+               return CMD_WARNING;
+       }
+
+       return CMD_SUCCESS;
+}
+
+DEFUN (no_default_vrf_vni_mapping,
+       no_default_vrf_vni_mapping_cmd,
+       "no vni " CMD_VNI_RANGE,
+       NO_STR
+       "VNI corresponding to DEFAULT VRF\n"
+       "VNI-ID")
+{
+       int ret = 0;
+       char err[ERR_STR_SZ];
+       vni_t vni = strtoul(argv[2]->arg, NULL, 10);
+       struct zebra_vrf *zvrf = NULL;
+
+       zvrf = vrf_info_lookup(VRF_DEFAULT);
+       if (!zvrf)
+               return CMD_WARNING;
+
+       ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0, 0);
+       if (ret != 0) {
+               vty_out(vty, "%s\n", err);
+               return CMD_WARNING;
+       }
+
+       return CMD_SUCCESS;
+}
+
 DEFUN (vrf_vni_mapping,
        vrf_vni_mapping_cmd,
-       "vni " CMD_VNI_RANGE,
-       "VNI\n"
-       "VNI-ID\n")
+       "vni " CMD_VNI_RANGE "[prefix-routes-only]",
+       "VNI corresponding to tenant VRF\n"
+       "VNI-ID\n"
+       "prefix-routes-only\n")
 {
        int ret = 0;
+       int filter = 0;
 
        ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
        vni_t vni = strtoul(argv[1]->arg, NULL, 10);
@@ -1920,7 +2430,13 @@ DEFUN (vrf_vni_mapping,
        assert(vrf);
        assert(zvrf);
 
-       ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 1);
+       if (argc == 3)
+               filter = 1;
+
+       /* Mark as having FRR configuration */
+       vrf_set_user_cfged(vrf);
+       ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ,
+                                             filter, 1);
        if (ret != 0) {
                vty_out(vty, "%s\n", err);
                return CMD_WARNING;
@@ -1933,7 +2449,7 @@ DEFUN (no_vrf_vni_mapping,
        no_vrf_vni_mapping_cmd,
        "no vni " CMD_VNI_RANGE,
        NO_STR
-       "VNI\n"
+       "VNI corresponding to tenant VRF\n"
        "VNI-ID")
 {
        int ret = 0;
@@ -1945,12 +2461,16 @@ DEFUN (no_vrf_vni_mapping,
        assert(vrf);
        assert(zvrf);
 
-       ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0);
+       ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0, 0);
        if (ret != 0) {
                vty_out(vty, "%s\n", err);
                return CMD_WARNING;
        }
 
+       /* If no other FRR config for this VRF, mark accordingly. */
+       if (!zebra_vrf_has_config(zvrf))
+               vrf_reset_user_cfged(vrf);
+
        return CMD_SUCCESS;
 }
 
@@ -1974,29 +2494,16 @@ DEFUN (show_vrf_vni,
                json_vrfs = json_object_new_array();
        }
 
-       RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
+       if (!uj)
+               vty_out(vty, "%-37s %-10s %-20s %-20s %-5s %-18s\n", "VRF",
+                       "VNI", "VxLAN IF", "L3-SVI", "State", "Rmac");
+
+       RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
                zvrf = vrf->info;
                if (!zvrf)
                        continue;
 
-               if (!zvrf->l3vni)
-                       continue;
-
-               if (!uj) {
-                       vty_out(vty, "vrf: %s VNI: %u",
-                               zvrf_name(zvrf),
-                               zvrf->l3vni);
-                       vty_out(vty, "\n");
-               } else {
-                       json_object *json_vrf = NULL;
-
-                       json_vrf = json_object_new_object();
-                       json_object_string_add(json_vrf, "vrf",
-                                              zvrf_name(zvrf));
-                       json_object_int_add(json_vrf, "l3vni",
-                                           zvrf->l3vni);
-                       json_object_array_add(json_vrfs, json_vrf);
-               }
+               zebra_vxlan_print_vrf_vni(vty, zvrf, json_vrfs);
        }
 
        if (uj) {
@@ -2009,6 +2516,19 @@ DEFUN (show_vrf_vni,
        return CMD_SUCCESS;
 }
 
+DEFUN (show_evpn_global,
+       show_evpn_global_cmd,
+       "show evpn [json]",
+       SHOW_STR
+       "EVPN\n"
+       JSON_STR)
+{
+       u_char uj = use_json(argc, argv);
+
+       zebra_vxlan_print_evpn(vty, uj);
+       return CMD_SUCCESS;
+}
+
 DEFUN (show_evpn_vni,
        show_evpn_vni_cmd,
        "show evpn vni [json]",
@@ -2044,44 +2564,13 @@ DEFUN (show_evpn_vni_vni,
        return CMD_SUCCESS;
 }
 
-DEFUN (show_evpn_l3vni,
-       show_evpn_l3vni_cmd,
-       "show evpn l3vni [json]",
-       SHOW_STR
-       "EVPN\n"
-       "L3 VNI\n"
-       JSON_STR)
-{
-       u_char uj = use_json(argc, argv);
-
-       zebra_vxlan_print_l3vnis(vty, uj);
-       return CMD_SUCCESS;
-}
-
-DEFUN (show_evpn_l3vni_vni,
-       show_evpn_l3vni_vni_cmd,
-       "show evpn l3vni " CMD_VNI_RANGE "[json]",
-       SHOW_STR
-       "EVPN\n"
-       "L3 VxLAN Network Identifier\n"
-       "VNI number\n"
-       JSON_STR)
-{
-       vni_t vni;
-       u_char uj = use_json(argc, argv);
-
-       vni = strtoul(argv[3]->arg, NULL, 10);
-       zebra_vxlan_print_l3vni(vty, vni, uj);
-       return CMD_SUCCESS;
-}
-
-DEFUN (show_evpn_rmac_l3vni_mac,
-       show_evpn_rmac_l3vni_mac_cmd,
-       "show evpn rmac l3vni " CMD_VNI_RANGE " mac WORD [json]",
+DEFUN (show_evpn_rmac_vni_mac,
+       show_evpn_rmac_vni_mac_cmd,
+       "show evpn rmac vni " CMD_VNI_RANGE " mac WORD [json]",
        SHOW_STR
        "EVPN\n"
        "RMAC\n"
-       "L3-VNI\n"
+       "L3 VNI\n"
        "VNI number\n"
        "MAC\n"
        "mac-address (e.g. 0a:0a:0a:0a:0a:0a)\n"
@@ -2100,13 +2589,13 @@ DEFUN (show_evpn_rmac_l3vni_mac,
        return CMD_SUCCESS;
 }
 
-DEFUN (show_evpn_rmac_l3vni,
-       show_evpn_rmac_l3vni_cmd,
-       "show evpn rmac l3vni " CMD_VNI_RANGE "[json]",
+DEFUN (show_evpn_rmac_vni,
+       show_evpn_rmac_vni_cmd,
+       "show evpn rmac vni " CMD_VNI_RANGE "[json]",
        SHOW_STR
        "EVPN\n"
        "RMAC\n"
-       "L3-VNI\n"
+       "L3 VNI\n"
        "VNI number\n"
        JSON_STR)
 {
@@ -2119,13 +2608,13 @@ DEFUN (show_evpn_rmac_l3vni,
        return CMD_SUCCESS;
 }
 
-DEFUN (show_evpn_rmac_l3vni_all,
-       show_evpn_rmac_l3vni_all_cmd,
-       "show evpn rmac l3vni all [json]",
+DEFUN (show_evpn_rmac_vni_all,
+       show_evpn_rmac_vni_all_cmd,
+       "show evpn rmac vni all [json]",
        SHOW_STR
        "EVPN\n"
        "RMAC addresses\n"
-       "L3-VNI\n"
+       "L3 VNI\n"
        "All VNIs\n"
        JSON_STR)
 {
@@ -2136,13 +2625,13 @@ DEFUN (show_evpn_rmac_l3vni_all,
        return CMD_SUCCESS;
 }
 
-DEFUN (show_evpn_nh_l3vni_ip,
-       show_evpn_nh_l3vni_ip_cmd,
-       "show evpn next-hops l3vni " CMD_VNI_RANGE " ip WORD [json]",
+DEFUN (show_evpn_nh_vni_ip,
+       show_evpn_nh_vni_ip_cmd,
+       "show evpn next-hops vni " CMD_VNI_RANGE " ip WORD [json]",
        SHOW_STR
        "EVPN\n"
        "Remote Vteps\n"
-       "L3-VNI\n"
+       "L3 VNI\n"
        "VNI number\n"
        "Ip address\n"
        "Host address (ipv4 or ipv6)\n"
@@ -2163,13 +2652,13 @@ DEFUN (show_evpn_nh_l3vni_ip,
        return CMD_SUCCESS;
 }
 
-DEFUN (show_evpn_nh_l3vni,
-       show_evpn_nh_l3vni_cmd,
-       "show evpn next-hops l3vni " CMD_VNI_RANGE "[json]",
+DEFUN (show_evpn_nh_vni,
+       show_evpn_nh_vni_cmd,
+       "show evpn next-hops vni " CMD_VNI_RANGE "[json]",
        SHOW_STR
        "EVPN\n"
        "Remote Vteps\n"
-       "L3-VNI\n"
+       "L3 VNI\n"
        "VNI number\n"
        JSON_STR)
 {
@@ -2182,13 +2671,13 @@ DEFUN (show_evpn_nh_l3vni,
        return CMD_SUCCESS;
 }
 
-DEFUN (show_evpn_nh_l3vni_all,
-       show_evpn_nh_l3vni_all_cmd,
-       "show evpn next-hops l3vni all [json]",
+DEFUN (show_evpn_nh_vni_all,
+       show_evpn_nh_vni_all_cmd,
+       "show evpn next-hops vni all [json]",
        SHOW_STR
        "EVPN\n"
        "Remote VTEPs\n"
-       "L3-VNI\n"
+       "L3 VNI\n"
        "All VNIs\n"
        JSON_STR)
 {
@@ -2419,11 +2908,8 @@ static int zebra_ip_config(struct vty *vty)
 {
        int write = 0;
 
-       write += static_config(vty, AFI_IP, SAFI_UNICAST, "ip route");
-       write += static_config(vty, AFI_IP, SAFI_MULTICAST, "ip mroute");
-       write += static_config(vty, AFI_IP6, SAFI_UNICAST, "ipv6 route");
-
        write += zebra_import_table_config(vty);
+
        return write;
 }
 
@@ -2510,10 +2996,10 @@ DEFUN_HIDDEN (zebra_workqueue_timer,
              "Work Queue\n"
              "Time in milliseconds\n")
 {
-       uint32_t timer = strtoul(argv[2]->arg, NULL, 10);
-       zebrad.ribq->spec.hold = timer;
+       uint32_t timer = strtoul(argv[2]->arg, NULL, 10);
+       zebrad.ribq->spec.hold = timer;
 
-       return CMD_SUCCESS;
+       return CMD_SUCCESS;
 }
 
 DEFUN_HIDDEN (no_zebra_workqueue_timer,
@@ -2524,9 +3010,9 @@ DEFUN_HIDDEN (no_zebra_workqueue_timer,
              "Work Queue\n"
              "Time in milliseconds\n")
 {
-       zebrad.ribq->spec.hold = ZEBRA_RIB_PROCESS_HOLD_TIME;
+       zebrad.ribq->spec.hold = ZEBRA_RIB_PROCESS_HOLD_TIME;
 
-       return CMD_SUCCESS;
+       return CMD_SUCCESS;
 }
 
 DEFUN (no_ip_zebra_import_table,
@@ -2578,8 +3064,8 @@ static int config_write_protocol(struct vty *vty)
                vty_out(vty, "zebra work-queue %u\n", zebrad.ribq->spec.hold);
 
        if (zebrad.packets_to_process != ZEBRA_ZAPI_PACKETS_TO_PROCESS)
-               vty_out(vty,
-                       "zebra zapi-packets %u\n", zebrad.packets_to_process);
+               vty_out(vty, "zebra zapi-packets %u\n",
+                       zebrad.packets_to_process);
 
        enum multicast_mode ipv4_multicast_mode = multicast_mode_ipv4_get();
 
@@ -2649,7 +3135,7 @@ DEFUN (show_zebra,
        vty_out(vty,
                "VRF                         Installs   Removals    Updates   Installs   Removals\n");
 
-       RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
+       RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
                struct zebra_vrf *zvrf = vrf->info;
 
                vty_out(vty, "%-25s %10" PRIu64 " %10" PRIu64 " %10" PRIu64
@@ -2859,8 +3345,11 @@ void zebra_vty_init(void)
        install_element(CONFIG_NODE, &ip_multicast_mode_cmd);
        install_element(CONFIG_NODE, &no_ip_multicast_mode_cmd);
        install_element(CONFIG_NODE, &ip_route_blackhole_cmd);
+       install_element(VRF_NODE, &ip_route_blackhole_vrf_cmd);
        install_element(CONFIG_NODE, &ip_route_address_interface_cmd);
+       install_element(VRF_NODE, &ip_route_address_interface_vrf_cmd);
        install_element(CONFIG_NODE, &ip_route_cmd);
+       install_element(VRF_NODE, &ip_route_vrf_cmd);
        install_element(CONFIG_NODE, &ip_zebra_import_table_distance_cmd);
        install_element(CONFIG_NODE, &no_ip_zebra_import_table_cmd);
        install_element(CONFIG_NODE, &zebra_workqueue_timer_cmd);
@@ -2871,6 +3360,7 @@ void zebra_vty_init(void)
        install_element(VIEW_NODE, &show_vrf_cmd);
        install_element(VIEW_NODE, &show_vrf_vni_cmd);
        install_element(VIEW_NODE, &show_route_cmd);
+       install_element(VIEW_NODE, &show_route_table_cmd);
        install_element(VIEW_NODE, &show_route_detail_cmd);
        install_element(VIEW_NODE, &show_route_summary_cmd);
        install_element(VIEW_NODE, &show_ip_nht_cmd);
@@ -2882,8 +3372,11 @@ void zebra_vty_init(void)
        install_element(VIEW_NODE, &show_ip_rpf_addr_cmd);
 
        install_element(CONFIG_NODE, &ipv6_route_blackhole_cmd);
+       install_element(VRF_NODE, &ipv6_route_blackhole_vrf_cmd);
        install_element(CONFIG_NODE, &ipv6_route_address_interface_cmd);
+       install_element(VRF_NODE, &ipv6_route_address_interface_vrf_cmd);
        install_element(CONFIG_NODE, &ipv6_route_cmd);
+       install_element(VRF_NODE, &ipv6_route_vrf_cmd);
        install_element(CONFIG_NODE, &ip_nht_default_route_cmd);
        install_element(CONFIG_NODE, &no_ip_nht_default_route_cmd);
        install_element(CONFIG_NODE, &ipv6_nht_default_route_cmd);
@@ -2893,16 +3386,15 @@ void zebra_vty_init(void)
        /* Commands for VRF */
        install_element(VIEW_NODE, &show_ipv6_mroute_vrf_all_cmd);
 
+       install_element(VIEW_NODE, &show_evpn_global_cmd);
        install_element(VIEW_NODE, &show_evpn_vni_cmd);
        install_element(VIEW_NODE, &show_evpn_vni_vni_cmd);
-       install_element(VIEW_NODE, &show_evpn_l3vni_cmd);
-       install_element(VIEW_NODE, &show_evpn_l3vni_vni_cmd);
-       install_element(VIEW_NODE, &show_evpn_rmac_l3vni_mac_cmd);
-       install_element(VIEW_NODE, &show_evpn_rmac_l3vni_cmd);
-       install_element(VIEW_NODE, &show_evpn_rmac_l3vni_all_cmd);
-       install_element(VIEW_NODE, &show_evpn_nh_l3vni_ip_cmd);
-       install_element(VIEW_NODE, &show_evpn_nh_l3vni_cmd);
-       install_element(VIEW_NODE, &show_evpn_nh_l3vni_all_cmd);
+       install_element(VIEW_NODE, &show_evpn_rmac_vni_mac_cmd);
+       install_element(VIEW_NODE, &show_evpn_rmac_vni_cmd);
+       install_element(VIEW_NODE, &show_evpn_rmac_vni_all_cmd);
+       install_element(VIEW_NODE, &show_evpn_nh_vni_ip_cmd);
+       install_element(VIEW_NODE, &show_evpn_nh_vni_cmd);
+       install_element(VIEW_NODE, &show_evpn_nh_vni_all_cmd);
        install_element(VIEW_NODE, &show_evpn_mac_vni_cmd);
        install_element(VIEW_NODE, &show_evpn_mac_vni_all_cmd);
        install_element(VIEW_NODE, &show_evpn_mac_vni_all_vtep_cmd);
@@ -2913,8 +3405,8 @@ void zebra_vty_init(void)
        install_element(VIEW_NODE, &show_evpn_neigh_vni_neigh_cmd);
        install_element(VIEW_NODE, &show_evpn_neigh_vni_vtep_cmd);
 
-       install_element(CONFIG_NODE, &no_vrf_vni_mapping_cmd);
+       install_element(CONFIG_NODE, &default_vrf_vni_mapping_cmd);
+       install_element(CONFIG_NODE, &no_default_vrf_vni_mapping_cmd);
        install_element(VRF_NODE, &vrf_vni_mapping_cmd);
        install_element(VRF_NODE, &no_vrf_vni_mapping_cmd);
-
 }