]> git.proxmox.com Git - mirror_frr.git/blobdiff - vrrpd/vrrp_vty.c
Merge pull request #5793 from ton31337/fix/formatting_show_bgp_summary_failed
[mirror_frr.git] / vrrpd / vrrp_vty.c
index ca2ef1842a65298e4b57e2939a8a3858384ad43c..892c8dadd44dfa868eee989055ed0be7332f99a5 100644 (file)
 #include "lib/command.h"
 #include "lib/if.h"
 #include "lib/ipaddr.h"
+#include "lib/json.h"
+#include "lib/northbound_cli.h"
 #include "lib/prefix.h"
 #include "lib/termtable.h"
 #include "lib/vty.h"
+#include "lib/vrf.h"
 
 #include "vrrp.h"
+#include "vrrp_debug.h"
 #include "vrrp_vty.h"
-#include "vrrp_memory.h"
+#include "vrrp_zebra.h"
 #ifndef VTYSH_EXTRACT_PL
 #include "vrrpd/vrrp_vty_clippy.c"
 #endif
 #define VRRP_VRID_STR "Virtual Router ID\n"
 #define VRRP_PRIORITY_STR "Virtual Router Priority\n"
 #define VRRP_ADVINT_STR "Virtual Router Advertisement Interval\n"
-#define VRRP_IP_STR "Virtual Router IPv4 address\n"
-
-#define VROUTER_GET_VTY(_vty, _ifp, _vrid, _vr)                                \
-       do {                                                                   \
-               _vr = vrrp_lookup(_ifp, _vrid);                                \
-               if (!_vr) {                                                    \
-                       vty_out(_vty,                                          \
-                               "%% Please configure VRRP instance %u\n",      \
-                               (unsigned int)_vrid);                          \
-                       return CMD_WARNING_CONFIG_FAILED;                      \
-               }                                                              \
-       } while (0);
-
-DEFUN_NOSH (show_debugging_vrrpd,
-           show_debugging_vrrpd_cmd,
-           "show debugging [vrrp]",
-           SHOW_STR
-           DEBUG_STR
-           "VRRP information\n")
-{
-       vty_out(vty, "VRRP debugging status\n");
+#define VRRP_IP_STR "Virtual Router IP address\n"
+#define VRRP_VERSION_STR "VRRP protocol version\n"
 
-       return CMD_SUCCESS;
-}
+#define VRRP_XPATH_ENTRY VRRP_XPATH "[virtual-router-id='%ld']"
 
+/* clang-format off */
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group
+ */
 DEFPY(vrrp_vrid,
       vrrp_vrid_cmd,
-      "[no] vrrp (1-255)$vrid",
+      "[no] vrrp (1-255)$vrid [version (2-3)]",
       NO_STR
       VRRP_STR
-      VRRP_VRID_STR)
+      VRRP_VRID_STR
+      VRRP_VERSION_STR
+      VRRP_VERSION_STR)
 {
-       VTY_DECLVAR_CONTEXT(interface, ifp);
+       char valbuf[20];
 
-       struct vrrp_vrouter *vr = vrrp_lookup(ifp, vrid);
+       snprintf(valbuf, sizeof(valbuf), "%ld", version ? version : vd.version);
 
-       if (no && vr)
-               vrrp_vrouter_destroy(vr);
-       else if (no && !vr)
-               vty_out(vty, "%% VRRP instance %ld does not exist on %s\n",
-                       vrid, ifp->name);
-       else if (!vr)
-               vrrp_vrouter_create(ifp, vrid);
-       else if (vr)
-               vty_out(vty, "%% VRRP instance %ld already exists on %s\n",
-                       vrid, ifp->name);
+       if (no)
+               nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
+       else {
+               nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL);
+               nb_cli_enqueue_change(vty, "./version", NB_OP_MODIFY, valbuf);
+       }
 
