From 862f2f374f27a541887a6dfc333583bdd4ad4de5 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 11 Dec 2018 17:55:21 +0000 Subject: [PATCH] vrrpd: ipv6 support Add initial support for IPv6. Signed-off-by: Quentin Young --- lib/sockunion.c | 2 +- lib/sockunion.h | 1 + vrrpd/vrrp.c | 417 +++++++++++++++++++++++++------------------- vrrpd/vrrp.h | 149 +++++++++++----- vrrpd/vrrp_arp.c | 24 +-- vrrpd/vrrp_arp.h | 4 +- vrrpd/vrrp_packet.c | 23 +-- vrrpd/vrrp_packet.h | 4 +- vrrpd/vrrp_vty.c | 149 +++++++++++----- 9 files changed, 474 insertions(+), 299 deletions(-) diff --git a/lib/sockunion.c b/lib/sockunion.c index bb5426d74a..5afd10eb48 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -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; diff --git a/lib/sockunion.h b/lib/sockunion.h index d7d26ba85a..b996735550 100644 --- a/lib/sockunion.h +++ b/lib/sockunion.h @@ -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); diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index bf43255d1c..0f70c6ddf2 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -19,13 +19,14 @@ */ #include -#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); } diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index b86aba9939..12789d5108 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -33,8 +33,10 @@ #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 ------------------------------------------------------------------- */ diff --git a/vrrpd/vrrp_arp.c b/vrrpd/vrrp_arp.c index 45c4071875..95e9de86a7 100644 --- a/vrrpd/vrrp_arp.c +++ b/vrrpd/vrrp_arp.c @@ -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); } diff --git a/vrrpd/vrrp_arp.h b/vrrpd/vrrp_arp.h index 57223835ef..1de94a1519 100644 --- a/vrrpd/vrrp_arp.h +++ b/vrrpd/vrrp_arp.h @@ -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 diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index 608be06a0c..631b8d7865 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -26,20 +26,15 @@ #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; diff --git a/vrrpd/vrrp_packet.h b/vrrpd/vrrp_packet.h index 245fada064..8d8d240bec 100644 --- a/vrrpd/vrrp_packet.h +++ b/vrrpd/vrrp_packet.h @@ -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); diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 7da6646999..1d01b920b1 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -19,11 +19,12 @@ */ #include -#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); } -- 2.39.5