]> git.puffer.fish Git - mirror/frr.git/commitdiff
vrrpd: ipv6 support
authorQuentin Young <qlyoung@cumulusnetworks.com>
Tue, 11 Dec 2018 17:55:21 +0000 (17:55 +0000)
committerQuentin Young <qlyoung@cumulusnetworks.com>
Fri, 17 May 2019 00:27:08 +0000 (00:27 +0000)
Add initial support for IPv6.

Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
lib/sockunion.c
lib/sockunion.h
vrrpd/vrrp.c
vrrpd/vrrp.h
vrrpd/vrrp_arp.c
vrrpd/vrrp_arp.h
vrrpd/vrrp_packet.c
vrrpd/vrrp_packet.h
vrrpd/vrrp_vty.c

index bb5426d74a8d70faa8698880563a56fcb143251a..5afd10eb4811ec6c127b3a99d75614aab937b25b 100644 (file)
@@ -163,7 +163,7 @@ int sockunion_accept(int sock, union sockunion *su)
 }
 
 /* Return sizeof union sockunion.  */
-static int sockunion_sizeof(const union sockunion *su)
+int sockunion_sizeof(const union sockunion *su)
 {
        int ret;
 
index d7d26ba85ad9beff8043ab6ed6c27926e4b8af9c..b9967355502e259a1360b76802fae40b7d127361 100644 (file)
@@ -83,6 +83,7 @@ extern void sockunion_set(union sockunion *, int family, const uint8_t *addr,
 
 extern union sockunion *sockunion_str2su(const char *str);
 extern int sockunion_accept(int sock, union sockunion *);
+extern int sockunion_sizeof(const union sockunion *su);
 extern int sockunion_stream_socket(union sockunion *);
 extern int sockopt_reuseaddr(int);
 extern int sockopt_reuseport(int);
index bf43255d1c003989e0c4fafd79dbfa0890af367a..0f70c6ddf2611046c1c122ac3b3e834115916652 100644 (file)
  */
 #include <zebra.h>
 
-#include "lib/memory.h"
+#include "lib/hash.h"
+#include "lib/hook.h"
 #include "lib/if.h"
 #include "lib/linklist.h"
+#include "lib/memory.h"
 #include "lib/prefix.h"
-#include "lib/hash.h"
+#include "lib/sockopt.h"
 #include "lib/vrf.h"
-#include "lib/hook.h"
 
 #include "vrrp.h"
 #include "vrrp_arp.h"
@@ -74,42 +75,25 @@ static void vrrp_mac_set(struct ethaddr *mac, bool v6, uint8_t vrid)
 }
 
 /*
- * Sets advertisement_interval and master_adver_interval on a Virtual Router,
- * then recalculates and sets skew_time and master_down_interval based on these
+ * Recalculates and sets skew_time and master_down_interval based
  * values.
  *
- * vr
- *    Virtual Router to operate on
- *
- * advertisement_interval
- *    Advertisement_Interval to set
- *
- * master_adver_interval
- *    Master_Adver_Interval to set
- */
-static void vrrp_update_times(struct vrrp_vrouter *vr,
-                             uint16_t advertisement_interval,
-                             uint16_t master_adver_interval)
-{
-       vr->advertisement_interval = advertisement_interval;
-       vr->master_adver_interval = master_adver_interval;
-       vr->skew_time = ((256 - vr->priority) * master_adver_interval) / 256;
-       vr->master_down_interval = (3 * master_adver_interval);
-       vr->master_down_interval += vr->skew_time;
-}
-
-/*
+ * r
+ *   VRRP Router to operate on
  */
-static void vrrp_reset_times(struct vrrp_vrouter *vr)
+static void vrrp_recalculate_timers(struct vrrp_router *r)
 {
-       vrrp_update_times(vr, vr->advertisement_interval, 0);
+       r->skew_time =
+               ((256 - r->vr->priority) * r->master_adver_interval) / 256;
+       r->master_down_interval = (3 * r->master_adver_interval);
+       r->master_down_interval += r->skew_time;
 }
 
 /*
  * Determines if a VRRP router is the owner of the specified address.
  *
  * vr
- *    VRRP router
+ *    Virtual Router
  *
  * Returns:
  *    whether or not vr owns the specified address
@@ -139,10 +123,10 @@ static bool vrrp_is_owner(struct vrrp_vrouter *vr, struct ipaddr *addr)
 
 void vrrp_set_priority(struct vrrp_vrouter *vr, uint8_t priority)
 {
-       if (vr->priority_conf == priority)
+       if (vr->priority == priority)
                return;
 
-       vr->priority_conf = priority;
+       vr->priority = priority;
 }
 
 void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr,
@@ -151,39 +135,80 @@ void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr,
        if (vr->advertisement_interval == advertisement_interval)
                return;
 
-       vrrp_update_times(vr, advertisement_interval, vr->master_adver_interval);
+       vr->advertisement_interval = advertisement_interval;
+       vrrp_recalculate_timers(vr->v4);
+       vrrp_recalculate_timers(vr->v6);
+}
+
+void vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4)
+{
+       struct ipaddr *v4_ins = XCALLOC(MTYPE_TMP, sizeof(struct ipaddr));
+
+       v4_ins->ipa_type = IPADDR_V4;
+       v4_ins->ipaddr_v4 = v4;
+       listnode_add(vr->v4->addrs, v4_ins);
 }
 
-void vrrp_add_ip(struct vrrp_vrouter *vr, struct in_addr v4)
+void vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6)
 {
-       struct in_addr *v4_ins = XCALLOC(MTYPE_TMP, sizeof(struct in_addr));
+       struct ipaddr *v6_ins = XCALLOC(MTYPE_TMP, sizeof(struct ipaddr));
 
-       *v4_ins = v4;
-       listnode_add(vr->v4, v4_ins);
+       v6_ins->ipa_type = IPADDR_V6;
+       memcpy(&v6_ins->ipaddr_v6, &v6, sizeof(struct in6_addr));
+       listnode_add(vr->v6->addrs, v6_ins);
+}
+
+void vrrp_add_ip(struct vrrp_vrouter *vr, struct ipaddr ip)
+{
+       if (ip.ipa_type == IPADDR_V4)
+               vrrp_add_ipv4(vr, ip.ipaddr_v4);
+       else if (ip.ipa_type == IPADDR_V6)
+               vrrp_add_ipv6(vr, ip.ipaddr_v6);
 }
 
 
 /* Creation and destruction ------------------------------------------------ */
 
+static struct vrrp_router *vrrp_router_create(struct vrrp_vrouter *vr,
+                                             int family)
+{
+       struct vrrp_router *r = XCALLOC(MTYPE_TMP, sizeof(struct vrrp_router));
+
+       r->sock = -1;
+       r->family = family;
+       r->vr = vr;
+       r->addrs = list_new();
+       r->priority = vr->priority;
+       r->fsm.state = VRRP_STATE_INITIALIZE;
+       vrrp_mac_set(&r->vmac, family == AF_INET6, vr->vrid);
+
+       return r;
+}
+
+static void vrrp_router_destroy(struct vrrp_router *r)
+{
+       if (r->sock >= 0)
+               close(r->sock);
+       /* FIXME: also delete list elements */
+       list_delete(&r->addrs);
+       XFREE(MTYPE_TMP, r);
+}
+
 struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid)
 {
        struct vrrp_vrouter *vr =
                XCALLOC(MTYPE_TMP, sizeof(struct vrrp_vrouter));
 
-       vr->sock = -1;
        vr->ifp = ifp;
        vr->vrid = vrid;
-       vr->v4 = list_new();
-       vr->v6 = list_new();
-       vr->priority_conf = VRRP_DEFAULT_PRIORITY;
        vr->priority = VRRP_DEFAULT_PRIORITY;
        vr->preempt_mode = true;
        vr->accept_mode = false;
-       vrrp_mac_set(&vr->vr_mac_v4, false, vrid);
-       vrrp_mac_set(&vr->vr_mac_v6, true, vrid);
-       vr->fsm.state = VRRP_STATE_INITIALIZE;
+
+       vr->v4 = vrrp_router_create(vr, AF_INET);
+       vr->v6 = vrrp_router_create(vr, AF_INET6);
+
        vrrp_set_advertisement_interval(vr, VRRP_DEFAULT_ADVINT);
-       vrrp_reset_times(vr);
 
        hash_get(vrrp_vrouters_hash, vr, hash_alloc_intern);
 
@@ -192,11 +217,9 @@ struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid)
 
 void vrrp_vrouter_destroy(struct vrrp_vrouter *vr)
 {
-       if (vr->sock >= 0)
-               close(vr->sock);
        vr->ifp = NULL;
-       list_delete(&vr->v4);
-       list_delete(&vr->v6);
+       vrrp_router_destroy(vr->v4);
+       vrrp_router_destroy(vr->v6);
        hash_release(vrrp_vrouters_hash, vr);
        XFREE(MTYPE_TMP, vr);
 }