-       return CMD_SUCCESS;
+       return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
+
+void cli_show_vrrp(struct vty *vty, struct lyd_node *dnode, bool show_defaults)
+{
+       const char *vrid = yang_dnode_get_string(dnode, "./virtual-router-id");
+       const char *ver = yang_dnode_get_string(dnode, "./version");
+
+       vty_out(vty, " vrrp %s", vrid);
+       if (show_defaults || !yang_dnode_is_default(dnode, "./version"))
+               vty_out(vty, " version %s", ver);
+       vty_out(vty, "\n");
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/shutdown
+ */
+DEFPY(vrrp_shutdown,
+      vrrp_shutdown_cmd,
+      "[no] vrrp (1-255)$vrid shutdown",
+      NO_STR
+      VRRP_STR
+      VRRP_VRID_STR
+      "Force VRRP router into administrative shutdown\n")
+{
+       nb_cli_enqueue_change(vty, "./shutdown", NB_OP_MODIFY,
+                             no ? "false" : "true");
+
+       return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
 }
 
+void cli_show_shutdown(struct vty *vty, struct lyd_node *dnode,
+                      bool show_defaults)
+{
+       const char *vrid = yang_dnode_get_string(dnode, "../virtual-router-id");
+       const bool shut = yang_dnode_get_bool(dnode, NULL);
+
+       vty_out(vty, " %svrrp %s shutdown\n", shut ? "" : "no ", vrid);
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/priority
+ */
 DEFPY(vrrp_priority,
       vrrp_priority_cmd,
-      "[no] vrrp (1-255)$vrid priority (1-254)",
-      NO_STR
+      "vrrp (1-255)$vrid priority (1-254)",
       VRRP_STR
       VRRP_VRID_STR
       VRRP_PRIORITY_STR
       "Priority value")
 {
-       VTY_DECLVAR_CONTEXT(interface, ifp);
+       nb_cli_enqueue_change(vty, "./priority", NB_OP_MODIFY, priority_str);
 
-       struct vrrp_vrouter *vr;
-       struct vrrp_router *r;
-       bool nr[2] = { false, false };
-       int ret = CMD_SUCCESS;
-       uint8_t newprio = no ? VRRP_DEFAULT_PRIORITY : priority;
-
-       VROUTER_GET_VTY(vty, ifp, vrid, vr);
-
-       r = vr->v4;
-       for (int i = 0; i < 2; i++) {
-               nr[i] = r->is_active && r->fsm.state != VRRP_STATE_INITIALIZE
-                       && vr->priority != newprio;
-               if (nr[i]) {
-                       vty_out(vty,
-                               "%% WARNING: Restarting %s Virtual Router %ld to update priority\n",
-                               family2str(r->family), vrid);
-                       (void)vrrp_event(r, VRRP_EVENT_SHUTDOWN);
-               }
-               r = vr->v6;
-       }
+       return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
 
-       vrrp_set_priority(vr, newprio);
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/priority
+ */
+DEFPY(no_vrrp_priority,
+      no_vrrp_priority_cmd,
+      "no vrrp (1-255)$vrid priority [(1-254)]",
+      NO_STR
+      VRRP_STR
+      VRRP_VRID_STR
+      VRRP_PRIORITY_STR
+      "Priority value")
+{
+       nb_cli_enqueue_change(vty, "./priority", NB_OP_MODIFY, NULL);
 
-       r = vr->v4;
-       for (int i = 0; i < 2; i++) {
-               if (nr[i]) {
-                       ret = vrrp_event(r, VRRP_EVENT_STARTUP);
-                       if (ret < 0)
-                               vty_out(vty,
-                                       "%% Failed to start Virtual Router %ld (%s)\n",
-                                       vrid, family2str(r->family));
-               }
-               r = vr->v6;
-       }
+       return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
 
-       return CMD_SUCCESS;
+void cli_show_priority(struct vty *vty, struct lyd_node *dnode,
+                      bool show_defaults)
+{
+       const char *vrid = yang_dnode_get_string(dnode, "../virtual-router-id");
+       const char *prio = yang_dnode_get_string(dnode, NULL);
+
+       vty_out(vty, " vrrp %s priority %s\n", vrid, prio);
 }
 
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/advertisement-interval
+ */
 DEFPY(vrrp_advertisement_interval,
       vrrp_advertisement_interval_cmd,
-      "[no] vrrp (1-255)$vrid advertisement-interval (1-4096)",
+      "vrrp (1-255)$vrid advertisement-interval (10-40950)",
+      VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR
+      "Advertisement interval in milliseconds; must be multiple of 10")
+{
+       char val[20];
+
+       /* all internal computations are in centiseconds */
+       advertisement_interval /= CS2MS;
+       snprintf(val, sizeof(val), "%ld", advertisement_interval);
+       nb_cli_enqueue_change(vty, "./advertisement-interval", NB_OP_MODIFY,
+                             val);
+
+       return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/advertisement-interval
+ */
+DEFPY(no_vrrp_advertisement_interval,
+      no_vrrp_advertisement_interval_cmd,
+      "no vrrp (1-255)$vrid advertisement-interval [(10-40950)]",
       NO_STR VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR
-      "Advertisement interval in centiseconds")
+      "Advertisement interval in milliseconds; must be multiple of 10")
 {
-       VTY_DECLVAR_CONTEXT(interface, ifp);
+       nb_cli_enqueue_change(vty, "./advertisement-interval", NB_OP_MODIFY,
+                             NULL);
 
-       struct vrrp_vrouter *vr;
-       uint16_t newadvint = no ? VRRP_DEFAULT_ADVINT : advertisement_interval;
+       return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
 
-       VROUTER_GET_VTY(vty, ifp, vrid, vr);
-       vrrp_set_advertisement_interval(vr, newadvint);
+void cli_show_advertisement_interval(struct vty *vty, struct lyd_node *dnode,
+                                    bool show_defaults)
+{
+       const char *vrid = yang_dnode_get_string(dnode, "../virtual-router-id");
+       uint16_t advint = yang_dnode_get_uint16(dnode, NULL);
 
-       return CMD_SUCCESS;
+       vty_out(vty, " vrrp %s advertisement-interval %u\n", vrid,
+               advint * CS2MS);
 }
 
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v4/virtual-address
+ */
 DEFPY(vrrp_ip,
       vrrp_ip_cmd,
       "[no] vrrp (1-255)$vrid ip A.B.C.D",
@@ -163,29 +214,25 @@ DEFPY(vrrp_ip,
       "Add IPv4 address\n"
       VRRP_IP_STR)
 {
-       VTY_DECLVAR_CONTEXT(interface, ifp);
-
-       struct vrrp_vrouter *vr;
-       int ret;
-
-       VROUTER_GET_VTY(vty, ifp, vrid, vr);
-       vrrp_add_ipv4(vr, ip);
+       int op = no ? NB_OP_DESTROY : NB_OP_CREATE;
+       nb_cli_enqueue_change(vty, "./v4/virtual-address", op, ip_str);
 
-       if (vr->v4->fsm.state == VRRP_STATE_INITIALIZE) {
-               vty_out(vty, "%% Activating Virtual Router %ld\n", vrid);
-               ret = vrrp_event(vr->v4, VRRP_EVENT_STARTUP);
-               ret = ret < 0 ? CMD_WARNING_CONFIG_FAILED : CMD_SUCCESS;
+       return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
 
-               if (ret == CMD_WARNING_CONFIG_FAILED)
-                       vty_out(vty, "%% Failed to start Virtual Router %ld\n",
-                               vrid);
-       } else {
-               ret = CMD_SUCCESS;
-       }
+void cli_show_ip(struct vty *vty, struct lyd_node *dnode, bool show_defaults)
+{
+       const char *vrid =
+               yang_dnode_get_string(dnode, "../../virtual-router-id");
+       const char *ipv4 = yang_dnode_get_string(dnode, NULL);
 
-       return ret;
+       vty_out(vty, " vrrp %s ip %s\n", vrid, ipv4);
 }
 
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/v6/virtual-address
+ */
 DEFPY(vrrp_ip6,
       vrrp_ip6_cmd,
       "[no] vrrp (1-255)$vrid ipv6 X:X::X:X",
@@ -195,29 +242,220 @@ DEFPY(vrrp_ip6,
       "Add IPv6 address\n"
       VRRP_IP_STR)
 {
-       VTY_DECLVAR_CONTEXT(interface, ifp);
+       int op = no ? NB_OP_DESTROY : NB_OP_CREATE;
+       nb_cli_enqueue_change(vty, "./v6/virtual-address", op, ipv6_str);
 
-       struct vrrp_vrouter *vr;
-       int ret;
+       return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
 
-       VROUTER_GET_VTY(vty, ifp, vrid, vr);
-       vrrp_add_ipv6(vr, ipv6);
+void cli_show_ipv6(struct vty *vty, struct lyd_node *dnode, bool show_defaults)
+{
+       const char *vrid =
+               yang_dnode_get_string(dnode, "../../virtual-router-id");
+       const char *ipv6 = yang_dnode_get_string(dnode, NULL);
 
-       if (vr->v6->fsm.state == VRRP_STATE_INITIALIZE) {
-               vty_out(vty, "%% Activating Virtual Router %ld\n", vrid);
-               ret = vrrp_event(vr->v6, VRRP_EVENT_STARTUP);
-               ret = ret < 0 ? CMD_WARNING_CONFIG_FAILED : CMD_SUCCESS;
+       vty_out(vty, " vrrp %s ipv6 %s\n", vrid, ipv6);
+}
 
-               if (ret == CMD_WARNING_CONFIG_FAILED)
-                       vty_out(vty, "%% Failed to start Virtual Router %ld\n",
-                               vrid);
-       } else {
-               ret = CMD_SUCCESS;
+/*
+ * XPath: /frr-interface:lib/interface/frr-vrrpd:vrrp/vrrp-group/preempt
+ */
+DEFPY(vrrp_preempt,
+      vrrp_preempt_cmd,
+      "[no] vrrp (1-255)$vrid preempt",
+      NO_STR
+      VRRP_STR
+      VRRP_VRID_STR
+      "Preempt mode\n")
+{
+       nb_cli_enqueue_change(vty, "./preempt", NB_OP_MODIFY,
+                             no ? "false" : "true");
+
+       return nb_cli_apply_changes(vty, VRRP_XPATH_ENTRY, vrid);
+}
+
+void cli_show_preempt(struct vty *vty, struct lyd_node *dnode,
+                     bool show_defaults)
+{
+       const char *vrid = yang_dnode_get_string(dnode, "../virtual-router-id");
+       const bool pre = yang_dnode_get_bool(dnode, NULL);
+
+       vty_out(vty, " %svrrp %s preempt\n", pre ? "" : "no ", vrid);
+}
+
+/* XXX: yang conversion */
+DEFPY(vrrp_autoconfigure,
+      vrrp_autoconfigure_cmd,
+      "[no] vrrp autoconfigure [version (2-3)]",
+      NO_STR
+      VRRP_STR
+      "Automatically set up VRRP instances on VRRP-compatible interfaces\n"
+      "Version for automatically configured instances\n"
+      VRRP_VERSION_STR)
+{
+       version = version ? version : 3;
+
+       if (!no)
+               vrrp_autoconfig_on(version);
+       else
+               vrrp_autoconfig_off();
+
+       return CMD_SUCCESS;
+}
+
+/* XXX: yang conversion */
+DEFPY(vrrp_default,
+      vrrp_default_cmd,
+      "[no] vrrp default <advertisement-interval$adv (10-40950)$advint|preempt$p|priority$prio (1-254)$prioval|shutdown$s>",
+      NO_STR
+      VRRP_STR
+      "Configure defaults for new VRRP instances\n"
+      VRRP_ADVINT_STR
+      "Advertisement interval in milliseconds\n"
+      "Preempt mode\n"
+      VRRP_PRIORITY_STR
+      "Priority value\n"
+      "Force VRRP router into administrative shutdown\n")
+{
+       if (adv) {
+               if (advint % CS2MS != 0) {
+                       vty_out(vty, "%% Value must be a multiple of %u\n",
+                               (unsigned int)CS2MS);
+                       return CMD_WARNING_CONFIG_FAILED;
+               }
+               /* all internal computations are in centiseconds */
+               advint /= CS2MS;
+               vd.advertisement_interval = no ? VRRP_DEFAULT_ADVINT : advint;
        }
+       if (p)
+               vd.preempt_mode = !no;
+       if (prio)
+               vd.priority = no ? VRRP_DEFAULT_PRIORITY : prioval;
+       if (s)
+               vd.shutdown = !no;
 
-       return ret;
+       return CMD_SUCCESS;
 }
 
+/* clang-format on */
+
+/*
+ * Build JSON representation of VRRP instance.
+ *
+ * vr
+ *    VRRP router to build json object from
+ *
+ * Returns:
+ *    JSON representation of VRRP instance. Must be freed by caller.
+ */
+static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr)
+{
+       char ethstr4[ETHER_ADDR_STRLEN];
+       char ethstr6[ETHER_ADDR_STRLEN];
+       char ipstr[INET6_ADDRSTRLEN];
+       const char *stastr4 = vrrp_state_names[vr->v4->fsm.state];
+       const char *stastr6 = vrrp_state_names[vr->v6->fsm.state];
+       char sipstr4[INET6_ADDRSTRLEN] = {};
+       char sipstr6[INET6_ADDRSTRLEN] = {};
+       struct listnode *ln;
+       struct ipaddr *ip;
+       struct json_object *j = json_object_new_object();
+       struct json_object *v4 = json_object_new_object();
+       struct json_object *v4_stats = json_object_new_object();
+       struct json_object *v4_addrs = json_object_new_array();
+       struct json_object *v6 = json_object_new_object();
+       struct json_object *v6_stats = json_object_new_object();
+       struct json_object *v6_addrs = json_object_new_array();
+
+       prefix_mac2str(&vr->v4->vmac, ethstr4, sizeof(ethstr4));
+       prefix_mac2str(&vr->v6->vmac, ethstr6, sizeof(ethstr6));
+
+       json_object_int_add(j, "vrid", vr->vrid);
+       json_object_int_add(j, "version", vr->version);
+       json_object_boolean_add(j, "autoconfigured", vr->autoconf);
+       json_object_boolean_add(j, "shutdown", vr->shutdown);
+       json_object_boolean_add(j, "preemptMode", vr->preempt_mode);
+       json_object_boolean_add(j, "acceptMode", vr->accept_mode);
+       json_object_string_add(j, "interface", vr->ifp->name);
+       json_object_int_add(j, "advertisementInterval",
+                           vr->advertisement_interval * CS2MS);
+       /* v4 */
+       json_object_string_add(v4, "interface",
+                              vr->v4->mvl_ifp ? vr->v4->mvl_ifp->name : "");
+       json_object_string_add(v4, "vmac", ethstr4);
+       ipaddr2str(&vr->v4->src, sipstr4, sizeof(sipstr4));
+       json_object_string_add(v4, "primaryAddress", sipstr4);
+       json_object_string_add(v4, "status", stastr4);
+       json_object_int_add(v4, "effectivePriority", vr->v4->priority);
+       json_object_int_add(v4, "masterAdverInterval",
+                           vr->v4->master_adver_interval * CS2MS);
+       json_object_int_add(v4, "skewTime", vr->v4->skew_time * CS2MS);
+       json_object_int_add(v4, "masterDownInterval",
+                           vr->v4->master_down_interval * CS2MS);
+       /* v4 stats */
+       json_object_int_add(v4_stats, "adverTx", vr->v4->stats.adver_tx_cnt);
+       json_object_int_add(v4_stats, "adverRx", vr->v4->stats.adver_rx_cnt);
+       json_object_int_add(v4_stats, "garpTx", vr->v4->stats.garp_tx_cnt);
+       json_object_int_add(v4_stats, "transitions", vr->v4->stats.trans_cnt);
+       json_object_object_add(v4, "stats", v4_stats);
+       /* v4 addrs */
+       if (vr->v4->addrs->count) {
+               for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ln, ip)) {
+                       inet_ntop(vr->v4->family, &ip->ipaddr_v4, ipstr,
+                                 sizeof(ipstr));
+                       json_object_array_add(v4_addrs,
+                                             json_object_new_string(ipstr));
+               }
+       }
+       json_object_object_add(v4, "addresses", v4_addrs);
+       json_object_object_add(j, "v4", v4);
+
+       /* v6 */
+       json_object_string_add(v6, "interface",
+                              vr->v6->mvl_ifp ? vr->v6->mvl_ifp->name : "");
+       json_object_string_add(v6, "vmac", ethstr6);
+       ipaddr2str(&vr->v6->src, sipstr6, sizeof(sipstr6));
+       if (strlen(sipstr6) == 0 && vr->v6->src.ip.addr == 0x00)
+               strlcat(sipstr6, "::", sizeof(sipstr6));
+       json_object_string_add(v6, "primaryAddress", sipstr6);
+       json_object_string_add(v6, "status", stastr6);
+       json_object_int_add(v6, "effectivePriority", vr->v6->priority);
+       json_object_int_add(v6, "masterAdverInterval",
+                           vr->v6->master_adver_interval * CS2MS);
+       json_object_int_add(v6, "skewTime", vr->v6->skew_time * CS2MS);
+       json_object_int_add(v6, "masterDownInterval",
+                           vr->v6->master_down_interval * CS2MS);
+       /* v6 stats */
+       json_object_int_add(v6_stats, "adverTx", vr->v6->stats.adver_tx_cnt);
+       json_object_int_add(v6_stats, "adverRx", vr->v6->stats.adver_rx_cnt);
+       json_object_int_add(v6_stats, "neighborAdverTx",
+                           vr->v6->stats.una_tx_cnt);
+       json_object_int_add(v6_stats, "transitions", vr->v6->stats.trans_cnt);
+       json_object_object_add(v6, "stats", v6_stats);
+       /* v6 addrs */
+       if (vr->v6->addrs->count) {
+               for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ln, ip)) {
+                       inet_ntop(vr->v6->family, &ip->ipaddr_v6, ipstr,
+                                 sizeof(ipstr));
+                       json_object_array_add(v6_addrs,
+                                             json_object_new_string(ipstr));
+               }
+       }
+       json_object_object_add(v6, "addresses", v6_addrs);
+       json_object_object_add(j, "v6", v6);
+
+       return j;
+}
+
+/*
+ * Dump VRRP instance status to VTY.
+ *
+ * vty
+ *    vty to dump to
+ *
+ * vr
+ *    VRRP router to dump
+ */
 static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr)
 {
        char ethstr4[ETHER_ADDR_STRLEN];
@@ -225,12 +463,18 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr)
        char ipstr[INET6_ADDRSTRLEN];
        const char *stastr4 = vrrp_state_names[vr->v4->fsm.state];
        const char *stastr6 = vrrp_state_names[vr->v6->fsm.state];
+       char sipstr4[INET6_ADDRSTRLEN] = {};
+       char sipstr6[INET6_ADDRSTRLEN] = {};
        struct listnode *ln;
        struct ipaddr *ip;
 
        struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
 
        ttable_add_row(tt, "%s|%" PRIu32, "Virtual Router ID", vr->vrid);
+       ttable_add_row(tt, "%s|%" PRIu8, "Protocol Version", vr->version);
+       ttable_add_row(tt, "%s|%s", "Autoconfigured",
+                      vr->autoconf ? "Yes" : "No");
+       ttable_add_row(tt, "%s|%s", "Shutdown", vr->shutdown ? "Yes" : "No");
        ttable_add_row(tt, "%s|%s", "Interface", vr->ifp->name);
        prefix_mac2str(&vr->v4->vmac, ethstr4, sizeof(ethstr4));
        prefix_mac2str(&vr->v6->vmac, ethstr6, sizeof(ethstr6));
@@ -238,6 +482,12 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr)
                       vr->v4->mvl_ifp ? vr->v4->mvl_ifp->name : "None");
        ttable_add_row(tt, "%s|%s", "VRRP interface (v6)",
                       vr->v6->mvl_ifp ? vr->v6->mvl_ifp->name : "None");
+       ipaddr2str(&vr->v4->src, sipstr4, sizeof(sipstr4));
+       ipaddr2str(&vr->v6->src, sipstr6, sizeof(sipstr6));
+       if (strlen(sipstr6) == 0 && vr->v6->src.ip.addr == 0x00)
+               strlcat(sipstr6, "::", sizeof(sipstr6));
+       ttable_add_row(tt, "%s|%s", "Primary IP (v4)", sipstr4);
+       ttable_add_row(tt, "%s|%s", "Primary IP (v6)", sipstr6);
        ttable_add_row(tt, "%s|%s", "Virtual MAC (v4)", ethstr4);
        ttable_add_row(tt, "%s|%s", "Virtual MAC (v6)", ethstr6);
        ttable_add_row(tt, "%s|%s", "Status (v4)", stastr4);
@@ -251,21 +501,42 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr)
                       vr->preempt_mode ? "Yes" : "No");
        ttable_add_row(tt, "%s|%s", "Accept Mode",
                       vr->accept_mode ? "Yes" : "No");
