*/
#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"
}
/*
- * 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
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,
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);
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);
}
/*
* 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);
}
}
* 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;
}
/* 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 */
/* ... */
}
* 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,
* 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;
}
/*
*/
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;
*/
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;
}
* 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;
}
* 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);
}
#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] "
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)
*/
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
*/
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;
};
/*
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
* 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 ----------------------------------------------------------- */
* 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..
* < 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 ------------------------------------------------------------------- */
*/
#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"
"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;
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)
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");
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);
}