@@ -214,39 +237,38 @@ struct vrrp_vrouter *vrrp_lookup(uint8_t vrid)
 /*
  * Create and broadcast VRRP ADVERTISEMENT message.
  *
- * vr
- *    Virtual Router for which to send ADVERTISEMENT
+ * r
+ *    VRRP Router for which to send ADVERTISEMENT
  */
-static void vrrp_send_advertisement(struct vrrp_vrouter *vr)
+static void vrrp_send_advertisement(struct vrrp_router *r)
 {
        struct vrrp_pkt *pkt;
        ssize_t pktlen;
-       struct in_addr *v4[vr->v4->count];
-       struct in6_addr *v6[vr->v6->count];
-       struct sockaddr_in dest;
+       struct ipaddr *addrs[r->addrs->count];
+       union sockunion dest;
 
-       list_to_array(vr->v4, (void **)v4, vr->v4->count);
-       list_to_array(vr->v6, (void **)v6, vr->v6->count);
+       list_to_array(r->addrs, (void **)addrs, r->addrs->count);
 
-       pktlen = vrrp_pkt_build(&pkt, vr->vrid, vr->priority,
-                               vr->advertisement_interval, false,
-                               vr->v4->count, (void **)&v4);
+       pktlen = vrrp_pkt_build(&pkt, r->vr->vrid, r->priority,
+                               r->vr->advertisement_interval, r->addrs->count,
+                               (struct ipaddr **)&addrs);
 
        if (pktlen > 0)
                zlog_hexdump(pkt, (size_t) pktlen);
        else
                zlog_warn("Could not build VRRP packet");
 
-       dest.sin_family = AF_INET;
-       dest.sin_addr.s_addr = htonl(VRRP_MCAST_GROUP_HEX);
+       const char *group =
+               r->family == AF_INET ? VRRP_MCASTV4_GROUP_STR : VRRP_MCASTV6_GROUP_STR;
+       str2sockunion(group, &dest);
 
-       ssize_t sent = sendto(vr->sock, pkt, (size_t)pktlen, 0, &dest,
-                             sizeof(struct sockaddr_in));
+       ssize_t sent = sendto(r->sock, pkt, (size_t)pktlen, 0, &dest.sa,
+                             sockunion_sizeof(&dest));
 
        if (sent < 0) {
                zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
                          "Failed to send VRRP Advertisement",
-                         vr->vrid);
+                         r->vr->vrid);
        }
 }
 
@@ -262,46 +284,73 @@ static void vrrp_recv_advertisement(struct thread *thread)
  * The first connected address on the Virtual Router's interface is used as the
  * interface address.
  *
- * vr
- *    Virtual Router for which to create listen socket
+ * r
+ *    VRRP Router for which to create listen socket
  */
-static int vrrp_socket(struct vrrp_vrouter *vr)
+static int vrrp_socket(struct vrrp_router *r)
 {
-       struct ip_mreqn req;
        int ret;
        struct connected *c;
 
        frr_elevate_privs(&vrrp_privs) {
-               vr->sock = socket(AF_INET, SOCK_RAW, IPPROTO_VRRP);
+               r->sock = socket(r->family, SOCK_RAW, IPPROTO_VRRP);
        }
 
-       if (vr->sock < 0) {
+       if (r->sock < 0) {
                zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
-                         "Can't create VRRP socket",
-                         vr->vrid);
+                         "Can't create %s VRRP socket",
+                         r->vr->vrid, r->family == AF_INET ? "v4" : "v6");
                return errno;
        }
 