-       ttable_add_row(tt, "%s|%" PRIu16" cs", "Advertisement Interval",
-                      vr->advertisement_interval);
-       ttable_add_row(tt, "%s|%" PRIu16" cs", "Master Advertisement Interval (v4)",
-                      vr->v4->master_adver_interval);
-       ttable_add_row(tt, "%s|%" PRIu16" cs", "Master Advertisement Interval (v6)",
-                      vr->v6->master_adver_interval);
-       ttable_add_row(tt, "%s|%" PRIu16" cs", "Skew Time (v4)", vr->v4->skew_time);
-       ttable_add_row(tt, "%s|%" PRIu16" cs", "Skew Time (v6)", vr->v6->skew_time);
-       ttable_add_row(tt, "%s|%" PRIu16" cs", "Master Down Interval (v4)",
-                      vr->v4->master_down_interval);
-       ttable_add_row(tt, "%s|%" PRIu16" cs", "Master Down Interval (v6)",
-                      vr->v6->master_down_interval);
+       ttable_add_row(tt, "%s|%d ms", "Advertisement Interval",
+                      vr->advertisement_interval * CS2MS);
+       ttable_add_row(tt, "%s|%d ms",
+                      "Master Advertisement Interval (v4)",
+                      vr->v4->master_adver_interval * CS2MS);
+       ttable_add_row(tt, "%s|%d ms",
+                      "Master Advertisement Interval (v6)",
+                      vr->v6->master_adver_interval * CS2MS);
+       ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Tx (v4)",
+                      vr->v4->stats.adver_tx_cnt);
+       ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Tx (v6)",
+                      vr->v6->stats.adver_tx_cnt);
+       ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Rx (v4)",
+                      vr->v4->stats.adver_rx_cnt);
+       ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Rx (v6)",
+                      vr->v6->stats.adver_rx_cnt);
+       ttable_add_row(tt, "%s|%" PRIu32, "Gratuitous ARP Tx (v4)",
+                      vr->v4->stats.garp_tx_cnt);
+       ttable_add_row(tt, "%s|%" PRIu32, "Neigh. Adverts Tx (v6)",
+                      vr->v6->stats.una_tx_cnt);
+       ttable_add_row(tt, "%s|%" PRIu32, "State transitions (v4)",
+                      vr->v4->stats.trans_cnt);
+       ttable_add_row(tt, "%s|%" PRIu32, "State transitions (v6)",
+                      vr->v6->stats.trans_cnt);
+       ttable_add_row(tt, "%s|%d ms", "Skew Time (v4)",
+                      vr->v4->skew_time * CS2MS);
+       ttable_add_row(tt, "%s|%d ms", "Skew Time (v6)",
+                      vr->v6->skew_time * CS2MS);
+       ttable_add_row(tt, "%s|%d ms", "Master Down Interval (v4)",
+                      vr->v4->master_down_interval * CS2MS);
+       ttable_add_row(tt, "%s|%d ms", "Master Down Interval (v6)",
+                      vr->v6->master_down_interval * CS2MS);
        ttable_add_row(tt, "%s|%u", "IPv4 Addresses", vr->v4->addrs->count);
 