-       /* Join the multicast group.*/
+       /*
+        * Configure V4 minimum TTL or V6 minimum Hop Limit for rx; packets not
+        * having at least these values will be dropped
+        *
+        * RFC 5798 5.1.1.3:
+        * 
+        *    The TTL MUST be set to 255.  A VRRP router receiving a packet
+        *    with the TTL not equal to 255 MUST discard the packet.
+        *
+        * RFC 5798 5.1.2.3:
+        *
+        *    The Hop Limit MUST be set to 255.  A VRRP router receiving a
+        *    packet with the Hop Limit not equal to 255 MUST discard the
+        *    packet.
+        */
+       sockopt_minttl(r->family, r->sock, 255);
 
-        /* FIXME: Use first address on the interface and for imr_interface */
-       if (!listcount(vr->ifp->connected))
+       if (!listcount(r->vr->ifp->connected)) {
+               zlog_warn(
+                       VRRP_LOGPFX VRRP_LOGPFX_VRID
+                       "No address on interface %s; cannot configure multicast",
+                       r->vr->vrid, r->vr->ifp->name);
+               close(r->sock);
                return -1;
+       }
 
-       c = listhead(vr->ifp->connected)->data;
+       c = listhead(r->vr->ifp->connected)->data;
        struct in_addr v4 = c->address->u.prefix4;
 
-       memset(&req, 0, sizeof(req));
-       req.imr_multiaddr.s_addr = htonl(VRRP_MCAST_GROUP_HEX);
-       req.imr_address = v4;
-       req.imr_ifindex = 0; // FIXME: vr->ifp->ifindex ?
-       ret = setsockopt(vr->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&req,
-                        sizeof(struct ip_mreq));
+       /* Join the multicast group.*/
+       if (r->family == AF_INET)
+               ret = setsockopt_ipv4_multicast(r->sock, IP_ADD_MEMBERSHIP, v4,
+                                               htonl(VRRP_MCASTV4_GROUP),
+                                               r->vr->ifp->ifindex);
+       else if (r->family == AF_INET6) {
+               struct ipv6_mreq mreq;
+               inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &mreq.ipv6mr_multiaddr);
+               mreq.ipv6mr_interface = r->vr->ifp->ifindex;
+               ret = setsockopt(r->sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq,
+                          sizeof(mreq));
+       }
+
        if (ret < 0) {
                zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
                          "Can't join VRRP multicast group",
-                         vr->vrid);
-               return errno;
+                         r->vr->vrid);
+               close(r->sock);
+               return -1;
        }
        return 0;
 }
@@ -309,27 +358,28 @@ static int vrrp_socket(struct vrrp_vrouter *vr)
 
 /* State machine ----------------------------------------------------------- */
 
-DEFINE_HOOK(vrrp_change_state_hook, (struct vrrp_vrouter *vr, int to), (vr, to));
+DEFINE_HOOK(vrrp_change_state_hook, (struct vrrp_router * r, int to), (r, to));
 
 /*
  * Handle any necessary actions during state change to MASTER state.
  *
- * vr
- *    Virtual Router to operate on
+ * r
+ *    VRRP Router to operate on
  */
-static void vrrp_change_state_master(struct vrrp_vrouter *vr)
+static void vrrp_change_state_master(struct vrrp_router *r)
 {
+       /* NOTHING */
 }
 
 /*
  * Handle any necessary actions during state change to BACKUP state.
  *
- * vr
+ * r
  *    Virtual Router to operate on
  */
-static void vrrp_change_state_backup(struct vrrp_vrouter *vr)
+static void vrrp_change_state_backup(struct vrrp_router *r)
 {
-       /* Uninstall ARP entry for vrouter MAC */
+       /* Uninstall ARP entry for router MAC */
        /* ... */
 }
 
@@ -339,16 +389,17 @@ static void vrrp_change_state_backup(struct vrrp_vrouter *vr)
  * This is not called for initial startup, only when transitioning from MASTER
  * or BACKUP.
  *
- * vr
- *    Virtual Router to operate on
+ * r
+ *    VRRP Router to operate on
  */
-static void vrrp_change_state_initialize(struct vrrp_vrouter *vr)
+static void vrrp_change_state_initialize(struct vrrp_router *r)
 {
-       /* Reset timers */
-       vrrp_reset_times(vr);
+       r->vr->advertisement_interval = r->vr->advertisement_interval;
+       r->master_adver_interval = 0;
+       vrrp_recalculate_timers(r);
 }
 
-void (*vrrp_change_state_handlers[])(struct vrrp_vrouter *vr) = {
+void (*vrrp_change_state_handlers[])(struct vrrp_router *vr) = {
        [VRRP_STATE_MASTER] = vrrp_change_state_master,
        [VRRP_STATE_BACKUP] = vrrp_change_state_backup,
        [VRRP_STATE_INITIALIZE] = vrrp_change_state_initialize,
@@ -358,20 +409,20 @@ void (*vrrp_change_state_handlers[])(struct vrrp_vrouter *vr) = {
  * Change Virtual Router FSM position. Handles transitional actions and calls
  * any subscribers to the state change hook.
  *
- * vr
+ * r
  *    Virtual Router for which to change state
  *
  * to
  *    State to change to
  */
-static void vrrp_change_state(struct vrrp_vrouter *vr, int to)
+static void vrrp_change_state(struct vrrp_router *r, int to)
 {
        /* Call our handlers, then any subscribers */
-       vrrp_change_state_handlers[to](vr);
-       hook_call(vrrp_change_state_hook, vr, to);
-       zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "%s -> %s", vr->vrid,
-                 vrrp_state_names[vr->fsm.state], vrrp_state_names[to]);
-       vr->fsm.state = to;
+       vrrp_change_state_handlers[to](r);
+       hook_call(vrrp_change_state_hook, r, to);
+       zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "%s -> %s", r->vr->vrid,
+                 vrrp_state_names[r->fsm.state], vrrp_state_names[to]);
+       r->fsm.state = to;
 }
 
 /*
@@ -379,22 +430,23 @@ static void vrrp_change_state(struct vrrp_vrouter *vr, int to)
  */
 static int vrrp_adver_timer_expire(struct thread *thread)
 {
-       struct vrrp_vrouter *vr = thread->arg;
+       struct vrrp_router *r = thread->arg;
 
-       zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Adver_Timer expired", vr->vrid);
+       zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Adver_Timer expired",
+                 r->vr->vrid);
 
-       if (vr->fsm.state == VRRP_STATE_MASTER) {
+       if (r->fsm.state == VRRP_STATE_MASTER) {
                /* Send an ADVERTISEMENT */
-               vrrp_send_advertisement(vr);
+               vrrp_send_advertisement(r);
 
                /* Reset the Adver_Timer to Advertisement_Interval */
-               thread_add_timer_msec(master, vrrp_adver_timer_expire, vr,
-                                     vr->advertisement_interval * 10,
-                                     &vr->t_adver_timer);
+               thread_add_timer_msec(master, vrrp_adver_timer_expire, r,
+                                     r->vr->advertisement_interval * 10,
+                                     &r->t_adver_timer);
        } else {
                zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
                          "Adver_Timer expired in state '%s'; this is a bug",
-                         vr->vrid, vrrp_state_names[vr->fsm.state]);
+                         r->vr->vrid, vrrp_state_names[r->fsm.state]);
        }
 
        return 0;
@@ -405,10 +457,10 @@ static int vrrp_adver_timer_expire(struct thread *thread)
  */
 static int vrrp_master_down_timer_expire(struct thread *thread)
 {
-       struct vrrp_vrouter *vr = thread->arg;
+       struct vrrp_router *r = thread->arg;
 
        zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Master_Down_Timer expired",
-                 vr->vrid);
+                 r->vr->vrid);
 
        return 0;
 }
@@ -422,69 +474,71 @@ static int vrrp_master_down_timer_expire(struct thread *thread)
  * This function will also initialize the program's global ARP subsystem if it
  * has not yet been initialized.
  *
- * vr
- *    Virtual Router on which to apply Startup event
+ * r
+ *    VRRP Router on which to apply Startup event
  *
  * Returns:
  *    < 0 if the session socket could not be created, or the state is not
  *        Initialize
  *      0 on success
  */
-static int vrrp_startup(struct vrrp_vrouter *vr)
+static int vrrp_startup(struct vrrp_router *r)
 {
        /* May only be called when the state is Initialize */
-       if (vr->fsm.state != VRRP_STATE_INITIALIZE)
+       if (r->fsm.state != VRRP_STATE_INITIALIZE)
                return -1;
 
        /* Initialize global gratuitous ARP socket if necessary */
-       if (!vrrp_garp_is_init())
+       if (r->family == AF_INET && !vrrp_garp_is_init())
                vrrp_garp_init();
 
        /* Create socket */
-       int ret = vrrp_socket(vr);
-       if (ret < 0)
-               return ret;
+       if (r->sock < 0) {
+               int ret = vrrp_socket(r);
+               if (ret < 0)
+                       return ret;
+       }
 
        /* Schedule listener */
        /* ... */
 
        /* configure effective priority */
-       struct in_addr *primary = (struct in_addr *) listhead(vr->v4)->data;
-       struct ipaddr primary_ipaddr;
-       primary_ipaddr.ipa_type = IPADDR_V4;
-       primary_ipaddr.ipaddr_v4 = *primary;
-
-       char ipbuf[INET_ADDRSTRLEN];
-       inet_ntop(AF_INET, primary, ipbuf, sizeof(ipbuf));
-
-       if (vrrp_is_owner(vr, &primary_ipaddr)) {
-               vr->priority = VRRP_PRIO_MASTER;
-               /* Timers depend on priority value, need to recalculate them */
-               vrrp_update_times(vr, vr->advertisement_interval,
-                                 vr->master_adver_interval);
+       struct ipaddr *primary = (struct ipaddr *)listhead(r->addrs)->data;
+
+       char ipbuf[INET6_ADDRSTRLEN];
+       inet_ntop(r->family, &primary->ip.addr, ipbuf, sizeof(ipbuf));
+
+       if (vrrp_is_owner(r->vr, primary)) {
+               r->priority = VRRP_PRIO_MASTER;
+               vrrp_recalculate_timers(r);
+
                zlog_info(
                        VRRP_LOGPFX VRRP_LOGPFX_VRID
                        "%s owns primary Virtual Router IP %s; electing self as Master",
-                       vr->vrid, vr->ifp->name, ipbuf);
+                       r->vr->vrid, r->vr->ifp->name, ipbuf);
        }
 
-       if (vr->priority == VRRP_PRIO_MASTER) {
-               vrrp_send_advertisement(vr);
-               vrrp_garp_send_all(vr);
+       if (r->priority == VRRP_PRIO_MASTER) {
+               vrrp_send_advertisement(r);
 
-               thread_add_timer_msec(master, vrrp_adver_timer_expire, vr,
-                                     vr->advertisement_interval * 10,
-                                     &vr->t_adver_timer);
-               vrrp_change_state(vr, VRRP_STATE_MASTER);
+               if (r->family == AF_INET)
+                       vrrp_garp_send_all(r);
+
+               thread_add_timer_msec(master, vrrp_adver_timer_expire, r,
+                                     r->vr->advertisement_interval * 10,
+                                     &r->t_adver_timer);
+               vrrp_change_state(r, VRRP_STATE_MASTER);
        } else {
-               vrrp_update_times(vr, vr->advertisement_interval,
-                                 vr->advertisement_interval);
-               thread_add_timer_msec(master, vrrp_master_down_timer_expire, vr,
-                                     vr->master_down_interval * 10,
-                                     &vr->t_master_down_timer);
-               vrrp_change_state(vr, VRRP_STATE_BACKUP);
+               r->master_adver_interval = r->vr->advertisement_interval;
+               vrrp_recalculate_timers(r);
+               thread_add_timer_msec(master, vrrp_master_down_timer_expire, r,
+                                     r->master_down_interval * 10,
+                                     &r->t_master_down_timer);
+               vrrp_change_state(r, VRRP_STATE_BACKUP);
        }
 
+       r->is_active = true;
+
        return 0;
 }
 
@@ -492,62 +546,57 @@ static int vrrp_startup(struct vrrp_vrouter *vr)
  * Shuts down a Virtual Router and transitions it to Initialize.
  *
  * This call must be idempotent; it is safe to call multiple times on the same
- * Virtual Router.
+ * VRRP Router.
  */
-static int vrrp_shutdown(struct vrrp_vrouter *vr)
+static int vrrp_shutdown(struct vrrp_router *r)
 {
-       switch (vr->fsm.state) {
-               case VRRP_STATE_MASTER:
-                       /* Cancel the Adver_Timer */
-                       THREAD_OFF(vr->t_adver_timer);
-                       /* Send an ADVERTISEMENT with Priority = 0 */
-                       uint8_t saved_prio = vr->priority;
-                       vr->priority = 0;
-                       vrrp_send_advertisement(vr);
-                       vr->priority = saved_prio;
-                       break;
-               case VRRP_STATE_BACKUP:
-                       /* Cancel the Master_Down_Timer */
-                       THREAD_OFF(vr->t_master_down_timer);
-                       break;
-               case VRRP_STATE_INITIALIZE:
-                       zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID
-                                 "Received '%s' event in '%s' state; ignoring",
-                                 vr->vrid,
-                                 vrrp_event_names[VRRP_EVENT_SHUTDOWN],
-                                 vrrp_state_names[VRRP_STATE_INITIALIZE]);
-                       break;
+       switch (r->fsm.state) {
+       case VRRP_STATE_MASTER:
+               /* Cancel the Adver_Timer */
+               THREAD_OFF(r->t_adver_timer);
+               /* Send an ADVERTISEMENT with Priority = 0 */
+               uint8_t saved_prio = r->priority;
+               r->priority = 0;
+               vrrp_send_advertisement(r);
+               r->priority = saved_prio;
+               break;
+       case VRRP_STATE_BACKUP:
+               /* Cancel the Master_Down_Timer */
+               THREAD_OFF(r->t_master_down_timer);
+               break;
+       case VRRP_STATE_INITIALIZE:
+               zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID
+                         "Received '%s' event in '%s' state; ignoring",
+                         r->vr->vrid, vrrp_event_names[VRRP_EVENT_SHUTDOWN],
+                         vrrp_state_names[VRRP_STATE_INITIALIZE]);
+               break;
        }
 
-       /* close socket */
-       if (vr->sock >= 0)
-               close(vr->sock);
-
        /* Transition to the Initialize state */
-       vrrp_change_state(vr, VRRP_STATE_INITIALIZE);
+       vrrp_change_state(r, VRRP_STATE_INITIALIZE);
 
        return 0;
 }
 