-       char fill[37];
+       char fill[35];
+
        memset(fill, '.', sizeof(fill));
        fill[sizeof(fill) - 1] = 0x00;
        if (vr->v4->addrs->count) {
@@ -287,53 +558,217 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr)
        }
 
        char *table = ttable_dump(tt, "\n");
+
        vty_out(vty, "\n%s\n", table);
        XFREE(MTYPE_TMP, table);
        ttable_del(tt);
+}
+
+/*
+ * Sort comparator, used when sorting VRRP instances for display purposes.
+ *
+ * Sorts by interface name first, then by VRID ascending.
+ */
+static int vrrp_instance_display_sort_cmp(const void **d1, const void **d2)
+{
+       const struct vrrp_vrouter *vr1 = *d1;
+       const struct vrrp_vrouter *vr2 = *d2;
+       int result;
 
+       result = strcmp(vr1->ifp->name, vr2->ifp->name);
+       result += !result * (vr1->vrid - vr2->vrid);
+
+       return result;
 }
 
+/* clang-format off */
+
 DEFPY(vrrp_vrid_show,
       vrrp_vrid_show_cmd,
-      "show vrrp [interface INTERFACE$ifn] [(1-255)$vrid]",
+      "show vrrp [interface INTERFACE$ifn] [(1-255)$vrid] [json$json]",
       SHOW_STR
       VRRP_STR
       INTERFACE_STR
       "Only show VRRP instances on this interface\n"
-      VRRP_VRID_STR)
+      VRRP_VRID_STR
+      JSON_STR)
 {
        struct vrrp_vrouter *vr;
        struct listnode *ln;
        struct list *ll = hash_to_list(vrrp_vrouters_hash);
+       struct json_object *j = json_object_new_array();
+
+       list_sort(ll, vrrp_instance_display_sort_cmp);
 
        for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) {
                if (ifn && !strmatch(ifn, vr->ifp->name))
                        continue;
-               if (vrid && vrid != vr->vrid)
+               if (vrid && ((uint8_t) vrid) != vr->vrid)
                        continue;
 
-               vrrp_show(vty, vr);
+               if (!json)
+                       vrrp_show(vty, vr);
+               else
+                       json_object_array_add(j, vrrp_build_json(vr));
        }
 
+       if (json)
+               vty_out(vty, "%s\n",
+                       json_object_to_json_string_ext(
+                               j, JSON_C_TO_STRING_PRETTY));
+
+       json_object_free(j);
+
        list_delete(&ll);
 
        return CMD_SUCCESS;
 }
 
-static struct cmd_node interface_node = {
-       INTERFACE_NODE,
-       "%s(config-if)# ", 1
-};
+DEFPY(vrrp_vrid_show_summary,
+      vrrp_vrid_show_summary_cmd,
+      "show vrrp [interface INTERFACE$ifn] [(1-255)$vrid] summary",
+      SHOW_STR
+      VRRP_STR
+      INTERFACE_STR
+      "Only show VRRP instances on this interface\n"
+      VRRP_VRID_STR
+      "Summarize all VRRP instances\n")
+{
+       struct vrrp_vrouter *vr;
+       struct listnode *ln;
+       struct list *ll = hash_to_list(vrrp_vrouters_hash);
+
+       list_sort(ll, vrrp_instance_display_sort_cmp);
+
+       struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
+
+       ttable_add_row(
+               tt, "Interface|VRID|Priority|IPv4|IPv6|State (v4)|State (v6)");
+       ttable_rowseps(tt, 0, BOTTOM, true, '-');
+
+       for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) {
+               if (ifn && !strmatch(ifn, vr->ifp->name))
+                       continue;
+               if (vrid && ((uint8_t)vrid) != vr->vrid)
+                       continue;
+
+               ttable_add_row(
+                       tt, "%s|%" PRIu8 "|%" PRIu8 "|%d|%d|%s|%s",
+                       vr->ifp->name, vr->vrid, vr->priority,
+                       vr->v4->addrs->count, vr->v6->addrs->count,
+                       vr->v4->fsm.state == VRRP_STATE_MASTER ? "Master"
+                                                              : "Backup",
+                       vr->v6->fsm.state == VRRP_STATE_MASTER ? "Master"
+                                                              : "Backup");
+       }
+
+       char *table = ttable_dump(tt, "\n");
+
+       vty_out(vty, "\n%s\n", table);
+       XFREE(MTYPE_TMP, table);
+       ttable_del(tt);
+
+       list_delete(&ll);
+
+       return CMD_SUCCESS;
+}
+
+
+DEFPY(debug_vrrp,
+      debug_vrrp_cmd,
+      "[no] debug vrrp [{protocol$proto|autoconfigure$ac|packets$pkt|sockets$sock|ndisc$ndisc|arp$arp|zebra$zebra}]",
+      NO_STR
+      DEBUG_STR
+      VRRP_STR
+      "Debug protocol state\n"
+      "Debug autoconfiguration\n"
+      "Debug sent and received packets\n"
+      "Debug socket creation and configuration\n"
+      "Debug Neighbor Discovery\n"
+      "Debug ARP\n"
+      "Debug Zebra events\n")
+{
+       /* If no specific are given on/off them all */
+       if (strmatch(argv[argc - 1]->text, "vrrp"))
+               vrrp_debug_set(NULL, 0, vty->node, !no, true, true, true, true,
+                              true, true, true);
+       else
+               vrrp_debug_set(NULL, 0, vty->node, !no, !!proto, !!ac, !!pkt,
+                              !!sock, !!ndisc, !!arp, !!zebra);
+
+       return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (show_debugging_vrrp,
+           show_debugging_vrrp_cmd,
+           "show debugging [vrrp]",
+           SHOW_STR
+           DEBUG_STR
+           "VRRP information\n")
+{
+       vty_out(vty, "VRRP debugging status:\n");
+
+       vrrp_debug_status_write(vty);
+
+       return CMD_SUCCESS;
+}
+
+/* clang-format on */
+
+/*
+ * Write per interface VRRP config.
+ */
+static int vrrp_config_write_interface(struct vty *vty)
+{
+       struct vrf *vrf;
+       int write = 0;
+
+       RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+               struct interface *ifp;
+
+               FOR_ALL_INTERFACES (vrf, ifp) {
+                       struct lyd_node *dnode;
+
+                       dnode = yang_dnode_get(
+                               running_config->dnode,
+                               "/frr-interface:lib/interface[name='%s'][vrf='%s']",
+                               ifp->name, vrf->name);
+                       if (dnode == NULL)
+                               continue;
+
+                       write = 1;
+                       nb_cli_show_dnode_cmds(vty, dnode, false);
+               }
+       }
+
+       return write;
+}
+
+static struct cmd_node interface_node = {INTERFACE_NODE, "%s(config-if)# ", 1};
+static struct cmd_node debug_node = {DEBUG_NODE, "", 1};
+static struct cmd_node vrrp_node = {VRRP_NODE, "", 1};
 
 void vrrp_vty_init(void)
 {
-       install_node(&interface_node, NULL);
+       install_node(&debug_node, vrrp_config_write_debug);
+       install_node(&interface_node, vrrp_config_write_interface);
+       install_node(&vrrp_node, vrrp_config_write_global);
        if_cmd_init();
-       install_element(VIEW_NODE, &show_debugging_vrrpd_cmd);
+
        install_element(VIEW_NODE, &vrrp_vrid_show_cmd);
+       install_element(VIEW_NODE, &vrrp_vrid_show_summary_cmd);
+       install_element(VIEW_NODE, &show_debugging_vrrp_cmd);
+       install_element(VIEW_NODE, &debug_vrrp_cmd);
+       install_element(CONFIG_NODE, &debug_vrrp_cmd);
+       install_element(CONFIG_NODE, &vrrp_autoconfigure_cmd);
+       install_element(CONFIG_NODE, &vrrp_default_cmd);
        install_element(INTERFACE_NODE, &vrrp_vrid_cmd);
+       install_element(INTERFACE_NODE, &vrrp_shutdown_cmd);
        install_element(INTERFACE_NODE, &vrrp_priority_cmd);
+       install_element(INTERFACE_NODE, &no_vrrp_priority_cmd);
        install_element(INTERFACE_NODE, &vrrp_advertisement_interval_cmd);
+       install_element(INTERFACE_NODE, &no_vrrp_advertisement_interval_cmd);
        install_element(INTERFACE_NODE, &vrrp_ip_cmd);
        install_element(INTERFACE_NODE, &vrrp_ip6_cmd);
+       install_element(INTERFACE_NODE, &vrrp_preempt_cmd);
 }