-static int (*vrrp_event_handlers[])(struct vrrp_vrouter *vr) = {
+static int (*vrrp_event_handlers[])(struct vrrp_router *r) = {
        [VRRP_EVENT_STARTUP] = vrrp_startup,
        [VRRP_EVENT_SHUTDOWN] = vrrp_shutdown,
 };
 
 /*
- * Spawn a VRRP FSM event on a Virtual Router.
+ * Spawn a VRRP FSM event on a VRRP Router.
  *
  * vr
- *    Virtual Router on which to spawn event
+ *    VRRP Router on which to spawn event
  *
  * event
  *    The event to spawn
  */
-int vrrp_event(struct vrrp_vrouter *vr, int event)
+int vrrp_event(struct vrrp_router *r, int event)
 {
-       zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "'%s' event", vr->vrid,
-                 vrrp_event_names[vr->fsm.state]);
-       return vrrp_event_handlers[event](vr);
+       zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "'%s' event", r->vr->vrid,
+                 vrrp_event_names[r->fsm.state]);
+       return vrrp_event_handlers[event](r);
 }
 
 
index b86aba993985f31d21a8baa8646251d4b0abc27c..12789d51085b9417d72e6e3042d8aff6026a9957 100644 (file)
 #define VRRP_DEFAULT_ADVINT 100
 #define VRRP_DEFAULT_PRIORITY 100
 #define VRRP_PRIO_MASTER 255
-#define VRRP_MCAST_GROUP "224.0.0.18"
-#define VRRP_MCAST_GROUP_HEX 0xe0000012
+#define VRRP_MCASTV4_GROUP_STR "224.0.0.18"
+#define VRRP_MCASTV6_GROUP_STR "ff02:0:0:0:0:0:0:12"
+#define VRRP_MCASTV4_GROUP 0xe0000012
+#define VRRP_MCASTV6_GROUP 0xff020000000000000000000000000012
 #define IPPROTO_VRRP 112
 
 #define VRRP_LOGPFX_VRID "[VRID: %u] "
@@ -49,43 +51,48 @@ extern struct zebra_privs_t vrrp_privs;
 struct hash *vrrp_vrouters_hash;
 
 /*
- * VRRP Virtual Router
+ * VRRP Router.
+ *
+ * This struct contains all state for a particular VRRP Router operating in a
+ * Virtual Router for either IPv4 or IPv6.
  */
-struct vrrp_vrouter {
+struct vrrp_router {
+       /*
+        * Whether this VRRP Router is active.
+        */
+       bool is_active;
+
        /* Socket */
        int sock;
 
-       /* Interface */
-       struct interface *ifp;
-
-       /* Virtual Router Identifier */
-       uint32_t vrid;
-
-       /* One or more IPv4 addresses associated with this Virtual Router. */
-       struct list *v4;
+       /*
+        * Address family of this Virtual Router.
+        * Either AF_INET or AF_INET6.
+        */
+       int family;
 
        /*
-        * One ore more IPv6 addresses associated with this Virtual Router. The
-        * first address must be the Link-Local address associated with the
-        * virtual router.
+        * Virtual Router this VRRP Router is participating in.
         */
-       struct list *v6;
+       struct vrrp_vrouter *vr;
 
-       /* Configured priority */
-       uint8_t priority_conf;
+       /*
+        * One or more IPvX addresses associated with this Virtual
+        * Router. The first address must be the "primary" address this
+        * Virtual Router is backing up in the case of IPv4. In the case of
+        * IPv6 it must be the link-local address of vr->ifp.
+        *
+        * Type: struct ipaddr *
+        */
+       struct list *addrs;
 
        /*
         * Effective priority
-        *    => priority if we are Backup
+        *    => vr->priority if we are Backup
         *    => 255 if we are Master
         */
        uint8_t priority;
 
-       /*
-        * Time interval between ADVERTISEMENTS (centiseconds). Default is 100
-        * centiseconds (1 second).
-        */
-       uint16_t advertisement_interval;
        /*
         * Advertisement interval contained in ADVERTISEMENTS received from the
         * Master (centiseconds)
@@ -105,6 +112,54 @@ struct vrrp_vrouter {
         */
        uint16_t master_down_interval;
 
+       /*
+        * The MAC address used for the source MAC address in VRRP
+        * advertisements, advertised in ARP requests/responses, and advertised
+        * in ND Neighbor Advertisements.
+        */
+       struct ethaddr vmac;
+
+       struct {
+               int state;
+       } fsm;
+
+       struct thread *t_master_down_timer;
+       struct thread *t_adver_timer;
+};
+
+/*
+ * VRRP Virtual Router.
+ *
+ * This struct contains all state and configuration for a given Virtual Router
+ * Identifier on a given interface, both v4 and v6.
+ *
+ * RFC5798 s. 1 states:
+ *    "Within a VRRP router, the virtual routers in each of the IPv4 and IPv6
+ *    address families are a domain unto themselves and do not overlap."
+ *
+ * This implementation has chosen the tuple (interface, VRID) as the key for a
+ * particular VRRP Router, and the rest of the program is designed around this
+ * assumption. Additionally, base protocol configuration parameters such as the
+ * advertisement interval and (configured) priority are shared between v4 and
+ * v6 instances. This corresponds to the choice made by other industrial
+ * implementations.
+ */
+struct vrrp_vrouter {
+       /* Interface */
+       struct interface *ifp;
+
+       /* Virtual Router Identifier */
+       uint32_t vrid;
+
+       /* Configured priority */
+       uint8_t priority;
+
+       /*
+        * Time interval between ADVERTISEMENTS (centiseconds). Default is 100
+        * centiseconds (1 second).
+        */
+       uint16_t advertisement_interval;
+
        /*
         * Controls whether a (starting or restarting) higher-priority Backup
         * router preempts a lower-priority Master router. Values are True to
@@ -119,20 +174,8 @@ struct vrrp_vrouter {
         */
        bool accept_mode;
 
-       /*
-        * The MAC address used for the source MAC address in VRRP
-        * advertisements and advertised in ARP responses as the MAC address to
-        * use for IP_Addresses.
-        */
-       struct ethaddr vr_mac_v4;
-       struct ethaddr vr_mac_v6;
-
-       struct thread *t_master_down_timer;
-       struct thread *t_adver_timer;
-
-       struct {
-               int state;
-       } fsm;
+       struct vrrp_router *v4;
+       struct vrrp_router *v6;
 };
 
 /*
@@ -191,7 +234,18 @@ void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr,
                                     uint16_t advertisement_interval);
 
 /*
- * Add IPv4 address to a VRRP Virtual Router.
+ * Add an IPvX address to a VRRP Virtual Router.
+ *
+ * vr
+ *    Virtual Router to add IPvx address to
+ *
+ * ip
+ *    Address to add
+ */
+void vrrp_add_ip(struct vrrp_vrouter *vr, struct ipaddr ip);
+
+/*
+ * Add an IPv4 address to a VRRP Virtual Router.
  *
  * vr
  *    Virtual Router to add IPv4 address to
@@ -199,7 +253,18 @@ void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr,
  * v4
  *    Address to add
  */
-void vrrp_add_ip(struct vrrp_vrouter *vr, struct in_addr v4);
+void vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4);
+
+/*
+ * Add an IPv6 address to a VRRP Virtual Router.
+ *
+ * vr
+ *    Virtual Router to add IPv6 address to
+ *
+ * v6
+ *    Address to add
+ */
+void vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6);
 
 
 /* State machine ----------------------------------------------------------- */
@@ -220,7 +285,7 @@ extern const char *vrrp_event_names[2];
  * Use this if you need to react to state changes to perform non-critical
  * tasks. Critical tasks should go in the internal state change handlers.
  */
-DECLARE_HOOK(vrrp_change_state_hook, (struct vrrp_vrouter *vr, int to), (vr, to));
+DECLARE_HOOK(vrrp_change_state_hook, (struct vrrp_router * r, int to), (r, to));
 
 /*
  * Trigger a VRRP event on a given Virtual Router..
@@ -236,7 +301,7 @@ DECLARE_HOOK(vrrp_change_state_hook, (struct vrrp_vrouter *vr, int to), (vr, to)
  *    < 0 if the event created an error
  *      0 otherwise
  */
-int vrrp_event(struct vrrp_vrouter *vr, int event);
+int vrrp_event(struct vrrp_router *r, int event);
 
 
 /* Other ------------------------------------------------------------------- */
index 45c4071875f29c4fac99371f3b5f47f7b12ec8eb..95e9de86a73aa1035faaa0b799f275d06ca2a637 100644 (file)
@@ -112,9 +112,9 @@ static ssize_t vrrp_build_garp(uint8_t *buf, struct interface *ifp,
        return arp_ptr - buf;
 }
 
-void vrrp_garp_send(struct vrrp_vrouter *vr, struct in_addr *v4)
+void vrrp_garp_send(struct vrrp_router *r, struct in_addr *v4)
 {
-       struct interface *ifp = vr->ifp;
+       struct interface *ifp = r->vr->ifp;
        uint8_t garpbuf[GARP_BUFFER_SIZE];
        ssize_t garpbuf_len;
        ssize_t sent_len;
@@ -125,7 +125,7 @@ void vrrp_garp_send(struct vrrp_vrouter *vr, struct in_addr *v4)
                zlog_warn(
                        VRRP_LOGPFX VRRP_LOGPFX_VRID
                        "Unable to send gratuitous ARP on %s; has IFF_NOARP\n",
-                       vr->vrid, ifp->name);
+                       r->vr->vrid, ifp->name);
                return;
        }
 
@@ -136,33 +136,35 @@ void vrrp_garp_send(struct vrrp_vrouter *vr, struct in_addr *v4)
        inet_ntop(AF_INET, v4, astr, sizeof(astr));
        zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID
                  "Sending gratuitous ARP on %s for %s",
-                 vr->vrid, ifp->name, astr);
+                 r->vr->vrid, ifp->name, astr);
        sent_len = vrrp_send_garp(ifp, garpbuf, garpbuf_len);
 
        if (sent_len < 0)
                zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID
                          "Error sending gratuitous ARP on %s for %s",
-                         vr->vrid, ifp->name, astr);
+                         r->vr->vrid, ifp->name, astr);
 }
 
-void vrrp_garp_send_all(struct vrrp_vrouter *vr)
+void vrrp_garp_send_all(struct vrrp_router *r)
 {
-       struct interface *ifp = vr->ifp;
+       assert(r->family == AF_INET);
+
+       struct interface *ifp = r->vr->ifp;
 
        /* If the interface doesn't support ARP, don't try sending */
        if (ifp->flags & IFF_NOARP) {
                zlog_warn(
                        VRRP_LOGPFX VRRP_LOGPFX_VRID
                        "Unable to send gratuitous ARP on %s; has IFF_NOARP\n",
-                       vr->vrid, ifp->name);
+                       r->vr->vrid, ifp->name);
                return;
        }
 
        struct listnode *ln;
-       struct in_addr *v4;
+       struct ipaddr *ip;
 
-       for (ALL_LIST_ELEMENTS_RO(vr->v4, ln, v4))
-               vrrp_garp_send(vr, v4);
+       for (ALL_LIST_ELEMENTS_RO(r->addrs, ln, ip))
+               vrrp_garp_send(r, &ip->ipaddr_v4);
 }
 
 
index 57223835ef2dc9ef578c75e0569f63aab39061fe..1de94a15191b42272366d1df04ad452f8b992548 100644 (file)
@@ -31,6 +31,6 @@
 extern void vrrp_garp_init(void);
 extern void vrrp_garp_fini(void);
 extern bool vrrp_garp_is_init(void);
-extern void vrrp_garp_send(struct vrrp_vrouter *vr, struct in_addr *v4);
-extern void vrrp_garp_send_all(struct vrrp_vrouter *vr);
+extern void vrrp_garp_send(struct vrrp_router *vr, struct in_addr *v4);
+extern void vrrp_garp_send_all(struct vrrp_router *vr);
 #endif
index 608be06a0c98416fb40600ca392a9dcce197c38b..631b8d786553ae6e932b3fbca61cc077153a9357 100644 (file)
 #include "vrrp_packet.h"
 
 ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, uint8_t vrid, uint8_t prio,
-                      uint16_t max_adver_int, bool v6, uint8_t numip,
-                      void **ips)
+                      uint16_t max_adver_int, uint8_t numip,
+                      struct ipaddr **ips)
 {
-       /* Used for pointer math when filling IPvX field */
-       struct in6_addr *v6ptr;
-       struct in_addr *v4ptr;
+       bool v6 = IS_IPADDR_V6(ips[0]);
 
        size_t addrsz = v6 ? sizeof(struct in6_addr) : sizeof(struct in_addr);
        size_t pktsize = sizeof(struct vrrp_hdr) + addrsz * numip;
        *pkt = XCALLOC(MTYPE_TMP, pktsize);
 
-       v6ptr = (struct in6_addr *) (*pkt)->addrs;
-       v4ptr = (struct in_addr *) (*pkt)->addrs;
-
        (*pkt)->hdr.vertype |= VRRP_VERSION << 4;
        (*pkt)->hdr.vertype |= VRRP_TYPE_ADVERTISEMENT;
        (*pkt)->hdr.vrid = vrid;
@@ -47,15 +42,11 @@ ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, uint8_t vrid, uint8_t prio,
        (*pkt)->hdr.naddr = numip;
        (*pkt)->hdr.v3.adver_int = htons(max_adver_int);
 
-       char buf[INET_ADDRSTRLEN];
+       uint8_t *aptr = (void *)(*pkt)->addrs;
+
        for (int i = 0; i < numip; i++) {
-               /* If v4, treat as array of v4 addresses */
-               inet_ntop(AF_INET, ips[i], buf, sizeof(buf));
-               if (!v6)
-                       memcpy(&v4ptr[i], ips[i], addrsz);
-               else
-                       memcpy(&v6ptr[i], ips[i], addrsz);
-               inet_ntop(AF_INET, &v4ptr[i], buf, sizeof(buf));
+               memcpy(aptr, &ips[i]->ip.addr, addrsz);
+               aptr += addrsz;
        }
        (*pkt)->hdr.chksum = 0;
 
index 245fada0641879d5cc44682171e04718750184bb..8d8d240bec6cb384bc45fe4841610bd878f68400 100644 (file)
@@ -92,5 +92,5 @@ struct vrrp_pkt {
  *    (v6 = true)
  */
 ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, uint8_t vrid, uint8_t prio,
-                      uint16_t max_adver_int, bool v6, uint8_t numip,
-                      void **ips);
+                      uint16_t max_adver_int, uint8_t numip,
+                      struct ipaddr **ips);
index 7da6646999aaded88c66e55a83845ed688ca0bfa..1d01b920b16f2791a8c7826344c8f7808ee8349e 100644 (file)
  */
 #include <zebra.h>
 
-#include "command.h"
-#include "vty.h"
-#include "if.h"
-#include "termtable.h"
-#include "prefix.h"
+#include "lib/command.h"
+#include "lib/if.h"
+#include "lib/ipaddr.h"
+#include "lib/prefix.h"
+#include "lib/termtable.h"
+#include "lib/vty.h"
 
 #include "vrrp.h"
 #include "vrrp_vty.h"
@@ -85,27 +86,36 @@ DEFPY(vrrp_priority,
       "Priority value; set 255 to designate this Virtual Router as Master\n")
 {
        struct vrrp_vrouter *vr;
-       bool need_restart = false;
+       struct vrrp_router *r;
+       bool nr[2] = { false, false };
        int ret = CMD_SUCCESS;
 
        VROUTER_GET_VTY(vty, vrid, vr);
 
-       need_restart = (vr->fsm.state != VRRP_STATE_INITIALIZE);
-
-       if (need_restart) {
-               vty_out(vty,
-                       "%% WARNING: Restarting Virtual Router %ld to update priority\n",
-                       vrid);
-               (void) vrrp_event(vr, VRRP_EVENT_SHUTDOWN);
+       r = vr->v4;
+       for (int i = 0; i < 2; i++) {
+               nr[i] = r->is_active && r->fsm.state != VRRP_STATE_INITIALIZE;
+               if (nr[i]) {
+                       vty_out(vty,
+                               "%% WARNING: Restarting Virtual Router %ld (%s) to update priority\n",
+                               vrid, r->family == AF_INET ? "v4" : "v6");
+                       (void)vrrp_event(r, VRRP_EVENT_SHUTDOWN);
+               }
+               r = vr->v6;
        }
 
        vrrp_set_priority(vr, priority);
 
-       if (need_restart) {
-               ret = vrrp_event(vr, VRRP_EVENT_STARTUP);
-               if (ret < 0)
-                       vty_out(vty, "%% Failed to start Virtual Router %ld\n",
-                               vrid);
+       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, r->family == AF_INET ? "v4" : "v6");
+               }
+               r = vr->v6;
        }
 
        return CMD_SUCCESS;
@@ -130,22 +140,52 @@ DEFPY(vrrp_advertisement_interval,
 
 DEFPY(vrrp_ip,
       vrrp_ip_cmd,
-      "[no] vrrp (1-255)$vrid ip A.B.C.D$ip",
+      "[no] vrrp (1-255)$vrid ip A.B.C.D",
       NO_STR
       VRRP_STR
       VRRP_VRID_STR
-      "Add IP address\n"
+      "Add IPv4 address\n"
       VRRP_IP_STR)
 {
        struct vrrp_vrouter *vr;
        int ret;
 
        VROUTER_GET_VTY(vty, vrid, vr);
-       vrrp_add_ip(vr, ip);
+       vrrp_add_ipv4(vr, ip);
 
-       if (vr->fsm.state == VRRP_STATE_INITIALIZE) {
+       if (vr->v4->fsm.state == VRRP_STATE_INITIALIZE) {
                vty_out(vty, "%% Activating Virtual Router %ld\n", vrid);
-               ret = vrrp_event(vr, VRRP_EVENT_STARTUP);
+               ret = vrrp_event(vr->v4, VRRP_EVENT_STARTUP);
+               ret = ret < 0 ? CMD_WARNING_CONFIG_FAILED : CMD_SUCCESS;
+
+               if (ret == CMD_WARNING_CONFIG_FAILED)
+                       vty_out(vty, "%% Failed to start Virtual Router %ld\n",
+                               vrid);
+       } else {
+               ret = CMD_SUCCESS;
+       }
+
+       return ret;
+}
+
+DEFPY(vrrp_ip6,
+      vrrp_ip6_cmd,
+      "[no] vrrp (1-255)$vrid ipv6 X:X::X:X",
+      NO_STR
+      VRRP_STR
+      VRRP_VRID_STR
+      "Add IPv6 address\n"
+      VRRP_IP_STR)
+{
+       struct vrrp_vrouter *vr;
+       int ret;
+
+       VROUTER_GET_VTY(vty, vrid, vr);
+       vrrp_add_ipv6(vr, ipv6);
+
+       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;
 
                if (ret == CMD_WARNING_CONFIG_FAILED)
@@ -160,43 +200,69 @@ DEFPY(vrrp_ip,
 
 static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr)
 {
-       char ethstr[ETHER_ADDR_STRLEN];
-       char ipstr[INET_ADDRSTRLEN];
-       const char *stastr = vrrp_state_names[vr->fsm.state];
+       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];
+       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);
-       prefix_mac2str(&vr->vr_mac_v4, ethstr, sizeof(ethstr));
-       ttable_add_row(tt, "%s|%s", "Virtual MAC", ethstr);
-       ttable_add_row(tt, "%s|%s", "Status", stastr);
+       prefix_mac2str(&vr->v4->vmac, ethstr4, sizeof(ethstr4));
+       prefix_mac2str(&vr->v6->vmac, ethstr6, sizeof(ethstr6));
+       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);
+       ttable_add_row(tt, "%s|%s", "Status (v6)", stastr6);
        ttable_add_row(tt, "%s|%" PRIu8, "Priority", vr->priority);
+       ttable_add_row(tt, "%s|%" PRIu8, "Effective Priority (v4)",
+                      vr->v4->priority);
+       ttable_add_row(tt, "%s|%" PRIu8, "Effective Priority (v6)",
+                      vr->v6->priority);
        ttable_add_row(tt, "%s|%s", "Preempt Mode",
                       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",
-                      vr->master_adver_interval);
-       ttable_add_row(tt, "%s|%" PRIu16" cs", "Skew Time", vr->skew_time);
-       ttable_add_row(tt, "%s|%" PRIu16" cs", "Master Down Interval",
-                      vr->master_down_interval);
-       ttable_add_row(tt, "%s|%u", "IPv4 Addresses", vr->v4->count);
+       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|%u", "IPv4 Addresses", vr->v4->addrs->count);
+       ttable_add_row(tt, "%s|%u", "IPv6 Addresses", vr->v6->addrs->count);
 
        char *table = ttable_dump(tt, "\n");
        vty_out(vty, "\n%s\n", table);
        XFREE(MTYPE_TMP, table);
        ttable_del(tt);
 
-       /* Dump IPv4 Addresses */
-       if (vr->v4->count) {
+       if (vr->v4->addrs->count) {
                vty_out(vty, " IPv4 Addresses\n");
                vty_out(vty, " --------------\n");
-               struct listnode *ln;
-               struct in_addr *v4;
-               for (ALL_LIST_ELEMENTS_RO(vr->v4, ln, v4)) {
-                       inet_ntop(AF_INET, v4, ipstr, sizeof(ipstr));
+               for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ln, ip)) {
+                       inet_ntop(vr->v4->family, &ip->ipaddr_v4, ipstr,
+                                 sizeof(ipstr));
+                       vty_out(vty, " %s\n", ipstr);
+               }
+               vty_out(vty, "\n");
+       }
+
+       if (vr->v6->addrs->count) {
+               vty_out(vty, " IPv6 Addresses\n");
+               vty_out(vty, " --------------\n");
+               for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ln, ip)) {
+                       inet_ntop(vr->v6->family, &ip->ipaddr_v6, ipstr,
+                                 sizeof(ipstr));
                        vty_out(vty, " %s\n", ipstr);
                }
                vty_out(vty, "\n");
@@ -241,4 +307,5 @@ void vrrp_vty_init(void)
        install_element(INTERFACE_NODE, &vrrp_priority_cmd);
        install_element(INTERFACE_NODE, &vrrp_advertisement_interval_cmd);
        install_element(INTERFACE_NODE, &vrrp_ip_cmd);
+       install_element(INTERFACE_NODE, &vrrp_ip6_cmd);
 }