From 5435a2bf61c4179e8b69af5d3a808b42f5387177 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 19 Nov 2018 20:51:52 +0000 Subject: [PATCH 001/153] vrrpd: initial commit Signed-off-by: Quentin Young --- Makefile.am | 3 +- configure.ac | 1 + vrrpd/Makefile | 10 ++ vrrpd/subdir.am | 32 ++++ vrrpd/vrrp.c | 351 ++++++++++++++++++++++++++++++++++++++++++++ vrrpd/vrrp.h | 162 ++++++++++++++++++++ vrrpd/vrrp_main.c | 151 +++++++++++++++++++ vrrpd/vrrp_memory.c | 24 +++ vrrpd/vrrp_memory.h | 27 ++++ vrrpd/vrrp_packet.c | 50 +++++++ vrrpd/vrrp_packet.h | 49 +++++++ vrrpd/vrrp_vty.c | 80 ++++++++++ vrrpd/vrrp_vty.h | 23 +++ vrrpd/vrrp_zebra.c | 39 +++++ vrrpd/vrrp_zebra.h | 29 ++++ vtysh/vtysh.h | 1 + 16 files changed, 1031 insertions(+), 1 deletion(-) create mode 100644 vrrpd/Makefile create mode 100644 vrrpd/subdir.am create mode 100644 vrrpd/vrrp.c create mode 100644 vrrpd/vrrp.h create mode 100644 vrrpd/vrrp_main.c create mode 100644 vrrpd/vrrp_memory.c create mode 100644 vrrpd/vrrp_memory.h create mode 100644 vrrpd/vrrp_packet.c create mode 100644 vrrpd/vrrp_packet.h create mode 100644 vrrpd/vrrp_vty.c create mode 100644 vrrpd/vrrp_vty.h create mode 100644 vrrpd/vrrp_zebra.c create mode 100644 vrrpd/vrrp_zebra.h diff --git a/Makefile.am b/Makefile.am index 11188ea157..166ac29d1c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -147,6 +147,7 @@ include staticd/subdir.am include bfdd/subdir.am include yang/subdir.am include yang/libyang_plugins/subdir.am +include vrrpd/subdir.am include vtysh/subdir.am include tests/subdir.am @@ -188,7 +189,6 @@ EXTRA_DIST += \ snapcraft/defaults \ snapcraft/helpers \ snapcraft/snap \ - \ babeld/Makefile \ bgpd/Makefile \ bgpd/rfp-example/librfp/Makefile \ @@ -218,6 +218,7 @@ EXTRA_DIST += \ vtysh/Makefile \ watchfrr/Makefile \ zebra/Makefile \ + vrrpd/Makefile \ # end noinst_HEADERS += defaults.h diff --git a/configure.ac b/configure.ac index b7ddf87b43..69f489dbc5 100755 --- a/configure.ac +++ b/configure.ac @@ -1602,6 +1602,7 @@ AM_CONDITIONAL([PBRD], [test "${enable_pbrd}" != "no"]) AM_CONDITIONAL([SHARPD], [test "${enable_sharpd}" = "yes"]) AM_CONDITIONAL([STATICD], [test "${enable_staticd}" != "no"]) AM_CONDITIONAL([FABRICD], [test "${enable_fabricd}" != "no"]) +AM_CONDITIONAL([VRRPD], [test "${enable_vrrpd}" != "no"]) if test "${enable_bgp_announce}" = "no";then AC_DEFINE([DISABLE_BGP_ANNOUNCE], [1], [Disable BGP installation to zebra]) diff --git a/vrrpd/Makefile b/vrrpd/Makefile new file mode 100644 index 0000000000..027c6ee1f8 --- /dev/null +++ b/vrrpd/Makefile @@ -0,0 +1,10 @@ +all: ALWAYS + @$(MAKE) -s -C .. vrrp/vrrp +%: ALWAYS + @$(MAKE) -s -C .. vrrp/$@ + +Makefile: + #nothing +ALWAYS: +.PHONY: ALWAYS makefiles +.SUFFIXES: diff --git a/vrrpd/subdir.am b/vrrpd/subdir.am new file mode 100644 index 0000000000..40dee9c145 --- /dev/null +++ b/vrrpd/subdir.am @@ -0,0 +1,32 @@ +# +# vrrpd +# + +if VRRPD +noinst_LIBRARIES += vrrpd/libvrrp.a +sbin_PROGRAMS += vrrpd/vrrpd +# dist_examples_DATA += staticd/staticd.conf.sample +vtysh_scan += $(top_srcdir)/vrrpd/vrrp_vty.c +man8 += $(MANBUILD)/vrrpd.8 +endif + +vrrpd_libvrrp_a_SOURCES = \ + vrrpd/vrrp_memory.c \ + vrrpd/vrrp_zebra.c \ + vrrpd/vrrp_vty.c \ + vrrpd/vrrp_packet.c \ + vrrpd/vrrp.c \ + # end + +noinst_HEADERS += \ + vrrpd/vrrp_memory.h \ + vrrpd/vrrp_zebra.h \ + vrrpd/vrrp_vty.h \ + vrrpd/vrrp.h \ + # end + +vrrpd/vrrp_vty_clippy.c: $(CLIPPY_DEPS) +vrrpd/vrrp_vty.$(OBJEXT): vrrpd/vrrp_vty_clippy.c + +vrrpd_vrrpd_SOURCES = vrrpd/vrrp_main.c +vrrpd_vrrpd_LDADD = vrrpd/libvrrp.a lib/libfrr.la @LIBCAP@ diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c new file mode 100644 index 0000000000..2cc0896605 --- /dev/null +++ b/vrrpd/vrrp.c @@ -0,0 +1,351 @@ +/* + * VRRPD global definitions + * Copyright (C) 2018 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "memory.h" +#include "if.h" +#include "linklist.h" +#include "prefix.h" +#include "hash.h" +#include "vrf.h" +#include "hook.h" + +#include "vrrp.h" + +/* Utility functions ------------------------------------------------------- */ + +/* + * Sets an ethaddr to RFC-defined Virtual Router MAC address. + * + * mac + * ethaddr to set + * + * v6 + * Whether this is a V6 or V4 Virtual Router MAC + * + * vrid + * Virtual Router Identifier + */ +static void vrrp_mac_set(struct ethaddr *mac, bool v6, uint8_t vrid) +{ + /* + * V4: 00-00-5E-00-01-{VRID} + * V6: 00-00-5E-00-02-{VRID} + */ + mac->octet[0] = 0x00; + mac->octet[1] = 0x00; + mac->octet[2] = 0x5E; + mac->octet[3] = 0x00; + mac->octet[4] = v6 ? 0x02 : 0x01; + mac->octet[5] = 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 + * 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) * vr->master_adver_interval; + vr->skew_time /= 256; + vr->master_down_interval = (3 * vr->master_adver_interval); + vr->master_down_interval /= 256; +} + +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->advint = VRRP_DEFAULT_ADVINT; + vr->is_master = false; + vr->priority = VRRP_DEFAULT_PRIORITY; + vr->advertisement_interval = VRRP_DEFAULT_ADVINT; + vr->master_adver_interval = 0; + vr->skew_time = 0; + vr->master_down_interval = 0; + 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; + + hash_get(vrrp_vrouters_hash, vr, hash_alloc_intern); + + return vr; +} + +struct vrrp_vrouter *vrrp_lookup(uint8_t vrid) +{ + struct vrrp_vrouter vr; + vr.vrid = vrid; + + return hash_lookup(vrrp_vrouters_hash, &vr); +} + +/* Network ----------------------------------------------------------------- */ + +/* + * Create and broadcast VRRP ADVERTISEMENT message. + * + * vr + * Virtual Router for which to send ADVERTISEMENT + */ +static void vrrp_send_advertisement(struct vrrp_vrouter *vr) +{ +} + +/* FIXME: +static void vrrp_recv_advertisement(struct thread *thread) +{ +} +*/ + +/* + * Create Virtual Router listen socket and join it to the VRRP multicast group. + * + * 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 + */ +static int vrrp_socket(struct vrrp_vrouter *vr) +{ + struct ip_mreqn req; + int ret; + + vr->sock = socket(AF_INET, SOCK_RAW, IPPROTO_VRRP); + + if (vr->sock < 0) { + /* FIXME */ + } + + /* Join the multicast group.*/ + + /* FIXME: Use first address on the interface and for imr_interface */ + struct connected *c = listhead(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)); + if (ret < 0) { + // int err = errno; + /* VRRP_LOG(("cant do IP_ADD_MEMBERSHIP errno=%d\n", err)); */ + return -1; + } + return 0; +} + + +/* State machine ----------------------------------------------------------- */ + +DEFINE_HOOK(vrrp_change_state_hook, (struct vrrp_vrouter *vr, int to), (vr, to)); + +/* + * Handle any necessary actions during state change to MASTER state. + * + * vr + * Virtual Router to operate on + */ +static void vrrp_change_state_master(struct vrrp_vrouter *vr) +{ +} + +/* + * Handle any necessary actions during state change to BACKUP state. + * + * vr + * Virtual Router to operate on + */ +static void vrrp_change_state_backup(struct vrrp_vrouter *vr) +{ + /* Uninstall ARP entry for vrouter MAC */ + /* ... */ +} + +/* + * Handle any necessary actions during state change to INITIALIZE state. + * + * This is not called for initial startup, only when transitioning from MASTER + * or BACKUP. + * + * vr + * Virtual Router to operate on + */ +static void vrrp_change_state_initialize(struct vrrp_vrouter *vr) +{ +} + +void (*vrrp_change_state_handlers[])(struct vrrp_vrouter *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 + * Virtual Router for which to change state + * + * to + * State to change to + */ +static void vrrp_change_state(struct vrrp_vrouter *vr, int to) +{ + /* Call our handlers, then any subscribers */ + vrrp_change_state_handlers[to](vr); + hook_call(vrrp_change_state_hook, vr, to); + vr->fsm.state = to; +} + +/* + * Called when Adver_Timer expires. + */ +static int vrrp_adver_timer_expire(struct thread *thread) +{ + struct vrrp_vrouter *vr = thread->arg; + + if (vr->fsm.state == VRRP_STATE_BACKUP) { + vrrp_send_advertisement(vr); + /* FIXME: vrrp_send_gratuitous_arp(vr); */ + } else if (vr->fsm.state == VRRP_STATE_MASTER) { + + } else if (vr->fsm.state == VRRP_STATE_INITIALIZE) { + assert(!"FUCK"); + } + return 0; +} + +/* + * Called when Master_Down timer expires. + */ +static int vrrp_master_down_timer_expire(struct thread *thread) +{ + /* struct vrrp_vrouter *vr = thread->arg; */ + + return 0; +} + +/* + * Event handler for Startup event. + * + * Creates sockets, sends advertisements and ARP requests, starts timers, + * updates state machine. + * + * vr + * Virtual Router on which to apply Startup event + */ +static void vrrp_startup(struct vrrp_vrouter *vr) +{ + /* Create socket */ + vrrp_socket(vr); + + /* Schedule listener */ + /* ... */ + + if (vr->priority == VRRP_PRIO_MASTER) { + vrrp_send_advertisement(vr); + /* FIXME: vrrp_send_gratuitous_arp(vr); */ + + 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); + } 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); + } +} + +static void vrrp_shutdown(struct vrrp_vrouter *vr) +{ + /* NOTHING */ +} + +static void (*vrrp_event_handlers[])(struct vrrp_vrouter *vr) = { + [VRRP_EVENT_STARTUP] = vrrp_startup, + [VRRP_EVENT_SHUTDOWN] = vrrp_shutdown, +}; + +/* + * Spawn a VRRP FSM event on a Virtual Router. + * + * vr + * Virtual Router on which to spawn event + * + * event + * The event to spawn + */ +void vrrp_event(struct vrrp_vrouter *vr, int event) +{ + vrrp_event_handlers[event](vr); +} + + +/* Other ------------------------------------------------------------------- */ + +static unsigned int vrrp_hash_key(void *arg) +{ + struct vrrp_vrouter *vr = arg; + + return vr->vrid; +} + +static bool vrrp_hash_cmp(const void *arg1, const void *arg2) +{ + const struct vrrp_vrouter *vr1 = arg1; + const struct vrrp_vrouter *vr2 = arg2; + + return vr1->vrid > vr2->vrid; +} + +void vrrp_init(void) +{ + vrrp_vrouters_hash = hash_create(&vrrp_hash_key, vrrp_hash_cmp, + "VRRP virtual router hash"); + vrf_init(NULL, NULL, NULL, NULL, NULL); +} diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h new file mode 100644 index 0000000000..308e3a8e10 --- /dev/null +++ b/vrrpd/vrrp.h @@ -0,0 +1,162 @@ +/* + * VRRPD global definitions + * Copyright (C) 2018 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __VRRP_H__ +#define __VRRP_H_ + +#include +#include "linklist.h" +#include "hash.h" +#include "if.h" +#include "thread.h" +#include "hook.h" + +/* Global definitions */ +#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 IPPROTO_VRRP 112 + +/* threadmaster */ +extern struct thread_master *master; + +/* Global hash of all Virtual Routers */ +struct hash *vrrp_vrouters_hash; + +/* + * VRRP Virtual Router + */ +struct vrrp_vrouter { + /* 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; + + /* + * One ore more IPv6 addresses associated with this Virtual Router. The + * first address must be the Link-Local address associated with the + * virtual router. + */ + struct list *v6; + + /* Time between ADVERTISEMENTS (centiseconds) */ + int advint; + + /* Whether this VRRP Router is currently the master */ + bool is_master; + + /* Priority */ + 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_adver_interval; + + /* + * Time to skew Master_Down_Interval in centiseconds. Calculated as: + * (((256 - priority) * Master_Adver_Interval) / 256) + */ + uint16_t skew_time; + + /* + * Time interval for Backup to declare Master down (centiseconds). + * Calculated as: + * (3 * Master_Adver_Interval) + Skew_time + */ + uint16_t master_down_interval; + + /* + * Controls whether a (starting or restarting) higher-priority Backup + * router preempts a lower-priority Master router. Values are True to + * allow preemption and False to prohibit preemption. Default is True. + */ + bool preempt_mode; + + /* + * Controls whether a virtual router in Master state will accept + * packets addressed to the address owner's IPvX address as its own if + * it is not the IPvX address owner. The default is False. + */ + 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; +}; + +/* State machine */ +#define VRRP_STATE_INITIALIZE 1 +#define VRRP_STATE_MASTER 2 +#define VRRP_STATE_BACKUP 3 +#define VRRP_EVENT_STARTUP 1 +#define VRRP_EVENT_SHUTDOWN 2 + +DECLARE_HOOK(vrrp_change_state_hook, (struct vrrp_vrouter *vr, int to), (vr, to)); +void vrrp_event(struct vrrp_vrouter *vr, int event); +/* End state machine */ + + +/* + * Initialize VRRP global datastructures. + */ +void vrrp_init(void); + +/* + * Create and register a new VRRP Virtual Router. + */ +struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid); + +/* + * Find VRRP Virtual Router by Virtual Router ID + */ +struct vrrp_vrouter *vrrp_lookup(uint8_t vrid); + +/* + * Trigger VRRP event + */ +void vrrp_event(struct vrrp_vrouter *vr, int event); + +#endif diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c new file mode 100644 index 0000000000..8c73375c9d --- /dev/null +++ b/vrrpd/vrrp_main.c @@ -0,0 +1,151 @@ +/* + * VRRP + * Copyright (C) 2018 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include +#include "getopt.h" +#include "thread.h" +#include "command.h" +#include "log.h" +#include "memory.h" +#include "privs.h" +#include "sigevent.h" +#include "libfrr.h" +#include "vrf.h" +#include "nexthop.h" +#include "filter.h" + +#include "vrrp.h" +#include "vrrp_zebra.h" +#include "vrrp_vty.h" + +char backup_config_file[256]; + +zebra_capabilities_t _caps_p[] = { +}; + +struct zebra_privs_t vrrp_privs = { +#if defined(FRR_USER) && defined(FRR_GROUP) + .user = FRR_USER, + .group = FRR_GROUP, +#endif +#if defined(VTY_GROUP) + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = array_size(_caps_p), + .cap_num_i = 0}; + +struct option longopts[] = { { 0 } }; + +/* Master of threads. */ +struct thread_master *master; + +/* SIGHUP handler. */ +static void sighup(void) +{ + zlog_info("SIGHUP received"); +} + +/* SIGINT / SIGTERM handler. */ +static void sigint(void) +{ + zlog_notice("Terminating on signal"); + + exit(0); +} + +/* SIGUSR1 handler. */ +static void sigusr1(void) +{ + zlog_rotate(); +} + +struct quagga_signal_t vrrp_signals[] = { + { + .signal = SIGHUP, + .handler = &sighup, + }, + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigint, + }, +}; + +static const struct frr_yang_module_info *vrrp_yang_modules[] = { +}; + +#define VRRP_VTY_PORT 2617 + +FRR_DAEMON_INFO(vrrpd, VRRP, .vty_port = VRRP_VTY_PORT, + .proghelp = "Virtual Router Redundancy Protocol", + .signals = vrrp_signals, + .n_signals = array_size(vrrp_signals), + .privs = &vrrp_privs, + .yang_modules = vrrp_yang_modules, + .n_yang_modules = array_size(vrrp_yang_modules), +) + +int main(int argc, char **argv, char **envp) +{ + frr_preinit(&vrrpd_di, argc, argv); + frr_opt_add("", longopts, ""); + + while (1) { + int opt; + + opt = frr_getopt(argc, argv, NULL); + + if (opt == EOF) + break; + + switch (opt) { + case 0: + break; + default: + frr_help_exit(1); + break; + } + } + + master = frr_init(); + + vrrp_zebra_init(); + vrrp_vty_init(); + vrrp_init(); + + snprintf(backup_config_file, sizeof(backup_config_file), + "%s/vrrpd.conf", frr_sysconfdir); + vrrpd_di.backup_config_file = backup_config_file; + + frr_config_fork(); + frr_run(master); + + /* Not reached. */ + return 0; +} diff --git a/vrrpd/vrrp_memory.c b/vrrpd/vrrp_memory.c new file mode 100644 index 0000000000..4a7c0b7e42 --- /dev/null +++ b/vrrpd/vrrp_memory.c @@ -0,0 +1,24 @@ +/* + * VRRPD memory types + * Copyright (C) 2018 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include +#include +#include "vrrp_memory.h" + +DEFINE_MGROUP(VRRP, "vrrpd") diff --git a/vrrpd/vrrp_memory.h b/vrrpd/vrrp_memory.h new file mode 100644 index 0000000000..f57a864804 --- /dev/null +++ b/vrrpd/vrrp_memory.h @@ -0,0 +1,27 @@ +/* + * VRRPD memory types + * Copyright (C) 2018 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __VRRP_MEMORY_H__ +#define __VRRP_MEMORY_H__ + +#include "memory.h" + +DECLARE_MGROUP(VRRP) + +#endif diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c new file mode 100644 index 0000000000..4cbcd771f2 --- /dev/null +++ b/vrrpd/vrrp_packet.c @@ -0,0 +1,50 @@ +/* + * VRRPD packet crafting + * Copyright (C) 2018 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "memory.h" +#include "ipaddr.h" + +#include "vrrp_packet.h" + +/* + * Builds a VRRP packet. + */ +struct vrrp_pkt *vrrp_pkt_build(uint8_t vrid, uint8_t prio, + uint16_t max_adver_int, bool v6, uint8_t numip, + void **ips) +{ + size_t addrsz = v6 ? sizeof(struct in6_addr) : sizeof(struct in_addr); + struct vrrp_pkt *pkt = + XCALLOC(MTYPE_TMP, sizeof(struct vrrp_pkt) + addrsz * numip); + + pkt->version = VRRP_VERSION; + pkt->type = VRRP_TYPE_ADVERTISEMENT; + pkt->vrid = vrid; + pkt->priority = prio; + pkt->rsvd = 0; + pkt->max_adver_int = max_adver_int; + for (uint8_t i = 0; i < numip; i++) + memcpy(&pkt->addrs[i].v4, ips[i], addrsz); + /* FIXME */ + pkt->cksum = 0; + + return pkt; +} diff --git a/vrrpd/vrrp_packet.h b/vrrpd/vrrp_packet.h new file mode 100644 index 0000000000..04116c6245 --- /dev/null +++ b/vrrpd/vrrp_packet.h @@ -0,0 +1,49 @@ +/* + * VRRPD packet crafting + * Copyright (C) 2018 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "memory.h" +#include "ipaddr.h" +#include "prefix.h" + +#define VRRP_VERSION 3 +#define VRRP_TYPE_ADVERTISEMENT 1 + +struct vrrp_pkt { + uint8_t version : 4; + uint8_t type : 4; + uint8_t vrid; + uint8_t priority; + uint8_t num_ip; + uint16_t rsvd : 4; + uint16_t max_adver_int : 12; + uint16_t cksum; + union { + struct in_addr v4; + struct in6_addr v6; + } addrs[]; +} __attribute((packed, aligned(1))); + +/* + * Builds a VRRP packet. + */ +struct vrrp_pkt *vrrp_pkt_build(uint8_t vrid, uint8_t prio, + uint16_t max_adver_int, bool v6, uint8_t numip, + void **ips); diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c new file mode 100644 index 0000000000..f438c24895 --- /dev/null +++ b/vrrpd/vrrp_vty.c @@ -0,0 +1,80 @@ +/* + * VRRP commands + * Copyright (C) 2018 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "command.h" +#include "vty.h" +#include "if.h" + +#include "vrrp.h" +#include "vrrp_vty.h" +#include "vrrp_memory.h" +//#ifndef VTYSH_EXTRACT_PL +//#include "vrrp/vrrp_vty_clippy.c" +//#endif + + +#define VRRP_STR "Virtual Router Redundancy Protocol\n" +#define VRRP_VRID_STR "Virtual Router ID\n" + +DEFUN_NOSH (show_debugging_vrrpd, + show_debugging_vrrpd_cmd, + "show debugging [vrrp]", + SHOW_STR + DEBUG_STR + "VRRP information\n") +{ + vty_out(vty, "VRRP debugging status\n"); + + return CMD_SUCCESS; +} + +DEFUN(vrrp_vrid, + vrrp_vrid_cmd, + "[no] vrrp (1-255)", + NO_STR + VRRP_STR + VRRP_VRID_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx = 0; + uint8_t vrid; + + argv_find(argv, argc, "(1-255)", &idx); + vrid = strtoul(argv[idx]->arg, NULL, 10); + + struct vrrp_vrouter *vr = vrrp_vrouter_create(ifp, vrid); + vrrp_event(vr, VRRP_EVENT_STARTUP); + + return CMD_SUCCESS; +} + +static struct cmd_node interface_node = { + INTERFACE_NODE, + "%s(config-if)# ", 1 +}; + +void vrrp_vty_init(void) +{ + install_node(&interface_node, NULL); + if_cmd_init(); + install_element(VIEW_NODE, &show_debugging_vrrpd_cmd); + install_element(INTERFACE_NODE, &vrrp_vrid_cmd); +} diff --git a/vrrpd/vrrp_vty.h b/vrrpd/vrrp_vty.h new file mode 100644 index 0000000000..2aa47ec20d --- /dev/null +++ b/vrrpd/vrrp_vty.h @@ -0,0 +1,23 @@ +/* + * VRRP commands + * Copyright (C) 2018 Cumulus Networks, Inc. + * Quentin Young + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __VRRP_VTY_H__ +#define __VRRP_VTY_H__ + +void vrrp_vty_init(void); +#endif diff --git a/vrrpd/vrrp_zebra.c b/vrrpd/vrrp_zebra.c new file mode 100644 index 0000000000..c96f9a5646 --- /dev/null +++ b/vrrpd/vrrp_zebra.c @@ -0,0 +1,39 @@ +/* + * Zebra interfacing + * Copyright (C) 2018 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "zclient.h" +#include "thread.h" + +#include "vrrp_zebra.h" + +/* Zebra structure to hold current status. */ +struct thread_master *master; +struct zclient *zclient; + +void vrrp_zebra_init(void) +{ + struct zclient_options opt = { .receive_notify = true }; + + zclient = zclient_new(master, &opt); + + zclient_init(zclient, 0, 0, &vrrp_privs); +} + diff --git a/vrrpd/vrrp_zebra.h b/vrrpd/vrrp_zebra.h new file mode 100644 index 0000000000..be6338f17f --- /dev/null +++ b/vrrpd/vrrp_zebra.h @@ -0,0 +1,29 @@ +/* + * Zebra interfacing + * Copyright (C) 2018 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __VRRP_ZEBRA_H__ +#define __VRRP_ZEBRA_H__ + +#include "zclient.h" +#include "thread.h" + +extern struct thread_master *master; +extern struct zebra_privs_t vrrp_privs; +extern void vrrp_zebra_init(void); +#endif diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index eb69a20b83..50b0ef4844 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -42,6 +42,7 @@ DECLARE_MGROUP(MVTYSH) #define VTYSH_STATICD 0x08000 #define VTYSH_BFDD 0x10000 #define VTYSH_FABRICD 0x20000 +#define VTYSH_VRRPD 0x40000 #define VTYSH_WAS_ACTIVE (-2) From 41ee544212ac5b9af8ccc347161d2417fc1ae27e Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 30 Nov 2018 08:45:36 +0000 Subject: [PATCH 002/153] vrrpd: implement gratuitous ARP Implement gratuitous ARP functionality. Ethernet only. Signed-off-by: Quentin Young --- vrrpd/subdir.am | 16 +++-- vrrpd/vrrp.c | 2 + vrrpd/vrrp.h | 6 +- vrrpd/vrrp_arp.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++ vrrpd/vrrp_arp.h | 35 +++++++++ 5 files changed, 229 insertions(+), 10 deletions(-) create mode 100644 vrrpd/vrrp_arp.c create mode 100644 vrrpd/vrrp_arp.h diff --git a/vrrpd/subdir.am b/vrrpd/subdir.am index 40dee9c145..5c040932e6 100644 --- a/vrrpd/subdir.am +++ b/vrrpd/subdir.am @@ -11,18 +11,20 @@ man8 += $(MANBUILD)/vrrpd.8 endif vrrpd_libvrrp_a_SOURCES = \ - vrrpd/vrrp_memory.c \ - vrrpd/vrrp_zebra.c \ - vrrpd/vrrp_vty.c \ - vrrpd/vrrp_packet.c \ vrrpd/vrrp.c \ + vrrpd/vrrp_arp.c \ + vrrpd/vrrp_memory.c \ + vrrpd/vrrp_packet.c \ + vrrpd/vrrp_vty.c \ + vrrpd/vrrp_zebra.c \ # end noinst_HEADERS += \ - vrrpd/vrrp_memory.h \ - vrrpd/vrrp_zebra.h \ - vrrpd/vrrp_vty.h \ vrrpd/vrrp.h \ + vrrpd/vrrp_arp.h \ + vrrpd/vrrp_memory.h \ + vrrpd/vrrp_vty.h \ + vrrpd/vrrp_zebra.h \ # end vrrpd/vrrp_vty_clippy.c: $(CLIPPY_DEPS) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 2cc0896605..f0a106ba05 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -17,6 +17,8 @@ * with this program; see the file COPYING; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include + #include "memory.h" #include "if.h" #include "linklist.h" diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 308e3a8e10..95feeb4dfc 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -17,8 +17,8 @@ * with this program; see the file COPYING; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __VRRP_H__ -#define __VRRP_H_ +#ifndef _VRRP_H +#define _VRRP_H #include #include "linklist.h" @@ -159,4 +159,4 @@ struct vrrp_vrouter *vrrp_lookup(uint8_t vrid); */ void vrrp_event(struct vrrp_vrouter *vr, int event); -#endif +#endif /* _VRRP_H */ diff --git a/vrrpd/vrrp_arp.c b/vrrpd/vrrp_arp.c new file mode 100644 index 0000000000..cbea02212b --- /dev/null +++ b/vrrpd/vrrp_arp.c @@ -0,0 +1,180 @@ +/* + * VRRP ARP primitives. + * + * Copyright (C) 2001-2017 Alexandre Cassen + * Portions: + * Copyright (C) 2018 Cumulus Networks + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include +#include + +#include "lib/if.h" +#include "lib/linklist.h" +#include "lib/log.h" +#include "lib/memory.h" +#include "lib/prefix.h" + +#include "vrrp.h" +#include "vrrp_arp.h" + +/* + * The size of the garp packet buffer should be the large enough to hold the + * largest arp packet to be sent + the size of the link layer header for the + * corresponding protocol. In this case we hardcode for Ethernet. + */ +#define GARP_BUFFER_SIZE \ + sizeof(struct ether_header) + sizeof(struct arphdr) + 2 * ETH_ALEN \ + + 2 * sizeof(struct in_addr) + +/* static vars */ +static int garp_fd = -1; + +/* Send the gratuitous ARP message */ +static ssize_t vrrp_send_garp(struct interface *ifp, uint8_t *buf, ssize_t pack_len) +{ + struct sockaddr_ll sll; + ssize_t len; + + /* Build the dst device */ + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + sll.sll_protocol = ETH_P_ARP; + sll.sll_ifindex = (int) ifp->ifindex; + sll.sll_halen = ifp->hw_addr_len; + memset(sll.sll_addr, 0xFF, ETH_ALEN); + + /* Send packet */ + len = sendto(garp_fd, buf, pack_len, 0, (struct sockaddr *)&sll, + sizeof(sll)); + + return len; +} + +/* Build a gratuitous ARP message over a specific interface */ +static ssize_t vrrp_build_garp(uint8_t *buf, struct interface *ifp, + struct in_addr *v4) +{ + uint8_t *arp_ptr; + + if (ifp->hw_addr_len == 0) + return -1; + + /* Build Ethernet header */ + struct ether_header *eth = (struct ether_header *) buf; + + memset(eth->ether_dhost, 0xFF, ETH_ALEN); + memcpy(eth->ether_shost, ifp->hw_addr, ETH_ALEN); + eth->ether_type = htons(ETHERTYPE_ARP); + + /* Build ARP payload */ + struct arphdr *arph = (struct arphdr *) (buf + ETHER_HDR_LEN); + + arph->ar_hrd = htons(HWTYPE_ETHER); + arph->ar_pro = htons(ETHERTYPE_IP); + arph->ar_hln = ifp->hw_addr_len; + arph->ar_pln = sizeof(struct in_addr); + arph->ar_op = htons(ARPOP_REQUEST); + arp_ptr = (uint8_t *)(arph + sizeof(struct arphdr)); + /* Source MAC: us */ + memcpy(arp_ptr, ifp->hw_addr, ifp->hw_addr_len); + arp_ptr += ifp->hw_addr_len; + /* Source IP: us */ + memcpy(arp_ptr, &v4, sizeof(struct in_addr)); + arp_ptr += sizeof(struct in_addr); + /* Dest MAC: broadcast */ + memset(arp_ptr, 0xFF, ETH_ALEN); + arp_ptr += ifp->hw_addr_len; + /* Dest IP: us */ + memcpy(arp_ptr, &v4, sizeof(struct in_addr)); + arp_ptr += sizeof(struct in_addr); + + return arp_ptr - buf; +} + +void vrrp_garp_send(struct vrrp_vrouter *vr, struct in_addr *v4) +{ + struct interface *ifp = vr->ifp; + uint8_t garpbuf[GARP_BUFFER_SIZE]; + ssize_t garpbuf_len; + ssize_t sent_len; + char astr[INET_ADDRSTRLEN]; + + /* If the interface doesn't support ARP, don't try sending */ + if (ifp->flags & IFF_NOARP) { + zlog_warn( + "Unable to send gratuitous ARP on %s; has IFF_NOARP\n", + ifp->name); + return; + } + + /* Build garp */ + garpbuf_len = vrrp_build_garp(garpbuf, ifp, v4); + + /* Send garp */ + inet_ntop(AF_INET, v4, astr, sizeof(astr)); + zlog_info("Sending gratuitous ARP on %s for %s", ifp->name, astr); + sent_len = vrrp_send_garp(ifp, garpbuf, garpbuf_len); + + if (sent_len < 0) + zlog_warn("Error sending gratuitous ARP on %s for %s", + ifp->name, astr); +} + +void vrrp_garp_send_all(struct vrrp_vrouter *vr) +{ + struct interface *ifp = vr->ifp; + + /* If the interface doesn't support ARP, don't try sending */ + if (ifp->flags & IFF_NOARP) { + zlog_warn( + "Unable to send gratuitous ARP on %s; has IFF_NOARP\n", + ifp->name); + return; + } + + struct listnode *ln; + struct in_addr *v4; + + for (ALL_LIST_ELEMENTS_RO(vr->v4, ln, v4)) + vrrp_garp_send(vr, v4); +} + + +void vrrp_garp_init(void) +{ + /* Create the socket descriptor */ + /* FIXME: why ETH_P_RARP? */ + garp_fd = socket(PF_PACKET, SOCK_RAW | SOCK_CLOEXEC, htons(ETH_P_RARP)); + + if (garp_fd > 0) + zlog_info("Initialized gratuitous ARP socket"); + else { + zlog_err("Error initializing gratuitous ARP socket"); + return; + } +} + +void vrrp_garp_fini(void) +{ + close(garp_fd); + garp_fd = -1; +} diff --git a/vrrpd/vrrp_arp.h b/vrrpd/vrrp_arp.h new file mode 100644 index 0000000000..d3c5651ab5 --- /dev/null +++ b/vrrpd/vrrp_arp.h @@ -0,0 +1,35 @@ +/* + * VRRPD global definitions + * Copyright (C) 2018 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef _VRRP_ARP_H +#define _VRRP_ARP_H + +#include + +#include "vrrp.h" + +/* FIXME: Use the kernel define for this */ +#define HWTYPE_ETHER 1 + +/* prototypes */ +extern void vrrp_garp_init(void); +extern void vrrp_garp_fini(void); +extern void vrrp_garp_send(struct vrrp_vrouter *vr, struct in_addr *v4); +extern void vrrp_garp_send_all(struct vrrp_vrouter *vr); +#endif From 0ae3b19ca6088c5cdda171583f6dc33898f2e7a9 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 3 Dec 2018 20:31:40 +0000 Subject: [PATCH 003/153] vrrpd: add .gitignore Signed-off-by: Quentin Young --- vrrpd/.gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 vrrpd/.gitignore diff --git a/vrrpd/.gitignore b/vrrpd/.gitignore new file mode 100644 index 0000000000..e1751b28ac --- /dev/null +++ b/vrrpd/.gitignore @@ -0,0 +1,2 @@ +libvrrp.a +vrrpd From a8144d7fc8521c1f01f73e28b68df20110ad7e81 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 3 Dec 2018 22:29:02 +0000 Subject: [PATCH 004/153] vrrpd: interface support Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 25 ++++++++++++++++++------- vrrpd/vrrp.h | 3 +-- vrrpd/vrrp_main.c | 2 ++ vrrpd/vrrp_vty.c | 7 ++++++- vtysh/vtysh.c | 1 + vtysh/vtysh.h | 4 ++-- 6 files changed, 30 insertions(+), 12 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index f0a106ba05..bf7da51fde 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -149,6 +149,7 @@ static int vrrp_socket(struct vrrp_vrouter *vr) { struct ip_mreqn req; int ret; + struct connected *c; vr->sock = socket(AF_INET, SOCK_RAW, IPPROTO_VRRP); @@ -159,7 +160,10 @@ static int vrrp_socket(struct vrrp_vrouter *vr) /* Join the multicast group.*/ /* FIXME: Use first address on the interface and for imr_interface */ - struct connected *c = listhead(vr->ifp->connected)->data; + if (!listcount(vr->ifp->connected)) + return -1; + + c = listhead(vr->ifp->connected)->data; struct in_addr v4 = c->address->u.prefix4; memset(&req, 0, sizeof(req)); @@ -277,10 +281,14 @@ static int vrrp_master_down_timer_expire(struct thread *thread) * vr * Virtual Router on which to apply Startup event */ -static void vrrp_startup(struct vrrp_vrouter *vr) +static int vrrp_startup(struct vrrp_vrouter *vr) { /* Create socket */ - vrrp_socket(vr); + int ret = vrrp_socket(vr); + if (ret < 0) { + zlog_warn("Cannot create VRRP socket\n"); + return ret; + } /* Schedule listener */ /* ... */ @@ -301,14 +309,17 @@ static void vrrp_startup(struct vrrp_vrouter *vr) &vr->t_master_down_timer); vrrp_change_state(vr, VRRP_STATE_BACKUP); } + + return 0; } -static void vrrp_shutdown(struct vrrp_vrouter *vr) +static int vrrp_shutdown(struct vrrp_vrouter *vr) { /* NOTHING */ + return 0; } -static void (*vrrp_event_handlers[])(struct vrrp_vrouter *vr) = { +static int (*vrrp_event_handlers[])(struct vrrp_vrouter *vr) = { [VRRP_EVENT_STARTUP] = vrrp_startup, [VRRP_EVENT_SHUTDOWN] = vrrp_shutdown, }; @@ -322,9 +333,9 @@ static void (*vrrp_event_handlers[])(struct vrrp_vrouter *vr) = { * event * The event to spawn */ -void vrrp_event(struct vrrp_vrouter *vr, int event) +int vrrp_event(struct vrrp_vrouter *vr, int event) { - vrrp_event_handlers[event](vr); + return vrrp_event_handlers[event](vr); } diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 95feeb4dfc..91c7284015 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -135,7 +135,6 @@ struct vrrp_vrouter { #define VRRP_EVENT_SHUTDOWN 2 DECLARE_HOOK(vrrp_change_state_hook, (struct vrrp_vrouter *vr, int to), (vr, to)); -void vrrp_event(struct vrrp_vrouter *vr, int event); /* End state machine */ @@ -157,6 +156,6 @@ struct vrrp_vrouter *vrrp_lookup(uint8_t vrid); /* * Trigger VRRP event */ -void vrrp_event(struct vrrp_vrouter *vr, int event); +int vrrp_event(struct vrrp_vrouter *vr, int event); #endif /* _VRRP_H */ diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c index 8c73375c9d..c470ec365b 100644 --- a/vrrpd/vrrp_main.c +++ b/vrrpd/vrrp_main.c @@ -31,6 +31,7 @@ #include "vrf.h" #include "nexthop.h" #include "filter.h" +#include "if.h" #include "vrrp.h" #include "vrrp_zebra.h" @@ -98,6 +99,7 @@ struct quagga_signal_t vrrp_signals[] = { }; static const struct frr_yang_module_info *vrrp_yang_modules[] = { + &frr_interface_info, }; #define VRRP_VTY_PORT 2617 diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index f438c24895..4c5ce7f855 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -61,7 +61,11 @@ DEFUN(vrrp_vrid, vrid = strtoul(argv[idx]->arg, NULL, 10); struct vrrp_vrouter *vr = vrrp_vrouter_create(ifp, vrid); - vrrp_event(vr, VRRP_EVENT_STARTUP); + int ret = vrrp_event(vr, VRRP_EVENT_STARTUP); + if (ret < 0) { + vty_out(vty, "%% Failed to start VRRP instance\n"); + return CMD_WARNING_CONFIG_FAILED; + } return CMD_SUCCESS; } @@ -76,5 +80,6 @@ void vrrp_vty_init(void) install_node(&interface_node, NULL); if_cmd_init(); install_element(VIEW_NODE, &show_debugging_vrrpd_cmd); + install_element(ENABLE_NODE, &show_debugging_vrrpd_cmd); install_element(INTERFACE_NODE, &vrrp_vrid_cmd); } diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 24effa7047..f1a5eca74b 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -137,6 +137,7 @@ struct vtysh_client vtysh_client[] = { {.fd = -1, .name = "pbrd", .flag = VTYSH_PBRD, .next = NULL}, {.fd = -1, .name = "staticd", .flag = VTYSH_STATICD, .next = NULL}, {.fd = -1, .name = "bfdd", .flag = VTYSH_BFDD, .next = NULL}, + {.fd = -1, .name = "vrrpd", .flag = VTYSH_VRRPD, .next = NULL}, }; enum vtysh_write_integrated vtysh_write_integrated = diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index 50b0ef4844..3b0b570a56 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -51,9 +51,9 @@ DECLARE_MGROUP(MVTYSH) /* watchfrr is not in ALL since library CLI functions should not be * run on it (logging & co. should stay in a fixed/frozen config, and * things like prefix lists are not even initialised) */ -#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD|VTYSH_FABRICD +#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD|VTYSH_FABRICD|VTYSH_VRRPD #define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_SHARPD|VTYSH_FABRICD -#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD|VTYSH_FABRICD +#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD|VTYSH_FABRICD|VTYSH_VRRPD #define VTYSH_NS VTYSH_ZEBRA #define VTYSH_VRF VTYSH_ZEBRA|VTYSH_PIMD|VTYSH_STATICD #define VTYSH_KEYS VTYSH_RIPD|VTYSH_EIGRPD From b6029d6a5f29e3fce036e3013b992e47af77639d Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 4 Dec 2018 20:41:37 +0000 Subject: [PATCH 005/153] vrrpd: zebra connections Signed-off-by: Quentin Young --- vrrpd/vrrp.h | 3 + vrrpd/vrrp_zebra.c | 217 +++++++++++++++++++++++++++++++++++++++++++-- vrrpd/vrrp_zebra.h | 5 -- 3 files changed, 212 insertions(+), 13 deletions(-) diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 91c7284015..f1b93b9242 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -38,6 +38,9 @@ /* threadmaster */ extern struct thread_master *master; +/* privileges */ +extern struct zebra_privs_t vrrp_privs; + /* Global hash of all Virtual Routers */ struct hash *vrrp_vrouters_hash; diff --git a/vrrpd/vrrp_zebra.c b/vrrpd/vrrp_zebra.c index c96f9a5646..26d0a5020a 100644 --- a/vrrpd/vrrp_zebra.c +++ b/vrrpd/vrrp_zebra.c @@ -19,21 +19,222 @@ */ #include -#include "zclient.h" -#include "thread.h" +#include "lib/if.h" +#include "lib/log.h" +#include "lib/prefix.h" +#include "lib/zclient.h" +#include "lib/vty.h" +#include "lib/linklist.h" +#include "vrrp.h" #include "vrrp_zebra.h" -/* Zebra structure to hold current status. */ -struct thread_master *master; -struct zclient *zclient; +static struct zclient *zclient = NULL; + +static void vrrp_zebra_connected(struct zclient *zclient) +{ + fprintf(stderr, "Zclient connected\n"); + zclient_send_reg_requests(zclient, VRF_DEFAULT); +} + +/* Router-id update message from zebra. */ +static int vrrp_router_id_update_zebra(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct prefix router_id; + + zebra_router_id_update_read(zclient->ibuf, &router_id); + + return 0; +} + +static int vrrp_zebra_if_add(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + + /* + * zebra api adds/dels interfaces using the same call + * interface_add_read below, see comments in lib/zclient.c + */ + ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); + if (!ifp) + return 0; + + /* FIXME: handle subinterface creation here */ + + return 0; +} + +static int vrrp_zebra_if_del(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + + ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); + if (!ifp) + return 0; + +#if 0 + if (VRRP_DEBUG_ZEBRA) { + zlog_debug( + "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", + __PRETTY_FUNCTION__, ifp->name, ifp->ifindex, vrf_id, + (long)ifp->flags, ifp->metric, ifp->mtu, + if_is_operative(ifp)); + } +#endif + + return 0; +} + +static int vrrp_zebra_if_state_up(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + + /* + * zebra api notifies interface up/down events by using the same call + * zebra_interface_state_read below, see comments in lib/zclient.c ifp = + * zebra_interface_state_read(zclient->ibuf, vrf_id); + */ + ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); + if (!ifp) + return 0; + +#if 0 + if (VRRP_DEBUG_ZEBRA) { + zlog_debug( + "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", + __PRETTY_FUNCTION__, ifp->name, ifp->ifindex, vrf_id, + (long)ifp->flags, ifp->metric, ifp->mtu, + if_is_operative(ifp)); + } +#endif + + return 0; +} + +static int vrrp_zebra_if_state_down(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + + /* + * zebra api notifies interface up/down events by using the same call + * zebra_interface_state_read below, see comments in lib/zclient.c + */ + ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); + if (!ifp) + return 0; + +#if 0 + if (VRRP_DEBUG_ZEBRA) { + zlog_debug( + "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", + __PRETTY_FUNCTION__, ifp->name, ifp->ifindex, vrf_id, + (long)ifp->flags, ifp->metric, ifp->mtu, + if_is_operative(ifp)); + } +#endif + + return 0; +} + +#ifdef VRRP_DEBUG_IFADDR_DUMP +static void dump_if_address(struct interface *ifp) +{ + struct connected *ifc; + struct listnode *node; + + zlog_debug("%s %s: interface %s addresses:", __FILE__, + __PRETTY_FUNCTION__, ifp->name); + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + struct prefix *p = ifc->address; + + if (p->family != AF_INET) + continue; + + zlog_debug("%s %s: interface %s address %s %s", __FILE__, + __PRETTY_FUNCTION__, ifp->name, + inet_ntoa(p->u.prefix4), + CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) + ? "secondary" + : "primary"); + } +} +#endif + +static int vrrp_zebra_if_address_add(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct connected *c; + + /* + * zebra api notifies address adds/dels events by using the same call + * interface_add_read below, see comments in lib/zclient.c + * + * zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD, ...) + * will add address to interface list by calling + * connected_add_by_prefix() + */ + c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + if (!c) + return 0; + +#if 0 + if (VRRP_DEBUG_ZEBRA) { + char buf[BUFSIZ]; + prefix2str(p, buf, BUFSIZ); + zlog_debug("%s: %s(%u) connected IP address %s flags %u %s", + __PRETTY_FUNCTION__, c->ifp->name, vrf_id, buf, + c->flags, + CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY) + ? "secondary" + : "primary"); + + } +#endif + + return 0; +} + +static int vrrp_zebra_if_address_del(int command, struct zclient *client, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct connected *c; + + /* + * zebra api notifies address adds/dels events by using the same call + * interface_add_read below, see comments in lib/zclient.c + * + * zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_DELETE, ...) + * will remove address from interface list by calling + * connected_delete_by_prefix() + */ + c = zebra_interface_address_read(command, client->ibuf, vrf_id); + if (!c) + return 0; + + return 0; +} void vrrp_zebra_init(void) { - struct zclient_options opt = { .receive_notify = true }; + /* Socket for receiving updates from Zebra daemon */ + zclient = zclient_new(master, &zclient_options_default); - zclient = zclient_new(master, &opt); + zclient->zebra_connected = vrrp_zebra_connected; + zclient->router_id_update = vrrp_router_id_update_zebra; + zclient->interface_add = vrrp_zebra_if_add; + zclient->interface_delete = vrrp_zebra_if_del; + zclient->interface_up = vrrp_zebra_if_state_up; + zclient->interface_down = vrrp_zebra_if_state_down; + zclient->interface_address_add = vrrp_zebra_if_address_add; + zclient->interface_address_delete = vrrp_zebra_if_address_del; zclient_init(zclient, 0, 0, &vrrp_privs); -} + zlog_notice("%s: zclient socket initialized", __PRETTY_FUNCTION__); +} diff --git a/vrrpd/vrrp_zebra.h b/vrrpd/vrrp_zebra.h index be6338f17f..03910db407 100644 --- a/vrrpd/vrrp_zebra.h +++ b/vrrpd/vrrp_zebra.h @@ -20,10 +20,5 @@ #ifndef __VRRP_ZEBRA_H__ #define __VRRP_ZEBRA_H__ -#include "zclient.h" -#include "thread.h" - -extern struct thread_master *master; -extern struct zebra_privs_t vrrp_privs; extern void vrrp_zebra_init(void); #endif From 4074400076685c13f21558c973713b35b38fa397 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 4 Dec 2018 20:42:17 +0000 Subject: [PATCH 006/153] vrrpd: get sockets working Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 31 +++++++++++++++++++------------ vrrpd/vrrp_arp.c | 12 +++++++++++- vrrpd/vrrp_arp.h | 1 + vrrpd/vrrp_main.c | 1 + vrrpd/vrrp_vty.c | 1 - 5 files changed, 32 insertions(+), 14 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index bf7da51fde..23f144a792 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -19,15 +19,16 @@ */ #include -#include "memory.h" -#include "if.h" -#include "linklist.h" -#include "prefix.h" -#include "hash.h" -#include "vrf.h" -#include "hook.h" +#include "lib/memory.h" +#include "lib/if.h" +#include "lib/linklist.h" +#include "lib/prefix.h" +#include "lib/hash.h" +#include "lib/vrf.h" +#include "lib/hook.h" #include "vrrp.h" +#include "vrrp_arp.h" /* Utility functions ------------------------------------------------------- */ @@ -151,12 +152,14 @@ static int vrrp_socket(struct vrrp_vrouter *vr) int ret; struct connected *c; - vr->sock = socket(AF_INET, SOCK_RAW, IPPROTO_VRRP); - - if (vr->sock < 0) { - /* FIXME */ + errno = 0; + frr_elevate_privs(&vrrp_privs) { + vr->sock = socket(AF_INET, SOCK_RAW, IPPROTO_VRRP); } + if (vr->sock < 0) + perror("Error opening VRRP socket"); + /* Join the multicast group.*/ /* FIXME: Use first address on the interface and for imr_interface */ @@ -283,6 +286,10 @@ static int vrrp_master_down_timer_expire(struct thread *thread) */ static int vrrp_startup(struct vrrp_vrouter *vr) { + /* Initialize global gratuitous ARP socket if necessary */ + if (!vrrp_garp_is_init()) + vrrp_garp_init(); + /* Create socket */ int ret = vrrp_socket(vr); if (ret < 0) { @@ -295,7 +302,7 @@ static int vrrp_startup(struct vrrp_vrouter *vr) if (vr->priority == VRRP_PRIO_MASTER) { vrrp_send_advertisement(vr); - /* FIXME: vrrp_send_gratuitous_arp(vr); */ + /* vrrp_garp_send(vr); */ thread_add_timer_msec(master, vrrp_adver_timer_expire, vr, vr->advertisement_interval * 10, diff --git a/vrrpd/vrrp_arp.c b/vrrpd/vrrp_arp.c index cbea02212b..a67af4e104 100644 --- a/vrrpd/vrrp_arp.c +++ b/vrrpd/vrrp_arp.c @@ -163,11 +163,16 @@ void vrrp_garp_init(void) { /* Create the socket descriptor */ /* FIXME: why ETH_P_RARP? */ - garp_fd = socket(PF_PACKET, SOCK_RAW | SOCK_CLOEXEC, htons(ETH_P_RARP)); + errno = 0; + frr_elevate_privs(&vrrp_privs) { + garp_fd = socket(PF_PACKET, SOCK_RAW | SOCK_CLOEXEC, + htons(ETH_P_RARP)); + } if (garp_fd > 0) zlog_info("Initialized gratuitous ARP socket"); else { + perror("Error initializing gratuitous ARP socket"); zlog_err("Error initializing gratuitous ARP socket"); return; } @@ -178,3 +183,8 @@ void vrrp_garp_fini(void) close(garp_fd); garp_fd = -1; } + +bool vrrp_garp_is_init(void) +{ + return garp_fd > 0; +} diff --git a/vrrpd/vrrp_arp.h b/vrrpd/vrrp_arp.h index d3c5651ab5..57223835ef 100644 --- a/vrrpd/vrrp_arp.h +++ b/vrrpd/vrrp_arp.h @@ -30,6 +30,7 @@ /* prototypes */ 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); #endif diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c index c470ec365b..d60f37d8fc 100644 --- a/vrrpd/vrrp_main.c +++ b/vrrpd/vrrp_main.c @@ -40,6 +40,7 @@ char backup_config_file[256]; zebra_capabilities_t _caps_p[] = { + ZCAP_NET_RAW, }; struct zebra_privs_t vrrp_privs = { diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 4c5ce7f855..f30aadd626 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -80,6 +80,5 @@ void vrrp_vty_init(void) install_node(&interface_node, NULL); if_cmd_init(); install_element(VIEW_NODE, &show_debugging_vrrpd_cmd); - install_element(ENABLE_NODE, &show_debugging_vrrpd_cmd); install_element(INTERFACE_NODE, &vrrp_vrid_cmd); } From c23edd740255f0199aaabeaed59aeeff81de9538 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 4 Dec 2018 21:28:25 +0000 Subject: [PATCH 007/153] vrrpd: config commands, enable ARP * Allow configuring priority * Allow configuring IPv4 addresses * Add show command for vrouters * Fix hash comparison function * Fix ARP packetization bugs * Enable sending of gratuitous ARP Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 51 +++++++++++------- vrrpd/vrrp.h | 49 +++++++++++++++-- vrrpd/vrrp_arp.c | 6 +-- vrrpd/vrrp_vty.c | 134 +++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 206 insertions(+), 34 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 23f144a792..fd0c62cb0d 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -58,21 +58,7 @@ static void vrrp_mac_set(struct ethaddr *mac, bool v6, uint8_t vrid) mac->octet[5] = 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 - * 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, +void vrrp_update_times(struct vrrp_vrouter *vr, uint16_t advertisement_interval, uint16_t master_adver_interval) { vr->advertisement_interval = advertisement_interval; @@ -83,6 +69,25 @@ static void vrrp_update_times(struct vrrp_vrouter *vr, uint16_t advertisement_in vr->master_down_interval /= 256; } +void vrrp_update_priority(struct vrrp_vrouter *vr, uint8_t priority) +{ + if (vr->priority == priority) + return; + + vr->priority = priority; + /* Timers depend on priority value, need to recalculate them */ + vrrp_update_times(vr, vr->advertisement_interval, + vr->master_adver_interval); +} + +void vrrp_add_ip(struct vrrp_vrouter *vr, struct in_addr v4) +{ + struct in_addr *v4_ins = XCALLOC(MTYPE_TMP, sizeof(struct in_addr)); + + *v4_ins = v4; + listnode_add(vr->v4, v4_ins); +} + struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid) { struct vrrp_vrouter *vr = @@ -93,7 +98,6 @@ struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid) vr->vrid = vrid; vr->v4 = list_new(); vr->v6 = list_new(); - vr->advint = VRRP_DEFAULT_ADVINT; vr->is_master = false; vr->priority = VRRP_DEFAULT_PRIORITY; vr->advertisement_interval = VRRP_DEFAULT_ADVINT; @@ -111,6 +115,17 @@ struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid) return vr; } +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); + hash_release(vrrp_vrouters_hash, vr); + XFREE(MTYPE_TMP, vr); +} + struct vrrp_vrouter *vrrp_lookup(uint8_t vrid) { struct vrrp_vrouter vr; @@ -302,7 +317,7 @@ static int vrrp_startup(struct vrrp_vrouter *vr) if (vr->priority == VRRP_PRIO_MASTER) { vrrp_send_advertisement(vr); - /* vrrp_garp_send(vr); */ + vrrp_garp_send_all(vr); thread_add_timer_msec(master, vrrp_adver_timer_expire, vr, vr->advertisement_interval * 10, @@ -360,7 +375,7 @@ static bool vrrp_hash_cmp(const void *arg1, const void *arg2) const struct vrrp_vrouter *vr1 = arg1; const struct vrrp_vrouter *vr2 = arg2; - return vr1->vrid > vr2->vrid; + return vr1->vrid == vr2->vrid; } void vrrp_init(void) diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index f1b93b9242..240144d3b3 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -67,9 +67,6 @@ struct vrrp_vrouter { */ struct list *v6; - /* Time between ADVERTISEMENTS (centiseconds) */ - int advint; - /* Whether this VRRP Router is currently the master */ bool is_master; @@ -151,6 +148,52 @@ void vrrp_init(void); */ struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid); +/* + * Destroy a VRRP Virtual Router. + */ +void vrrp_vrouter_destroy(struct vrrp_vrouter *vr); + +/* + * Sets advertisement_interval and master_adver_interval on a Virtual Router, + * then recalculates and sets skew_time and master_down_interval based on these + * values. + * + * vr + * Virtual Router to operate on + * + * advertisement_interval + * Advertisement_Interval to set + * + * master_adver_interval + * Master_Adver_Interval to set + */ +void vrrp_update_times(struct vrrp_vrouter *vr, uint16_t advertisement_interval, + uint16_t master_adver_interval); + +/* + * Change the priority of a VRRP Virtual Router. + * + * Recalculates timers using new priority. + * + * vr + * Virtual Router to change priority of + * + * priority + * New priority + */ +void vrrp_update_priority(struct vrrp_vrouter *vr, uint8_t priority); + +/* + * Add 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); + /* * Find VRRP Virtual Router by Virtual Router ID */ diff --git a/vrrpd/vrrp_arp.c b/vrrpd/vrrp_arp.c index a67af4e104..f917c7ec3a 100644 --- a/vrrpd/vrrp_arp.c +++ b/vrrpd/vrrp_arp.c @@ -93,18 +93,18 @@ static ssize_t vrrp_build_garp(uint8_t *buf, struct interface *ifp, arph->ar_hln = ifp->hw_addr_len; arph->ar_pln = sizeof(struct in_addr); arph->ar_op = htons(ARPOP_REQUEST); - arp_ptr = (uint8_t *)(arph + sizeof(struct arphdr)); + arp_ptr = (uint8_t *)(arph + 1); /* Source MAC: us */ memcpy(arp_ptr, ifp->hw_addr, ifp->hw_addr_len); arp_ptr += ifp->hw_addr_len; /* Source IP: us */ - memcpy(arp_ptr, &v4, sizeof(struct in_addr)); + memcpy(arp_ptr, v4, sizeof(struct in_addr)); arp_ptr += sizeof(struct in_addr); /* Dest MAC: broadcast */ memset(arp_ptr, 0xFF, ETH_ALEN); arp_ptr += ifp->hw_addr_len; /* Dest IP: us */ - memcpy(arp_ptr, &v4, sizeof(struct in_addr)); + memcpy(arp_ptr, v4, sizeof(struct in_addr)); arp_ptr += sizeof(struct in_addr); return arp_ptr - buf; diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index f30aadd626..6c7fc5f194 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -22,17 +22,32 @@ #include "command.h" #include "vty.h" #include "if.h" +#include "termtable.h" +#include "prefix.h" #include "vrrp.h" #include "vrrp_vty.h" #include "vrrp_memory.h" -//#ifndef VTYSH_EXTRACT_PL -//#include "vrrp/vrrp_vty_clippy.c" -//#endif +#ifndef VTYSH_EXTRACT_PL +#include "vrrpd/vrrp_vty_clippy.c" +#endif #define VRRP_STR "Virtual Router Redundancy Protocol\n" #define VRRP_VRID_STR "Virtual Router ID\n" +#define VRRP_PRIORITY_STR "Virtual Router Priority\n" +#define VRRP_IP_STR "Virtual Router IPv4 address\n" + +#define VROUTER_GET_VTY(_vty, _vrid, _vr) \ + do { \ + _vr = vrrp_lookup(_vrid); \ + if (!_vr) { \ + vty_out(_vty, \ + "%% Please configure VRRP instance %u\n", \ + (unsigned int)_vrid); \ + return CMD_WARNING_CONFIG_FAILED; \ + } \ + } while (0); DEFUN_NOSH (show_debugging_vrrpd, show_debugging_vrrpd_cmd, @@ -46,19 +61,14 @@ DEFUN_NOSH (show_debugging_vrrpd, return CMD_SUCCESS; } -DEFUN(vrrp_vrid, +DEFPY(vrrp_vrid, vrrp_vrid_cmd, - "[no] vrrp (1-255)", + "[no] vrrp (1-255)$vrid", NO_STR VRRP_STR VRRP_VRID_STR) { VTY_DECLVAR_CONTEXT(interface, ifp); - int idx = 0; - uint8_t vrid; - - argv_find(argv, argc, "(1-255)", &idx); - vrid = strtoul(argv[idx]->arg, NULL, 10); struct vrrp_vrouter *vr = vrrp_vrouter_create(ifp, vrid); int ret = vrrp_event(vr, VRRP_EVENT_STARTUP); @@ -70,6 +80,107 @@ DEFUN(vrrp_vrid, return CMD_SUCCESS; } +DEFPY(vrrp_priority, + vrrp_priority_cmd, + "[no] vrrp (1-255)$vrid priority (1-254)", + NO_STR + VRRP_STR + VRRP_VRID_STR + VRRP_PRIORITY_STR + "Priority value\n") +{ + struct vrrp_vrouter *vr; + + VROUTER_GET_VTY(vty, vrid, vr); + vrrp_update_priority(vr, priority); + + return CMD_SUCCESS; +} + +DEFPY(vrrp_ip, + vrrp_ip_cmd, + "[no] vrrp (1-255)$vrid ip A.B.C.D$ip", + NO_STR + VRRP_STR + VRRP_VRID_STR + "Add IP address\n" + VRRP_IP_STR) +{ + struct vrrp_vrouter *vr; + + VROUTER_GET_VTY(vty, vrid, vr); + vrrp_add_ip(vr, ip); + + return CMD_SUCCESS; +} + +DEFPY(vrrp_vrid_show, + vrrp_vrid_show_cmd, + "show vrrp [(1-255)$vrid]", + SHOW_STR + VRRP_STR + VRRP_VRID_STR) +{ + struct vrrp_vrouter *vr; + char ethstr[ETHER_ADDR_STRLEN]; + char ipstr[INET_ADDRSTRLEN]; + const char *stastr; + + VROUTER_GET_VTY(vty, vrid, vr); + + switch (vr->fsm.state) { + case VRRP_STATE_INITIALIZE: + stastr = "Initialize"; + break; + case VRRP_STATE_MASTER: + stastr = "Master"; + break; + case VRRP_STATE_BACKUP: + stastr = "Backup"; + break; + } + + 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); + ttable_add_row(tt, "%s|%" PRIu8, "Priority", vr->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, "Advertisement Interval", + vr->advertisement_interval); + ttable_add_row(tt, "%s|%" PRIu16, "Master Advertisement Interval", + vr->master_adver_interval); + ttable_add_row(tt, "%s|%" PRIu16, "Skew Time", vr->skew_time); + ttable_add_row(tt, "%s|%" PRIu16, "Master Down Interval", + vr->master_down_interval); + ttable_add_row(tt, "%s|%u", "IPv4 Addresses", vr->v4->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) { + 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)); + vty_out(vty, " %s\n", ipstr); + } + vty_out(vty, "\n"); + } + + return CMD_SUCCESS; +} + static struct cmd_node interface_node = { INTERFACE_NODE, "%s(config-if)# ", 1 @@ -80,5 +191,8 @@ void vrrp_vty_init(void) install_node(&interface_node, NULL); if_cmd_init(); install_element(VIEW_NODE, &show_debugging_vrrpd_cmd); + install_element(VIEW_NODE, &vrrp_vrid_show_cmd); install_element(INTERFACE_NODE, &vrrp_vrid_cmd); + install_element(INTERFACE_NODE, &vrrp_priority_cmd); + install_element(INTERFACE_NODE, &vrrp_ip_cmd); } From 1d21789e169e903b0de56ddec110e573728f7139 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 6 Dec 2018 21:31:05 +0000 Subject: [PATCH 008/153] vrrpd: clean up configuration code, fix skew bug * Update vrrp.[ch] file header to be more accurate * Make vrrp_update_times() private again * Add times reset function and use it * Add priority and advertisement interval setter functions and use them * Add command to change advertisement interval * Allow showing all VRRP instances * Improve doc comments on functions * Add ability to shutdown router * Reorganize vrrp.h * Add doc comments to vrrp.h * Fix bug where Skew_time was not used to compute Master_Down_Interval Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 83 ++++++++++++++++++++++++++++++----- vrrpd/vrrp.h | 112 ++++++++++++++++++++++++++++++----------------- vrrpd/vrrp_vty.c | 96 ++++++++++++++++++++++++++++++++-------- 3 files changed, 220 insertions(+), 71 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index fd0c62cb0d..4d68d617fc 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1,5 +1,5 @@ /* - * VRRPD global definitions + * VRRPD global definitions and state machine * Copyright (C) 2018 Cumulus Networks, Inc. * Quentin Young * @@ -58,18 +58,42 @@ static void vrrp_mac_set(struct ethaddr *mac, bool v6, uint8_t vrid) mac->octet[5] = vrid; } -void vrrp_update_times(struct vrrp_vrouter *vr, uint16_t advertisement_interval, - uint16_t master_adver_interval) +/* + * Sets advertisement_interval and master_adver_interval on a Virtual Router, + * then recalculates and sets skew_time and master_down_interval based on these + * 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) * vr->master_adver_interval; vr->skew_time /= 256; vr->master_down_interval = (3 * vr->master_adver_interval); - vr->master_down_interval /= 256; + vr->master_down_interval /= 256 + vr->skew_time; } -void vrrp_update_priority(struct vrrp_vrouter *vr, uint8_t priority) +/* + */ +static void vrrp_reset_times(struct vrrp_vrouter *vr) +{ + vrrp_update_times(vr, vr->advertisement_interval, 0); +} + +/* Configuration controllers ----------------------------------------------- */ + +void vrrp_set_priority(struct vrrp_vrouter *vr, uint8_t priority) { if (vr->priority == priority) return; @@ -80,6 +104,15 @@ void vrrp_update_priority(struct vrrp_vrouter *vr, uint8_t priority) vr->master_adver_interval); } +void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr, + uint16_t advertisement_interval) +{ + if (vr->advertisement_interval == advertisement_interval) + return; + + vrrp_update_times(vr, advertisement_interval, vr->master_adver_interval); +} + void vrrp_add_ip(struct vrrp_vrouter *vr, struct in_addr v4) { struct in_addr *v4_ins = XCALLOC(MTYPE_TMP, sizeof(struct in_addr)); @@ -88,6 +121,9 @@ void vrrp_add_ip(struct vrrp_vrouter *vr, struct in_addr v4) listnode_add(vr->v4, v4_ins); } + +/* Creation and destruction ------------------------------------------------ */ + struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid) { struct vrrp_vrouter *vr = @@ -100,15 +136,12 @@ struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid) vr->v6 = list_new(); vr->is_master = false; vr->priority = VRRP_DEFAULT_PRIORITY; - vr->advertisement_interval = VRRP_DEFAULT_ADVINT; - vr->master_adver_interval = 0; - vr->skew_time = 0; - vr->master_down_interval = 0; 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; + vrrp_reset_times(vr); hash_get(vrrp_vrouters_hash, vr, hash_alloc_intern); @@ -236,6 +269,8 @@ static void vrrp_change_state_backup(struct vrrp_vrouter *vr) */ static void vrrp_change_state_initialize(struct vrrp_vrouter *vr) { + /* Reset timers */ + vrrp_reset_times(vr); } void (*vrrp_change_state_handlers[])(struct vrrp_vrouter *vr) = { @@ -294,13 +329,25 @@ static int vrrp_master_down_timer_expire(struct thread *thread) * Event handler for Startup event. * * Creates sockets, sends advertisements and ARP requests, starts timers, - * updates state machine. + * and transitions the Virtual Router to either Master or Backup states. + * + * 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 + * + * 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) { + /* May only be called when the state is Initialize */ + if (vr->fsm.state != VRRP_STATE_INITIALIZE) + return -1; + /* Initialize global gratuitous ARP socket if necessary */ if (!vrrp_garp_is_init()) vrrp_garp_init(); @@ -335,9 +382,23 @@ static int vrrp_startup(struct vrrp_vrouter *vr) 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. + */ static int vrrp_shutdown(struct vrrp_vrouter *vr) { - /* NOTHING */ + /* close socket */ + if (vr->sock >= 0) + close(vr->sock); + + /* cancel all threads */ + /* ... */ + + vrrp_change_state(vr, VRRP_STATE_INITIALIZE); + return 0; } diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 240144d3b3..1564cced59 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -1,5 +1,5 @@ /* - * VRRPD global definitions + * VRRPD global definitions and state machine * Copyright (C) 2018 Cumulus Networks, Inc. * Quentin Young * @@ -21,11 +21,13 @@ #define _VRRP_H #include -#include "linklist.h" -#include "hash.h" -#include "if.h" -#include "thread.h" -#include "hook.h" + +#include "lib/hash.h" +#include "lib/hook.h" +#include "lib/if.h" +#include "lib/linklist.h" +#include "lib/privs.h" +#include "lib/thread.h" /* Global definitions */ #define VRRP_DEFAULT_ADVINT 100 @@ -127,24 +129,22 @@ struct vrrp_vrouter { } fsm; }; -/* State machine */ -#define VRRP_STATE_INITIALIZE 1 -#define VRRP_STATE_MASTER 2 -#define VRRP_STATE_BACKUP 3 -#define VRRP_EVENT_STARTUP 1 -#define VRRP_EVENT_SHUTDOWN 2 - -DECLARE_HOOK(vrrp_change_state_hook, (struct vrrp_vrouter *vr, int to), (vr, to)); -/* End state machine */ - - /* * Initialize VRRP global datastructures. */ void vrrp_init(void); + +/* Creation and destruction ------------------------------------------------ */ + /* * Create and register a new VRRP Virtual Router. + * + * ifp + * Base interface to configure VRRP on + * + * vrid + * Virtual Router Identifier */ struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid); @@ -153,27 +153,13 @@ struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid); */ void vrrp_vrouter_destroy(struct vrrp_vrouter *vr); -/* - * Sets advertisement_interval and master_adver_interval on a Virtual Router, - * then recalculates and sets skew_time and master_down_interval based on these - * values. - * - * vr - * Virtual Router to operate on - * - * advertisement_interval - * Advertisement_Interval to set - * - * master_adver_interval - * Master_Adver_Interval to set - */ -void vrrp_update_times(struct vrrp_vrouter *vr, uint16_t advertisement_interval, - uint16_t master_adver_interval); + +/* Configuration controllers ----------------------------------------------- */ /* * Change the priority of a VRRP Virtual Router. * - * Recalculates timers using new priority. + * Also recalculates timers using new priority. * * vr * Virtual Router to change priority of @@ -181,10 +167,22 @@ void vrrp_update_times(struct vrrp_vrouter *vr, uint16_t advertisement_interval, * priority * New priority */ -void vrrp_update_priority(struct vrrp_vrouter *vr, uint8_t priority); +void vrrp_set_priority(struct vrrp_vrouter *vr, uint8_t priority); /* - * Add IPv4 address to a VRRP Virtual Router + * Set Advertisement Interval on this Virtual Router. + * + * vr + * Virtual Router to change priority of + * + * advertisement_interval + * New advertisement interval + */ +void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr, + uint16_t advertisement_interval); + +/* + * Add IPv4 address to a VRRP Virtual Router. * * vr * Virtual Router to add IPv4 address to @@ -194,14 +192,46 @@ void vrrp_update_priority(struct vrrp_vrouter *vr, uint8_t priority); */ void vrrp_add_ip(struct vrrp_vrouter *vr, struct in_addr v4); + +/* State machine ----------------------------------------------------------- */ + +#define VRRP_STATE_INITIALIZE 1 +#define VRRP_STATE_MASTER 2 +#define VRRP_STATE_BACKUP 3 +#define VRRP_EVENT_STARTUP 1 +#define VRRP_EVENT_SHUTDOWN 2 + +/* + * This hook called whenever the state of a Virtual Router changes, after the + * specific internal state handlers have run. + * + * 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)); + +/* + * Trigger a VRRP event on a given Virtual Router.. + * + * vr + * Virtual Router to operate on + * + * event + * Event to kick off. All event related processing will have completed upon + * return of this function. + * + * Returns: + * < 0 if the event created an error + * 0 otherwise + */ +int vrrp_event(struct vrrp_vrouter *vr, int event); + + +/* Other ------------------------------------------------------------------- */ + /* * Find VRRP Virtual Router by Virtual Router ID */ struct vrrp_vrouter *vrrp_lookup(uint8_t vrid); -/* - * Trigger VRRP event - */ -int vrrp_event(struct vrrp_vrouter *vr, int event); - #endif /* _VRRP_H */ diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 6c7fc5f194..9fda418f75 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -70,29 +70,60 @@ DEFPY(vrrp_vrid, { VTY_DECLVAR_CONTEXT(interface, ifp); - struct vrrp_vrouter *vr = vrrp_vrouter_create(ifp, vrid); - int ret = vrrp_event(vr, VRRP_EVENT_STARTUP); - if (ret < 0) { - vty_out(vty, "%% Failed to start VRRP instance\n"); - return CMD_WARNING_CONFIG_FAILED; - } + vrrp_vrouter_create(ifp, vrid); return CMD_SUCCESS; } DEFPY(vrrp_priority, vrrp_priority_cmd, - "[no] vrrp (1-255)$vrid priority (1-254)", + "[no] vrrp (1-255)$vrid priority (1-255)", NO_STR VRRP_STR VRRP_VRID_STR VRRP_PRIORITY_STR - "Priority value\n") + "Priority value; set 255 to designate this Virtual Router as Master\n") +{ + struct vrrp_vrouter *vr; + bool need_restart = 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); + } + + 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); + } + + return CMD_SUCCESS; +} + +DEFPY(vrrp_advertisement_interval, + vrrp_advertisement_interval_cmd, + "[no] vrrp (1-255)$vrid advertisement-interval (1-4096)", + NO_STR + VRRP_STR + VRRP_VRID_STR + VRRP_PRIORITY_STR + "Priority value; set 255 to designate this Virtual Router as Master\n") { struct vrrp_vrouter *vr; VROUTER_GET_VTY(vty, vrid, vr); - vrrp_update_priority(vr, priority); + vrrp_set_advertisement_interval(vr, advertisement_interval); return CMD_SUCCESS; } @@ -107,27 +138,32 @@ DEFPY(vrrp_ip, VRRP_IP_STR) { struct vrrp_vrouter *vr; + int ret; VROUTER_GET_VTY(vty, vrid, vr); vrrp_add_ip(vr, ip); - return CMD_SUCCESS; + if (vr->fsm.state == VRRP_STATE_INITIALIZE) { + vty_out(vty, "%% Activating Virtual Router %ld\n", vrid); + ret = vrrp_event(vr, 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_vrid_show, - vrrp_vrid_show_cmd, - "show vrrp [(1-255)$vrid]", - SHOW_STR - VRRP_STR - VRRP_VRID_STR) +static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) { - struct vrrp_vrouter *vr; char ethstr[ETHER_ADDR_STRLEN]; char ipstr[INET_ADDRSTRLEN]; const char *stastr; - VROUTER_GET_VTY(vty, vrid, vr); - switch (vr->fsm.state) { case VRRP_STATE_INITIALIZE: stastr = "Initialize"; @@ -177,6 +213,27 @@ DEFPY(vrrp_vrid_show, } vty_out(vty, "\n"); } +} + +DEFPY(vrrp_vrid_show, + vrrp_vrid_show_cmd, + "show vrrp [(1-255)$vrid]", + SHOW_STR + VRRP_STR + VRRP_VRID_STR) +{ + struct vrrp_vrouter *vr; + + if (vrid) { + VROUTER_GET_VTY(vty, vrid, vr); + vrrp_show(vty, vr); + } else { + struct list *ll = hash_to_list(vrrp_vrouters_hash); + struct listnode *ln; + + for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) + vrrp_show(vty, vr); + } return CMD_SUCCESS; } @@ -194,5 +251,6 @@ void vrrp_vty_init(void) install_element(VIEW_NODE, &vrrp_vrid_show_cmd); install_element(INTERFACE_NODE, &vrrp_vrid_cmd); install_element(INTERFACE_NODE, &vrrp_priority_cmd); + install_element(INTERFACE_NODE, &vrrp_advertisement_interval_cmd); install_element(INTERFACE_NODE, &vrrp_ip_cmd); } From 1485d9e5252f6ec427c39f3c870f5079dd3c9bd9 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 6 Dec 2018 22:19:11 +0000 Subject: [PATCH 009/153] vrrpd: fix more timer bugs * Fix overflow in skew time computation * Fix accidentally removed initial setting of advertisement interval Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 4d68d617fc..184b88ca60 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -78,10 +78,9 @@ static void vrrp_update_times(struct vrrp_vrouter *vr, { vr->advertisement_interval = advertisement_interval; vr->master_adver_interval = master_adver_interval; - vr->skew_time = (256 - vr->priority) * vr->master_adver_interval; - vr->skew_time /= 256; - vr->master_down_interval = (3 * vr->master_adver_interval); - vr->master_down_interval /= 256 + vr->skew_time; + 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; } /* @@ -141,6 +140,7 @@ struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid) vrrp_mac_set(&vr->vr_mac_v4, false, vrid); vrrp_mac_set(&vr->vr_mac_v6, true, vrid); vr->fsm.state = VRRP_STATE_INITIALIZE; + vrrp_set_advertisement_interval(vr, VRRP_DEFAULT_ADVINT); vrrp_reset_times(vr); hash_get(vrrp_vrouters_hash, vr, hash_alloc_intern); From 69f63d1af96b14027152700fafed55c0e5f80562 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 6 Dec 2018 22:20:05 +0000 Subject: [PATCH 010/153] vrrpd: add unit when showing time values Add 'cs' unit to end of centisecond values when displaying to user Signed-off-by: Quentin Young --- vrrpd/vrrp_vty.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 9fda418f75..4a03dc785b 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -187,12 +187,12 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) vr->preempt_mode ? "Yes" : "No"); ttable_add_row(tt, "%s|%s", "Accept Mode", vr->accept_mode ? "Yes" : "No"); - ttable_add_row(tt, "%s|%" PRIu16, "Advertisement Interval", + ttable_add_row(tt, "%s|%" PRIu16" cs", "Advertisement Interval", vr->advertisement_interval); - ttable_add_row(tt, "%s|%" PRIu16, "Master Advertisement Interval", + ttable_add_row(tt, "%s|%" PRIu16" cs", "Master Advertisement Interval", vr->master_adver_interval); - ttable_add_row(tt, "%s|%" PRIu16, "Skew Time", vr->skew_time); - ttable_add_row(tt, "%s|%" PRIu16, "Master Down 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); From 4440e3cdf7860a036836216ffa80a6ef82096901 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 7 Dec 2018 22:06:42 +0000 Subject: [PATCH 011/153] lib: add list_to_array Signed-off-by: Quentin Young --- lib/linklist.c | 15 +++++++++++++++ lib/linklist.h | 20 ++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/lib/linklist.c b/lib/linklist.c index 40c4b27169..43bc709325 100644 --- a/lib/linklist.c +++ b/lib/linklist.c @@ -334,3 +334,18 @@ struct listnode *listnode_add_force(struct list **list, void *val) *list = list_new(); return listnode_add(*list, val); } + +void **list_to_array(struct list *list, void **arr, size_t arrlen) +{ + struct listnode *ln; + void *vp; + size_t idx = 0; + + for (ALL_LIST_ELEMENTS_RO(list, ln, vp)) { + arr[idx++] = vp; + if (idx == arrlen) + break; + } + + return arr; +} diff --git a/lib/linklist.h b/lib/linklist.h index c30d8d314a..c2b289596d 100644 --- a/lib/linklist.h +++ b/lib/linklist.h @@ -239,6 +239,26 @@ extern struct list *list_dup(struct list *l); extern void list_sort(struct list *list, int (*cmp)(const void **, const void **)); +/* + * Convert a list to an array of void pointers. + * + * Starts from the list head and ends either on the last node of the list or + * when the provided array cannot store any more elements. + * + * list + * list to convert + * + * arr + * Pre-allocated array of void * + * + * arrlen + * Number of elements in arr + * + * Returns: + * arr + */ +void **list_to_array(struct list *list, void **arr, size_t arrlen); + /* * Delete a list and NULL its pointer. * From ef4cc1ebfffaf74c303fd1b5c96d739f08766751 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 6 Dec 2018 22:51:55 +0000 Subject: [PATCH 012/153] vrrpd: merge keepalived packet header Pick up some smartness from keepalived. Signed-off-by: Quentin Young --- vrrpd/vrrp_packet.c | 14 +++++++------- vrrpd/vrrp_packet.h | 27 ++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index 4cbcd771f2..a5eb40fda8 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -35,16 +35,16 @@ struct vrrp_pkt *vrrp_pkt_build(uint8_t vrid, uint8_t prio, struct vrrp_pkt *pkt = XCALLOC(MTYPE_TMP, sizeof(struct vrrp_pkt) + addrsz * numip); - pkt->version = VRRP_VERSION; - pkt->type = VRRP_TYPE_ADVERTISEMENT; - pkt->vrid = vrid; - pkt->priority = prio; - pkt->rsvd = 0; - pkt->max_adver_int = max_adver_int; + pkt->hdr.version = VRRP_VERSION; + pkt->hdr.type = VRRP_TYPE_ADVERTISEMENT; + pkt->hdr.vrid = vrid; + pkt->hdr.priority = prio; + pkt->hdr.v3.rsvd = 0; + pkt->hdr.v3.adver_int = max_adver_int; for (uint8_t i = 0; i < numip; i++) memcpy(&pkt->addrs[i].v4, ips[i], addrsz); /* FIXME */ - pkt->cksum = 0; + pkt->hdr.chksum = 0; return pkt; } diff --git a/vrrpd/vrrp_packet.h b/vrrpd/vrrp_packet.h index 04116c6245..5ff08f95fe 100644 --- a/vrrpd/vrrp_packet.h +++ b/vrrpd/vrrp_packet.h @@ -26,15 +26,32 @@ #define VRRP_VERSION 3 #define VRRP_TYPE_ADVERTISEMENT 1 -struct vrrp_pkt { +/* + * Shared header for VRRPv2/v3 packets. + */ +struct vrrp_hdr { uint8_t version : 4; uint8_t type : 4; uint8_t vrid; uint8_t priority; - uint8_t num_ip; - uint16_t rsvd : 4; - uint16_t max_adver_int : 12; - uint16_t cksum; + uint8_t naddr; + union { + struct { + uint8_t auth_type; + /* advertisement interval (in sec) */ + uint8_t adver_int; + } v2; + struct { + /* advertisement interval (in centiseconds) */ + uint16_t rsvd : 4; + uint16_t adver_int : 12; + } v3; + }; + uint16_t chksum; +}; + +struct vrrp_pkt { + struct vrrp_hdr hdr; union { struct in_addr v4; struct in6_addr v6; From 3eca38577afdd262e72740f16e0b1a6057d0aed6 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 7 Dec 2018 23:36:09 +0000 Subject: [PATCH 013/153] vrrpd: fix packet encode * Properly encode VRRP packets * Calculate checksum appropriately * Update signature to provide caller both packet and result length Signed-off-by: Quentin Young --- vrrpd/vrrp_packet.c | 58 +++++++++++++++++++++++++++++---------------- vrrpd/vrrp_packet.h | 46 ++++++++++++++++++++++++++++------- 2 files changed, 75 insertions(+), 29 deletions(-) diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index a5eb40fda8..608be06a0c 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -19,32 +19,48 @@ */ #include -#include "memory.h" -#include "ipaddr.h" +#include "lib/memory.h" +#include "lib/ipaddr.h" +#include "lib/checksum.h" #include "vrrp_packet.h" -/* - * Builds a VRRP packet. - */ -struct vrrp_pkt *vrrp_pkt_build(uint8_t vrid, uint8_t prio, - uint16_t max_adver_int, bool v6, uint8_t numip, - void **ips) +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) { + /* Used for pointer math when filling IPvX field */ + struct in6_addr *v6ptr; + struct in_addr *v4ptr; + size_t addrsz = v6 ? sizeof(struct in6_addr) : sizeof(struct in_addr); - struct vrrp_pkt *pkt = - XCALLOC(MTYPE_TMP, sizeof(struct vrrp_pkt) + addrsz * numip); + size_t pktsize = sizeof(struct vrrp_hdr) + addrsz * numip; + *pkt = XCALLOC(MTYPE_TMP, pktsize); - pkt->hdr.version = VRRP_VERSION; - pkt->hdr.type = VRRP_TYPE_ADVERTISEMENT; - pkt->hdr.vrid = vrid; - pkt->hdr.priority = prio; - pkt->hdr.v3.rsvd = 0; - pkt->hdr.v3.adver_int = max_adver_int; - for (uint8_t i = 0; i < numip; i++) - memcpy(&pkt->addrs[i].v4, ips[i], addrsz); - /* FIXME */ - pkt->hdr.chksum = 0; + v6ptr = (struct in6_addr *) (*pkt)->addrs; + v4ptr = (struct in_addr *) (*pkt)->addrs; - return pkt; + (*pkt)->hdr.vertype |= VRRP_VERSION << 4; + (*pkt)->hdr.vertype |= VRRP_TYPE_ADVERTISEMENT; + (*pkt)->hdr.vrid = vrid; + (*pkt)->hdr.priority = prio; + (*pkt)->hdr.naddr = numip; + (*pkt)->hdr.v3.adver_int = htons(max_adver_int); + + char buf[INET_ADDRSTRLEN]; + 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)); + } + (*pkt)->hdr.chksum = 0; + + uint16_t chksum = in_cksum(*pkt, pktsize); + (*pkt)->hdr.chksum = htons(chksum); + + return pktsize; } diff --git a/vrrpd/vrrp_packet.h b/vrrpd/vrrp_packet.h index 5ff08f95fe..245fada064 100644 --- a/vrrpd/vrrp_packet.h +++ b/vrrpd/vrrp_packet.h @@ -30,8 +30,12 @@ * Shared header for VRRPv2/v3 packets. */ struct vrrp_hdr { - uint8_t version : 4; - uint8_t type : 4; + /* + * H L H L + * 0000 0000 + * ver type + */ + uint8_t vertype; uint8_t vrid; uint8_t priority; uint8_t naddr; @@ -42,9 +46,13 @@ struct vrrp_hdr { uint8_t adver_int; } v2; struct { - /* advertisement interval (in centiseconds) */ - uint16_t rsvd : 4; - uint16_t adver_int : 12; + /* + * advertisement interval (in centiseconds) + * H L H L + * 0000 000000000000 + * rsvd adver_int + */ + uint16_t adver_int; } v3; }; uint16_t chksum; @@ -60,7 +68,29 @@ struct vrrp_pkt { /* * Builds a VRRP packet. + * + * pkt + * Pointer to store pointer to result buffer in + * + * vrid + * Virtual Router Identifier + * + * prio + * Virtual Router Priority + * + * max_adver_int + * time between ADVERTISEMENTs + * + * v6 + * whether 'ips' is an array of v4 or v6 addresses + * + * numip + * number of IPvX addresses in 'ips' + * + * ips + * array of pointer to either struct in_addr (v6 = false) or struct in6_addr + * (v6 = true) */ -struct vrrp_pkt *vrrp_pkt_build(uint8_t vrid, uint8_t prio, - uint16_t max_adver_int, bool v6, uint8_t numip, - void **ips); +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); From 247aa469575c98b4063b37d07732c11e728e549a Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 7 Dec 2018 23:37:14 +0000 Subject: [PATCH 014/153] vrrpd: implement advertisement send Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 184b88ca60..c7ef6dc6f8 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -29,6 +29,7 @@ #include "vrrp.h" #include "vrrp_arp.h" +#include "vrrp_packet.h" /* Utility functions ------------------------------------------------------- */ @@ -177,6 +178,29 @@ struct vrrp_vrouter *vrrp_lookup(uint8_t vrid) */ static void vrrp_send_advertisement(struct vrrp_vrouter *vr) { + 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; + + list_to_array(vr->v4, (void **)v4, vr->v4->count); + list_to_array(vr->v6, (void **)v6, vr->v6->count); + + pktlen = vrrp_pkt_build(&pkt, vr->vrid, vr->priority, + vr->advertisement_interval, false, + vr->v4->count, (void **)&v4); + + 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); + + ssize_t sent = sendto(vr->sock, pkt, (size_t)pktlen, 0, &dest, + sizeof(struct sockaddr_in)); } /* FIXME: From 4ec944086b343d30a3467eabf234e2670fbd0c2c Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Sat, 8 Dec 2018 00:16:27 +0000 Subject: [PATCH 015/153] vrrpd: improve logging Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 53 ++++++++++++++++++++++++++++++++++++++---------- vrrpd/vrrp.h | 15 +++++++++----- vrrpd/vrrp_arp.c | 23 +++++++++++++-------- vrrpd/vrrp_vty.c | 14 +------------ 4 files changed, 68 insertions(+), 37 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index c7ef6dc6f8..73493fc15b 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -31,6 +31,20 @@ #include "vrrp_arp.h" #include "vrrp_packet.h" +#define VRRP_LOGPFX "[CORE] " + +const char *vrrp_state_names[3] = { + [VRRP_STATE_INITIALIZE] = "Initialize", + [VRRP_STATE_MASTER] = "Master", + [VRRP_STATE_BACKUP] = "Backup", +}; + +const char *vrrp_event_names[2] = { + [VRRP_EVENT_STARTUP] = "Startup", + [VRRP_EVENT_SHUTDOWN] = "Shutdown", +}; + + /* Utility functions ------------------------------------------------------- */ /* @@ -201,6 +215,12 @@ static void vrrp_send_advertisement(struct vrrp_vrouter *vr) ssize_t sent = sendto(vr->sock, pkt, (size_t)pktlen, 0, &dest, sizeof(struct sockaddr_in)); + + if (sent < 0) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Failed to send VRRP Advertisement", + vr->vrid); + } } /* FIXME: @@ -224,13 +244,16 @@ static int vrrp_socket(struct vrrp_vrouter *vr) int ret; struct connected *c; - errno = 0; frr_elevate_privs(&vrrp_privs) { vr->sock = socket(AF_INET, SOCK_RAW, IPPROTO_VRRP); } - if (vr->sock < 0) - perror("Error opening VRRP socket"); + if (vr->sock < 0) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Can't create VRRP socket", + vr->vrid); + return errno; + } /* Join the multicast group.*/ @@ -248,9 +271,10 @@ static int vrrp_socket(struct vrrp_vrouter *vr) ret = setsockopt(vr->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&req, sizeof(struct ip_mreq)); if (ret < 0) { - // int err = errno; - /* VRRP_LOG(("cant do IP_ADD_MEMBERSHIP errno=%d\n", err)); */ - return -1; + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Can't join VRRP multicast group", + vr->vrid); + return errno; } return 0; } @@ -318,6 +342,8 @@ static void vrrp_change_state(struct vrrp_vrouter *vr, 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; } @@ -328,6 +354,8 @@ static int vrrp_adver_timer_expire(struct thread *thread) { struct vrrp_vrouter *vr = thread->arg; + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Adver_Timer expired", vr->vrid); + if (vr->fsm.state == VRRP_STATE_BACKUP) { vrrp_send_advertisement(vr); /* FIXME: vrrp_send_gratuitous_arp(vr); */ @@ -340,11 +368,14 @@ static int vrrp_adver_timer_expire(struct thread *thread) } /* - * Called when Master_Down timer expires. + * Called when Master_Down_Timer expires. */ static int vrrp_master_down_timer_expire(struct thread *thread) { - /* struct vrrp_vrouter *vr = thread->arg; */ + struct vrrp_vrouter *vr = thread->arg; + + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Master_Down_Timer expired", + vr->vrid); return 0; } @@ -378,10 +409,8 @@ static int vrrp_startup(struct vrrp_vrouter *vr) /* Create socket */ int ret = vrrp_socket(vr); - if (ret < 0) { - zlog_warn("Cannot create VRRP socket\n"); + if (ret < 0) return ret; - } /* Schedule listener */ /* ... */ @@ -442,6 +471,8 @@ static int (*vrrp_event_handlers[])(struct vrrp_vrouter *vr) = { */ int vrrp_event(struct vrrp_vrouter *vr, 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); } diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 1564cced59..6ce0e03bf4 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -37,6 +37,8 @@ #define VRRP_MCAST_GROUP_HEX 0xe0000012 #define IPPROTO_VRRP 112 +#define VRRP_LOGPFX_VRID "[VRID: %u] " + /* threadmaster */ extern struct thread_master *master; @@ -195,11 +197,14 @@ void vrrp_add_ip(struct vrrp_vrouter *vr, struct in_addr v4); /* State machine ----------------------------------------------------------- */ -#define VRRP_STATE_INITIALIZE 1 -#define VRRP_STATE_MASTER 2 -#define VRRP_STATE_BACKUP 3 -#define VRRP_EVENT_STARTUP 1 -#define VRRP_EVENT_SHUTDOWN 2 +#define VRRP_STATE_INITIALIZE 0 +#define VRRP_STATE_MASTER 1 +#define VRRP_STATE_BACKUP 2 +#define VRRP_EVENT_STARTUP 0 +#define VRRP_EVENT_SHUTDOWN 1 + +extern const char *vrrp_state_names[3]; +extern const char *vrrp_event_names[2]; /* * This hook called whenever the state of a Virtual Router changes, after the diff --git a/vrrpd/vrrp_arp.c b/vrrpd/vrrp_arp.c index f917c7ec3a..45c4071875 100644 --- a/vrrpd/vrrp_arp.c +++ b/vrrpd/vrrp_arp.c @@ -36,6 +36,8 @@ #include "vrrp.h" #include "vrrp_arp.h" +#define VRRP_LOGPFX "[ARP] " + /* * The size of the garp packet buffer should be the large enough to hold the * largest arp packet to be sent + the size of the link layer header for the @@ -121,8 +123,9 @@ void vrrp_garp_send(struct vrrp_vrouter *vr, struct in_addr *v4) /* 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", - ifp->name); + vr->vrid, ifp->name); return; } @@ -131,12 +134,15 @@ void vrrp_garp_send(struct vrrp_vrouter *vr, struct in_addr *v4) /* Send garp */ inet_ntop(AF_INET, v4, astr, sizeof(astr)); - zlog_info("Sending gratuitous ARP on %s for %s", ifp->name, astr); + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Sending gratuitous ARP on %s for %s", + vr->vrid, ifp->name, astr); sent_len = vrrp_send_garp(ifp, garpbuf, garpbuf_len); if (sent_len < 0) - zlog_warn("Error sending gratuitous ARP on %s for %s", - ifp->name, astr); + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Error sending gratuitous ARP on %s for %s", + vr->vrid, ifp->name, astr); } void vrrp_garp_send_all(struct vrrp_vrouter *vr) @@ -146,8 +152,9 @@ void vrrp_garp_send_all(struct vrrp_vrouter *vr) /* 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", - ifp->name); + vr->vrid, ifp->name); return; } @@ -170,10 +177,10 @@ void vrrp_garp_init(void) } if (garp_fd > 0) - zlog_info("Initialized gratuitous ARP socket"); + zlog_info(VRRP_LOGPFX "Initialized gratuitous ARP socket"); else { - perror("Error initializing gratuitous ARP socket"); - zlog_err("Error initializing gratuitous ARP socket"); + zlog_err(VRRP_LOGPFX + "Error initializing gratuitous ARP socket"); return; } } diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 4a03dc785b..7da6646999 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -162,19 +162,7 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) { char ethstr[ETHER_ADDR_STRLEN]; char ipstr[INET_ADDRSTRLEN]; - const char *stastr; - - switch (vr->fsm.state) { - case VRRP_STATE_INITIALIZE: - stastr = "Initialize"; - break; - case VRRP_STATE_MASTER: - stastr = "Master"; - break; - case VRRP_STATE_BACKUP: - stastr = "Backup"; - break; - } + const char *stastr = vrrp_state_names[vr->fsm.state]; struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); From 3e7a4043ff23b77901b5a3ccd980efac577bb59d Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Sat, 8 Dec 2018 00:49:35 +0000 Subject: [PATCH 016/153] vrrpd: handle rescheduling Adver_Timer, Shutdown * Reschedule Adver_Timer when necessary * Handle Shutdown event appropriately for all states Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 73493fc15b..0fd5e25481 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -356,14 +356,20 @@ static int vrrp_adver_timer_expire(struct thread *thread) zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Adver_Timer expired", vr->vrid); - if (vr->fsm.state == VRRP_STATE_BACKUP) { + if (vr->fsm.state == VRRP_STATE_MASTER) { + /* Send an ADVERTISEMENT */ vrrp_send_advertisement(vr); - /* FIXME: vrrp_send_gratuitous_arp(vr); */ - } else if (vr->fsm.state == VRRP_STATE_MASTER) { - } else if (vr->fsm.state == VRRP_STATE_INITIALIZE) { - assert(!"FUCK"); + /* 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); + } 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]); } + return 0; } @@ -443,13 +449,34 @@ static int vrrp_startup(struct vrrp_vrouter *vr) */ static int vrrp_shutdown(struct vrrp_vrouter *vr) { + 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; + } + /* close socket */ if (vr->sock >= 0) close(vr->sock); - /* cancel all threads */ - /* ... */ - + /* Transition to the Initialize state */ vrrp_change_state(vr, VRRP_STATE_INITIALIZE); return 0; From 5d3730c5f14c4cf60d965093ff4945101092be9e Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 10 Dec 2018 22:16:10 +0000 Subject: [PATCH 017/153] vrrpd: elect self to Master when owning v4 address If the primary address is v4, and we own the address on our configured interface, set ourselves to Master. This introduces the concept of a separate priority value used to store the configured vs effective priority. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++------ vrrpd/vrrp.h | 17 ++++++++++----- 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 0fd5e25481..bf43255d1c 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -105,17 +105,44 @@ static void vrrp_reset_times(struct vrrp_vrouter *vr) vrrp_update_times(vr, vr->advertisement_interval, 0); } +/* + * Determines if a VRRP router is the owner of the specified address. + * + * vr + * VRRP router + * + * Returns: + * whether or not vr owns the specified address + */ +static bool vrrp_is_owner(struct vrrp_vrouter *vr, struct ipaddr *addr) +{ + struct prefix *p; + struct prefix_ipv4 p4; + struct prefix_ipv6 p6; + + if (IS_IPADDR_V4(addr)) { + p4.family = AF_INET; + p4.prefixlen = IPV4_MAX_BITLEN; + p4.prefix = addr->ipaddr_v4; + p = (struct prefix *)&p4; + } else { + p6.family = AF_INET6; + p6.prefixlen = IPV6_MAX_BITLEN; + memcpy(&p6.prefix, &addr->ipaddr_v6, sizeof(struct in6_addr)); + p = (struct prefix *)&p6; + } + + return !!connected_lookup_prefix_exact(vr->ifp, p); +} + /* Configuration controllers ----------------------------------------------- */ void vrrp_set_priority(struct vrrp_vrouter *vr, uint8_t priority) { - if (vr->priority == priority) + if (vr->priority_conf == priority) return; - vr->priority = priority; - /* Timers depend on priority value, need to recalculate them */ - vrrp_update_times(vr, vr->advertisement_interval, - vr->master_adver_interval); + vr->priority_conf = priority; } void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr, @@ -148,7 +175,7 @@ struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid) vr->vrid = vrid; vr->v4 = list_new(); vr->v6 = list_new(); - vr->is_master = false; + vr->priority_conf = VRRP_DEFAULT_PRIORITY; vr->priority = VRRP_DEFAULT_PRIORITY; vr->preempt_mode = true; vr->accept_mode = false; @@ -421,6 +448,26 @@ static int vrrp_startup(struct vrrp_vrouter *vr) /* 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); + zlog_info( + VRRP_LOGPFX VRRP_LOGPFX_VRID + "%s owns primary Virtual Router IP %s; electing self as Master", + vr->vrid, vr->ifp->name, ipbuf); + } + if (vr->priority == VRRP_PRIO_MASTER) { vrrp_send_advertisement(vr); vrrp_garp_send_all(vr); diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 6ce0e03bf4..b86aba9939 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -71,10 +71,14 @@ struct vrrp_vrouter { */ struct list *v6; - /* Whether this VRRP Router is currently the master */ - bool is_master; + /* Configured priority */ + uint8_t priority_conf; - /* Priority */ + /* + * Effective priority + * => priority if we are Backup + * => 255 if we are Master + */ uint8_t priority; /* @@ -159,9 +163,12 @@ void vrrp_vrouter_destroy(struct vrrp_vrouter *vr); /* Configuration controllers ----------------------------------------------- */ /* - * Change the priority of a VRRP Virtual Router. + * Change the configured priority of a VRRP Virtual Router. * - * Also recalculates timers using new priority. + * Note that this only changes the configured priority of the Virtual Router. + * The currently effective priority will not be changed; to change the + * effective priority, the Virtual Router must be restarted by issuing a + * VRRP_EVENT_SHUTDOWN followed by a VRRP_EVENT_STARTUP. * * vr * Virtual Router to change priority of From 862f2f374f27a541887a6dfc333583bdd4ad4de5 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 11 Dec 2018 17:55:21 +0000 Subject: [PATCH 018/153] vrrpd: ipv6 support Add initial support for IPv6. Signed-off-by: Quentin Young --- lib/sockunion.c | 2 +- lib/sockunion.h | 1 + vrrpd/vrrp.c | 413 +++++++++++++++++++++++++------------------- 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, 472 insertions(+), 297 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 + * r + * VRRP Router to operate on */ -static void vrrp_update_times(struct vrrp_vrouter *vr, - uint16_t advertisement_interval, - uint16_t master_adver_interval) +static void vrrp_recalculate_timers(struct vrrp_router *r) { - 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; -} - -/* - */ -static void vrrp_reset_times(struct vrrp_vrouter *vr) -{ - 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_ip(struct vrrp_vrouter *vr, struct in_addr v4) +void vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4) { - struct in_addr *v4_ins = XCALLOC(MTYPE_TMP, sizeof(struct in_addr)); + struct ipaddr *v4_ins = XCALLOC(MTYPE_TMP, sizeof(struct ipaddr)); - *v4_ins = v4; - listnode_add(vr->v4, v4_ins); + v4_ins->ipa_type = IPADDR_V4; + v4_ins->ipaddr_v4 = v4; + listnode_add(vr->v4->addrs, v4_ins); +} + +void vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6) +{ + struct ipaddr *v6_ins = XCALLOC(MTYPE_TMP, sizeof(struct ipaddr)); + + 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; + struct ipaddr *primary = (struct ipaddr *)listhead(r->addrs)->data; - char ipbuf[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, primary, ipbuf, sizeof(ipbuf)); + 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); - 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); 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); } From 91188ca6c19868cfbf45df693cc599c3850f6526 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 19 Dec 2018 16:48:36 +0000 Subject: [PATCH 019/153] vrrpd: read and validate vrrp advertisements * Validate IPvX headers and packet contents * Remove filter of non-255 TTL IPv4 packets; better to receive, log and drop them ourselves * Set outgoing packet TTL / hop limit to 255 * Use existing sockopt functions Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 165 +++++++++++++++++++++++++++++++++---------- vrrpd/vrrp.h | 10 +++ vrrpd/vrrp_packet.c | 166 +++++++++++++++++++++++++++++++++++++++++++- vrrpd/vrrp_packet.h | 80 ++++++++++++++++++++- 4 files changed, 378 insertions(+), 43 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 0f70c6ddf2..285071f1c4 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -24,6 +24,7 @@ #include "lib/if.h" #include "lib/linklist.h" #include "lib/memory.h" +#include "lib/network.h" #include "lib/prefix.h" #include "lib/sockopt.h" #include "lib/vrf.h" @@ -174,8 +175,8 @@ static struct vrrp_router *vrrp_router_create(struct vrrp_vrouter *vr, { struct vrrp_router *r = XCALLOC(MTYPE_TMP, sizeof(struct vrrp_router)); - r->sock = -1; r->family = family; + r->sock = -1; r->vr = vr; r->addrs = list_new(); r->priority = vr->priority; @@ -235,7 +236,7 @@ struct vrrp_vrouter *vrrp_lookup(uint8_t vrid) /* Network ----------------------------------------------------------------- */ /* - * Create and broadcast VRRP ADVERTISEMENT message. + * Create and multicast a VRRP ADVERTISEMENT message. * * r * VRRP Router for which to send ADVERTISEMENT @@ -272,11 +273,77 @@ static void vrrp_send_advertisement(struct vrrp_router *r) } } -/* FIXME: -static void vrrp_recv_advertisement(struct thread *thread) +static void vrrp_recv_advertisement(struct vrrp_router *r, struct vrrp_pkt *pkt, + size_t pktsize) { + char dumpbuf[BUFSIZ]; + vrrp_pkt_dump(dumpbuf, sizeof(dumpbuf), pkt); + zlog_debug("Received VRRP Advertisement:\n%s", dumpbuf); +} + +/* + * Read and process next IPvX datagram. + */ +static int vrrp_read(struct thread *thread) +{ + struct vrrp_router *r = thread->arg; + + struct vrrp_pkt *pkt; + ssize_t pktsize; + ssize_t nbytes; + bool resched; + char errbuf[BUFSIZ]; + uint8_t control[64]; + + struct msghdr m; + struct iovec iov; + iov.iov_base = r->ibuf; + iov.iov_len = sizeof(r->ibuf); + m.msg_name = NULL; + m.msg_namelen = 0; + m.msg_iov = &iov; + m.msg_iovlen = 1; + m.msg_control = control; + m.msg_controllen = sizeof(control); + + nbytes = recvmsg(r->sock, &m, MSG_DONTWAIT); + + if ((nbytes < 0 && ERRNO_IO_RETRY(errno))) { + resched = true; + goto done; + } else if (nbytes <= 0) { + vrrp_event(r, VRRP_EVENT_SHUTDOWN); + resched = false; + goto done; + } + + zlog_debug(VRRP_LOGPFX VRRP_LOGPFX_VRID "Received %s datagram: ", + r->vr->vrid, family2str(r->family)); + zlog_hexdump(r->ibuf, nbytes); + + pktsize = vrrp_parse_datagram(r->family, &m, nbytes, &pkt, errbuf, + sizeof(errbuf)); + + if (pktsize < 0) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + "%s datagram invalid: %s", + r->vr->vrid, family2str(r->family), errbuf); + } else { + zlog_debug(VRRP_LOGPFX VRRP_LOGPFX_VRID "Packet looks good", + r->vr->vrid); + vrrp_recv_advertisement(r, pkt, pktsize); + } + + resched = true; + +done: + memset(r->ibuf, 0x00, sizeof(r->ibuf)); + + if (resched) + thread_add_read(master, vrrp_read, r, r->sock, &r->t_read); + + return 0; } -*/ /* * Create Virtual Router listen socket and join it to the VRRP multicast group. @@ -290,6 +357,7 @@ static void vrrp_recv_advertisement(struct thread *thread) static int vrrp_socket(struct vrrp_router *r) { int ret; + bool failed = false; struct connected *c; frr_elevate_privs(&vrrp_privs) { @@ -300,44 +368,55 @@ static int vrrp_socket(struct vrrp_router *r) zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID "Can't create %s VRRP socket", r->vr->vrid, r->family == AF_INET ? "v4" : "v6"); - return errno; + failed = true; + goto done; } - /* - * 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); - 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; + failed = true; + goto done; } - c = listhead(r->vr->ifp->connected)->data; - struct in_addr v4 = c->address->u.prefix4; + if (r->family == AF_INET) { + int ttl = 255; + ret = setsockopt(r->sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, + sizeof(ttl)); + if (ret < 0) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Failed to set outgoing multicast TTL count to 255; RFC 5798 compliant implementations will drop our packets", + r->vr->vrid); + } - /* Join the multicast group.*/ - if (r->family == AF_INET) + c = listhead(r->vr->ifp->connected)->data; + struct in_addr v4 = c->address->u.prefix4; + + /* Join VRRP IPv4 multicast group */ ret = setsockopt_ipv4_multicast(r->sock, IP_ADD_MEMBERSHIP, v4, htonl(VRRP_MCASTV4_GROUP), r->vr->ifp->ifindex); - else if (r->family == AF_INET6) { + } else if (r->family == AF_INET6) { + ret = setsockopt_ipv6_multicast_hops(r->sock, 255); + if (ret < 0) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Failed to set outgoing multicast hop count to 255; RFC 5798 compliant implementations will drop our packets", + r->vr->vrid); + } + ret = setsockopt_ipv6_hoplimit(r->sock, 1); + if (ret < 0) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Failed to request IPv6 Hop Limit delivery", + r->vr->vrid); + failed = true; + goto done; + } + + /* Join VRRP IPv6 multicast group */ struct ipv6_mreq mreq; inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &mreq.ipv6mr_multiaddr); mreq.ipv6mr_interface = r->vr->ifp->ifindex; @@ -347,12 +426,22 @@ static int vrrp_socket(struct vrrp_router *r) if (ret < 0) { zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Can't join VRRP multicast group", - r->vr->vrid); - close(r->sock); - return -1; + "Failed to join VRRP %s multicast group", + r->vr->vrid, family2str(r->family)); + failed = true; } - return 0; +done: + ret = 0; + if (failed) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Failed to initialize VRRP %s router", + r->vr->vrid, family2str(r->family)); + if (r->sock >= 0) + close(r->sock); + ret = -1; + } + + return ret; } @@ -495,14 +584,14 @@ static int vrrp_startup(struct vrrp_router *r) /* Create socket */ if (r->sock < 0) { int ret = vrrp_socket(r); - if (ret < 0) + if (ret < 0 || r->sock < 0) return ret; } /* Schedule listener */ - /* ... */ + thread_add_read(master, vrrp_read, r, r->sock, &r->t_read); - /* configure effective priority */ + /* Configure effective priority */ struct ipaddr *primary = (struct ipaddr *)listhead(r->addrs)->data; char ipbuf[INET6_ADDRSTRLEN]; diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 12789d5108..64e29eec00 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -21,12 +21,14 @@ #define _VRRP_H #include +#include #include "lib/hash.h" #include "lib/hook.h" #include "lib/if.h" #include "lib/linklist.h" #include "lib/privs.h" +#include "lib/stream.h" #include "lib/thread.h" /* Global definitions */ @@ -65,6 +67,9 @@ struct vrrp_router { /* Socket */ int sock; + /* Socket read buffer */ + uint8_t ibuf[IP_MAXPACKET]; + /* * Address family of this Virtual Router. * Either AF_INET or AF_INET6. @@ -125,6 +130,8 @@ struct vrrp_router { struct thread *t_master_down_timer; struct thread *t_adver_timer; + struct thread *t_read; + struct thread *t_write; }; /* @@ -148,6 +155,9 @@ struct vrrp_vrouter { /* Interface */ struct interface *ifp; + /* Version */ + uint8_t version; + /* Virtual Router Identifier */ uint32_t vrid; diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index 631b8d7865..d305081574 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -18,13 +18,37 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include +#include +#include +#include -#include "lib/memory.h" -#include "lib/ipaddr.h" #include "lib/checksum.h" +#include "lib/ipaddr.h" +#include "lib/memory.h" #include "vrrp_packet.h" +/* clang-format off */ +const char *vrrp_packet_names[16] = { + [0] = "Unknown", + [VRRP_TYPE_ADVERTISEMENT] = "ADVERTISEMENT", + [2] = "Unknown", + [3] = "Unknown", + [4] = "Unknown", + [5] = "Unknown", + [6] = "Unknown", + [7] = "Unknown", + [8] = "Unknown", + [9] = "Unknown", + [10] = "Unknown", + [11] = "Unknown", + [12] = "Unknown", + [13] = "Unknown", + [14] = "Unknown", + [15] = "Unknown", +}; +/* clang-format on */ + ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, uint8_t vrid, uint8_t prio, uint16_t max_adver_int, uint8_t numip, struct ipaddr **ips) @@ -32,7 +56,7 @@ ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, uint8_t vrid, uint8_t prio, 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; + size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, numip); *pkt = XCALLOC(MTYPE_TMP, pktsize); (*pkt)->hdr.vertype |= VRRP_VERSION << 4; @@ -50,8 +74,144 @@ ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, uint8_t vrid, uint8_t prio, } (*pkt)->hdr.chksum = 0; + /* FIXME: v6 checksum */ uint16_t chksum = in_cksum(*pkt, pktsize); (*pkt)->hdr.chksum = htons(chksum); return pktsize; } + +size_t vrrp_pkt_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt) +{ + if (buflen < 1) + return 0; + + char tmpbuf[BUFSIZ]; + size_t rs = 0; + struct vrrp_hdr *hdr = &pkt->hdr; + + buf[0] = 0x00; + snprintf(tmpbuf, sizeof(tmpbuf), "Version: %u\n", (hdr->vertype >> 4)); + rs += strlcat(buf, tmpbuf, buflen); + snprintf(tmpbuf, sizeof(tmpbuf), "Type: %u (%s)\n", + (hdr->vertype & 0x0F), + vrrp_packet_names[(hdr->vertype & 0x0F)]); + rs += strlcat(buf, tmpbuf, buflen); + snprintf(tmpbuf, sizeof(tmpbuf), "VRID: %u\n", hdr->vrid); + rs += strlcat(buf, tmpbuf, buflen); + snprintf(tmpbuf, sizeof(tmpbuf), "Priority: %u\n", hdr->priority); + rs += strlcat(buf, tmpbuf, buflen); + snprintf(tmpbuf, sizeof(tmpbuf), "Count IPvX: %u\n", hdr->naddr); + rs += strlcat(buf, tmpbuf, buflen); + snprintf(tmpbuf, sizeof(tmpbuf), "Max Adver Int: %u\n", + ntohs(hdr->v3.adver_int)); + rs += strlcat(buf, tmpbuf, buflen); + snprintf(tmpbuf, sizeof(tmpbuf), "Checksum: %x\n", ntohs(hdr->chksum)); + rs += strlcat(buf, tmpbuf, buflen); + + return rs; +} + +ssize_t vrrp_parse_datagram(int family, struct msghdr *m, size_t read, + struct vrrp_pkt **pkt, char *errmsg, + size_t errmsg_len) +{ + /* Source (MAC & IP), Dest (MAC & IP) TTL validation done by kernel */ + size_t addrsz = (family == AF_INET) ? sizeof(struct in_addr) + : sizeof(struct in6_addr); + + size_t pktsize; + uint8_t *buf = m->msg_iov->iov_base; + +#define VRRP_PKT_VCHECK(cond, _f, ...) \ + do { \ + if (!(cond)) { \ + if (errmsg) \ + snprintf(errmsg, errmsg_len, (_f), \ + ##__VA_ARGS__); \ + return -1; \ + } \ + } while (0) + + /* IPvX header check */ + + if (family == AF_INET) { + VRRP_PKT_VCHECK( + read >= sizeof(struct ip), + "Datagram not large enough to contain IP header"); + + struct ip *ip = (struct ip *)buf; + + /* IP total length check */ + VRRP_PKT_VCHECK( + ntohs(ip->ip_len) == read, + "IPv4 packet length field does not match # received bytes; %u != %lu", + ntohs(ip->ip_len), read); + + /* TTL check */ + VRRP_PKT_VCHECK(ip->ip_ttl == 255, "IPv4 TTL is not 255"); + + *pkt = (struct vrrp_pkt *)(buf + (ip->ip_hl << 2)); + pktsize = read - (ip->ip_hl << 2); + + /* IP empty packet check */ + VRRP_PKT_VCHECK(pktsize > 0, "IPv4 packet has no payload"); + } else if (family == AF_INET6) { + struct cmsghdr *c; + for (c = CMSG_FIRSTHDR(m); c != NULL; CMSG_NXTHDR(m, c)) { + if (c->cmsg_level == IPPROTO_IPV6 + && c->cmsg_type == IPV6_HOPLIMIT) + break; + } + + VRRP_PKT_VCHECK(!!c, "IPv6 Hop Limit not received"); + + uint8_t *hoplimit = CMSG_DATA(c); + VRRP_PKT_VCHECK(*hoplimit == 255, "IPv6 Hop Limit is not 255"); + + *pkt = (struct vrrp_pkt *)buf; + pktsize = read; + } else { + assert(!"Unknown address family"); + } + + /* Size check */ + size_t minsize = (family == AF_INET) ? VRRP_MIN_PKT_SIZE_V4 + : VRRP_MIN_PKT_SIZE_V6; + size_t maxsize = (family == AF_INET) ? VRRP_MAX_PKT_SIZE_V4 + : VRRP_MAX_PKT_SIZE_V6; + VRRP_PKT_VCHECK(pktsize >= minsize, + "VRRP packet is undersized (%lu < %lu)", pktsize, + VRRP_MIN_PKT_SIZE); + VRRP_PKT_VCHECK(pktsize <= maxsize, + "VRRP packet is oversized (%lu > %lu)", pktsize, + VRRP_MAX_PKT_SIZE); + /* Version check */ + VRRP_PKT_VCHECK(((*pkt)->hdr.vertype >> 4) != 2, "VRPPv2 unsupported"); + VRRP_PKT_VCHECK(((*pkt)->hdr.vertype >> 4) == 3, "Bad version %u", + (*pkt)->hdr.vertype >> 4); + /* Type check */ + VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %u", + (*pkt)->hdr.vertype & 0x0f); + /* Priority check */ + VRRP_PKT_VCHECK((*pkt)->hdr.priority == 255 + || (*pkt)->hdr.priority == 0, + "Bad priority %u", (*pkt)->hdr.priority); + /* # addresses check */ + size_t ves = VRRP_PKT_SIZE(family, (*pkt)->hdr.naddr); + VRRP_PKT_VCHECK(pktsize == ves, "Packet has incorrect # addresses"); + /* FIXME: checksum check */ + /* ... */ + + /* Addresses check */ + char vbuf[INET6_ADDRSTRLEN]; + uint8_t *p = (uint8_t *)(*pkt)->addrs; + for (uint8_t i = 0; i < (*pkt)->hdr.naddr; i++) { + VRRP_PKT_VCHECK(inet_ntop(family, p, vbuf, sizeof(vbuf)), + "Bad IP address, #%u", i); + p += addrsz; + } + + /* Everything checks out */ + return pktsize; +} diff --git a/vrrpd/vrrp_packet.h b/vrrpd/vrrp_packet.h index 8d8d240bec..7a4a338dae 100644 --- a/vrrpd/vrrp_packet.h +++ b/vrrpd/vrrp_packet.h @@ -26,6 +26,8 @@ #define VRRP_VERSION 3 #define VRRP_TYPE_ADVERTISEMENT 1 +extern const char *vrrp_packet_names[16]; + /* * Shared header for VRRPv2/v3 packets. */ @@ -56,15 +58,37 @@ struct vrrp_hdr { } v3; }; uint16_t chksum; -}; +} __attribute__((packed)); + +#define VRRP_HDR_SIZE sizeof(struct vrrp_hdr) struct vrrp_pkt { struct vrrp_hdr hdr; + /* + * When used, this is actually an array of one or the other, not an + * array of union. If N v4 addresses are stored then + * sizeof(addrs) == N * sizeof(struct in_addr). + */ union { struct in_addr v4; struct in6_addr v6; } addrs[]; -} __attribute((packed, aligned(1))); +} __attribute__((packed)); + +#define VRRP_PKT_SIZE(_f, _naddr) \ + ({ \ + size_t _asz = ((_f) == AF_INET) ? sizeof(struct in_addr) \ + : sizeof(struct in6_addr); \ + sizeof(struct vrrp_hdr) + (_asz * (_naddr)); \ + }) + +#define VRRP_MIN_PKT_SIZE_V4 VRRP_PKT_SIZE(AF_INET, 1) +#define VRRP_MAX_PKT_SIZE_V4 VRRP_PKT_SIZE(AF_INET, 255) +#define VRRP_MIN_PKT_SIZE_V6 VRRP_PKT_SIZE(AF_INET6, 1) +#define VRRP_MAX_PKT_SIZE_V6 VRRP_PKT_SIZE(AF_INET6, 255) + +#define VRRP_MIN_PKT_SIZE VRRP_MIN_PKT_SIZE_V4 +#define VRRP_MAX_PKT_SIZE VRRP_MAX_PKT_SIZE_V6 /* * Builds a VRRP packet. @@ -94,3 +118,55 @@ struct vrrp_pkt { ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, uint8_t vrid, uint8_t prio, uint16_t max_adver_int, uint8_t numip, struct ipaddr **ips); + +/* + * Dumps a VRRP packet to a string. + * + * Currently only dumps the header. + * + * buf + * Buffer to store string representation + * + * buflen + * Size of buf + * + * pkt + * Packet to dump to a string + * + * Returns: + * # bytes written to buf + */ +size_t vrrp_pkt_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt); + + +/* + * Parses a VRRP packet, checking for illegal or invalid data. + * + * This function does not check that the local router is not the IPvX owner for + * the addresses received; that should be done by the caller. + * + * family + * Address family of received packet + * + * m + * msghdr containing results of recvmsg() on VRRP router socket + * + * read + * return value of recvmsg() on VRRP router socket; must be non-negative + * + * pkt + * Pointer to pointer to set to location of VRRP packet within buf + * + * errmsg + * Buffer to store human-readable error message in case of error; may be + * NULL, in which case no message will be stored + * + * errmsg_len + * Size of errmsg + * + * Returns: + * Size of VRRP packet, or -1 upon error + */ +ssize_t vrrp_parse_datagram(int family, struct msghdr *m, size_t read, + struct vrrp_pkt **pkt, char *errmsg, + size_t errmsg_len); From bb54fa3a0035e29aa97fc6638d71b8f57fe366e4 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 21 Dec 2018 20:31:10 +0000 Subject: [PATCH 020/153] vrrpd: fix memleak when sending advertisements Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 285071f1c4..2eaf39fbb5 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -266,6 +266,8 @@ static void vrrp_send_advertisement(struct vrrp_router *r) ssize_t sent = sendto(r->sock, pkt, (size_t)pktlen, 0, &dest.sa, sockunion_sizeof(&dest)); + XFREE(MTYPE_TMP, pkt); + if (sent < 0) { zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID "Failed to send VRRP Advertisement", From dad18a2fd76a8bd30dff5d1fa030a69962349b85 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 7 Jan 2019 19:02:53 +0000 Subject: [PATCH 021/153] vrrpd: add initial macvlan support * Search for macvlan interfaces with the appropriate name and MAC address when starting up a new VRRP instance * Split VRRP socket into two; one for Tx, one for Rx * Bind Tx socket to the macvlan subinterface so our VRRP advertisements go out with the correct MAC address * Send ARP requests from this macvlan subinterface * Improve error messaging Signed-off-by: Quentin Young --- lib/if.c | 28 ++++++ lib/if.h | 2 + vrrpd/vrrp.c | 229 ++++++++++++++++++++++++++++++++++++++------ vrrpd/vrrp.h | 9 +- vrrpd/vrrp_arp.c | 4 +- vrrpd/vrrp_packet.c | 8 +- 6 files changed, 244 insertions(+), 36 deletions(-) diff --git a/lib/if.c b/lib/if.c index 38f3f45ed1..8888411903 100644 --- a/lib/if.c +++ b/lib/if.c @@ -389,6 +389,34 @@ struct interface *if_lookup_prefix(struct prefix *prefix, vrf_id_t vrf_id) return NULL; } +size_t if_lookup_by_hwaddr(const uint8_t *hw_addr, size_t addrsz, + struct interface ***result, vrf_id_t vrf_id) +{ + struct vrf *vrf = vrf_lookup_by_id(vrf_id); + + struct list *rs = list_new(); + struct interface *ifp; + + FOR_ALL_INTERFACES (vrf, ifp) { + if (ifp->hw_addr_len == (int)addrsz + && !memcmp(hw_addr, ifp->hw_addr, addrsz)) + listnode_add(rs, ifp); + } + + if (rs->count) { + *result = XCALLOC(MTYPE_TMP, + sizeof(struct interface *) * rs->count); + list_to_array(rs, (void **)*result, rs->count); + } + + int count = rs->count; + + list_delete(&rs); + + return count; +} + + /* Get interface by name if given name interface doesn't exist create one. */ struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id) diff --git a/lib/if.h b/lib/if.h index d26d4dd68b..a98f907c5c 100644 --- a/lib/if.h +++ b/lib/if.h @@ -482,6 +482,8 @@ extern struct connected *if_lookup_address(void *matchaddr, int family, vrf_id_t vrf_id); extern struct interface *if_lookup_prefix(struct prefix *prefix, vrf_id_t vrf_id); +size_t if_lookup_by_hwaddr(const uint8_t *hw_addr, size_t addrsz, + struct interface ***result, vrf_id_t vrf_id); /* These 3 functions are to be used when the ifname argument is terminated by a '\0' character: */ diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 2eaf39fbb5..171f6abdb2 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -27,6 +27,7 @@ #include "lib/network.h" #include "lib/prefix.h" #include "lib/sockopt.h" +#include "lib/sockunion.h" #include "lib/vrf.h" #include "vrrp.h" @@ -93,6 +94,13 @@ static void vrrp_recalculate_timers(struct vrrp_router *r) /* * Determines if a VRRP router is the owner of the specified address. * + * The determining factor for whether an interface is the address owner is + * simply whether the address is assigned to the VRRP subinterface by someone + * other than vrrpd. + * + * This function should always return the correct answer regardless of + * master/backup status. + * * vr * Virtual Router * @@ -104,20 +112,30 @@ static bool vrrp_is_owner(struct vrrp_vrouter *vr, struct ipaddr *addr) struct prefix *p; struct prefix_ipv4 p4; struct prefix_ipv6 p6; + struct vrrp_router *r; if (IS_IPADDR_V4(addr)) { p4.family = AF_INET; p4.prefixlen = IPV4_MAX_BITLEN; p4.prefix = addr->ipaddr_v4; p = (struct prefix *)&p4; + r = vr->v4; } else { p6.family = AF_INET6; p6.prefixlen = IPV6_MAX_BITLEN; memcpy(&p6.prefix, &addr->ipaddr_v6, sizeof(struct in6_addr)); p = (struct prefix *)&p6; + r = vr->v6; } - return !!connected_lookup_prefix_exact(vr->ifp, p); + bool have_addr = !!connected_lookup_prefix_exact(r->mvl_ifp, p); + + /* did we assign it? */ + /* FIXME: this check is wrong, we need a flag to set when we install + * addresses on an interface when assuming master status; then + * ownership status is determined by (have_addr && !flag) in master + * state */ + return have_addr; } /* Configuration controllers ----------------------------------------------- */ @@ -176,20 +194,70 @@ static struct vrrp_router *vrrp_router_create(struct vrrp_vrouter *vr, struct vrrp_router *r = XCALLOC(MTYPE_TMP, sizeof(struct vrrp_router)); r->family = family; - r->sock = -1; + r->sock_rx = -1; + r->sock_tx = -1; 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); + /* Search for existing interface with computed MAC address */ + struct interface **ifps; + size_t ifps_cnt = if_lookup_by_hwaddr( + r->vmac.octet, sizeof(r->vmac.octet), &ifps, VRF_DEFAULT); + + /* + * Filter to only those interfaces whose names begin with VRRP + * interface name. E.g. if this VRRP instance was configured on eth0, + * then we filter the list to only keep interfaces matching ^eth0.* + * + * If there are still multiple interfaces we just select the first one, + * as it should be functionally identical to the others. + */ + unsigned int candidates = 0; + struct interface *selection = NULL; + for (unsigned int i = 0; i < ifps_cnt; i++) { + zlog_info("Found VRRP interface %s", ifps[i]->name); + if (strncmp(ifps[i]->name, r->vr->ifp->name, + strlen(r->vr->ifp->name))) + ifps[i] = NULL; + else { + selection = selection ? selection : ifps[i]; + candidates++; + } + } + + XFREE(MTYPE_TMP, ifps); + + char ethstr[ETHER_ADDR_STRLEN]; + prefix_mac2str(&r->vmac, ethstr, sizeof(ethstr)); + + assert(!!selection == !!candidates); + + if (candidates == 0) + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + "No interface found w/ MAC %s; using default", + r->vr->vrid, ethstr); + else if (candidates > 1) + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Multiple VRRP interfaces found; using %s", + r->vr->vrid, selection->name); + else + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Selected %s", + r->vr->vrid, selection->name); + + r->mvl_ifp = selection ? selection : r->vr->ifp; + return r; } static void vrrp_router_destroy(struct vrrp_router *r) { - if (r->sock >= 0) - close(r->sock); + if (r->sock_rx >= 0) + close(r->sock_rx); + if (r->sock_tx >= 0) + close(r->sock_tx); /* FIXME: also delete list elements */ list_delete(&r->addrs); XFREE(MTYPE_TMP, r); @@ -263,7 +331,7 @@ static void vrrp_send_advertisement(struct vrrp_router *r) r->family == AF_INET ? VRRP_MCASTV4_GROUP_STR : VRRP_MCASTV6_GROUP_STR; str2sockunion(group, &dest); - ssize_t sent = sendto(r->sock, pkt, (size_t)pktlen, 0, &dest.sa, + ssize_t sent = sendto(r->sock_tx, pkt, (size_t)pktlen, 0, &dest.sa, sockunion_sizeof(&dest)); XFREE(MTYPE_TMP, pkt); @@ -308,7 +376,7 @@ static int vrrp_read(struct thread *thread) m.msg_control = control; m.msg_controllen = sizeof(control); - nbytes = recvmsg(r->sock, &m, MSG_DONTWAIT); + nbytes = recvmsg(r->sock_rx, &m, MSG_DONTWAIT); if ((nbytes < 0 && ERRNO_IO_RETRY(errno))) { resched = true; @@ -342,38 +410,123 @@ done: memset(r->ibuf, 0x00, sizeof(r->ibuf)); if (resched) - thread_add_read(master, vrrp_read, r, r->sock, &r->t_read); + thread_add_read(master, vrrp_read, r, r->sock_rx, &r->t_read); return 0; } /* - * Create Virtual Router listen socket and join it to the VRRP multicast group. + * Finds the first connected address of the appropriate family on a VRRP + * router's interface and binds the Tx socket of the VRRP router to that + * address. + * + * r + * VRRP router to operate on + * + * Returns: + * 0 on success + * -1 on failure + */ +static int vrrp_bind_to_primary_connected(struct vrrp_router *r) +{ + char ipstr[INET6_ADDRSTRLEN]; + + struct listnode *ln; + struct connected *c = NULL; + for (ALL_LIST_ELEMENTS_RO(r->mvl_ifp->connected, ln, c)) + if (c->address->family == r->family) + break; + + if (c == NULL) { + zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Failed to find %s address to bind on %s", + r->vr->vrid, family2str(r->family), r->mvl_ifp->name); + return -1; + } + + struct sockaddr_in sa4 = { + .sin_family = AF_INET, + .sin_addr = c->address->u.prefix4, + }; + struct sockaddr_in6 sa6 = { + .sin6_family = AF_INET6, + .sin6_addr = c->address->u.prefix6, + }; + + struct sockaddr *sa = r->family == AF_INET ? (struct sockaddr *)&sa4 + : (struct sockaddr *)&sa6; + + sockopt_reuseaddr(r->sock_tx); + if (bind(r->sock_tx, sa, sizeof(struct sockaddr)) < 0) { + zlog_err( + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Failed to bind Tx socket to primary IP address %s: %s", + r->vr->vrid, + inet_ntop(r->family, + (const void *)&c->address->u.prefix, ipstr, + sizeof(ipstr)), + safe_strerror(errno)); + return -1; + } else { + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Bound Tx socket to primary IP address %s", + r->vr->vrid, + inet_ntop(r->family, + (const void *)&c->address->u.prefix, ipstr, + sizeof(ipstr))); + } + + return 0; +} + +/* + * Creates and configures VRRP router sockets. + * + * This function: + * - Creates two sockets, one for Tx, one for Rx + * - Joins the Rx socket to the appropriate VRRP multicast group + * - Sets the Tx socket to set the TTL (v4) or Hop Limit (v6) field to 255 for + * all transmitted IPvX packets + * - Requests the kernel to deliver IPv6 header values needed to validate VRRP + * packets + * - FIXME: Binds the Tx socket to the first address on the macvlan + * subinterface. + * + * If any of the above fail, the sockets are closed. The only exception is if + * the TTL / Hop Limit settings fail; these are logged, but configuration + * proceeds. * * The first connected address on the Virtual Router's interface is used as the * interface address. * * r * VRRP Router for which to create listen socket + * + * Returns: + * 0 on success + * -1 on failure */ static int vrrp_socket(struct vrrp_router *r) { int ret; bool failed = false; - struct connected *c; - frr_elevate_privs(&vrrp_privs) { - r->sock = socket(r->family, SOCK_RAW, IPPROTO_VRRP); + frr_elevate_privs(&vrrp_privs) + { + r->sock_rx = socket(r->family, SOCK_RAW, IPPROTO_VRRP); + r->sock_tx = socket(r->family, SOCK_RAW, IPPROTO_VRRP); } - if (r->sock < 0) { + if (r->sock_rx < 0 || r->sock_tx < 0) { + const char *rxtx = r->sock_rx < 0 ? "Rx" : "Tx"; zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Can't create %s VRRP socket", - r->vr->vrid, r->family == AF_INET ? "v4" : "v6"); + "Can't create %s VRRP %s socket", + r->vr->vrid, family2str(r->family), rxtx); failed = true; goto done; } + /* Configure sockets */ if (!listcount(r->vr->ifp->connected)) { zlog_warn( VRRP_LOGPFX VRRP_LOGPFX_VRID @@ -384,8 +537,9 @@ static int vrrp_socket(struct vrrp_router *r) } if (r->family == AF_INET) { + /* Set Tx socket to always Tx with TTL set to 255 */ int ttl = 255; - ret = setsockopt(r->sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, + ret = setsockopt(r->sock_tx, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); if (ret < 0) { zlog_warn( @@ -394,22 +548,22 @@ static int vrrp_socket(struct vrrp_router *r) r->vr->vrid); } - c = listhead(r->vr->ifp->connected)->data; + /* Join Rx socket to VRRP IPv4 multicast group */ + struct connected *c = listhead(r->vr->ifp->connected)->data; struct in_addr v4 = c->address->u.prefix4; - - /* Join VRRP IPv4 multicast group */ - ret = setsockopt_ipv4_multicast(r->sock, IP_ADD_MEMBERSHIP, v4, - htonl(VRRP_MCASTV4_GROUP), + ret = setsockopt_ipv4_multicast(r->sock_rx, IP_ADD_MEMBERSHIP, + v4, htonl(VRRP_MCASTV4_GROUP), r->vr->ifp->ifindex); } else if (r->family == AF_INET6) { - ret = setsockopt_ipv6_multicast_hops(r->sock, 255); + /* Always transmit IPv6 packets with hop limit set to 255 */ + ret = setsockopt_ipv6_multicast_hops(r->sock_tx, 255); if (ret < 0) { zlog_warn( VRRP_LOGPFX VRRP_LOGPFX_VRID "Failed to set outgoing multicast hop count to 255; RFC 5798 compliant implementations will drop our packets", r->vr->vrid); } - ret = setsockopt_ipv6_hoplimit(r->sock, 1); + ret = setsockopt_ipv6_hoplimit(r->sock_rx, 1); if (ret < 0) { zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID "Failed to request IPv6 Hop Limit delivery", @@ -420,10 +574,11 @@ static int vrrp_socket(struct vrrp_router *r) /* Join VRRP IPv6 multicast group */ struct ipv6_mreq mreq; - inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &mreq.ipv6mr_multiaddr); + 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)); + ret = setsockopt(r->sock_rx, IPPROTO_IPV6, IPV6_JOIN_GROUP, + &mreq, sizeof(mreq)); } if (ret < 0) { @@ -431,15 +586,29 @@ static int vrrp_socket(struct vrrp_router *r) "Failed to join VRRP %s multicast group", r->vr->vrid, family2str(r->family)); failed = true; + goto done; + } else { + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Joined %s VRRP multicast group", + r->vr->vrid, family2str(r->family)); } + + /* Bind Tx socket to link-local address */ + if (vrrp_bind_to_primary_connected(r) < 0) { + failed = true; + goto done; + } + done: ret = 0; if (failed) { zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID "Failed to initialize VRRP %s router", r->vr->vrid, family2str(r->family)); - if (r->sock >= 0) - close(r->sock); + if (r->sock_rx >= 0) + close(r->sock_rx); + if (r->sock_tx >= 0) + close(r->sock_tx); ret = -1; } @@ -584,14 +753,14 @@ static int vrrp_startup(struct vrrp_router *r) vrrp_garp_init(); /* Create socket */ - if (r->sock < 0) { + if (r->sock_rx < 0 || r->sock_tx < 0) { int ret = vrrp_socket(r); - if (ret < 0 || r->sock < 0) + if (ret < 0 || r->sock_tx < 0 || r->sock_rx < 0) return ret; } /* Schedule listener */ - thread_add_read(master, vrrp_read, r, r->sock, &r->t_read); + thread_add_read(master, vrrp_read, r, r->sock_rx, &r->t_read); /* Configure effective priority */ struct ipaddr *primary = (struct ipaddr *)listhead(r->addrs)->data; diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 64e29eec00..c51e3e32c4 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -64,8 +64,13 @@ struct vrrp_router { */ bool is_active; - /* Socket */ - int sock; + /* Rx socket: Rx from parent of mvl_ifp */ + int sock_rx; + /* Tx socket; Tx from mvl_ifp */ + int sock_tx; + + /* macvlan interface */ + struct interface *mvl_ifp; /* Socket read buffer */ uint8_t ibuf[IP_MAXPACKET]; diff --git a/vrrpd/vrrp_arp.c b/vrrpd/vrrp_arp.c index 95e9de86a7..e7a037d089 100644 --- a/vrrpd/vrrp_arp.c +++ b/vrrpd/vrrp_arp.c @@ -114,7 +114,7 @@ static ssize_t vrrp_build_garp(uint8_t *buf, struct interface *ifp, void vrrp_garp_send(struct vrrp_router *r, struct in_addr *v4) { - struct interface *ifp = r->vr->ifp; + struct interface *ifp = r->mvl_ifp; uint8_t garpbuf[GARP_BUFFER_SIZE]; ssize_t garpbuf_len; ssize_t sent_len; @@ -149,7 +149,7 @@ void vrrp_garp_send_all(struct vrrp_router *r) { assert(r->family == AF_INET); - struct interface *ifp = r->vr->ifp; + struct interface *ifp = r->mvl_ifp; /* If the interface doesn't support ARP, don't try sending */ if (ifp->flags & IFF_NOARP) { diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index d305081574..5010b4701d 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -149,7 +149,9 @@ ssize_t vrrp_parse_datagram(int family, struct msghdr *m, size_t read, ntohs(ip->ip_len), read); /* TTL check */ - VRRP_PKT_VCHECK(ip->ip_ttl == 255, "IPv4 TTL is not 255"); + VRRP_PKT_VCHECK(ip->ip_ttl == 255, + "IPv4 TTL is %" PRIu8 "; should be 255", + ip->ip_ttl); *pkt = (struct vrrp_pkt *)(buf + (ip->ip_hl << 2)); pktsize = read - (ip->ip_hl << 2); @@ -167,7 +169,9 @@ ssize_t vrrp_parse_datagram(int family, struct msghdr *m, size_t read, VRRP_PKT_VCHECK(!!c, "IPv6 Hop Limit not received"); uint8_t *hoplimit = CMSG_DATA(c); - VRRP_PKT_VCHECK(*hoplimit == 255, "IPv6 Hop Limit is not 255"); + VRRP_PKT_VCHECK(*hoplimit == 255, + "IPv6 Hop Limit is %" PRIu8 "; should be 255", + *hoplimit); *pkt = (struct vrrp_pkt *)buf; pktsize = read; From 10133a59968b84018098a6b5fffdbcc0d0389a4f Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 16 Jan 2019 20:01:45 +0000 Subject: [PATCH 022/153] vrrpd: handle incoming advertisements Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++- vrrpd/vrrp.h | 3 ++ 2 files changed, 127 insertions(+), 1 deletion(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 171f6abdb2..f761028846 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -165,6 +165,18 @@ void vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4) v4_ins->ipa_type = IPADDR_V4; v4_ins->ipaddr_v4 = v4; + + if (!vrrp_is_owner(vr, v4_ins) && vr->v4->is_owner) { + char ipbuf[INET6_ADDRSTRLEN]; + ipaddr2str(v4_ins, ipbuf, sizeof(ipbuf)); + zlog_err( + VRRP_LOGPFX VRRP_LOGPFX_VRID + "This VRRP router is not the address owner of %s, but is the address owner of other addresses; this config is unsupported.", + vr->vrid, ipbuf); + /* FIXME: indicate failure with rc */ + return; + } + listnode_add(vr->v4->addrs, v4_ins); } @@ -174,6 +186,18 @@ void vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6) v6_ins->ipa_type = IPADDR_V6; memcpy(&v6_ins->ipaddr_v6, &v6, sizeof(struct in6_addr)); + + if (!vrrp_is_owner(vr, v6_ins) && vr->v6->is_owner) { + char ipbuf[INET6_ADDRSTRLEN]; + ipaddr2str(v6_ins, ipbuf, sizeof(ipbuf)); + zlog_err( + VRRP_LOGPFX VRRP_LOGPFX_VRID + "This VRRP router is not the address owner of %s, but is the address owner of other addresses; this config is unsupported.", + vr->vrid, ipbuf); + /* FIXME: indicate failure with rc */ + return; + } + listnode_add(vr->v6->addrs, v6_ins); } @@ -303,6 +327,11 @@ struct vrrp_vrouter *vrrp_lookup(uint8_t vrid) /* Network ----------------------------------------------------------------- */ +/* Forward decls */ +static void vrrp_change_state(struct vrrp_router *r, int to); +static int vrrp_adver_timer_expire(struct thread *thread); +static int vrrp_master_down_timer_expire(struct thread *thread); + /* * Create and multicast a VRRP ADVERTISEMENT message. * @@ -343,12 +372,104 @@ static void vrrp_send_advertisement(struct vrrp_router *r) } } -static void vrrp_recv_advertisement(struct vrrp_router *r, struct vrrp_pkt *pkt, +/* + * Receive and parse VRRP advertisement. + * + * By the time we get here all fields have been validated for basic correctness + * and the packet is a valid VRRP packet. + * + * However, we have not validated whether the VRID is correct for this virtual + * router, nor whether the priority is correct (i.e. is not 255 when we are the + * address owner). + */ +static int vrrp_recv_advertisement(struct vrrp_router *r, struct vrrp_pkt *pkt, size_t pktsize) { char dumpbuf[BUFSIZ]; vrrp_pkt_dump(dumpbuf, sizeof(dumpbuf), pkt); zlog_debug("Received VRRP Advertisement:\n%s", dumpbuf); + + /* Check that VRID matches our configured VRID */ + if (pkt->hdr.vrid != r->vr->vrid) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID + "%s datagram invalid: Advertisement contains VRID %" PRIu8 + " which does not match our instance", + r->vr->vrid, family2str(r->family), pkt->hdr.vrid); + return -1; + } + + /* Verify that we are not the IPvX address owner */ + if (r->is_owner) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID + "%s datagram invalid: Received advertisement but we are the address owner", + r->vr->vrid, family2str(r->family)); + return -1; + } + + /* Check that # IPs received matches our # configured IPs */ + if (pkt->hdr.naddr != r->addrs->count) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + "%s datagram has %" PRIu8 + " addresses, but this VRRP instance has %u", + r->vr->vrid, family2str(r->family), pkt->hdr.naddr, + r->addrs->count); + } + + switch (r->fsm.state) { + case VRRP_STATE_MASTER: + if (pkt->hdr.priority == 0) { + vrrp_send_advertisement(r); + THREAD_OFF(r->t_adver_timer); + thread_add_timer_msec( + master, vrrp_adver_timer_expire, r, + r->vr->advertisement_interval * 10, + &r->t_adver_timer); + /* FIXME: 6.4.3 mandates checking sender IP address */ + } else if (pkt->hdr.priority > r->priority) { + zlog_err("NOT IMPLEMENTED"); + THREAD_OFF(r->t_adver_timer); + r->master_adver_interval = ntohs(pkt->hdr.v3.adver_int); + vrrp_recalculate_timers(r); + THREAD_OFF(r->t_master_down_timer); + 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); + } else { + /* Discard advertisement */ + } + break; + case VRRP_STATE_BACKUP: + if (pkt->hdr.priority == 0) { + THREAD_OFF(r->t_master_down_timer); + thread_add_timer_msec( + master, vrrp_master_down_timer_expire, r, + r->skew_time * 10, &r->t_master_down_timer); + } else if (r->vr->preempt_mode == false + || pkt->hdr.priority >= r->priority) { + r->master_adver_interval = ntohs(pkt->hdr.v3.adver_int); + vrrp_recalculate_timers(r); + THREAD_OFF(r->t_master_down_timer); + thread_add_timer_msec(master, + vrrp_master_down_timer_expire, r, + r->master_down_interval * 10, + &r->t_master_down_timer); + } else if (r->vr->preempt_mode == true + && pkt->hdr.priority < r->priority) { + /* Discard advertisement */ + } + break; + case VRRP_STATE_INITIALIZE: + zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Received ADVERTISEMENT in state %s; this is a bug", + r->vr->vrid, vrrp_state_names[r->fsm.state]); + break; + } + + return 0; } /* @@ -404,6 +525,8 @@ static int vrrp_read(struct thread *thread) vrrp_recv_advertisement(r, pkt, pktsize); } + XFREE(MTYPE_TMP, pkt); + resched = true; done: diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index c51e3e32c4..98948a76fc 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -64,6 +64,9 @@ struct vrrp_router { */ bool is_active; + /* Whether we are the address owner */ + bool is_owner; + /* Rx socket: Rx from parent of mvl_ifp */ int sock_rx; /* Tx socket; Tx from mvl_ifp */ From c7e3b83d154e8e607c4ff6f11a764fda259cba54 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 16 Jan 2019 20:09:17 +0000 Subject: [PATCH 023/153] vrrpd: transition to master when adv timer expires Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index f761028846..1f89ef7b96 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -845,6 +845,14 @@ static int vrrp_master_down_timer_expire(struct thread *thread) zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Master_Down_Timer expired", r->vr->vrid); + vrrp_send_advertisement(r); + 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); + return 0; } From 7e205b4a8ff1b16c800c2d5bbf198abf0a07f901 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 16 Jan 2019 23:14:40 +0000 Subject: [PATCH 024/153] vrrpd: fix ownership discovery and mcast OIF * Look for virtual IP ownership on the parent of the macvlan, not the macvlan itself * IPv4: bind socket to real IP of the macvlan parent, but transmit on the macvlan interface * IPv6: bind socket to IPv6 link local of the macvlan interface Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 116 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 73 insertions(+), 43 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 1f89ef7b96..3677e9c76d 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -107,35 +107,15 @@ static void vrrp_recalculate_timers(struct vrrp_router *r) * Returns: * whether or not vr owns the specified address */ -static bool vrrp_is_owner(struct vrrp_vrouter *vr, struct ipaddr *addr) +static bool vrrp_is_owner(struct interface *ifp, struct ipaddr *addr) { - struct prefix *p; - struct prefix_ipv4 p4; - struct prefix_ipv6 p6; - struct vrrp_router *r; + struct prefix p; - if (IS_IPADDR_V4(addr)) { - p4.family = AF_INET; - p4.prefixlen = IPV4_MAX_BITLEN; - p4.prefix = addr->ipaddr_v4; - p = (struct prefix *)&p4; - r = vr->v4; - } else { - p6.family = AF_INET6; - p6.prefixlen = IPV6_MAX_BITLEN; - memcpy(&p6.prefix, &addr->ipaddr_v6, sizeof(struct in6_addr)); - p = (struct prefix *)&p6; - r = vr->v6; - } + p.family = IS_IPADDR_V4(addr) ? AF_INET : AF_INET6; + p.prefixlen = IS_IPADDR_V4(addr) ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN; + memcpy(&p.u, &addr->ip, sizeof(addr->ip)); - bool have_addr = !!connected_lookup_prefix_exact(r->mvl_ifp, p); - - /* did we assign it? */ - /* FIXME: this check is wrong, we need a flag to set when we install - * addresses on an interface when assuming master status; then - * ownership status is determined by (have_addr && !flag) in master - * state */ - return have_addr; + return !!connected_lookup_prefix_exact(ifp, &p); } /* Configuration controllers ----------------------------------------------- */ @@ -166,7 +146,7 @@ void vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4) v4_ins->ipa_type = IPADDR_V4; v4_ins->ipaddr_v4 = v4; - if (!vrrp_is_owner(vr, v4_ins) && vr->v4->is_owner) { + if (!vrrp_is_owner(vr->ifp, v4_ins) && vr->v4->is_owner) { char ipbuf[INET6_ADDRSTRLEN]; ipaddr2str(v4_ins, ipbuf, sizeof(ipbuf)); zlog_err( @@ -187,7 +167,7 @@ void vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6) v6_ins->ipa_type = IPADDR_V6; memcpy(&v6_ins->ipaddr_v6, &v6, sizeof(struct in6_addr)); - if (!vrrp_is_owner(vr, v6_ins) && vr->v6->is_owner) { + if (!vrrp_is_owner(vr->ifp, v6_ins) && vr->v6->is_owner) { char ipbuf[INET6_ADDRSTRLEN]; ipaddr2str(v6_ins, ipbuf, sizeof(ipbuf)); zlog_err( @@ -271,7 +251,7 @@ static struct vrrp_router *vrrp_router_create(struct vrrp_vrouter *vr, zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Selected %s", r->vr->vrid, selection->name); - r->mvl_ifp = selection ? selection : r->vr->ifp; + r->mvl_ifp = selection; return r; } @@ -553,34 +533,44 @@ done: static int vrrp_bind_to_primary_connected(struct vrrp_router *r) { char ipstr[INET6_ADDRSTRLEN]; + struct interface *ifp; + + /* + * A slight quirk: the RFC specifies that advertisements under IPv6 must + * be transmitted using the link local address of the source interface + */ + ifp = r->family == AF_INET ? r->vr->ifp : r->mvl_ifp; struct listnode *ln; struct connected *c = NULL; - for (ALL_LIST_ELEMENTS_RO(r->mvl_ifp->connected, ln, c)) + for (ALL_LIST_ELEMENTS_RO(ifp->connected, ln, c)) if (c->address->family == r->family) break; if (c == NULL) { zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID "Failed to find %s address to bind on %s", - r->vr->vrid, family2str(r->family), r->mvl_ifp->name); + r->vr->vrid, family2str(r->family), ifp->name); return -1; } - struct sockaddr_in sa4 = { - .sin_family = AF_INET, - .sin_addr = c->address->u.prefix4, - }; - struct sockaddr_in6 sa6 = { - .sin6_family = AF_INET6, - .sin6_addr = c->address->u.prefix6, - }; + union sockunion su; + memset(&su, 0x00, sizeof(su)); - struct sockaddr *sa = r->family == AF_INET ? (struct sockaddr *)&sa4 - : (struct sockaddr *)&sa6; + switch (r->family) { + case AF_INET: + su.sin.sin_family = AF_INET; + su.sin.sin_addr = c->address->u.prefix4; + break; + case AF_INET6: + su.sin6.sin6_family = AF_INET6; + su.sin6.sin6_scope_id = ifp->ifindex; + su.sin6.sin6_addr = c->address->u.prefix6; + break; + } sockopt_reuseaddr(r->sock_tx); - if (bind(r->sock_tx, sa, sizeof(struct sockaddr)) < 0) { + if (bind(r->sock_tx, (const struct sockaddr *)&su, sizeof(su)) < 0) { zlog_err( VRRP_LOGPFX VRRP_LOGPFX_VRID "Failed to bind Tx socket to primary IP address %s: %s", @@ -677,6 +667,23 @@ static int vrrp_socket(struct vrrp_router *r) ret = setsockopt_ipv4_multicast(r->sock_rx, IP_ADD_MEMBERSHIP, v4, htonl(VRRP_MCASTV4_GROUP), r->vr->ifp->ifindex); + + /* Set outgoing interface for advertisements */ + struct ip_mreqn mreqn = {}; + mreqn.imr_ifindex = r->mvl_ifp->ifindex; + ret = setsockopt(r->sock_tx, IPPROTO_IP, IP_MULTICAST_IF, + (void *)&mreqn, sizeof(mreqn)); + if (ret < 0) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Could not set %s as outgoing multicast interface", + r->vr->vrid, r->mvl_ifp->name); + failed = true; + goto done; + } + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Set %s as outgoing multicast interface", + r->vr->vrid, r->mvl_ifp->name); } else if (r->family == AF_INET6) { /* Always transmit IPv6 packets with hop limit set to 255 */ ret = setsockopt_ipv6_multicast_hops(r->sock_tx, 255); @@ -702,6 +709,21 @@ static int vrrp_socket(struct vrrp_router *r) mreq.ipv6mr_interface = r->vr->ifp->ifindex; ret = setsockopt(r->sock_rx, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)); + + /* Set outgoing interface for advertisements */ + ret = setsockopt(r->sock_tx, IPPROTO_IPV6, IPV6_MULTICAST_IF, + &r->mvl_ifp->ifindex, sizeof(ifindex_t)); + if (ret < 0) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Could not set %s as outgoing multicast interface", + r->vr->vrid, r->mvl_ifp->name); + failed = true; + goto done; + } + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Set %s as outgoing multicast interface", + r->vr->vrid, r->mvl_ifp->name); } if (ret < 0) { @@ -879,6 +901,14 @@ static int vrrp_startup(struct vrrp_router *r) if (r->fsm.state != VRRP_STATE_INITIALIZE) return -1; + /* Must have a valid macvlan interface available */ + if (r->mvl_ifp == NULL) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + "No appropriate interface for %s VRRP found", + r->vr->vrid, family2str(r->family)); + return -1; + } + /* Initialize global gratuitous ARP socket if necessary */ if (r->family == AF_INET && !vrrp_garp_is_init()) vrrp_garp_init(); @@ -899,7 +929,7 @@ static int vrrp_startup(struct vrrp_router *r) char ipbuf[INET6_ADDRSTRLEN]; inet_ntop(r->family, &primary->ip.addr, ipbuf, sizeof(ipbuf)); - if (vrrp_is_owner(r->vr, primary)) { + if (vrrp_is_owner(r->vr->ifp, primary)) { r->priority = VRRP_PRIO_MASTER; vrrp_recalculate_timers(r); From b523b2419e1da54c906ab5a5892ea91f47b94acb Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 17 Jan 2019 22:34:25 +0000 Subject: [PATCH 025/153] vrrpd: bind sockets to interfaces Bind Rx socket to inbound interface. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 106 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 92 insertions(+), 14 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 3677e9c76d..93ede7b88f 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -602,8 +602,6 @@ static int vrrp_bind_to_primary_connected(struct vrrp_router *r) * all transmitted IPvX packets * - Requests the kernel to deliver IPv6 header values needed to validate VRRP * packets - * - FIXME: Binds the Tx socket to the first address on the macvlan - * subinterface. * * If any of the above fail, the sockets are closed. The only exception is if * the TTL / Hop Limit settings fail; these are logged, but configuration @@ -661,12 +659,58 @@ static int vrrp_socket(struct vrrp_router *r) r->vr->vrid); } + /* Bind Rx socket to exact interface */ + vrrp_privs.change(ZPRIVS_RAISE); + { + ret = setsockopt(r->sock_rx, SOL_SOCKET, + SO_BINDTODEVICE, r->vr->ifp->name, + strlen(r->vr->ifp->name)); + } + vrrp_privs.change(ZPRIVS_LOWER); + if (ret) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Failed to bind Rx socket to %s: %s", + r->vr->vrid, r->vr->ifp->name, + safe_strerror(errno)); + failed = true; + goto done; + } + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Bound Rx socket to %s", + r->vr->vrid, r->vr->ifp->name); + + /* Bind Rx socket to v4 multicast address */ + struct sockaddr_in sa = {0}; + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = htonl(VRRP_MCASTV4_GROUP); + if (bind(r->sock_rx, (struct sockaddr *)&sa, sizeof(sa))) { + zlog_err( + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Failed to bind Rx socket to VRRP %s multicast group: %s", + r->vr->vrid, family2str(r->family), + safe_strerror(errno)); + failed = true; + goto done; + } + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Bound Rx socket to VRRP %s multicast group", + r->vr->vrid, family2str(r->family)); + /* Join Rx socket to VRRP IPv4 multicast group */ struct connected *c = listhead(r->vr->ifp->connected)->data; struct in_addr v4 = c->address->u.prefix4; ret = setsockopt_ipv4_multicast(r->sock_rx, IP_ADD_MEMBERSHIP, v4, htonl(VRRP_MCASTV4_GROUP), r->vr->ifp->ifindex); + if (ret < 0) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Failed to join VRRP %s multicast group", + r->vr->vrid, family2str(r->family)); + failed = true; + goto done; + } + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Joined %s VRRP multicast group", + r->vr->vrid, family2str(r->family)); /* Set outgoing interface for advertisements */ struct ip_mreqn mreqn = {}; @@ -702,6 +746,42 @@ static int vrrp_socket(struct vrrp_router *r) goto done; } + /* Bind Rx socket to exact interface */ + vrrp_privs.change(ZPRIVS_RAISE); + { + ret = setsockopt(r->sock_rx, SOL_SOCKET, + SO_BINDTODEVICE, r->vr->ifp->name, + strlen(r->vr->ifp->name)); + } + vrrp_privs.change(ZPRIVS_LOWER); + if (ret) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Failed to bind Rx socket to %s: %s", + r->vr->vrid, r->vr->ifp->name, + safe_strerror(errno)); + failed = true; + goto done; + } + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Bound Rx socket to %s", + r->vr->vrid, r->vr->ifp->name); + + /* Bind Rx socket to v6 multicast address */ + struct sockaddr_in6 sa = {0}; + sa.sin6_family = AF_INET6; + inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &sa.sin6_addr); + if (bind(r->sock_rx, (struct sockaddr *)&sa, sizeof(sa))) { + zlog_err( + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Failed to bind Rx socket to VRRP %s multicast group: %s", + r->vr->vrid, family2str(r->family), + safe_strerror(errno)); + failed = true; + goto done; + } + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Bound Rx socket to VRRP %s multicast group", + r->vr->vrid, family2str(r->family)); + /* Join VRRP IPv6 multicast group */ struct ipv6_mreq mreq; inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, @@ -709,6 +789,16 @@ static int vrrp_socket(struct vrrp_router *r) mreq.ipv6mr_interface = r->vr->ifp->ifindex; ret = setsockopt(r->sock_rx, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)); + if (ret < 0) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Failed to join VRRP %s multicast group", + r->vr->vrid, family2str(r->family)); + failed = true; + goto done; + } + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Joined %s VRRP multicast group", + r->vr->vrid, family2str(r->family)); /* Set outgoing interface for advertisements */ ret = setsockopt(r->sock_tx, IPPROTO_IPV6, IPV6_MULTICAST_IF, @@ -726,18 +816,6 @@ static int vrrp_socket(struct vrrp_router *r) r->vr->vrid, r->mvl_ifp->name); } - if (ret < 0) { - zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Failed to join VRRP %s multicast group", - r->vr->vrid, family2str(r->family)); - failed = true; - goto done; - } else { - zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Joined %s VRRP multicast group", - r->vr->vrid, family2str(r->family)); - } - /* Bind Tx socket to link-local address */ if (vrrp_bind_to_primary_connected(r) < 0) { failed = true; From 17b48d7d1150ba4b4ee689a925f54b2a236b1d23 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 22 Jan 2019 22:39:47 +0000 Subject: [PATCH 026/153] lib: add internet checksum with pseudoheaders Add convenience functions to compute the Internet checksum of a data block, including a pseudoheader. Signed-off-by: Quentin Young --- lib/checksum.c | 18 ++++++++++++++++++ lib/checksum.h | 27 ++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/lib/checksum.c b/lib/checksum.c index 18e3850474..3473370041 100644 --- a/lib/checksum.c +++ b/lib/checksum.c @@ -46,6 +46,24 @@ int /* return checksum in low-order 16 bits */ return (answer); } +int in_cksum_with_ph4(struct ipv4_ph *ph, void *data, int nbytes) +{ + uint8_t dat[sizeof(struct ipv4_ph) + nbytes]; + + memcpy(dat, ph, sizeof(struct ipv4_ph)); + memcpy(dat + sizeof(struct ipv4_ph), data, nbytes); + return in_cksum(dat, sizeof(dat)); +} + +int in_cksum_with_ph6(struct ipv6_ph *ph, void *data, int nbytes) +{ + uint8_t dat[sizeof(struct ipv6_ph) + nbytes]; + + memcpy(dat, ph, sizeof(struct ipv6_ph)); + memcpy(dat + sizeof(struct ipv6_ph), data, nbytes); + return in_cksum(dat, sizeof(dat)); +} + /* Fletcher Checksum -- Refer to RFC1008. */ #define MODX 4102U /* 5802 should be fine */ diff --git a/lib/checksum.h b/lib/checksum.h index 7d50371439..56771d4f24 100644 --- a/lib/checksum.h +++ b/lib/checksum.h @@ -1,8 +1,33 @@ +#include +#include + #ifdef __cplusplus extern "C" { #endif -extern int in_cksum(void *, int); + +/* IPv4 pseudoheader */ +struct ipv4_ph { + struct in_addr src; + struct in_addr dst; + uint8_t rsvd; + uint8_t proto; + uint16_t len; +} __attribute__((packed)); + +/* IPv6 pseudoheader */ +struct ipv6_ph { + struct in6_addr src; + struct in6_addr dst; + uint32_t ulpl; + uint8_t zero[3]; + uint8_t next_hdr; +} __attribute__((packed)); + +extern int in_cksum(void *data, int nbytes); +extern int in_cksum_with_ph4(struct ipv4_ph *ph, void *data, int nbytes); +extern int in_cksum_with_ph6(struct ipv6_ph *ph, void *data, int nbytes); + #define FLETCHER_CHECKSUM_VALIDATE 0xffff extern uint16_t fletcher_checksum(uint8_t *, const size_t len, const uint16_t offset); From 8071d5c3e30a68d7bb9ca1bbf48b45f64b459a12 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 22 Jan 2019 22:49:58 +0000 Subject: [PATCH 027/153] vrrpd: compute VRRPv3 checksum Correctly compute VRRPv3 checksum. Pseudoheaders are used for both IPv4 and IPv6. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 12 +++++++++--- vrrpd/vrrp.h | 3 +++ vrrpd/vrrp_packet.c | 24 +++++++++++++++++++----- vrrpd/vrrp_packet.h | 4 ++-- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 93ede7b88f..fcc1cec51c 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -327,7 +327,7 @@ static void vrrp_send_advertisement(struct vrrp_router *r) list_to_array(r->addrs, (void **)addrs, r->addrs->count); - pktlen = vrrp_pkt_build(&pkt, r->vr->vrid, r->priority, + pktlen = vrrp_pkt_build(&pkt, &r->src, r->vr->vrid, r->priority, r->vr->advertisement_interval, r->addrs->count, (struct ipaddr **)&addrs); @@ -347,8 +347,8 @@ static void vrrp_send_advertisement(struct vrrp_router *r) if (sent < 0) { zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Failed to send VRRP Advertisement", - r->vr->vrid); + "Failed to send VRRP Advertisement: %s", + r->vr->vrid, safe_strerror(errno)); } } @@ -523,6 +523,8 @@ done: * router's interface and binds the Tx socket of the VRRP router to that * address. * + * Also sets src field of vrrp_router. + * * r * VRRP router to operate on * @@ -559,10 +561,14 @@ static int vrrp_bind_to_primary_connected(struct vrrp_router *r) switch (r->family) { case AF_INET: + r->src.ipa_type = IPADDR_V4; + r->src.ipaddr_v4 = c->address->u.prefix4; su.sin.sin_family = AF_INET; su.sin.sin_addr = c->address->u.prefix4; break; case AF_INET6: + r->src.ipa_type = IPADDR_V6; + r->src.ipaddr_v6 = c->address->u.prefix6; su.sin6.sin6_family = AF_INET6; su.sin6.sin6_scope_id = ifp->ifindex; su.sin6.sin6_addr = c->address->u.prefix6; diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 98948a76fc..f68ff85224 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -75,6 +75,9 @@ struct vrrp_router { /* macvlan interface */ struct interface *mvl_ifp; + /* Source address for advertisements */ + struct ipaddr src; + /* Socket read buffer */ uint8_t ibuf[IP_MAXPACKET]; diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index 5010b4701d..8147da68e0 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -26,6 +26,7 @@ #include "lib/ipaddr.h" #include "lib/memory.h" +#include "vrrp.h" #include "vrrp_packet.h" /* clang-format off */ @@ -49,8 +50,8 @@ const char *vrrp_packet_names[16] = { }; /* clang-format on */ -ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, uint8_t vrid, uint8_t prio, - uint16_t max_adver_int, uint8_t numip, +ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, struct ipaddr *src, uint8_t vrid, + uint8_t prio, uint16_t max_adver_int, uint8_t numip, struct ipaddr **ips) { bool v6 = IS_IPADDR_V6(ips[0]); @@ -72,11 +73,24 @@ ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, uint8_t vrid, uint8_t prio, memcpy(aptr, &ips[i]->ip.addr, addrsz); aptr += addrsz; } + (*pkt)->hdr.chksum = 0; - /* FIXME: v6 checksum */ - uint16_t chksum = in_cksum(*pkt, pktsize); - (*pkt)->hdr.chksum = htons(chksum); + if (v6) { + struct ipv6_ph ph = {}; + ph.src = src->ipaddr_v6; + inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &ph.dst); + ph.ulpl = htons(pktsize); + ph.next_hdr = 112; + (*pkt)->hdr.chksum = in_cksum_with_ph6(&ph, *pkt, pktsize); + } else { + struct ipv4_ph ph = {}; + ph.src = src->ipaddr_v4; + inet_pton(AF_INET, VRRP_MCASTV4_GROUP_STR, &ph.dst); + ph.proto = 112; + ph.len = htons(pktsize); + (*pkt)->hdr.chksum = in_cksum_with_ph4(&ph, *pkt, pktsize); + } return pktsize; } diff --git a/vrrpd/vrrp_packet.h b/vrrpd/vrrp_packet.h index 7a4a338dae..3a5b161fb7 100644 --- a/vrrpd/vrrp_packet.h +++ b/vrrpd/vrrp_packet.h @@ -115,8 +115,8 @@ struct vrrp_pkt { * array of pointer to either struct in_addr (v6 = false) or struct in6_addr * (v6 = true) */ -ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, uint8_t vrid, uint8_t prio, - uint16_t max_adver_int, uint8_t numip, +ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, struct ipaddr *src, uint8_t vrid, + uint8_t prio, uint16_t max_adver_int, uint8_t numip, struct ipaddr **ips); /* From b79640e4b06b7c4e3e8a45d4029113fb8ba91a28 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 23 Jan 2019 22:30:02 +0000 Subject: [PATCH 028/153] vrrpd: fix a few bad XFREEs Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index fcc1cec51c..94f59f37ff 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -232,7 +232,8 @@ static struct vrrp_router *vrrp_router_create(struct vrrp_vrouter *vr, } } - XFREE(MTYPE_TMP, ifps); + if (ifps_cnt) + XFREE(MTYPE_TMP, ifps); char ethstr[ETHER_ADDR_STRLEN]; prefix_mac2str(&r->vmac, ethstr, sizeof(ethstr)); @@ -505,8 +506,6 @@ static int vrrp_read(struct thread *thread) vrrp_recv_advertisement(r, pkt, pktsize); } - XFREE(MTYPE_TMP, pkt); - resched = true; done: From 64b44915925af89eecaea1001fd7340a598c0a4d Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 23 Jan 2019 22:31:25 +0000 Subject: [PATCH 029/153] vrrpd: remove bad priority check Signed-off-by: Quentin Young --- vrrpd/vrrp_packet.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index 8147da68e0..bf1197422e 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -211,10 +211,6 @@ ssize_t vrrp_parse_datagram(int family, struct msghdr *m, size_t read, /* Type check */ VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %u", (*pkt)->hdr.vertype & 0x0f); - /* Priority check */ - VRRP_PKT_VCHECK((*pkt)->hdr.priority == 255 - || (*pkt)->hdr.priority == 0, - "Bad priority %u", (*pkt)->hdr.priority); /* # addresses check */ size_t ves = VRRP_PKT_SIZE(family, (*pkt)->hdr.naddr); VRRP_PKT_VCHECK(pktsize == ves, "Packet has incorrect # addresses"); From 6e9529edd655a99e21672649c21621c0a27454bf Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 23 Jan 2019 22:59:07 +0000 Subject: [PATCH 030/153] vrrpd: disable multicast loopback Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 94f59f37ff..90ea25d776 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -664,6 +664,9 @@ static int vrrp_socket(struct vrrp_router *r) r->vr->vrid); } + /* Turn off multicast loop on Tx */ + setsockopt_ipv4_multicast_loop(r->sock_tx, 0); + /* Bind Rx socket to exact interface */ vrrp_privs.change(ZPRIVS_RAISE); { @@ -751,6 +754,9 @@ static int vrrp_socket(struct vrrp_router *r) goto done; } + /* Turn off multicast loop on Tx */ + setsockopt_ipv6_multicast_loop(r->sock_tx, 0); + /* Bind Rx socket to exact interface */ vrrp_privs.change(ZPRIVS_RAISE); { From bac08ded236867e7a0fb1806ae2fc77beff91002 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 24 Jan 2019 20:36:48 +0000 Subject: [PATCH 031/153] vrrpd: fix priority setting Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 2 ++ vrrpd/vrrp_vty.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 90ea25d776..9793eaf00f 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -126,6 +126,8 @@ void vrrp_set_priority(struct vrrp_vrouter *vr, uint8_t priority) return; vr->priority = priority; + vr->v4->priority = priority; + vr->v6->priority = priority; } void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr, diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 1d01b920b1..3726841ed1 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -78,12 +78,12 @@ DEFPY(vrrp_vrid, DEFPY(vrrp_priority, vrrp_priority_cmd, - "[no] vrrp (1-255)$vrid priority (1-255)", + "[no] vrrp (1-255)$vrid priority (1-254)", NO_STR VRRP_STR VRRP_VRID_STR VRRP_PRIORITY_STR - "Priority value; set 255 to designate this Virtual Router as Master\n") + "Priority value") { struct vrrp_vrouter *vr; struct vrrp_router *r; From 73b5cb19683b85df197ee5ae28155e194df14d0d Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 25 Jan 2019 16:26:13 +0000 Subject: [PATCH 032/153] vrrpd: unset active flag on shutdown Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 9793eaf00f..f0f7aedb85 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1087,6 +1087,8 @@ static int vrrp_shutdown(struct vrrp_router *r) /* Transition to the Initialize state */ vrrp_change_state(r, VRRP_STATE_INITIALIZE); + r->is_active = false; + return 0; } From 6287cefe9cefc32bda53dd7aa30adb3be8869443 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 25 Jan 2019 17:54:34 +0000 Subject: [PATCH 033/153] vrrpd: implement `no` variants of commands Except removing v4/v6 addresses Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 24 +++++++++++++++++++++--- vrrpd/vrrp.h | 4 +++- vrrpd/vrrp_vty.c | 36 ++++++++++++++++++++++++------------ 3 files changed, 48 insertions(+), 16 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index f0f7aedb85..c6ca897f44 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -194,6 +194,12 @@ void vrrp_add_ip(struct vrrp_vrouter *vr, struct ipaddr ip) /* Creation and destruction ------------------------------------------------ */ +static void vrrp_router_addr_list_del_cb(void *val) +{ + struct ipaddr *ip = val; + XFREE(MTYPE_TMP, ip); +} + static struct vrrp_router *vrrp_router_create(struct vrrp_vrouter *vr, int family) { @@ -204,6 +210,7 @@ static struct vrrp_router *vrrp_router_create(struct vrrp_vrouter *vr, r->sock_tx = -1; r->vr = vr; r->addrs = list_new(); + r->addrs->del = vrrp_router_addr_list_del_cb; r->priority = vr->priority; r->fsm.state = VRRP_STATE_INITIALIZE; vrrp_mac_set(&r->vmac, family == AF_INET6, vr->vrid); @@ -261,10 +268,14 @@ static struct vrrp_router *vrrp_router_create(struct vrrp_vrouter *vr, static void vrrp_router_destroy(struct vrrp_router *r) { + if (r->is_active) + vrrp_event(r, VRRP_EVENT_SHUTDOWN); + if (r->sock_rx >= 0) close(r->sock_rx); if (r->sock_tx >= 0) close(r->sock_tx); + /* FIXME: also delete list elements */ list_delete(&r->addrs); XFREE(MTYPE_TMP, r); @@ -272,8 +283,12 @@ static void vrrp_router_destroy(struct vrrp_router *r) struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid) { - struct vrrp_vrouter *vr = - XCALLOC(MTYPE_TMP, sizeof(struct vrrp_vrouter)); + struct vrrp_vrouter *vr = vrrp_lookup(vrid); + + if (vr) + return vr; + + vr = XCALLOC(MTYPE_TMP, sizeof(struct vrrp_vrouter)); vr->ifp = ifp; vr->vrid = vrid; @@ -293,9 +308,9 @@ struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid) void vrrp_vrouter_destroy(struct vrrp_vrouter *vr) { - vr->ifp = NULL; vrrp_router_destroy(vr->v4); vrrp_router_destroy(vr->v6); + vr->ifp = NULL; hash_release(vrrp_vrouters_hash, vr); XFREE(MTYPE_TMP, vr); } @@ -913,6 +928,9 @@ void (*vrrp_change_state_handlers[])(struct vrrp_router *vr) = { */ static void vrrp_change_state(struct vrrp_router *r, int to) { + if (r->fsm.state == to) + return; + /* Call our handlers, then any subscribers */ vrrp_change_state_handlers[to](r); hook_call(vrrp_change_state_hook, r, to); diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index f68ff85224..417b23a030 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -219,7 +219,9 @@ void vrrp_init(void); struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid); /* - * Destroy a VRRP Virtual Router. + * Destroy a VRRP Virtual Router, freeing all its resources. + * + * If there are any running VRRP instances, these are stopped and destroyed. */ void vrrp_vrouter_destroy(struct vrrp_vrouter *vr); diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 3726841ed1..6edde0ca02 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -37,6 +37,7 @@ #define VRRP_STR "Virtual Router Redundancy Protocol\n" #define VRRP_VRID_STR "Virtual Router ID\n" #define VRRP_PRIORITY_STR "Virtual Router Priority\n" +#define VRRP_ADVINT_STR "Virtual Router Advertisement Interval\n" #define VRRP_IP_STR "Virtual Router IPv4 address\n" #define VROUTER_GET_VTY(_vty, _vrid, _vr) \ @@ -71,7 +72,18 @@ DEFPY(vrrp_vrid, { VTY_DECLVAR_CONTEXT(interface, ifp); - vrrp_vrouter_create(ifp, vrid); + struct vrrp_vrouter *vr = vrrp_lookup(vrid); + + if (no && vr) + vrrp_vrouter_destroy(vr); + else if (no && !vr) + vty_out(vty, "%% VRRP instance %ld does not exist on %s\n", + vrid, ifp->name); + else if (!vr) + vrrp_vrouter_create(ifp, vrid); + else if (vr) + vty_out(vty, "%% VRRP instance %ld already exists on %s\n", + vrid, ifp->name); return CMD_SUCCESS; } @@ -89,22 +101,24 @@ DEFPY(vrrp_priority, struct vrrp_router *r; bool nr[2] = { false, false }; int ret = CMD_SUCCESS; + uint8_t newprio = no ? VRRP_DEFAULT_PRIORITY : priority; VROUTER_GET_VTY(vty, vrid, vr); r = vr->v4; for (int i = 0; i < 2; i++) { - nr[i] = r->is_active && r->fsm.state != VRRP_STATE_INITIALIZE; + nr[i] = r->is_active && r->fsm.state != VRRP_STATE_INITIALIZE + && vr->priority != newprio; if (nr[i]) { vty_out(vty, - "%% WARNING: Restarting Virtual Router %ld (%s) to update priority\n", - vrid, r->family == AF_INET ? "v4" : "v6"); + "%% WARNING: Restarting %s Virtual Router %ld to update priority\n", + family2str(r->family), vrid); (void)vrrp_event(r, VRRP_EVENT_SHUTDOWN); } r = vr->v6; } - vrrp_set_priority(vr, priority); + vrrp_set_priority(vr, newprio); r = vr->v4; for (int i = 0; i < 2; i++) { @@ -113,7 +127,7 @@ DEFPY(vrrp_priority, if (ret < 0) vty_out(vty, "%% Failed to start Virtual Router %ld (%s)\n", - vrid, r->family == AF_INET ? "v4" : "v6"); + vrid, family2str(r->family)); } r = vr->v6; } @@ -124,16 +138,14 @@ DEFPY(vrrp_priority, DEFPY(vrrp_advertisement_interval, vrrp_advertisement_interval_cmd, "[no] vrrp (1-255)$vrid advertisement-interval (1-4096)", - NO_STR - VRRP_STR - VRRP_VRID_STR - VRRP_PRIORITY_STR - "Priority value; set 255 to designate this Virtual Router as Master\n") + NO_STR VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR + "Advertisement interval in centiseconds") { struct vrrp_vrouter *vr; + uint16_t newadvint = no ? VRRP_DEFAULT_ADVINT : advertisement_interval; VROUTER_GET_VTY(vty, vrid, vr); - vrrp_set_advertisement_interval(vr, advertisement_interval); + vrrp_set_advertisement_interval(vr, newadvint); return CMD_SUCCESS; } From 85467974e8abcd5517e7cc6c67574c1d4c9f19b0 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 25 Jan 2019 18:48:41 +0000 Subject: [PATCH 034/153] vrrpd: allow searching for interfaces late Break out code for assigning macvlan interface to a vrrp router into its own function so it can be called multiple times. This allows bringing up IPv4 and IPv6 at different times if all the interfaces are not created yet. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 51 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index c6ca897f44..0cc9ed0e93 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -200,21 +200,18 @@ static void vrrp_router_addr_list_del_cb(void *val) XFREE(MTYPE_TMP, ip); } -static struct vrrp_router *vrrp_router_create(struct vrrp_vrouter *vr, - int family) +/* + * Search for a suitable macvlan subinterface we can attach to, and if found, + * attach to it. + * + * r + * Router to attach to interface + * + * Returns: + * Whether an interface was successfully attached + */ +static bool vrrp_attach_interface(struct vrrp_router *r) { - struct vrrp_router *r = XCALLOC(MTYPE_TMP, sizeof(struct vrrp_router)); - - r->family = family; - r->sock_rx = -1; - r->sock_tx = -1; - r->vr = vr; - r->addrs = list_new(); - r->addrs->del = vrrp_router_addr_list_del_cb; - r->priority = vr->priority; - r->fsm.state = VRRP_STATE_INITIALIZE; - vrrp_mac_set(&r->vmac, family == AF_INET6, vr->vrid); - /* Search for existing interface with computed MAC address */ struct interface **ifps; size_t ifps_cnt = if_lookup_by_hwaddr( @@ -231,7 +228,6 @@ static struct vrrp_router *vrrp_router_create(struct vrrp_vrouter *vr, unsigned int candidates = 0; struct interface *selection = NULL; for (unsigned int i = 0; i < ifps_cnt; i++) { - zlog_info("Found VRRP interface %s", ifps[i]->name); if (strncmp(ifps[i]->name, r->vr->ifp->name, strlen(r->vr->ifp->name))) ifps[i] = NULL; @@ -251,7 +247,7 @@ static struct vrrp_router *vrrp_router_create(struct vrrp_vrouter *vr, if (candidates == 0) zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID - "No interface found w/ MAC %s; using default", + "No interface found w/ MAC %s", r->vr->vrid, ethstr); else if (candidates > 1) zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID @@ -263,6 +259,27 @@ static struct vrrp_router *vrrp_router_create(struct vrrp_vrouter *vr, r->mvl_ifp = selection; + return !!r->mvl_ifp; + +} + +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->family = family; + r->sock_rx = -1; + r->sock_tx = -1; + r->vr = vr; + r->addrs = list_new(); + r->addrs->del = vrrp_router_addr_list_del_cb; + r->priority = vr->priority; + r->fsm.state = VRRP_STATE_INITIALIZE; + vrrp_mac_set(&r->vmac, family == AF_INET6, vr->vrid); + + vrrp_attach_interface(r); + return r; } @@ -1011,7 +1028,7 @@ static int vrrp_startup(struct vrrp_router *r) return -1; /* Must have a valid macvlan interface available */ - if (r->mvl_ifp == NULL) { + if (r->mvl_ifp == NULL && !vrrp_attach_interface(r)) { zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID "No appropriate interface for %s VRRP found", r->vr->vrid, family2str(r->family)); From 667179cae42b6d39db7a0bb1f76b7591f26e720f Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 25 Jan 2019 21:48:17 +0000 Subject: [PATCH 035/153] lib: add function to get iface link-local Signed-off-by: Quentin Young --- lib/if.c | 13 +++++++++++++ lib/if.h | 1 + 2 files changed, 14 insertions(+) diff --git a/lib/if.c b/lib/if.c index 8888411903..3f489e0c3e 100644 --- a/lib/if.c +++ b/lib/if.c @@ -904,6 +904,19 @@ struct connected *connected_add_by_prefix(struct interface *ifp, return ifc; } +struct connected *connected_get_linklocal(struct interface *ifp) +{ + struct listnode *n; + struct connected *c = NULL; + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, n, c)) { + if (c->address->family == AF_INET6 + && IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) + break; + } + return c; +} + #if 0 /* this route_table of struct connected's is unused \ * however, it would be good to use a route_table rather than \ * a list.. \ diff --git a/lib/if.h b/lib/if.h index a98f907c5c..ef596d45dc 100644 --- a/lib/if.h +++ b/lib/if.h @@ -542,6 +542,7 @@ extern struct connected *connected_lookup_prefix_exact(struct interface *, extern struct nbr_connected *nbr_connected_new(void); extern void nbr_connected_free(struct nbr_connected *); struct nbr_connected *nbr_connected_check(struct interface *, struct prefix *); +struct connected *connected_get_linklocal(struct interface *ifp); /* link parameters */ struct if_link_params *if_link_params_get(struct interface *); From 4f52e9a685177d34486aff19d9f3c486443c9b41 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Sun, 27 Jan 2019 23:08:01 +0000 Subject: [PATCH 036/153] vrrpd: send ICMPv6 Neighbor Advertisements Signed-off-by: Quentin Young --- vrrpd/subdir.am | 2 + vrrpd/vrrp.c | 10 ++ vrrpd/vrrp_ndisc.c | 228 +++++++++++++++++++++++++++++++++++++++++++++ vrrpd/vrrp_ndisc.h | 74 +++++++++++++++ 4 files changed, 314 insertions(+) create mode 100644 vrrpd/vrrp_ndisc.c create mode 100644 vrrpd/vrrp_ndisc.h diff --git a/vrrpd/subdir.am b/vrrpd/subdir.am index 5c040932e6..633a4a8e73 100644 --- a/vrrpd/subdir.am +++ b/vrrpd/subdir.am @@ -14,6 +14,7 @@ vrrpd_libvrrp_a_SOURCES = \ vrrpd/vrrp.c \ vrrpd/vrrp_arp.c \ vrrpd/vrrp_memory.c \ + vrrpd/vrrp_ndisc.c \ vrrpd/vrrp_packet.c \ vrrpd/vrrp_vty.c \ vrrpd/vrrp_zebra.c \ @@ -23,6 +24,7 @@ noinst_HEADERS += \ vrrpd/vrrp.h \ vrrpd/vrrp_arp.h \ vrrpd/vrrp_memory.h \ + vrrpd/vrrp_ndisc.h \ vrrpd/vrrp_vty.h \ vrrpd/vrrp_zebra.h \ # end diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 0cc9ed0e93..1e7117eaff 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -32,6 +32,7 @@ #include "vrrp.h" #include "vrrp_arp.h" +#include "vrrp_ndisc.h" #include "vrrp_packet.h" #define VRRP_LOGPFX "[CORE] " @@ -181,6 +182,9 @@ void vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6) } listnode_add(vr->v6->addrs, v6_ins); + + if (vr->v6->fsm.state == VRRP_STATE_MASTER) + vrrp_ndisc_una_send(vr->v6, v6_ins); } void vrrp_add_ip(struct vrrp_vrouter *vr, struct ipaddr ip) @@ -996,6 +1000,8 @@ static int vrrp_master_down_timer_expire(struct thread *thread) vrrp_send_advertisement(r); if (r->family == AF_INET) vrrp_garp_send_all(r); + if (r->family == AF_INET6) + vrrp_ndisc_una_send_all(r); thread_add_timer_msec(master, vrrp_adver_timer_expire, r, r->vr->advertisement_interval * 10, &r->t_adver_timer); @@ -1038,6 +1044,8 @@ static int vrrp_startup(struct vrrp_router *r) /* Initialize global gratuitous ARP socket if necessary */ if (r->family == AF_INET && !vrrp_garp_is_init()) vrrp_garp_init(); + if (r->family == AF_INET6 && !vrrp_ndisc_is_init()) + vrrp_ndisc_init(); /* Create socket */ if (r->sock_rx < 0 || r->sock_tx < 0) { @@ -1070,6 +1078,8 @@ static int vrrp_startup(struct vrrp_router *r) if (r->family == AF_INET) vrrp_garp_send_all(r); + if (r->family == AF_INET6) + vrrp_ndisc_una_send_all(r); thread_add_timer_msec(master, vrrp_adver_timer_expire, r, r->vr->advertisement_interval * 10, diff --git a/vrrpd/vrrp_ndisc.c b/vrrpd/vrrp_ndisc.c new file mode 100644 index 0000000000..ce6c62c07e --- /dev/null +++ b/vrrpd/vrrp_ndisc.c @@ -0,0 +1,228 @@ +/* + * VRRP Neighbor Discovery. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Quentin Young + * Portions: + * Copyright (C) 2001-2017 Alexandre Cassen + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include +#include +#include +#include + +#include "lib/checksum.h" +#include "lib/if.h" +#include "lib/ipaddr.h" +#include "lib/log.h" + +#include "vrrp_ndisc.h" + +#define VRRP_LOGPFX "[NDISC] " + +#define VRRP_NDISC_HOPLIMIT 255 +#define VRRP_NDISC_SIZE \ + ETHER_HDR_LEN + sizeof(struct ip6_hdr) \ + + sizeof(struct nd_neighbor_advert) \ + + sizeof(struct nd_opt_hdr) + ETH_ALEN + +/* static vars */ +static int ndisc_fd = -1; + +/* + * Build an unsolicited Neighbour Advertisement. + * + * ifp + * Interface to send Neighbor Advertisement on + * + * ip + * IP address to send Neighbor Advertisement for + * + * buf + * Buffer to fill with IPv6 Neighbor Advertisement message. Includes + * Ethernet header. + * + * bufsiz + * Size of buf. + * + * Returns; + * -1 if bufsiz is too small + * 0 otherwise + */ +static int vrrp_ndisc_una_build(struct interface *ifp, struct ipaddr *ip, + uint8_t *buf, size_t bufsiz) +{ + if (bufsiz < VRRP_NDISC_SIZE) + return -1; + + memset(buf, 0x00, bufsiz); + + struct ether_header *eth = (struct ether_header *)buf; + struct ip6_hdr *ip6h = (struct ip6_hdr *)((char *)eth + ETHER_HDR_LEN); + struct nd_neighbor_advert *ndh = + (struct nd_neighbor_advert *)((char *)ip6h + + sizeof(struct ip6_hdr)); + struct icmp6_hdr *icmp6h = &ndh->nd_na_hdr; + struct nd_opt_hdr *nd_opt_h = + (struct nd_opt_hdr *)((char *)ndh + + sizeof(struct nd_neighbor_advert)); + char *nd_opt_lladdr = + (char *)((char *)nd_opt_h + sizeof(struct nd_opt_hdr)); + char *lladdr = (char *)ifp->hw_addr; + + /* + * An IPv6 packet with a multicast destination address DST, consisting + * of the sixteen octets DST[1] through DST[16], is transmitted to the + * Ethernet multicast address whose first two octets are the value 3333 + * hexadecimal and whose last four octets are the last four octets of + * DST. + * - RFC2464.7 + * + * In this case we are sending to the all nodes multicast address, so + * the last four octets are 0x00 0x00 0x00 0x01. + */ + memset(eth->ether_dhost, 0, ETH_ALEN); + eth->ether_dhost[0] = 0x33; + eth->ether_dhost[1] = 0x33; + eth->ether_dhost[5] = 1; + + /* Set source Ethernet address to interface link layer address */ + memcpy(eth->ether_shost, lladdr, ETH_ALEN); + eth->ether_type = htons(ETHERTYPE_IPV6); + + /* IPv6 Header */ + ip6h->ip6_vfc = 6 << 4; + ip6h->ip6_plen = htons(sizeof(struct nd_neighbor_advert) + + sizeof(struct nd_opt_hdr) + ETH_ALEN); + ip6h->ip6_nxt = IPPROTO_ICMPV6; + ip6h->ip6_hlim = VRRP_NDISC_HOPLIMIT; + memcpy(&ip6h->ip6_src, &ip->ipaddr_v6, sizeof(struct in6_addr)); + /* All nodes multicast address */ + ip6h->ip6_dst.s6_addr[0] = 0xFF; + ip6h->ip6_dst.s6_addr[1] = 0x02; + ip6h->ip6_dst.s6_addr[15] = 0x01; + + /* ICMPv6 Header */ + ndh->nd_na_type = ND_NEIGHBOR_ADVERT; + ndh->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER; + ndh->nd_na_flags_reserved |= ND_NA_FLAG_OVERRIDE; + memcpy(&ndh->nd_na_target, &ip->ipaddr_v6, sizeof(struct in6_addr)); + + /* NDISC Option header */ + nd_opt_h->nd_opt_type = ND_OPT_TARGET_LINKADDR; + nd_opt_h->nd_opt_len = 1; + memcpy(nd_opt_lladdr, lladdr, ETH_ALEN); + + /* Compute checksum */ + uint32_t len = sizeof(struct nd_neighbor_advert) + + sizeof(struct nd_opt_hdr) + ETH_ALEN; + struct ipv6_ph ph = {}; + ph.src = ip6h->ip6_src; + ph.dst = ip6h->ip6_dst; + ph.ulpl = htonl(len); + ph.next_hdr = IPPROTO_ICMPV6; + icmp6h->icmp6_cksum = in_cksum_with_ph6(&ph, (void *)icmp6h, len); + + return 0; +} + +int vrrp_ndisc_una_send(struct vrrp_router *r, struct ipaddr *ip) +{ + assert(r->family == AF_INET6); + + int ret = 0; + struct interface *ifp = r->mvl_ifp; + + uint8_t buf[VRRP_NDISC_SIZE]; + ret = vrrp_ndisc_una_build(ifp, ip, buf, sizeof(buf)); + + if (ret == -1) + return ret; + + struct sockaddr_ll sll; + ssize_t len; + + /* Build the dst device */ + memset(&sll, 0, sizeof(sll)); + sll.sll_family = AF_PACKET; + memcpy(sll.sll_addr, ifp->hw_addr, ETH_ALEN); + sll.sll_halen = ETH_ALEN; + sll.sll_ifindex = (int)ifp->ifindex; + + char ipbuf[INET6_ADDRSTRLEN]; + ipaddr2str(ip, ipbuf, sizeof(ipbuf)); + + zlog_debug(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Sending unsolicited Neighbor Advertisement on %s for %s", + r->vr->vrid, ifp->name, ipbuf); + + len = sendto(ndisc_fd, buf, VRRP_NDISC_SIZE, 0, (struct sockaddr *)&sll, + sizeof(sll)); + + if (len < 0) { + zlog_err( + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Error sending unsolicited Neighbor Advertisement on %s for %s", + r->vr->vrid, ifp->name, ipbuf); + ret = -1; + } + + return ret; +} + +int vrrp_ndisc_una_send_all(struct vrrp_router *r) +{ + assert(r->family == AF_INET6); + + struct listnode *ln; + struct ipaddr *ip; + + for (ALL_LIST_ELEMENTS_RO(r->addrs, ln, ip)) + vrrp_ndisc_una_send(r, ip); + + return 0; +} + +void vrrp_ndisc_init(void) +{ + vrrp_privs.change(ZPRIVS_RAISE); + { + ndisc_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IPV6)); + } + vrrp_privs.change(ZPRIVS_LOWER); + + if (ndisc_fd > 0) + zlog_info( + VRRP_LOGPFX + "Initialized unsolicited neighbor advertisement socket"); + else + zlog_err( + VRRP_LOGPFX + "Error initializing unsolicited neighbor advertisement socket"); +} + +void vrrp_ndisc_fini(void) +{ + close(ndisc_fd); + ndisc_fd = -1; +} + +bool vrrp_ndisc_is_init(void) +{ + return ndisc_fd > 0; +} diff --git a/vrrpd/vrrp_ndisc.h b/vrrpd/vrrp_ndisc.h new file mode 100644 index 0000000000..262d3db038 --- /dev/null +++ b/vrrpd/vrrp_ndisc.h @@ -0,0 +1,74 @@ +/* + * VRRP Neighbor Discovery. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef _VRRP_NDISC_H +#define _VRRP_NDISC_H + +#include +#include +#include + +#include "vrrp.h" + +/* + * Initialize VRRP neighbor discovery. + */ +extern void vrrp_ndisc_init(void); + +/* + * Check whether VRRP Neighbor Discovery is initialized. + * + * Returns: + * True if initialized, false otherwise + */ +extern bool vrrp_ndisc_is_init(void); + +/* + * Finish VRRP Neighbor Discovery. + */ +extern void vrrp_ndisc_fini(void); + +/* + * Send VRRP Neighbor Advertisement. + * + * ifp + * Interface to transmit on + * + * ip + * IPv6 address to send Neighbor Advertisement for + * + * Returns: + * -1 on failure + * 0 otherwise + */ +extern int vrrp_ndisc_una_send(struct vrrp_router *r, struct ipaddr *ip); + +/* + * Send VRRP Neighbor Advertisements for all virtual IPs. + * + * r + * Virtual Router to send NA's for + * + * Returns: + * -1 on failure + * 0 otherwise + */ +extern int vrrp_ndisc_una_send_all(struct vrrp_router *r); + +#endif From f3fe00478044cc6a3b6f980500f8c70f5ec128b8 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 28 Jan 2019 20:15:00 +0000 Subject: [PATCH 037/153] vrrpd: send ND Router Advertisements Send ND Router Advertisements when IPv6 VR is in Master state. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 14 +++++++++++--- vrrpd/vrrp.h | 1 + vrrpd/vrrp_zebra.c | 6 ++++++ vrrpd/vrrp_zebra.h | 1 + 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 1e7117eaff..0c0ef9709a 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -34,6 +34,7 @@ #include "vrrp_arp.h" #include "vrrp_ndisc.h" #include "vrrp_packet.h" +#include "vrrp_zebra.h" #define VRRP_LOGPFX "[CORE] " @@ -900,7 +901,9 @@ DEFINE_HOOK(vrrp_change_state_hook, (struct vrrp_router * r, int to), (r, to)); */ static void vrrp_change_state_master(struct vrrp_router *r) { - /* NOTHING */ + /* Enable ND Router Advertisements */ + if (r->family == AF_INET6) + vrrp_zebra_radv_set(r, true); } /* @@ -911,8 +914,9 @@ static void vrrp_change_state_master(struct vrrp_router *r) */ static void vrrp_change_state_backup(struct vrrp_router *r) { - /* Uninstall ARP entry for router MAC */ - /* ... */ + /* Disable ND Router Advertisements */ + if (r->family == AF_INET6) + vrrp_zebra_radv_set(r, false); } /* @@ -929,6 +933,10 @@ static void vrrp_change_state_initialize(struct vrrp_router *r) r->vr->advertisement_interval = r->vr->advertisement_interval; r->master_adver_interval = 0; vrrp_recalculate_timers(r); + + /* Disable ND Router Advertisements */ + if (r->family == AF_INET6) + vrrp_zebra_radv_set(r, false); } void (*vrrp_change_state_handlers[])(struct vrrp_router *vr) = { diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 417b23a030..0249b3e9af 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -34,6 +34,7 @@ /* Global definitions */ #define VRRP_DEFAULT_ADVINT 100 #define VRRP_DEFAULT_PRIORITY 100 +#define VRRP_RADV_INT 16 #define VRRP_PRIO_MASTER 255 #define VRRP_MCASTV4_GROUP_STR "224.0.0.18" #define VRRP_MCASTV6_GROUP_STR "ff02:0:0:0:0:0:0:12" diff --git a/vrrpd/vrrp_zebra.c b/vrrpd/vrrp_zebra.c index 26d0a5020a..1c4b0a3828 100644 --- a/vrrpd/vrrp_zebra.c +++ b/vrrpd/vrrp_zebra.c @@ -220,6 +220,12 @@ static int vrrp_zebra_if_address_del(int command, struct zclient *client, return 0; } +void vrrp_zebra_radv_set(struct vrrp_router *r, bool enable) +{ + zclient_send_interface_radv_req(zclient, VRF_DEFAULT, r->mvl_ifp, + enable, VRRP_RADV_INT); +} + void vrrp_zebra_init(void) { /* Socket for receiving updates from Zebra daemon */ diff --git a/vrrpd/vrrp_zebra.h b/vrrpd/vrrp_zebra.h index 03910db407..70871cd89b 100644 --- a/vrrpd/vrrp_zebra.h +++ b/vrrpd/vrrp_zebra.h @@ -21,4 +21,5 @@ #define __VRRP_ZEBRA_H__ extern void vrrp_zebra_init(void); +extern void vrrp_zebra_radv_set(struct vrrp_router *r, bool enable); #endif From 5302f67b1300219479edded34e01126691a9f689 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 29 Jan 2019 19:17:15 +0000 Subject: [PATCH 038/153] vrrpd: improve show vrrp * Dump all relevant interface names for each instance * Compact IPvX address display Signed-off-by: Quentin Young --- vrrpd/vrrp_vty.c | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 6edde0ca02..553e3947be 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -223,8 +223,13 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); ttable_add_row(tt, "%s|%" PRIu32, "Virtual Router ID", vr->vrid); + ttable_add_row(tt, "%s|%s", "Interface", vr->ifp->name); prefix_mac2str(&vr->v4->vmac, ethstr4, sizeof(ethstr4)); prefix_mac2str(&vr->v6->vmac, ethstr6, sizeof(ethstr6)); + ttable_add_row(tt, "%s|%s", "VRRP interface (v4)", + vr->v4->mvl_ifp ? vr->v4->mvl_ifp->name : "None"); + ttable_add_row(tt, "%s|%s", "VRRP interface (v6)", + vr->v6->mvl_ifp ? vr->v6->mvl_ifp->name : "None"); ttable_add_row(tt, "%s|%s", "Virtual MAC (v4)", ethstr4); ttable_add_row(tt, "%s|%s", "Virtual MAC (v6)", ethstr6); ttable_add_row(tt, "%s|%s", "Status (v4)", stastr4); @@ -251,34 +256,33 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) 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); + + char fill[37]; + memset(fill, '.', sizeof(fill)); + fill[sizeof(fill) - 1] = 0x00; + if (vr->v4->addrs->count) { + for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ln, ip)) { + inet_ntop(vr->v4->family, &ip->ipaddr_v4, ipstr, + sizeof(ipstr)); + ttable_add_row(tt, "%s|%s", fill, ipstr); + } + } + ttable_add_row(tt, "%s|%u", "IPv6 Addresses", vr->v6->addrs->count); + if (vr->v6->addrs->count) { + for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ln, ip)) { + inet_ntop(vr->v6->family, &ip->ipaddr_v6, ipstr, + sizeof(ipstr)); + ttable_add_row(tt, "%s|%s", fill, ipstr); + } + } + char *table = ttable_dump(tt, "\n"); vty_out(vty, "\n%s\n", table); XFREE(MTYPE_TMP, table); ttable_del(tt); - if (vr->v4->addrs->count) { - vty_out(vty, " IPv4 Addresses\n"); - vty_out(vty, " --------------\n"); - 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"); - } } DEFPY(vrrp_vrid_show, @@ -299,6 +303,8 @@ DEFPY(vrrp_vrid_show, for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) vrrp_show(vty, vr); + + list_delete_and_null(&ll); } return CMD_SUCCESS; From 63d4bd12ca9a7fc666dcd112c940f4ac693b32ae Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 29 Jan 2019 19:59:01 +0000 Subject: [PATCH 039/153] vrrpd: fix headers * Add include guards where missing * Add include guard comments where missing * Fix copyright notices * Sort includes Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 6 +++--- vrrpd/vrrp.h | 12 ++++++------ vrrpd/vrrp_arp.c | 10 ++++------ vrrpd/vrrp_arp.h | 18 +++++++++--------- vrrpd/vrrp_main.c | 33 +++++++++++++++++---------------- vrrpd/vrrp_memory.c | 7 ++++--- vrrpd/vrrp_memory.h | 10 +++++----- vrrpd/vrrp_ndisc.c | 3 ++- vrrpd/vrrp_ndisc.h | 8 ++++---- vrrpd/vrrp_packet.c | 6 +++--- vrrpd/vrrp_packet.h | 17 +++++++++++------ vrrpd/vrrp_vty.c | 6 +++--- vrrpd/vrrp_vty.h | 10 ++++++---- vrrpd/vrrp_zebra.c | 10 +++++----- vrrpd/vrrp_zebra.h | 9 +++++---- 15 files changed, 87 insertions(+), 78 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 0c0ef9709a..08517c79d1 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1,7 +1,7 @@ /* - * VRRPD global definitions and state machine - * Copyright (C) 2018 Cumulus Networks, Inc. - * Quentin Young + * VRRP global definitions and state machine. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 0249b3e9af..d060b95dfa 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -1,7 +1,7 @@ /* - * VRRPD global definitions and state machine - * Copyright (C) 2018 Cumulus Networks, Inc. - * Quentin Young + * VRRP global definitions and state machine. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -17,8 +17,8 @@ * with this program; see the file COPYING; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef _VRRP_H -#define _VRRP_H +#ifndef __VRRP_H__ +#define __VRRP_H__ #include #include @@ -335,4 +335,4 @@ int vrrp_event(struct vrrp_router *r, int event); */ struct vrrp_vrouter *vrrp_lookup(uint8_t vrid); -#endif /* _VRRP_H */ +#endif /* __VRRP_H__ */ diff --git a/vrrpd/vrrp_arp.c b/vrrpd/vrrp_arp.c index e7a037d089..7e77d7df13 100644 --- a/vrrpd/vrrp_arp.c +++ b/vrrpd/vrrp_arp.c @@ -1,10 +1,9 @@ /* - * VRRP ARP primitives. - * + * VRRP ARP handling. * Copyright (C) 2001-2017 Alexandre Cassen * Portions: - * Copyright (C) 2018 Cumulus Networks - * Quentin Young + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -20,11 +19,10 @@ * this program; see the file COPYING; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - #include -#include #include +#include #include #include "lib/if.h" diff --git a/vrrpd/vrrp_arp.h b/vrrpd/vrrp_arp.h index 1de94a1519..21f2c4edd1 100644 --- a/vrrpd/vrrp_arp.h +++ b/vrrpd/vrrp_arp.h @@ -1,7 +1,7 @@ /* - * VRRPD global definitions - * Copyright (C) 2018 Cumulus Networks, Inc. - * Quentin Young + * VRRP ARP handling. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -13,12 +13,12 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software + * You should have received a copy of the GNU General Public License along with + * this program; see the file COPYING; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef _VRRP_ARP_H -#define _VRRP_ARP_H +#ifndef __VRRP_ARP_H__ +#define __VRRP_ARP_H__ #include @@ -27,10 +27,10 @@ /* FIXME: Use the kernel define for this */ #define HWTYPE_ETHER 1 -/* prototypes */ 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_router *vr, struct in_addr *v4); extern void vrrp_garp_send_all(struct vrrp_router *vr); -#endif + +#endif /* __VRRP_ARP_H__ */ diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c index d60f37d8fc..daaadffab5 100644 --- a/vrrpd/vrrp_main.c +++ b/vrrpd/vrrp_main.c @@ -1,7 +1,7 @@ /* - * VRRP - * Copyright (C) 2018 Cumulus Networks, Inc. - * Quentin Young + * VRRP entry point. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -20,22 +20,23 @@ #include #include -#include "getopt.h" -#include "thread.h" -#include "command.h" -#include "log.h" -#include "memory.h" -#include "privs.h" -#include "sigevent.h" -#include "libfrr.h" -#include "vrf.h" -#include "nexthop.h" -#include "filter.h" -#include "if.h" + +#include "lib/command.h" +#include "lib/filter.h" +#include "lib/getopt.h" +#include "lib/if.h" +#include "lib/libfrr.h" +#include "lib/log.h" +#include "lib/memory.h" +#include "lib/nexthop.h" +#include "lib/privs.h" +#include "lib/sigevent.h" +#include "lib/thread.h" +#include "lib/vrf.h" #include "vrrp.h" -#include "vrrp_zebra.h" #include "vrrp_vty.h" +#include "vrrp_zebra.h" char backup_config_file[256]; diff --git a/vrrpd/vrrp_memory.c b/vrrpd/vrrp_memory.c index 4a7c0b7e42..9ce7c90413 100644 --- a/vrrpd/vrrp_memory.c +++ b/vrrpd/vrrp_memory.c @@ -1,7 +1,7 @@ /* - * VRRPD memory types - * Copyright (C) 2018 Cumulus Networks, Inc. - * Quentin Young + * VRRP memory types. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -19,6 +19,7 @@ */ #include #include + #include "vrrp_memory.h" DEFINE_MGROUP(VRRP, "vrrpd") diff --git a/vrrpd/vrrp_memory.h b/vrrpd/vrrp_memory.h index f57a864804..ae6975478d 100644 --- a/vrrpd/vrrp_memory.h +++ b/vrrpd/vrrp_memory.h @@ -1,7 +1,7 @@ /* - * VRRPD memory types - * Copyright (C) 2018 Cumulus Networks, Inc. - * Quentin Young + * VRRP memory types. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -20,8 +20,8 @@ #ifndef __VRRP_MEMORY_H__ #define __VRRP_MEMORY_H__ -#include "memory.h" +#include "lib/memory.h" DECLARE_MGROUP(VRRP) -#endif +#endif /* __VRRP_MEMORY_H__ */ diff --git a/vrrpd/vrrp_ndisc.c b/vrrpd/vrrp_ndisc.c index ce6c62c07e..73ee172aa8 100644 --- a/vrrpd/vrrp_ndisc.c +++ b/vrrpd/vrrp_ndisc.c @@ -1,7 +1,8 @@ /* * VRRP Neighbor Discovery. * Copyright (C) 2019 Cumulus Networks, Inc. - * Quentin Young + * Quentin Young + * * Portions: * Copyright (C) 2001-2017 Alexandre Cassen * diff --git a/vrrpd/vrrp_ndisc.h b/vrrpd/vrrp_ndisc.h index 262d3db038..efbef348d0 100644 --- a/vrrpd/vrrp_ndisc.h +++ b/vrrpd/vrrp_ndisc.h @@ -1,7 +1,7 @@ /* * VRRP Neighbor Discovery. * Copyright (C) 2019 Cumulus Networks, Inc. - * Quentin Young + * Quentin Young * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -17,8 +17,8 @@ * this program; see the file COPYING; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef _VRRP_NDISC_H -#define _VRRP_NDISC_H +#ifndef __VRRP_NDISC_H__ +#define __VRRP_NDISC_H__ #include #include @@ -71,4 +71,4 @@ extern int vrrp_ndisc_una_send(struct vrrp_router *r, struct ipaddr *ip); */ extern int vrrp_ndisc_una_send_all(struct vrrp_router *r); -#endif +#endif /* __VRRP_NDISC_H__ */ diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index bf1197422e..fd0256d6e3 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -1,7 +1,7 @@ /* - * VRRPD packet crafting - * Copyright (C) 2018 Cumulus Networks, Inc. - * Quentin Young + * VRRP packet crafting. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/vrrpd/vrrp_packet.h b/vrrpd/vrrp_packet.h index 3a5b161fb7..df43ee7604 100644 --- a/vrrpd/vrrp_packet.h +++ b/vrrpd/vrrp_packet.h @@ -1,7 +1,7 @@ /* - * VRRPD packet crafting - * Copyright (C) 2018 Cumulus Networks, Inc. - * Quentin Young + * VRRP packet crafting. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -17,11 +17,14 @@ * with this program; see the file COPYING; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifndef __VRRP_PACKET_H__ +#define __VRRP_PACKET_H__ + #include -#include "memory.h" -#include "ipaddr.h" -#include "prefix.h" +#include "lib/ipaddr.h" +#include "lib/memory.h" +#include "lib/prefix.h" #define VRRP_VERSION 3 #define VRRP_TYPE_ADVERTISEMENT 1 @@ -170,3 +173,5 @@ size_t vrrp_pkt_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt); ssize_t vrrp_parse_datagram(int family, struct msghdr *m, size_t read, struct vrrp_pkt **pkt, char *errmsg, size_t errmsg_len); + +#endif /* __VRRP_PACKET_H__ */ diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 553e3947be..ff8f1e68e6 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -1,7 +1,7 @@ /* - * VRRP commands - * Copyright (C) 2018 Cumulus Networks, Inc. - * Quentin Young + * VRRP CLI commands. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/vrrpd/vrrp_vty.h b/vrrpd/vrrp_vty.h index 2aa47ec20d..377321ec4a 100644 --- a/vrrpd/vrrp_vty.h +++ b/vrrpd/vrrp_vty.h @@ -1,7 +1,8 @@ /* - * VRRP commands - * Copyright (C) 2018 Cumulus Networks, Inc. - * Quentin Young + * VRRP CLI commands. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young + * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) @@ -20,4 +21,5 @@ #define __VRRP_VTY_H__ void vrrp_vty_init(void); -#endif + +#endif /* __VRRP_VTY_H__ */ diff --git a/vrrpd/vrrp_zebra.c b/vrrpd/vrrp_zebra.c index 1c4b0a3828..1bd5aa013f 100644 --- a/vrrpd/vrrp_zebra.c +++ b/vrrpd/vrrp_zebra.c @@ -1,7 +1,7 @@ /* - * Zebra interfacing - * Copyright (C) 2018 Cumulus Networks, Inc. - * Quentin Young + * VRRP Zebra interfacing. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -20,11 +20,11 @@ #include #include "lib/if.h" +#include "lib/linklist.h" #include "lib/log.h" #include "lib/prefix.h" -#include "lib/zclient.h" #include "lib/vty.h" -#include "lib/linklist.h" +#include "lib/zclient.h" #include "vrrp.h" #include "vrrp_zebra.h" diff --git a/vrrpd/vrrp_zebra.h b/vrrpd/vrrp_zebra.h index 70871cd89b..5e8ff09543 100644 --- a/vrrpd/vrrp_zebra.h +++ b/vrrpd/vrrp_zebra.h @@ -1,7 +1,7 @@ /* - * Zebra interfacing - * Copyright (C) 2018 Cumulus Networks, Inc. - * Quentin Young + * VRRP Zebra interfacing. + * Copyright (C) 2018-2019 Cumulus Networks, Inc. + * Quentin Young * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -22,4 +22,5 @@ extern void vrrp_zebra_init(void); extern void vrrp_zebra_radv_set(struct vrrp_router *r, bool enable); -#endif + +#endif /* __VRRP_ZEBRA_H__ */ From 4f0b6b451a92fef15408320adaaacc71bc783a1e Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 29 Jan 2019 20:51:35 +0000 Subject: [PATCH 040/153] vrrpd: same VRID, different interface Identify VRRP instances by the 2-tuple (ifp, vrid) instead of by VRID, allowing the same instance to be configured on different interfaces. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 19 ++++++++++++++----- vrrpd/vrrp.h | 2 +- vrrpd/vrrp_vty.c | 46 ++++++++++++++++++++++++++++------------------ 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 08517c79d1..682c85adb5 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -305,7 +305,7 @@ static void vrrp_router_destroy(struct vrrp_router *r) struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid) { - struct vrrp_vrouter *vr = vrrp_lookup(vrid); + struct vrrp_vrouter *vr = vrrp_lookup(ifp, vrid); if (vr) return vr; @@ -332,15 +332,15 @@ void vrrp_vrouter_destroy(struct vrrp_vrouter *vr) { vrrp_router_destroy(vr->v4); vrrp_router_destroy(vr->v6); - vr->ifp = NULL; hash_release(vrrp_vrouters_hash, vr); XFREE(MTYPE_TMP, vr); } -struct vrrp_vrouter *vrrp_lookup(uint8_t vrid) +struct vrrp_vrouter *vrrp_lookup(struct interface *ifp, uint8_t vrid) { struct vrrp_vrouter vr; vr.vrid = vrid; + vr.ifp = ifp; return hash_lookup(vrrp_vrouters_hash, &vr); } @@ -1173,7 +1173,11 @@ static unsigned int vrrp_hash_key(void *arg) { struct vrrp_vrouter *vr = arg; - return vr->vrid; + char key[IFNAMSIZ + 64]; + snprintf(key, sizeof(key), "%d%s%u", vr->ifp->ifindex, vr->ifp->name, + vr->vrid); + + return string_hash_make(key); } static bool vrrp_hash_cmp(const void *arg1, const void *arg2) @@ -1181,7 +1185,12 @@ static bool vrrp_hash_cmp(const void *arg1, const void *arg2) const struct vrrp_vrouter *vr1 = arg1; const struct vrrp_vrouter *vr2 = arg2; - return vr1->vrid == vr2->vrid; + if (vr1->ifp != vr2->ifp) + return 0; + if (vr1->vrid != vr2->vrid) + return 0; + + return 1; } void vrrp_init(void) diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index d060b95dfa..e8535cb691 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -333,6 +333,6 @@ int vrrp_event(struct vrrp_router *r, int event); /* * Find VRRP Virtual Router by Virtual Router ID */ -struct vrrp_vrouter *vrrp_lookup(uint8_t vrid); +struct vrrp_vrouter *vrrp_lookup(struct interface *ifp, uint8_t vrid); #endif /* __VRRP_H__ */ diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index ff8f1e68e6..ca2ef1842a 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -40,9 +40,9 @@ #define VRRP_ADVINT_STR "Virtual Router Advertisement Interval\n" #define VRRP_IP_STR "Virtual Router IPv4 address\n" -#define VROUTER_GET_VTY(_vty, _vrid, _vr) \ +#define VROUTER_GET_VTY(_vty, _ifp, _vrid, _vr) \ do { \ - _vr = vrrp_lookup(_vrid); \ + _vr = vrrp_lookup(_ifp, _vrid); \ if (!_vr) { \ vty_out(_vty, \ "%% Please configure VRRP instance %u\n", \ @@ -72,7 +72,7 @@ DEFPY(vrrp_vrid, { VTY_DECLVAR_CONTEXT(interface, ifp); - struct vrrp_vrouter *vr = vrrp_lookup(vrid); + struct vrrp_vrouter *vr = vrrp_lookup(ifp, vrid); if (no && vr) vrrp_vrouter_destroy(vr); @@ -97,13 +97,15 @@ DEFPY(vrrp_priority, VRRP_PRIORITY_STR "Priority value") { + VTY_DECLVAR_CONTEXT(interface, ifp); + struct vrrp_vrouter *vr; struct vrrp_router *r; bool nr[2] = { false, false }; int ret = CMD_SUCCESS; uint8_t newprio = no ? VRRP_DEFAULT_PRIORITY : priority; - VROUTER_GET_VTY(vty, vrid, vr); + VROUTER_GET_VTY(vty, ifp, vrid, vr); r = vr->v4; for (int i = 0; i < 2; i++) { @@ -141,10 +143,12 @@ DEFPY(vrrp_advertisement_interval, NO_STR VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR "Advertisement interval in centiseconds") { + VTY_DECLVAR_CONTEXT(interface, ifp); + struct vrrp_vrouter *vr; uint16_t newadvint = no ? VRRP_DEFAULT_ADVINT : advertisement_interval; - VROUTER_GET_VTY(vty, vrid, vr); + VROUTER_GET_VTY(vty, ifp, vrid, vr); vrrp_set_advertisement_interval(vr, newadvint); return CMD_SUCCESS; @@ -159,10 +163,12 @@ DEFPY(vrrp_ip, "Add IPv4 address\n" VRRP_IP_STR) { + VTY_DECLVAR_CONTEXT(interface, ifp); + struct vrrp_vrouter *vr; int ret; - VROUTER_GET_VTY(vty, vrid, vr); + VROUTER_GET_VTY(vty, ifp, vrid, vr); vrrp_add_ipv4(vr, ip); if (vr->v4->fsm.state == VRRP_STATE_INITIALIZE) { @@ -189,10 +195,12 @@ DEFPY(vrrp_ip6, "Add IPv6 address\n" VRRP_IP_STR) { + VTY_DECLVAR_CONTEXT(interface, ifp); + struct vrrp_vrouter *vr; int ret; - VROUTER_GET_VTY(vty, vrid, vr); + VROUTER_GET_VTY(vty, ifp, vrid, vr); vrrp_add_ipv6(vr, ipv6); if (vr->v6->fsm.state == VRRP_STATE_INITIALIZE) { @@ -287,26 +295,28 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) DEFPY(vrrp_vrid_show, vrrp_vrid_show_cmd, - "show vrrp [(1-255)$vrid]", + "show vrrp [interface INTERFACE$ifn] [(1-255)$vrid]", SHOW_STR VRRP_STR + INTERFACE_STR + "Only show VRRP instances on this interface\n" VRRP_VRID_STR) { struct vrrp_vrouter *vr; + struct listnode *ln; + struct list *ll = hash_to_list(vrrp_vrouters_hash); + + for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) { + if (ifn && !strmatch(ifn, vr->ifp->name)) + continue; + if (vrid && vrid != vr->vrid) + continue; - if (vrid) { - VROUTER_GET_VTY(vty, vrid, vr); vrrp_show(vty, vr); - } else { - struct list *ll = hash_to_list(vrrp_vrouters_hash); - struct listnode *ln; - - for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) - vrrp_show(vty, vr); - - list_delete_and_null(&ll); } + list_delete(&ll); + return CMD_SUCCESS; } From 2cd909020d730e5457ea211a21242b5181deb334 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 30 Jan 2019 21:47:55 +0000 Subject: [PATCH 041/153] vrrpd: handle address deletion, don't accept dupes * Do nothing if user tries to add the same IP twice * Implement deletion of IPs * Deactivate virtual router if all IPs are deleted * Deduplicate add / remove code Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 129 ++++++++++++++++++++++++++++++++++------------- vrrpd/vrrp.h | 94 ++++++++++++++++++++++++++++++++-- vrrpd/vrrp_vty.c | 78 ++++++++++++++++++++-------- 3 files changed, 243 insertions(+), 58 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 682c85adb5..cc93b94a9b 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -143,57 +143,118 @@ void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr, vrrp_recalculate_timers(vr->v6); } -void vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4) +static bool vrrp_has_ip(struct vrrp_vrouter *vr, struct ipaddr *ip) { - struct ipaddr *v4_ins = XCALLOC(MTYPE_TMP, sizeof(struct ipaddr)); + size_t cmpsz = ip->ipa_type == IPADDR_V4 ? sizeof(struct in_addr) + : sizeof(struct in6_addr); + struct vrrp_router *r = ip->ipa_type == IPADDR_V4 ? vr->v4 : vr->v6; + struct listnode *ln; + struct ipaddr *iter; - v4_ins->ipa_type = IPADDR_V4; - v4_ins->ipaddr_v4 = v4; + for (ALL_LIST_ELEMENTS_RO(r->addrs, ln, iter)) + if (!memcmp(&iter->ip, &ip->ip, cmpsz)) + return true; - if (!vrrp_is_owner(vr->ifp, v4_ins) && vr->v4->is_owner) { + return false; +} + +int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip, bool activate) +{ + if (vrrp_has_ip(r->vr, ip)) + return 0; + + if (!vrrp_is_owner(r->vr->ifp, ip) && r->is_owner) { char ipbuf[INET6_ADDRSTRLEN]; - ipaddr2str(v4_ins, ipbuf, sizeof(ipbuf)); + inet_ntop(r->family, &ip->ip, ipbuf, sizeof(ipbuf)); zlog_err( VRRP_LOGPFX VRRP_LOGPFX_VRID "This VRRP router is not the address owner of %s, but is the address owner of other addresses; this config is unsupported.", - vr->vrid, ipbuf); - /* FIXME: indicate failure with rc */ - return; + r->vr->vrid, ipbuf); + return -1; } - listnode_add(vr->v4->addrs, v4_ins); -} + struct ipaddr *new = XCALLOC(MTYPE_TMP, sizeof(struct ipaddr)); -void vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6) -{ - struct ipaddr *v6_ins = XCALLOC(MTYPE_TMP, sizeof(struct ipaddr)); + *new = *ip; + listnode_add(r->addrs, new); - v6_ins->ipa_type = IPADDR_V6; - memcpy(&v6_ins->ipaddr_v6, &v6, sizeof(struct in6_addr)); + bool do_activate = (activate && r->fsm.state == VRRP_STATE_INITIALIZE); + int ret = 0; - if (!vrrp_is_owner(vr->ifp, v6_ins) && vr->v6->is_owner) { - char ipbuf[INET6_ADDRSTRLEN]; - ipaddr2str(v6_ins, ipbuf, sizeof(ipbuf)); - zlog_err( - VRRP_LOGPFX VRRP_LOGPFX_VRID - "This VRRP router is not the address owner of %s, but is the address owner of other addresses; this config is unsupported.", - vr->vrid, ipbuf); - /* FIXME: indicate failure with rc */ - return; + if (do_activate) + ret = vrrp_event(r, VRRP_EVENT_STARTUP); + else if (r->fsm.state == VRRP_STATE_MASTER) { + switch (r->family) { + case AF_INET: + vrrp_garp_send(r, &new->ipaddr_v4); + break; + case AF_INET6: + vrrp_ndisc_una_send(r, new); + break; + } } - listnode_add(vr->v6->addrs, v6_ins); - - if (vr->v6->fsm.state == VRRP_STATE_MASTER) - vrrp_ndisc_una_send(vr->v6, v6_ins); + return ret; } -void vrrp_add_ip(struct vrrp_vrouter *vr, struct ipaddr ip) +int vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4, bool activate) { - 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); + struct ipaddr ip; + ip.ipa_type = IPADDR_V4; + ip.ipaddr_v4 = v4; + return vrrp_add_ip(vr->v4, &ip, activate); +} + +int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6, bool activate) +{ + struct ipaddr ip; + ip.ipa_type = IPADDR_V6; + ip.ipaddr_v6 = v6; + return vrrp_add_ip(vr->v6, &ip, activate); +} + +int vrrp_del_ip(struct vrrp_router *r, struct ipaddr *ip, bool deactivate) +{ + size_t cmpsz = ip->ipa_type == IPADDR_V4 ? sizeof(struct in_addr) + : sizeof(struct in6_addr); + struct listnode *ln, *nn; + struct ipaddr *iter; + int ret = 0; + + if (!vrrp_has_ip(r->vr, ip)) + return 0; + + if (deactivate && r->addrs->count == 1 + && r->fsm.state != VRRP_STATE_INITIALIZE) + ret = vrrp_event(r, VRRP_EVENT_SHUTDOWN); + + /* + * Don't delete IP if we failed to deactivate, otherwise we'll run into + * issues later trying to build a VRRP advertisement with no IPs + */ + if (ret == 0) { + for (ALL_LIST_ELEMENTS(r->addrs, ln, nn, iter)) + if (!memcmp(&iter->ip, &ip->ip, cmpsz)) + list_delete_node(r->addrs, ln); + } + + return ret; +} + +int vrrp_del_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6, bool deactivate) +{ + struct ipaddr ip; + ip.ipa_type = IPADDR_V6; + ip.ipaddr_v6 = v6; + return vrrp_del_ip(vr->v6, &ip, deactivate); +} + +int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4, bool deactivate) +{ + struct ipaddr ip; + ip.ipa_type = IPADDR_V4; + ip.ipaddr_v4 = v4; + return vrrp_del_ip(vr->v4, &ip, deactivate); } diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index e8535cb691..6ee2ad6619 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -260,13 +260,21 @@ void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr, /* * Add an IPvX address to a VRRP Virtual Router. * - * vr + * r * Virtual Router to add IPvx address to * * ip * Address to add + * + * activate + * Whether to automatically start the VRRP router if this is the first IP + * address added. + * + * Returns: + * -1 on error + * 0 otherwise */ -void vrrp_add_ip(struct vrrp_vrouter *vr, struct ipaddr ip); +int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip, bool activate); /* * Add an IPv4 address to a VRRP Virtual Router. @@ -276,8 +284,16 @@ void vrrp_add_ip(struct vrrp_vrouter *vr, struct ipaddr ip); * * v4 * Address to add + * + * activate + * Whether to automatically start the VRRP router if this is the first IP + * address added. + * + * Returns: + * -1 on error + * 0 otherwise */ -void vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4); +int vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4, bool activate); /* * Add an IPv6 address to a VRRP Virtual Router. @@ -287,9 +303,79 @@ void vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4); * * v6 * Address to add + * + * activate + * Whether to automatically start the VRRP router if this is the first IP + * address added. + * + * Returns: + * -1 on error + * 0 otherwise */ -void vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6); +int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6, bool activate); +/* + * Remove an IP address from a VRRP Virtual Router. + * + * r + * Virtual Router to remove IP address from + * + * ip + * Address to remove + * + * deactivate + * Whether to automatically stop the VRRP router if removing v4 would leave + * us with an empty address list. If this is not true and ip is the only IP + * address backed up by this virtual router, this function will not remove + * the address and return failure. + * + * Returns: + * -1 on error + * 0 otherwise + */ +int vrrp_del_ip(struct vrrp_router *r, struct ipaddr *ip, bool deactivate); + +/* + * Remove an IPv4 address from a VRRP Virtual Router. + * + * vr + * Virtual Router to remove IPv4 address from + * + * v4 + * Address to remove + * + * deactivate + * Whether to automatically stop the VRRP router if removing v4 would leave + * us with an empty address list. If this is not true and v4 is the only + * IPv4 address backed up by this virtual router, this function will not + * remove the address and return failure. + * + * Returns: + * -1 on error + * 0 otherwise + */ +int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4, bool deactivate); + +/* + * Remove an IPv6 address from a VRRP Virtual Router. + * + * vr + * Virtual Router to remove IPv6 address from + * + * v6 + * Address to remove + * + * deactivate + * Whether to automatically stop the VRRP router if removing v5 would leave + * us with an empty address list. If this is not true and v4 is the only + * IPv6 address backed up by this virtual router, this function will not + * remove the address and return failure. + * + * Returns: + * -1 on error + * 0 otherwise + */ +int vrrp_del_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6, bool deactivate); /* State machine ----------------------------------------------------------- */ diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index ca2ef1842a..14693bd0c0 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -166,21 +166,40 @@ DEFPY(vrrp_ip, VTY_DECLVAR_CONTEXT(interface, ifp); struct vrrp_vrouter *vr; - int ret; + bool deactivated = false; + bool activated = false; + bool failed = false; + int ret = CMD_SUCCESS; VROUTER_GET_VTY(vty, ifp, vrid, vr); - vrrp_add_ipv4(vr, ip); - if (vr->v4->fsm.state == VRRP_STATE_INITIALIZE) { - vty_out(vty, "%% Activating Virtual Router %ld\n", vrid); - ret = vrrp_event(vr->v4, VRRP_EVENT_STARTUP); - ret = ret < 0 ? CMD_WARNING_CONFIG_FAILED : CMD_SUCCESS; + bool will_activate = (vr->v4->fsm.state == VRRP_STATE_INITIALIZE); - if (ret == CMD_WARNING_CONFIG_FAILED) - vty_out(vty, "%% Failed to start Virtual Router %ld\n", - vrid); + if (no) { + int oldstate = vr->v4->fsm.state; + failed = vrrp_del_ipv4(vr, ip, true); + deactivated = (vr->v4->fsm.state == VRRP_STATE_INITIALIZE + && oldstate != VRRP_STATE_INITIALIZE); } else { - ret = CMD_SUCCESS; + int oldstate = vr->v4->fsm.state; + failed = vrrp_add_ipv4(vr, ip, true); + activated = (vr->v4->fsm.state != VRRP_STATE_INITIALIZE + && oldstate == VRRP_STATE_INITIALIZE); + } + + if (activated) + vty_out(vty, "%% Activated IPv4 Virtual Router %ld\n", vrid); + if (deactivated) + vty_out(vty, "%% Deactivated IPv4 Virtual Router %ld\n", vrid); + if (failed) { + vty_out(vty, "%% Failed to %s virtual IP", + no ? "remove" : "add"); + ret = CMD_WARNING_CONFIG_FAILED; + if (will_activate && !activated) { + vty_out(vty, + "%% Failed to activate IPv4 Virtual Router %ld\n", + vrid); + } } return ret; @@ -198,21 +217,40 @@ DEFPY(vrrp_ip6, VTY_DECLVAR_CONTEXT(interface, ifp); struct vrrp_vrouter *vr; - int ret; + bool deactivated = false; + bool activated = false; + bool failed = false; + int ret = CMD_SUCCESS; VROUTER_GET_VTY(vty, ifp, 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; + bool will_activate = (vr->v6->fsm.state == VRRP_STATE_INITIALIZE); - if (ret == CMD_WARNING_CONFIG_FAILED) - vty_out(vty, "%% Failed to start Virtual Router %ld\n", - vrid); + if (no) { + int oldstate = vr->v6->fsm.state; + failed = vrrp_del_ipv6(vr, ipv6, true); + deactivated = (vr->v6->fsm.state == VRRP_STATE_INITIALIZE + && oldstate != VRRP_STATE_INITIALIZE); } else { - ret = CMD_SUCCESS; + int oldstate = vr->v6->fsm.state; + failed = vrrp_add_ipv6(vr, ipv6, true); + activated = (vr->v6->fsm.state != VRRP_STATE_INITIALIZE + && oldstate == VRRP_STATE_INITIALIZE); + } + + if (activated) + vty_out(vty, "%% Activated IPv6 Virtual Router %ld\n", vrid); + if (deactivated) + vty_out(vty, "%% Deactivated IPv6 Virtual Router %ld\n", vrid); + if (failed) { + vty_out(vty, "%% Failed to %s virtual IP", + no ? "remove" : "add"); + ret = CMD_WARNING_CONFIG_FAILED; + if (will_activate && !activated) { + vty_out(vty, + "%% Failed to activate IPv4 Virtual Router %ld\n", + vrid); + } } return ret; From 72df9d93a57e0022104fd7caeb3c67e46221357b Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 1 Feb 2019 17:55:56 +0000 Subject: [PATCH 042/153] vrrpd: use correct mtypes Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 16 +++++++++------- vrrpd/vrrp_memory.c | 8 ++++++-- vrrpd/vrrp_memory.h | 7 ++++++- vrrpd/vrrp_packet.c | 3 ++- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index cc93b94a9b..37846d6253 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -32,6 +32,7 @@ #include "vrrp.h" #include "vrrp_arp.h" +#include "vrrp_memory.h" #include "vrrp_ndisc.h" #include "vrrp_packet.h" #include "vrrp_zebra.h" @@ -173,7 +174,7 @@ int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip, bool activate) return -1; } - struct ipaddr *new = XCALLOC(MTYPE_TMP, sizeof(struct ipaddr)); + struct ipaddr *new = XCALLOC(MTYPE_VRRP_IP, sizeof(struct ipaddr)); *new = *ip; listnode_add(r->addrs, new); @@ -263,7 +264,7 @@ int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4, bool deactivate) static void vrrp_router_addr_list_del_cb(void *val) { struct ipaddr *ip = val; - XFREE(MTYPE_TMP, ip); + XFREE(MTYPE_VRRP_IP, ip); } /* @@ -332,7 +333,8 @@ static bool vrrp_attach_interface(struct vrrp_router *r) static struct vrrp_router *vrrp_router_create(struct vrrp_vrouter *vr, int family) { - struct vrrp_router *r = XCALLOC(MTYPE_TMP, sizeof(struct vrrp_router)); + struct vrrp_router *r = + XCALLOC(MTYPE_VRRP_RTR, sizeof(struct vrrp_router)); r->family = family; r->sock_rx = -1; @@ -361,7 +363,7 @@ static void vrrp_router_destroy(struct vrrp_router *r) /* FIXME: also delete list elements */ list_delete(&r->addrs); - XFREE(MTYPE_TMP, r); + XFREE(MTYPE_VRRP_RTR, r); } struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid) @@ -371,7 +373,7 @@ struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid) if (vr) return vr; - vr = XCALLOC(MTYPE_TMP, sizeof(struct vrrp_vrouter)); + vr = XCALLOC(MTYPE_VRRP_RTR, sizeof(struct vrrp_vrouter)); vr->ifp = ifp; vr->vrid = vrid; @@ -394,7 +396,7 @@ void vrrp_vrouter_destroy(struct vrrp_vrouter *vr) vrrp_router_destroy(vr->v4); vrrp_router_destroy(vr->v6); hash_release(vrrp_vrouters_hash, vr); - XFREE(MTYPE_TMP, vr); + XFREE(MTYPE_VRRP_RTR, vr); } struct vrrp_vrouter *vrrp_lookup(struct interface *ifp, uint8_t vrid) @@ -444,7 +446,7 @@ static void vrrp_send_advertisement(struct vrrp_router *r) ssize_t sent = sendto(r->sock_tx, pkt, (size_t)pktlen, 0, &dest.sa, sockunion_sizeof(&dest)); - XFREE(MTYPE_TMP, pkt); + XFREE(MTYPE_VRRP_PKT, pkt); if (sent < 0) { zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID diff --git a/vrrpd/vrrp_memory.c b/vrrpd/vrrp_memory.c index 9ce7c90413..30eef523cd 100644 --- a/vrrpd/vrrp_memory.c +++ b/vrrpd/vrrp_memory.c @@ -18,8 +18,12 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include -#include + +#include "lib/memory.h" #include "vrrp_memory.h" -DEFINE_MGROUP(VRRP, "vrrpd") +DEFINE_MGROUP(VRRPD, "vrrpd"); +DEFINE_MTYPE(VRRPD, VRRP_IP, "VRRP IP address"); +DEFINE_MTYPE(VRRPD, VRRP_PKT, "VRRP packet"); +DEFINE_MTYPE(VRRPD, VRRP_RTR, "VRRP Router"); diff --git a/vrrpd/vrrp_memory.h b/vrrpd/vrrp_memory.h index ae6975478d..c3025d1acb 100644 --- a/vrrpd/vrrp_memory.h +++ b/vrrpd/vrrp_memory.h @@ -20,8 +20,13 @@ #ifndef __VRRP_MEMORY_H__ #define __VRRP_MEMORY_H__ +#include + #include "lib/memory.h" -DECLARE_MGROUP(VRRP) +DECLARE_MGROUP(VRRPD); +DECLARE_MTYPE(VRRP_IP); +DECLARE_MTYPE(VRRP_PKT); +DECLARE_MTYPE(VRRP_RTR); #endif /* __VRRP_MEMORY_H__ */ diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index fd0256d6e3..eca3af0437 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -27,6 +27,7 @@ #include "lib/memory.h" #include "vrrp.h" +#include "vrrp_memory.h" #include "vrrp_packet.h" /* clang-format off */ @@ -58,7 +59,7 @@ ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, struct ipaddr *src, uint8_t vrid, size_t addrsz = v6 ? sizeof(struct in6_addr) : sizeof(struct in_addr); size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, numip); - *pkt = XCALLOC(MTYPE_TMP, pktsize); + *pkt = XCALLOC(MTYPE_VRRP_PKT, pktsize); (*pkt)->hdr.vertype |= VRRP_VERSION << 4; (*pkt)->hdr.vertype |= VRRP_TYPE_ADVERTISEMENT; From d9e01e1cabcc9a15e946922b32af5550cfb5282b Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 1 Feb 2019 18:49:16 +0000 Subject: [PATCH 043/153] vrrpd: cleanup vrrp packet crafting code * Prefix all packet functions with 'vrrp_pkt' * Break out checksum computation into separate function * Accept version field when building advertisements * Update doc comments Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 20 +++++------ vrrpd/vrrp_packet.c | 84 +++++++++++++++++++++++++++++++-------------- vrrpd/vrrp_packet.h | 28 +++++++++------ 3 files changed, 87 insertions(+), 45 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 37846d6253..edac0f66cb 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -424,18 +424,18 @@ static int vrrp_master_down_timer_expire(struct thread *thread); static void vrrp_send_advertisement(struct vrrp_router *r) { struct vrrp_pkt *pkt; - ssize_t pktlen; + ssize_t pktsz; struct ipaddr *addrs[r->addrs->count]; union sockunion dest; list_to_array(r->addrs, (void **)addrs, r->addrs->count); - pktlen = vrrp_pkt_build(&pkt, &r->src, r->vr->vrid, r->priority, - r->vr->advertisement_interval, r->addrs->count, - (struct ipaddr **)&addrs); + pktsz = vrrp_pkt_adver_build(&pkt, &r->src, 3, r->vr->vrid, r->priority, + r->vr->advertisement_interval, + r->addrs->count, (struct ipaddr **)&addrs); - if (pktlen > 0) - zlog_hexdump(pkt, (size_t) pktlen); + if (pktsz > 0) + zlog_hexdump(pkt, (size_t) pktsz); else zlog_warn("Could not build VRRP packet"); @@ -443,7 +443,7 @@ static void vrrp_send_advertisement(struct vrrp_router *r) r->family == AF_INET ? VRRP_MCASTV4_GROUP_STR : VRRP_MCASTV6_GROUP_STR; str2sockunion(group, &dest); - ssize_t sent = sendto(r->sock_tx, pkt, (size_t)pktlen, 0, &dest.sa, + ssize_t sent = sendto(r->sock_tx, pkt, (size_t)pktsz, 0, &dest.sa, sockunion_sizeof(&dest)); XFREE(MTYPE_VRRP_PKT, pkt); @@ -469,7 +469,7 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct vrrp_pkt *pkt, size_t pktsize) { char dumpbuf[BUFSIZ]; - vrrp_pkt_dump(dumpbuf, sizeof(dumpbuf), pkt); + vrrp_pkt_adver_dump(dumpbuf, sizeof(dumpbuf), pkt); zlog_debug("Received VRRP Advertisement:\n%s", dumpbuf); /* Check that VRID matches our configured VRID */ @@ -595,8 +595,8 @@ static int vrrp_read(struct thread *thread) r->vr->vrid, family2str(r->family)); zlog_hexdump(r->ibuf, nbytes); - pktsize = vrrp_parse_datagram(r->family, &m, nbytes, &pkt, errbuf, - sizeof(errbuf)); + pktsize = vrrp_pkt_parse_datagram(r->family, &m, nbytes, &pkt, errbuf, + sizeof(errbuf)); if (pktsize < 0) { zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index eca3af0437..2d5129b285 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -51,17 +51,67 @@ const char *vrrp_packet_names[16] = { }; /* clang-format on */ -ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, struct ipaddr *src, uint8_t vrid, - uint8_t prio, uint16_t max_adver_int, uint8_t numip, - struct ipaddr **ips) +/* + * Compute the VRRP checksum. + * + * Checksum is not set in the packet, just computed. + * + * pkt + * VRRP packet, fully filled out except for checksum field. + * + * pktsize + * sizeof(*pkt) + * + * src + * IP address that pkt will be transmitted from. + * + * Returns: + * VRRP checksum in network byte order. + */ +static uint16_t vrrp_pkt_checksum(struct vrrp_pkt *pkt, size_t pktsize, + struct ipaddr *src) { + uint16_t chksum; + bool v6 = (src->ipa_type == IPADDR_V6); + + uint16_t chksum_pre = pkt->hdr.chksum; + pkt->hdr.chksum = 0; + + if (v6) { + struct ipv6_ph ph = {}; + ph.src = src->ipaddr_v6; + inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &ph.dst); + ph.ulpl = htons(pktsize); + ph.next_hdr = 112; + chksum = in_cksum_with_ph6(&ph, pkt, pktsize); + } else { + struct ipv4_ph ph = {}; + ph.src = src->ipaddr_v4; + inet_pton(AF_INET, VRRP_MCASTV4_GROUP_STR, &ph.dst); + ph.proto = 112; + ph.len = htons(pktsize); + chksum = in_cksum_with_ph4(&ph, pkt, pktsize); + } + + pkt->hdr.chksum = chksum_pre; + + return chksum; +} + +ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src, + uint8_t version, uint8_t vrid, uint8_t prio, + uint16_t max_adver_int, uint8_t numip, + struct ipaddr **ips) +{ + assert(version >= 2 && version <= 3); + bool v6 = IS_IPADDR_V6(ips[0]); size_t addrsz = v6 ? sizeof(struct in6_addr) : sizeof(struct in_addr); size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, numip); *pkt = XCALLOC(MTYPE_VRRP_PKT, pktsize); - (*pkt)->hdr.vertype |= VRRP_VERSION << 4; + (*pkt)->hdr.vertype |= version << 4; (*pkt)->hdr.vertype |= VRRP_TYPE_ADVERTISEMENT; (*pkt)->hdr.vrid = vrid; (*pkt)->hdr.priority = prio; @@ -75,28 +125,12 @@ ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, struct ipaddr *src, uint8_t vrid, aptr += addrsz; } - (*pkt)->hdr.chksum = 0; - - if (v6) { - struct ipv6_ph ph = {}; - ph.src = src->ipaddr_v6; - inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &ph.dst); - ph.ulpl = htons(pktsize); - ph.next_hdr = 112; - (*pkt)->hdr.chksum = in_cksum_with_ph6(&ph, *pkt, pktsize); - } else { - struct ipv4_ph ph = {}; - ph.src = src->ipaddr_v4; - inet_pton(AF_INET, VRRP_MCASTV4_GROUP_STR, &ph.dst); - ph.proto = 112; - ph.len = htons(pktsize); - (*pkt)->hdr.chksum = in_cksum_with_ph4(&ph, *pkt, pktsize); - } + (*pkt)->hdr.chksum = vrrp_pkt_checksum(*pkt, pktsize, src); return pktsize; } -size_t vrrp_pkt_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt) +size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt) { if (buflen < 1) return 0; @@ -127,9 +161,9 @@ size_t vrrp_pkt_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt) return rs; } -ssize_t vrrp_parse_datagram(int family, struct msghdr *m, size_t read, - struct vrrp_pkt **pkt, char *errmsg, - size_t errmsg_len) +ssize_t vrrp_pkt_parse_datagram(int family, struct msghdr *m, size_t read, + struct vrrp_pkt **pkt, char *errmsg, + size_t errmsg_len) { /* Source (MAC & IP), Dest (MAC & IP) TTL validation done by kernel */ size_t addrsz = (family == AF_INET) ? sizeof(struct in_addr) diff --git a/vrrpd/vrrp_packet.h b/vrrpd/vrrp_packet.h index df43ee7604..ed577ddcfa 100644 --- a/vrrpd/vrrp_packet.h +++ b/vrrpd/vrrp_packet.h @@ -26,7 +26,6 @@ #include "lib/memory.h" #include "lib/prefix.h" -#define VRRP_VERSION 3 #define VRRP_TYPE_ADVERTISEMENT 1 extern const char *vrrp_packet_names[16]; @@ -94,11 +93,19 @@ struct vrrp_pkt { #define VRRP_MAX_PKT_SIZE VRRP_MAX_PKT_SIZE_V6 /* - * Builds a VRRP packet. + * Builds a VRRP ADVERTISEMENT packet. * * pkt * Pointer to store pointer to result buffer in * + * src + * Source address packet will be transmitted from. This is needed to compute + * the VRRP checksum. The returned packet must be sent in an IP datagram with + * the source address equal to this field, or the checksum will be invalid. + * + * version + * VRRP version; must be 2 or 3 + * * vrid * Virtual Router Identifier * @@ -118,12 +125,13 @@ struct vrrp_pkt { * array of pointer to either struct in_addr (v6 = false) or struct in6_addr * (v6 = true) */ -ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, struct ipaddr *src, uint8_t vrid, - uint8_t prio, uint16_t max_adver_int, uint8_t numip, - struct ipaddr **ips); +ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src, + uint8_t version, uint8_t vrid, uint8_t prio, + uint16_t max_adver_int, uint8_t numip, + struct ipaddr **ips); /* - * Dumps a VRRP packet to a string. + * Dumps a VRRP ADVERTISEMENT packet to a string. * * Currently only dumps the header. * @@ -139,7 +147,7 @@ ssize_t vrrp_pkt_build(struct vrrp_pkt **pkt, struct ipaddr *src, uint8_t vrid, * Returns: * # bytes written to buf */ -size_t vrrp_pkt_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt); +size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt); /* @@ -170,8 +178,8 @@ size_t vrrp_pkt_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt); * Returns: * Size of VRRP packet, or -1 upon error */ -ssize_t vrrp_parse_datagram(int family, struct msghdr *m, size_t read, - struct vrrp_pkt **pkt, char *errmsg, - size_t errmsg_len); +ssize_t vrrp_pkt_parse_datagram(int family, struct msghdr *m, size_t read, + struct vrrp_pkt **pkt, char *errmsg, + size_t errmsg_len); #endif /* __VRRP_PACKET_H__ */ From d04bb25aced1db97f9af795acea99ba682a3ad14 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 1 Feb 2019 20:54:44 +0000 Subject: [PATCH 044/153] vrrpd: check rx'd advertisement checksum And retrieve source address, since we need it anyway. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 9 ++++++--- vrrpd/vrrp_packet.c | 23 +++++++++++++++++++---- vrrpd/vrrp_packet.h | 7 +++++-- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index edac0f66cb..411c3c7e7b 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -568,6 +568,7 @@ static int vrrp_read(struct thread *thread) bool resched; char errbuf[BUFSIZ]; uint8_t control[64]; + struct ipaddr src = {}; struct msghdr m; struct iovec iov; @@ -595,8 +596,8 @@ static int vrrp_read(struct thread *thread) r->vr->vrid, family2str(r->family)); zlog_hexdump(r->ibuf, nbytes); - pktsize = vrrp_pkt_parse_datagram(r->family, &m, nbytes, &pkt, errbuf, - sizeof(errbuf)); + pktsize = vrrp_pkt_parse_datagram(r->family, &m, nbytes, &src, &pkt, + errbuf, sizeof(errbuf)); if (pktsize < 0) { zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID @@ -847,7 +848,9 @@ static int vrrp_socket(struct vrrp_router *r) "Failed to set outgoing multicast hop count to 255; RFC 5798 compliant implementations will drop our packets", r->vr->vrid); } - ret = setsockopt_ipv6_hoplimit(r->sock_rx, 1); + + /* Request hop limit delivery */ + setsockopt_ipv6_hoplimit(r->sock_rx, 1); if (ret < 0) { zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID "Failed to request IPv6 Hop Limit delivery", diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index 2d5129b285..ccfdd8f883 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -162,8 +162,8 @@ size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt) } ssize_t vrrp_pkt_parse_datagram(int family, struct msghdr *m, size_t read, - struct vrrp_pkt **pkt, char *errmsg, - size_t errmsg_len) + struct ipaddr *src, struct vrrp_pkt **pkt, + char *errmsg, size_t errmsg_len) { /* Source (MAC & IP), Dest (MAC & IP) TTL validation done by kernel */ size_t addrsz = (family == AF_INET) ? sizeof(struct in_addr) @@ -207,6 +207,10 @@ ssize_t vrrp_pkt_parse_datagram(int family, struct msghdr *m, size_t read, /* IP empty packet check */ VRRP_PKT_VCHECK(pktsize > 0, "IPv4 packet has no payload"); + + /* Extract source address */ + src->ipa_type = IPADDR_V4; + src->ipaddr_v4 = ip->ip_src; } else if (family == AF_INET6) { struct cmsghdr *c; for (c = CMSG_FIRSTHDR(m); c != NULL; CMSG_NXTHDR(m, c)) { @@ -224,6 +228,12 @@ ssize_t vrrp_pkt_parse_datagram(int family, struct msghdr *m, size_t read, *pkt = (struct vrrp_pkt *)buf; pktsize = read; + + /* Extract source address */ + src->ipa_type = IPADDR_V6; + struct sockaddr_in6 *sa = m->msg_name; + memcpy(&src->ipaddr_v6, &sa->sin6_addr, + sizeof(struct in6_addr)); } else { assert(!"Unknown address family"); } @@ -239,6 +249,13 @@ ssize_t vrrp_pkt_parse_datagram(int family, struct msghdr *m, size_t read, VRRP_PKT_VCHECK(pktsize <= maxsize, "VRRP packet is oversized (%lu > %lu)", pktsize, VRRP_MAX_PKT_SIZE); + + /* Checksum check */ + uint16_t chksum = vrrp_pkt_checksum(*pkt, pktsize, src); + VRRP_PKT_VCHECK((*pkt)->hdr.chksum == chksum, + "Bad VRRP checksum %" PRIu16 "; should be %" PRIu16 "", + (*pkt)->hdr.chksum, chksum); + /* Version check */ VRRP_PKT_VCHECK(((*pkt)->hdr.vertype >> 4) != 2, "VRPPv2 unsupported"); VRRP_PKT_VCHECK(((*pkt)->hdr.vertype >> 4) == 3, "Bad version %u", @@ -249,8 +266,6 @@ ssize_t vrrp_pkt_parse_datagram(int family, struct msghdr *m, size_t read, /* # addresses check */ size_t ves = VRRP_PKT_SIZE(family, (*pkt)->hdr.naddr); VRRP_PKT_VCHECK(pktsize == ves, "Packet has incorrect # addresses"); - /* FIXME: checksum check */ - /* ... */ /* Addresses check */ char vbuf[INET6_ADDRSTRLEN]; diff --git a/vrrpd/vrrp_packet.h b/vrrpd/vrrp_packet.h index ed577ddcfa..af51287718 100644 --- a/vrrpd/vrrp_packet.h +++ b/vrrpd/vrrp_packet.h @@ -165,6 +165,9 @@ size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt); * read * return value of recvmsg() on VRRP router socket; must be non-negative * + * src + * Pointer to struct ipaddr to store address of datagram sender + * * pkt * Pointer to pointer to set to location of VRRP packet within buf * @@ -179,7 +182,7 @@ size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt); * Size of VRRP packet, or -1 upon error */ ssize_t vrrp_pkt_parse_datagram(int family, struct msghdr *m, size_t read, - struct vrrp_pkt **pkt, char *errmsg, - size_t errmsg_len); + struct ipaddr *src, struct vrrp_pkt **pkt, + char *errmsg, size_t errmsg_len); #endif /* __VRRP_PACKET_H__ */ From 0f1f98e8377b624b0c67e4ca845fb142d4db7812 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 1 Feb 2019 21:22:18 +0000 Subject: [PATCH 045/153] vrrpd: use address cmp for priority tiebreak Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 411c3c7e7b..2605a84dee 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -464,9 +464,25 @@ static void vrrp_send_advertisement(struct vrrp_router *r) * However, we have not validated whether the VRID is correct for this virtual * router, nor whether the priority is correct (i.e. is not 255 when we are the * address owner). + * + * r + * VRRP Router associated with the socket this advertisement was received on + * + * src + * Source address of sender + * + * pkt + * The advertisement they sent + * + * pktsize + * Size of advertisement + * + * Returns: + * -1 if advertisement is invalid + * 0 otherwise */ -static int vrrp_recv_advertisement(struct vrrp_router *r, struct vrrp_pkt *pkt, - size_t pktsize) +static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, + struct vrrp_pkt *pkt, size_t pktsize) { char dumpbuf[BUFSIZ]; vrrp_pkt_adver_dump(dumpbuf, sizeof(dumpbuf), pkt); @@ -500,8 +516,14 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct vrrp_pkt *pkt, r->addrs->count); } + int addrcmp; + size_t cmpsz = IS_IPADDR_V6(src) ? sizeof(struct in6_addr) + : sizeof(struct in_addr); + switch (r->fsm.state) { case VRRP_STATE_MASTER: + addrcmp = memcmp(&src->ip, &r->src.ip, cmpsz); + if (pkt->hdr.priority == 0) { vrrp_send_advertisement(r); THREAD_OFF(r->t_adver_timer); @@ -509,9 +531,8 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct vrrp_pkt *pkt, master, vrrp_adver_timer_expire, r, r->vr->advertisement_interval * 10, &r->t_adver_timer); - /* FIXME: 6.4.3 mandates checking sender IP address */ - } else if (pkt->hdr.priority > r->priority) { - zlog_err("NOT IMPLEMENTED"); + } else if (pkt->hdr.priority > r->priority + || ((pkt->hdr.priority == r->priority) && addrcmp > 0)) { THREAD_OFF(r->t_adver_timer); r->master_adver_interval = ntohs(pkt->hdr.v3.adver_int); vrrp_recalculate_timers(r); @@ -606,7 +627,7 @@ static int vrrp_read(struct thread *thread) } else { zlog_debug(VRRP_LOGPFX VRRP_LOGPFX_VRID "Packet looks good", r->vr->vrid); - vrrp_recv_advertisement(r, pkt, pktsize); + vrrp_recv_advertisement(r, &src, pkt, pktsize); } resched = true; From 4f838de4201322e6a74e1a0bb0639f22f27d4772 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 1 Feb 2019 21:23:02 +0000 Subject: [PATCH 046/153] lib: add sizing macro to ipaddr.h Useful for getting the size of the relevant data in the `ip` field of struct ipaddr. Signed-off-by: Quentin Young --- lib/ipaddr.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/ipaddr.h b/lib/ipaddr.h index f4ddadc66e..503431a7c0 100644 --- a/lib/ipaddr.h +++ b/lib/ipaddr.h @@ -56,6 +56,9 @@ struct ipaddr { #define SET_IPADDR_V4(p) (p)->ipa_type = IPADDR_V4 #define SET_IPADDR_V6(p) (p)->ipa_type = IPADDR_V6 +#define IPADDRSZ(p) \ + IS_IPADDR_V4((p)) ? sizeof(struct in_addr) : sizeof(struct in6_addr) + static inline int str2ipaddr(const char *str, struct ipaddr *ip) { int ret; From e920b0b28915972882c2dd10b83e6b211ae66f8f Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 1 Feb 2019 21:26:45 +0000 Subject: [PATCH 047/153] vrrpd: use ipaddr size macro Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 12 +++--------- vrrpd/vrrp_packet.c | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 2605a84dee..258c45898b 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -146,14 +146,12 @@ void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr, static bool vrrp_has_ip(struct vrrp_vrouter *vr, struct ipaddr *ip) { - size_t cmpsz = ip->ipa_type == IPADDR_V4 ? sizeof(struct in_addr) - : sizeof(struct in6_addr); struct vrrp_router *r = ip->ipa_type == IPADDR_V4 ? vr->v4 : vr->v6; struct listnode *ln; struct ipaddr *iter; for (ALL_LIST_ELEMENTS_RO(r->addrs, ln, iter)) - if (!memcmp(&iter->ip, &ip->ip, cmpsz)) + if (!memcmp(&iter->ip, &ip->ip, IPADDRSZ(ip))) return true; return false; @@ -216,8 +214,6 @@ int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6, bool activate) int vrrp_del_ip(struct vrrp_router *r, struct ipaddr *ip, bool deactivate) { - size_t cmpsz = ip->ipa_type == IPADDR_V4 ? sizeof(struct in_addr) - : sizeof(struct in6_addr); struct listnode *ln, *nn; struct ipaddr *iter; int ret = 0; @@ -235,7 +231,7 @@ int vrrp_del_ip(struct vrrp_router *r, struct ipaddr *ip, bool deactivate) */ if (ret == 0) { for (ALL_LIST_ELEMENTS(r->addrs, ln, nn, iter)) - if (!memcmp(&iter->ip, &ip->ip, cmpsz)) + if (!memcmp(&iter->ip, &ip->ip, IPADDRSZ(ip))) list_delete_node(r->addrs, ln); } @@ -517,12 +513,10 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, } int addrcmp; - size_t cmpsz = IS_IPADDR_V6(src) ? sizeof(struct in6_addr) - : sizeof(struct in_addr); switch (r->fsm.state) { case VRRP_STATE_MASTER: - addrcmp = memcmp(&src->ip, &r->src.ip, cmpsz); + addrcmp = memcmp(&src->ip, &r->src.ip, IPADDRSZ(src)); if (pkt->hdr.priority == 0) { vrrp_send_advertisement(r); diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index ccfdd8f883..14c3420619 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -107,7 +107,7 @@ ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src, bool v6 = IS_IPADDR_V6(ips[0]); - size_t addrsz = v6 ? sizeof(struct in6_addr) : sizeof(struct in_addr); + size_t addrsz = IPADDRSZ(ips[0]); size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, numip); *pkt = XCALLOC(MTYPE_VRRP_PKT, pktsize); From 3708883c5ea252cc0276b501311d3cb658d76c01 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 4 Feb 2019 17:42:39 +0000 Subject: [PATCH 048/153] vrrpd: better logging for advertisement rx Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 258c45898b..0ad4390ce8 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -480,9 +480,14 @@ static void vrrp_send_advertisement(struct vrrp_router *r) static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, struct vrrp_pkt *pkt, size_t pktsize) { + char sipstr[INET6_ADDRSTRLEN]; + ipaddr2str(src, sipstr, sizeof(sipstr)); + char dumpbuf[BUFSIZ]; vrrp_pkt_adver_dump(dumpbuf, sizeof(dumpbuf), pkt); - zlog_debug("Received VRRP Advertisement:\n%s", dumpbuf); + zlog_debug(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Received VRRP Advertisement from %s:\n%s", + r->vr->vrid, sipstr, dumpbuf); /* Check that VRID matches our configured VRID */ if (pkt->hdr.vrid != r->vr->vrid) { @@ -527,6 +532,11 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, &r->t_adver_timer); } else if (pkt->hdr.priority > r->priority || ((pkt->hdr.priority == r->priority) && addrcmp > 0)) { + zlog_info( + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Received advertisement from %s w/ priority %" PRIu8 + "; switching to Backup", + r->vr->vrid, sipstr, pkt->hdr.priority); THREAD_OFF(r->t_adver_timer); r->master_adver_interval = ntohs(pkt->hdr.v3.adver_int); vrrp_recalculate_timers(r); @@ -538,6 +548,9 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, vrrp_change_state(r, VRRP_STATE_BACKUP); } else { /* Discard advertisement */ + zlog_debug(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Discarding advertisement from %s", + r->vr->vrid, sipstr); } break; case VRRP_STATE_BACKUP: @@ -558,6 +571,9 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, } else if (r->vr->preempt_mode == true && pkt->hdr.priority < r->priority) { /* Discard advertisement */ + zlog_debug(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Discarding advertisement from %s", + r->vr->vrid, sipstr); } break; case VRRP_STATE_INITIALIZE: From 999668407ea0f9492a93b226e0d06ab970c0ad82 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 4 Feb 2019 19:56:12 +0000 Subject: [PATCH 049/153] vrrpd: add support for VRRPv2 Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 45 ++++++++++++++++++++++++++++++++++++--------- vrrpd/vrrp.h | 3 ++- vrrpd/vrrp_packet.c | 30 +++++++++++++++++++++++------- vrrpd/vrrp_vty.c | 19 ++++++++++++++++--- 4 files changed, 77 insertions(+), 20 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 0ad4390ce8..e5018d5853 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -88,8 +88,8 @@ static void vrrp_mac_set(struct ethaddr *mac, bool v6, uint8_t vrid) */ static void vrrp_recalculate_timers(struct vrrp_router *r) { - r->skew_time = - ((256 - r->vr->priority) * r->master_adver_interval) / 256; + uint16_t skmai = (r->vr->version - 2) * r->master_adver_interval; + r->skew_time = ((256 - r->vr->priority) * skmai) / 256; r->master_down_interval = (3 * r->master_adver_interval); r->master_down_interval += r->skew_time; } @@ -362,16 +362,21 @@ static void vrrp_router_destroy(struct vrrp_router *r) XFREE(MTYPE_VRRP_RTR, r); } -struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid) +struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid, + uint8_t version) { struct vrrp_vrouter *vr = vrrp_lookup(ifp, vrid); if (vr) return vr; + if (version != 2 && version != 3) + return NULL; + vr = XCALLOC(MTYPE_VRRP_RTR, sizeof(struct vrrp_vrouter)); vr->ifp = ifp; + vr->version = version; vr->vrid = vrid; vr->priority = VRRP_DEFAULT_PRIORITY; vr->preempt_mode = true; @@ -426,8 +431,8 @@ static void vrrp_send_advertisement(struct vrrp_router *r) list_to_array(r->addrs, (void **)addrs, r->addrs->count); - pktsz = vrrp_pkt_adver_build(&pkt, &r->src, 3, r->vr->vrid, r->priority, - r->vr->advertisement_interval, + pktsz = vrrp_pkt_adver_build(&pkt, &r->src, r->vr->version, r->vr->vrid, + r->priority, r->vr->advertisement_interval, r->addrs->count, (struct ipaddr **)&addrs); if (pktsz > 0) @@ -459,7 +464,8 @@ static void vrrp_send_advertisement(struct vrrp_router *r) * * However, we have not validated whether the VRID is correct for this virtual * router, nor whether the priority is correct (i.e. is not 255 when we are the - * address owner). + * address owner), nor whether the advertisement interval equals our own + * configured value (this check is only performed in VRRPv2). * * r * VRRP Router associated with the socket this advertisement was received on @@ -508,6 +514,21 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, return -1; } + /* If v2, verify that adver time matches ours */ + bool adveq = (pkt->hdr.v2.adver_int + == MAX(r->vr->advertisement_interval / 100, 1)); + if (r->vr->version == 2 && !adveq) { + zlog_warn( + VRRP_LOGPFX VRRP_LOGPFX_VRID + "%s datagram invalid: Received advertisement with advertisement interval %" PRIu8 + " unequal to our configured value %u", + r->vr->vrid, family2str(r->family), + pkt->hdr.v2.adver_int, + MAX(r->vr->advertisement_interval / 100, 1)); + return -1; + } + + /* Check that # IPs received matches our # configured IPs */ if (pkt->hdr.naddr != r->addrs->count) { zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID @@ -515,7 +536,7 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, " addresses, but this VRRP instance has %u", r->vr->vrid, family2str(r->family), pkt->hdr.naddr, r->addrs->count); - } + } int addrcmp; @@ -538,7 +559,10 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, "; switching to Backup", r->vr->vrid, sipstr, pkt->hdr.priority); THREAD_OFF(r->t_adver_timer); - r->master_adver_interval = ntohs(pkt->hdr.v3.adver_int); + if (r->vr->version == 3) { + r->master_adver_interval = + htons(pkt->hdr.v3.adver_int); + } vrrp_recalculate_timers(r); THREAD_OFF(r->t_master_down_timer); thread_add_timer_msec(master, @@ -561,7 +585,10 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, r->skew_time * 10, &r->t_master_down_timer); } else if (r->vr->preempt_mode == false || pkt->hdr.priority >= r->priority) { - r->master_adver_interval = ntohs(pkt->hdr.v3.adver_int); + if (r->vr->version == 3) { + r->master_adver_interval = + ntohs(pkt->hdr.v3.adver_int); + } vrrp_recalculate_timers(r); THREAD_OFF(r->t_master_down_timer); thread_add_timer_msec(master, diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 6ee2ad6619..0df588b221 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -217,7 +217,8 @@ void vrrp_init(void); * vrid * Virtual Router Identifier */ -struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid); +struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid, + uint8_t version); /* * Destroy a VRRP Virtual Router, freeing all its resources. diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index 14c3420619..fb72d921eb 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -84,13 +84,17 @@ static uint16_t vrrp_pkt_checksum(struct vrrp_pkt *pkt, size_t pktsize, ph.ulpl = htons(pktsize); ph.next_hdr = 112; chksum = in_cksum_with_ph6(&ph, pkt, pktsize); - } else { + } else if (!v6 && ((pkt->hdr.vertype >> 4) == 3)) { struct ipv4_ph ph = {}; ph.src = src->ipaddr_v4; inet_pton(AF_INET, VRRP_MCASTV4_GROUP_STR, &ph.dst); ph.proto = 112; ph.len = htons(pktsize); chksum = in_cksum_with_ph4(&ph, pkt, pktsize); + } else if (!v6 && ((pkt->hdr.vertype >> 4) == 2)) { + chksum = in_cksum(pkt, pktsize); + } else { + assert(!"Invalid VRRP protocol version"); } pkt->hdr.chksum = chksum_pre; @@ -103,10 +107,11 @@ ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src, uint16_t max_adver_int, uint8_t numip, struct ipaddr **ips) { - assert(version >= 2 && version <= 3); - bool v6 = IS_IPADDR_V6(ips[0]); + assert(version >= 2 && version <= 3); + assert(!(version == 2 && v6)); + size_t addrsz = IPADDRSZ(ips[0]); size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, numip); *pkt = XCALLOC(MTYPE_VRRP_PKT, pktsize); @@ -116,7 +121,12 @@ ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src, (*pkt)->hdr.vrid = vrid; (*pkt)->hdr.priority = prio; (*pkt)->hdr.naddr = numip; - (*pkt)->hdr.v3.adver_int = htons(max_adver_int); + if (version == 3) + (*pkt)->hdr.v3.adver_int = htons(max_adver_int); + else if (version == 2) { + (*pkt)->hdr.v2.auth_type = 0; + (*pkt)->hdr.v2.adver_int = MAX(max_adver_int / 100, 1); + } uint8_t *aptr = (void *)(*pkt)->addrs; @@ -257,9 +267,9 @@ ssize_t vrrp_pkt_parse_datagram(int family, struct msghdr *m, size_t read, (*pkt)->hdr.chksum, chksum); /* Version check */ - VRRP_PKT_VCHECK(((*pkt)->hdr.vertype >> 4) != 2, "VRPPv2 unsupported"); - VRRP_PKT_VCHECK(((*pkt)->hdr.vertype >> 4) == 3, "Bad version %u", - (*pkt)->hdr.vertype >> 4); + uint8_t version = (*pkt)->hdr.vertype >> 4; + VRRP_PKT_VCHECK(version == 3 || version == 2, "Bad version %u", + version); /* Type check */ VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %u", (*pkt)->hdr.vertype & 0x0f); @@ -267,6 +277,12 @@ ssize_t vrrp_pkt_parse_datagram(int family, struct msghdr *m, size_t read, size_t ves = VRRP_PKT_SIZE(family, (*pkt)->hdr.naddr); VRRP_PKT_VCHECK(pktsize == ves, "Packet has incorrect # addresses"); + /* auth type check */ + if (version == 2) + VRRP_PKT_VCHECK((*pkt)->hdr.v2.auth_type == 0, + "Bad authentication type %" PRIu8, + (*pkt)->hdr.v2.auth_type); + /* Addresses check */ char vbuf[INET6_ADDRSTRLEN]; uint8_t *p = (uint8_t *)(*pkt)->addrs; diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 14693bd0c0..4229a6e353 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -39,6 +39,7 @@ #define VRRP_PRIORITY_STR "Virtual Router Priority\n" #define VRRP_ADVINT_STR "Virtual Router Advertisement Interval\n" #define VRRP_IP_STR "Virtual Router IPv4 address\n" +#define VRRP_VERSION_STR "VRRP protocol version\n" #define VROUTER_GET_VTY(_vty, _ifp, _vrid, _vr) \ do { \ @@ -65,22 +66,27 @@ DEFUN_NOSH (show_debugging_vrrpd, DEFPY(vrrp_vrid, vrrp_vrid_cmd, - "[no] vrrp (1-255)$vrid", + "[no] vrrp (1-255)$vrid [version (2-3)]", NO_STR VRRP_STR - VRRP_VRID_STR) + VRRP_VRID_STR + VRRP_VERSION_STR + VRRP_VERSION_STR) { VTY_DECLVAR_CONTEXT(interface, ifp); struct vrrp_vrouter *vr = vrrp_lookup(ifp, vrid); + if (version == 0) + version = 3; + if (no && vr) vrrp_vrouter_destroy(vr); else if (no && !vr) vty_out(vty, "%% VRRP instance %ld does not exist on %s\n", vrid, ifp->name); else if (!vr) - vrrp_vrouter_create(ifp, vrid); + vrrp_vrouter_create(ifp, vrid, version); else if (vr) vty_out(vty, "%% VRRP instance %ld already exists on %s\n", vrid, ifp->name); @@ -224,6 +230,12 @@ DEFPY(vrrp_ip6, VROUTER_GET_VTY(vty, ifp, vrid, vr); + if (vr->version != 3) { + vty_out(vty, + "%% Cannot add IPv6 address to VRRPv2 virtual router\n"); + return CMD_WARNING_CONFIG_FAILED; + } + bool will_activate = (vr->v6->fsm.state == VRRP_STATE_INITIALIZE); if (no) { @@ -269,6 +281,7 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); ttable_add_row(tt, "%s|%" PRIu32, "Virtual Router ID", vr->vrid); + ttable_add_row(tt, "%s|%" PRIu8, "Protocol Version", vr->version); ttable_add_row(tt, "%s|%s", "Interface", vr->ifp->name); prefix_mac2str(&vr->v4->vmac, ethstr4, sizeof(ethstr4)); prefix_mac2str(&vr->v6->vmac, ethstr6, sizeof(ethstr6)); From c3bd894e4d2395d2bc72b0d7a94984f04e93adb1 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 5 Feb 2019 22:02:40 +0000 Subject: [PATCH 050/153] vrrpd: protodown macvlan in backup state Signed-off-by: Quentin Young --- lib/zclient.c | 19 +++++++++++++++++++ lib/zclient.h | 4 ++++ vrrpd/vrrp.c | 4 ++++ vrrpd/vrrp_zebra.c | 6 ++++++ vrrpd/vrrp_zebra.h | 6 ++++++ zebra/if_netlink.c | 26 ++++++++++++++++++++++++++ zebra/if_netlink.h | 14 ++++++++++++++ zebra/interface.c | 6 +++++- zebra/interface.h | 1 + zebra/zapi_msg.c | 25 +++++++++++++++++++++++++ 10 files changed, 110 insertions(+), 1 deletion(-) diff --git a/lib/zclient.c b/lib/zclient.c index 96a78efad6..6647a1ab18 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -555,6 +555,25 @@ void zclient_send_interface_radv_req(struct zclient *zclient, vrf_id_t vrf_id, zclient_send_message(zclient); } +int zclient_send_interface_protodown(struct zclient *zclient, vrf_id_t vrf_id, + struct interface *ifp, bool down) +{ + struct stream *s; + + if (zclient->sock < 0) + return -1; + + s = zclient->obuf; + stream_reset(s); + zclient_create_header(s, ZEBRA_INTERFACE_SET_PROTODOWN, vrf_id); + stream_putl(s, ifp->ifindex); + stream_putc(s, !!down); + stream_putw_at(s, 0, stream_get_endp(s)); + zclient_send_message(zclient); + + return 0; +} + /* Make connection to zebra daemon. */ int zclient_start(struct zclient *zclient) { diff --git a/lib/zclient.h b/lib/zclient.h index c46d63bfab..09f0acad84 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -76,6 +76,7 @@ typedef enum { ZEBRA_INTERFACE_UP, ZEBRA_INTERFACE_DOWN, ZEBRA_INTERFACE_SET_MASTER, + ZEBRA_INTERFACE_SET_PROTODOWN, ZEBRA_ROUTE_ADD, ZEBRA_ROUTE_DELETE, ZEBRA_ROUTE_NOTIFY_OWNER, @@ -466,6 +467,9 @@ extern void zclient_send_interface_radv_req(struct zclient *zclient, vrf_id_t vrf_id, struct interface *ifp, int enable, int ra_interval); +extern int zclient_send_interface_protodown(struct zclient *zclient, + vrf_id_t vrf_id, + struct interface *ifp, bool down); /* Send redistribute command to zebra daemon. Do not update zclient state. */ extern int zebra_redistribute_send(int command, struct zclient *, afi_t, diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index e5018d5853..76f8dfea5d 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1028,6 +1028,8 @@ static void vrrp_change_state_master(struct vrrp_router *r) /* Enable ND Router Advertisements */ if (r->family == AF_INET6) vrrp_zebra_radv_set(r, true); + + vrrp_zclient_send_interface_protodown(r->mvl_ifp, false); } /* @@ -1041,6 +1043,8 @@ static void vrrp_change_state_backup(struct vrrp_router *r) /* Disable ND Router Advertisements */ if (r->family == AF_INET6) vrrp_zebra_radv_set(r, false); + + vrrp_zclient_send_interface_protodown(r->mvl_ifp, true); } /* diff --git a/vrrpd/vrrp_zebra.c b/vrrpd/vrrp_zebra.c index 1bd5aa013f..c41915cbd9 100644 --- a/vrrpd/vrrp_zebra.c +++ b/vrrpd/vrrp_zebra.c @@ -226,6 +226,12 @@ void vrrp_zebra_radv_set(struct vrrp_router *r, bool enable) enable, VRRP_RADV_INT); } +int vrrp_zclient_send_interface_protodown(struct interface *ifp, bool down) +{ + return zclient_send_interface_protodown(zclient, VRF_DEFAULT, ifp, + down); +} + void vrrp_zebra_init(void) { /* Socket for receiving updates from Zebra daemon */ diff --git a/vrrpd/vrrp_zebra.h b/vrrpd/vrrp_zebra.h index 5e8ff09543..84bcba23c1 100644 --- a/vrrpd/vrrp_zebra.h +++ b/vrrpd/vrrp_zebra.h @@ -20,7 +20,13 @@ #ifndef __VRRP_ZEBRA_H__ #define __VRRP_ZEBRA_H__ +#include + +#include "lib/if.h" + extern void vrrp_zebra_init(void); extern void vrrp_zebra_radv_set(struct vrrp_router *r, bool enable); +extern int vrrp_zclient_send_interface_protodown(struct interface *ifp, + bool down); #endif /* __VRRP_ZEBRA_H__ */ diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index ce0834f190..e7d988cd9f 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -1396,6 +1396,32 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) return 0; } +int netlink_protodown(struct interface *ifp, bool down) +{ + struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + + struct { + struct nlmsghdr n; + struct ifinfomsg ifa; + char buf[NL_PKT_BUF_SIZE]; + } req; + + memset(&req, 0, sizeof req); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_SETLINK; + req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid; + + req.ifa.ifi_index = ifp->ifindex; + + addattr_l(&req.n, sizeof req, IFLA_PROTO_DOWN, &down, 4); + addattr_l(&req.n, sizeof req, IFLA_LINK, &ifp->ifindex, 4); + + return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, + 0); +} + /* Interface information read by netlink. */ void interface_list(struct zebra_ns *zns) { diff --git a/zebra/if_netlink.h b/zebra/if_netlink.h index 710fd52558..29fd2aca35 100644 --- a/zebra/if_netlink.h +++ b/zebra/if_netlink.h @@ -32,6 +32,20 @@ extern int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); extern int interface_lookup_netlink(struct zebra_ns *zns); +/* + * Set protodown status of interface. + * + * ifp + * Interface to set protodown on. + * + * down + * If true, set protodown on. If false, set protodown off. + * + * Returns: + * 0 + */ +int netlink_protodown(struct interface *ifp, bool down); + #ifdef __cplusplus } #endif diff --git a/zebra/interface.c b/zebra/interface.c index b0ddcaf8bc..2ed8a82000 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -47,6 +47,7 @@ #include "zebra/irdp.h" #include "zebra/zebra_ptm.h" #include "zebra/rt_netlink.h" +#include "zebra/if_netlink.h" #include "zebra/interface.h" #include "zebra/zebra_vxlan.h" #include "zebra/zebra_errors.h" @@ -1063,7 +1064,10 @@ void zebra_if_update_all_links(void) } } - +void zebra_if_set_protodown(struct interface *ifp, bool down) +{ + netlink_protodown(ifp, down); +} /* Output prefix string to vty. */ static int prefix_vty_out(struct vty *vty, struct prefix *p) diff --git a/zebra/interface.h b/zebra/interface.h index bbb5445cc6..6a3914451a 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -422,6 +422,7 @@ extern void if_handle_vrf_change(struct interface *ifp, vrf_id_t vrf_id); extern void zebra_if_update_link(struct interface *ifp, ifindex_t link_ifindex, ns_id_t ns_id); extern void zebra_if_update_all_links(void); +extern void zebra_if_set_protodown(struct interface *ifp, bool down); extern void vrf_add_update(struct vrf *vrfp); diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 03b9653ce6..a497331702 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1336,6 +1336,30 @@ static void zread_interface_delete(ZAPI_HANDLER_ARGS) { } +/* + * Handle message requesting interface be set up or down. + */ +static void zread_interface_set_protodown(ZAPI_HANDLER_ARGS) +{ + ifindex_t ifindex; + struct interface *ifp; + char down; + + STREAM_GETL(msg, ifindex); + STREAM_GETC(msg, down); + + /* set ifdown */ + ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), ifindex); + zlog_info("Setting interface %s (%u): protodown %s", ifp->name, ifindex, + down ? "on" : "off"); + + zebra_if_set_protodown(ifp, down); + +stream_failure: + return; +} + + void zserv_nexthop_num_warn(const char *caller, const struct prefix *p, const unsigned int nexthop_num) { @@ -2412,6 +2436,7 @@ void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_ROUTER_ID_DELETE] = zread_router_id_delete, [ZEBRA_INTERFACE_ADD] = zread_interface_add, [ZEBRA_INTERFACE_DELETE] = zread_interface_delete, + [ZEBRA_INTERFACE_SET_PROTODOWN] = zread_interface_set_protodown, [ZEBRA_ROUTE_ADD] = zread_route_add, [ZEBRA_ROUTE_DELETE] = zread_route_del, [ZEBRA_REDISTRIBUTE_ADD] = zebra_redistribute_add, From 2884f9bbe40a2dce56f910695c000de83bd361c1 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 6 Feb 2019 16:38:38 +0000 Subject: [PATCH 051/153] vrrpd: fix skew time calculation Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 76f8dfea5d..a03c675010 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -88,8 +88,8 @@ static void vrrp_mac_set(struct ethaddr *mac, bool v6, uint8_t vrid) */ static void vrrp_recalculate_timers(struct vrrp_router *r) { - uint16_t skmai = (r->vr->version - 2) * r->master_adver_interval; - r->skew_time = ((256 - r->vr->priority) * skmai) / 256; + uint16_t skm = (r->vr->version == 3) ? r->master_adver_interval : 1; + r->skew_time = ((256 - r->vr->priority) * skm) / 256; r->master_down_interval = (3 * r->master_adver_interval); r->master_down_interval += r->skew_time; } From 8cb3d80332d24ab9b92791893329fe7a3c542ed8 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 6 Feb 2019 16:49:19 +0000 Subject: [PATCH 052/153] vrrpd: specify version when parsing vrrp packet Move a bit more validation into vrrp_packet.c Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 4 ++-- vrrpd/vrrp_packet.c | 16 +++++++++------- vrrpd/vrrp_packet.h | 21 +++++++++++++++------ 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index a03c675010..265cce33fb 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -654,8 +654,8 @@ static int vrrp_read(struct thread *thread) r->vr->vrid, family2str(r->family)); zlog_hexdump(r->ibuf, nbytes); - pktsize = vrrp_pkt_parse_datagram(r->family, &m, nbytes, &src, &pkt, - errbuf, sizeof(errbuf)); + pktsize = vrrp_pkt_parse_datagram(r->family, r->vr->version, &m, nbytes, + &src, &pkt, errbuf, sizeof(errbuf)); if (pktsize < 0) { zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index fb72d921eb..0a569f6369 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -171,9 +171,10 @@ size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt) return rs; } -ssize_t vrrp_pkt_parse_datagram(int family, struct msghdr *m, size_t read, - struct ipaddr *src, struct vrrp_pkt **pkt, - char *errmsg, size_t errmsg_len) +ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m, + size_t read, struct ipaddr *src, + struct vrrp_pkt **pkt, char *errmsg, + size_t errmsg_len) { /* Source (MAC & IP), Dest (MAC & IP) TTL validation done by kernel */ size_t addrsz = (family == AF_INET) ? sizeof(struct in_addr) @@ -260,19 +261,20 @@ ssize_t vrrp_pkt_parse_datagram(int family, struct msghdr *m, size_t read, "VRRP packet is oversized (%lu > %lu)", pktsize, VRRP_MAX_PKT_SIZE); + /* Version check */ + uint8_t pktver = (*pkt)->hdr.vertype >> 4; + VRRP_PKT_VCHECK(pktver == version, "Bad version %u", pktver); + /* Checksum check */ uint16_t chksum = vrrp_pkt_checksum(*pkt, pktsize, src); VRRP_PKT_VCHECK((*pkt)->hdr.chksum == chksum, "Bad VRRP checksum %" PRIu16 "; should be %" PRIu16 "", (*pkt)->hdr.chksum, chksum); - /* Version check */ - uint8_t version = (*pkt)->hdr.vertype >> 4; - VRRP_PKT_VCHECK(version == 3 || version == 2, "Bad version %u", - version); /* Type check */ VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %u", (*pkt)->hdr.vertype & 0x0f); + /* # addresses check */ size_t ves = VRRP_PKT_SIZE(family, (*pkt)->hdr.naddr); VRRP_PKT_VCHECK(pktsize == ves, "Packet has incorrect # addresses"); diff --git a/vrrpd/vrrp_packet.h b/vrrpd/vrrp_packet.h index af51287718..2061b63c61 100644 --- a/vrrpd/vrrp_packet.h +++ b/vrrpd/vrrp_packet.h @@ -153,17 +153,25 @@ size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt); /* * Parses a VRRP packet, checking for illegal or invalid data. * - * This function does not check that the local router is not the IPvX owner for - * the addresses received; that should be done by the caller. + * This function parses both VRRPv2 and VRRPv3 packets. Which version is + * expected is determined by the version argument. For example, if version is 3 + * and the received packet has version field 2 it will fail to parse. + * + * Note that this function only checks whether the packet itself is a valid + * VRRP packet. It is up to the caller to validate whether the VRID is correct, + * priority and timer values are correct, etc. * * family * Address family of received packet * + * version + * VRRP version to use for validation + * * m * msghdr containing results of recvmsg() on VRRP router socket * * read - * return value of recvmsg() on VRRP router socket; must be non-negative + * Return value of recvmsg() on VRRP router socket; must be non-negative * * src * Pointer to struct ipaddr to store address of datagram sender @@ -181,8 +189,9 @@ size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt); * Returns: * Size of VRRP packet, or -1 upon error */ -ssize_t vrrp_pkt_parse_datagram(int family, struct msghdr *m, size_t read, - struct ipaddr *src, struct vrrp_pkt **pkt, - char *errmsg, size_t errmsg_len); +ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m, + size_t read, struct ipaddr *src, + struct vrrp_pkt **pkt, char *errmsg, + size_t errmsg_len); #endif /* __VRRP_PACKET_H__ */ From 8ec512164cfc1b0b14d99e6030ec124abb61f2fc Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 6 Feb 2019 21:19:06 +0000 Subject: [PATCH 053/153] vrrpd: add cli for preempt mode Signed-off-by: Quentin Young --- vrrpd/vrrp_vty.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 4229a6e353..70711d12a9 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -268,6 +268,25 @@ DEFPY(vrrp_ip6, return ret; } +DEFPY(vrrp_preempt, + vrrp_preempt_cmd, + "[no] vrrp (1-255)$vrid preempt", + NO_STR + VRRP_STR + VRRP_VRID_STR + "Preempt mode\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + + struct vrrp_vrouter *vr; + + VROUTER_GET_VTY(vty, ifp, vrid, vr); + + vr->preempt_mode = !no; + + return CMD_SUCCESS; +} + static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) { char ethstr4[ETHER_ADDR_STRLEN]; @@ -387,4 +406,5 @@ void vrrp_vty_init(void) install_element(INTERFACE_NODE, &vrrp_advertisement_interval_cmd); install_element(INTERFACE_NODE, &vrrp_ip_cmd); install_element(INTERFACE_NODE, &vrrp_ip6_cmd); + install_element(INTERFACE_NODE, &vrrp_preempt_cmd); } From 53e60e5c5819ea3a602d1bd60b50b0b30caf7702 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 7 Feb 2019 23:48:49 +0000 Subject: [PATCH 054/153] vrrpd: autoconfig support Signed-off-by: Quentin Young --- lib/if.h | 4 +++ lib/zclient.c | 3 ++ vrrpd/vrrp.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++ vrrpd/vrrp.h | 38 ++++++++++++++++++----- vrrpd/vrrp_vty.c | 21 +++++++++++++ vrrpd/vrrp_zebra.c | 3 +- zebra/zapi_msg.c | 2 ++ 7 files changed, 139 insertions(+), 8 deletions(-) diff --git a/lib/if.h b/lib/if.h index ef596d45dc..2dc1a7b2de 100644 --- a/lib/if.h +++ b/lib/if.h @@ -225,6 +225,10 @@ struct interface { not work as expected. */ ifindex_t ifindex; + /* + * ifindex of parent interface, if any + */ + ifindex_t link_ifindex; #define IFINDEX_INTERNAL 0 /* Zebra internal interface status */ diff --git a/lib/zclient.c b/lib/zclient.c index 6647a1ab18..0972590ca6 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1400,6 +1400,8 @@ stream_failure: * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | bandwidth | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | parent ifindex | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Link Layer Type | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Harware Address Length | @@ -1580,6 +1582,7 @@ void zebra_interface_if_set_value(struct stream *s, struct interface *ifp) ifp->mtu = stream_getl(s); ifp->mtu6 = stream_getl(s); ifp->bandwidth = stream_getl(s); + ifp->link_ifindex = stream_getl(s); ifp->ll_type = stream_getl(s); ifp->hw_addr_len = stream_getl(s); if (ifp->hw_addr_len) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 265cce33fb..bf804abb3d 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1297,6 +1297,81 @@ int vrrp_event(struct vrrp_router *r, int event) /* Other ------------------------------------------------------------------- */ +static struct vrrp_vrouter * +vrrp_autoconfig_autocreate(struct interface *mvl_ifp) +{ + struct interface *p; + struct vrrp_vrouter *vr; + + p = if_lookup_by_index(mvl_ifp->link_ifindex, VRF_DEFAULT); + uint8_t vrid = mvl_ifp->hw_addr[5]; + + zlog_info(VRRP_LOGPFX "Autoconfiguring VRRP on %s", p->name); + + /* If it already exists, skip it */ + vr = vrrp_lookup(p, vrid); + if (vr) { + zlog_info(VRRP_LOGPFX "VRRP instance %" PRIu8 + "already configured on %s", + vrid, p->name); + return vr; + } + + /* create a new one */ + vr = vrrp_vrouter_create(p, vrid, vrrp_autoconfig_version); + + if (!vr) + return NULL; + + /* add connected addresses as vips */ + struct listnode *ln; + struct connected *c = NULL; + for (ALL_LIST_ELEMENTS_RO(mvl_ifp->connected, ln, c)) + if (c->address->family == AF_INET) + vrrp_add_ipv4(vr, c->address->u.prefix4, false); + else if (c->address->family == AF_INET6) { + if (!IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) + vrrp_add_ipv6(vr, c->address->u.prefix6, false); + } + + if (vr->v4->addrs->count) + vrrp_event(vr->v4, VRRP_EVENT_STARTUP); + if (vr->v6->addrs->count) + vrrp_event(vr->v6, VRRP_EVENT_STARTUP); + + vr->autoconf = true; + + return vr; +} + +static bool vrrp_ifp_is_mvl(struct interface *ifp) +{ + struct ethaddr vmac4; + struct ethaddr vmac6; + vrrp_mac_set(&vmac4, 0, 0x00); + vrrp_mac_set(&vmac6, 1, 0x00); + + return !memcmp(ifp->hw_addr, vmac4.octet, sizeof(vmac4.octet) - 1) + || !memcmp(ifp->hw_addr, vmac6.octet, sizeof(vmac6.octet) - 1); +} + +int vrrp_autoconfig(struct interface *ifp) +{ + if (ifp && vrrp_ifp_is_mvl(ifp)) { + vrrp_autoconfig_autocreate(ifp); + return 0; + } + + /* Loop through interfaces, looking for compatible macvlan devices. */ + struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + + FOR_ALL_INTERFACES (vrf, ifp) + if (vrrp_ifp_is_mvl(ifp)) + vrrp_autoconfig_autocreate(ifp); + + return 0; +} + static unsigned int vrrp_hash_key(void *arg) { struct vrrp_vrouter *vr = arg; @@ -1323,6 +1398,7 @@ static bool vrrp_hash_cmp(const void *arg1, const void *arg2) void vrrp_init(void) { + vrrp_autoconfig_version = 3; vrrp_vrouters_hash = hash_create(&vrrp_hash_key, vrrp_hash_cmp, "VRRP virtual router hash"); vrf_init(NULL, NULL, NULL, NULL, NULL); diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 0df588b221..57ec55eea4 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -53,13 +53,17 @@ extern struct zebra_privs_t vrrp_privs; /* Global hash of all Virtual Routers */ struct hash *vrrp_vrouters_hash; -/* - * VRRP Router. - * - * This struct contains all state for a particular VRRP Router operating in a - * Virtual Router for either IPv4 or IPv6. - */ -struct vrrp_router { +/* Whether to automatically configure VRRP instances */ +static bool vrrp_autoconfig_on; +static int vrrp_autoconfig_version; + + /* + * VRRP Router. + * + * This struct contains all state for a particular VRRP Router operating + * in a Virtual Router for either IPv4 or IPv6. + */ + struct vrrp_router { /* * Whether this VRRP Router is active. */ @@ -164,6 +168,9 @@ struct vrrp_router { * implementations. */ struct vrrp_vrouter { + /* Whether this instance was automatically configured */ + bool autoconf; + /* Interface */ struct interface *ifp; @@ -417,6 +424,23 @@ int vrrp_event(struct vrrp_router *r, int event); /* Other ------------------------------------------------------------------- */ +/* + * Search for and automatically configure VRRP instances on interfaces. + * + * ifp + * Interface to autoconfig. If it is a macvlan interface and has a VRRP MAC, + * a VRRP instance corresponding to VMAC assigned to macvlan will be created + * on the parent interface and all addresses on the macvlan interface except + * the v6 link local will be configured as VRRP addresses. If NULL, this + * treatment will be applied to all existing interfaces matching the above + * criterion. + * + * Returns: + * -1 on failure + * 0 otherwise + */ +int vrrp_autoconfig(struct interface *ifp); + /* * Find VRRP Virtual Router by Virtual Router ID */ diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 70711d12a9..c73e5cb556 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -287,6 +287,26 @@ DEFPY(vrrp_preempt, return CMD_SUCCESS; } +DEFPY(vrrp_autoconf, + vrrp_autoconf_cmd, + "[no] vrrp autoconfig [version (2-3)]", + NO_STR + VRRP_STR + "Automatically set up VRRP instances on VRRP-compatible interfaces\n" + "Version for automatically configured instances\n" + VRRP_VERSION_STR) +{ + vrrp_autoconfig_on = !no; + version = version ? version : 3; + + if (vrrp_autoconfig_on) + vrrp_autoconfig(NULL); + + vrrp_autoconfig_version = !no ? version : vrrp_autoconfig_version; + + return CMD_SUCCESS; +} + static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) { char ethstr4[ETHER_ADDR_STRLEN]; @@ -401,6 +421,7 @@ void vrrp_vty_init(void) if_cmd_init(); install_element(VIEW_NODE, &show_debugging_vrrpd_cmd); install_element(VIEW_NODE, &vrrp_vrid_show_cmd); + install_element(CONFIG_NODE, &vrrp_autoconf_cmd); install_element(INTERFACE_NODE, &vrrp_vrid_cmd); install_element(INTERFACE_NODE, &vrrp_priority_cmd); install_element(INTERFACE_NODE, &vrrp_advertisement_interval_cmd); diff --git a/vrrpd/vrrp_zebra.c b/vrrpd/vrrp_zebra.c index c41915cbd9..e7967f9326 100644 --- a/vrrpd/vrrp_zebra.c +++ b/vrrpd/vrrp_zebra.c @@ -61,7 +61,8 @@ static int vrrp_zebra_if_add(int command, struct zclient *zclient, if (!ifp) return 0; - /* FIXME: handle subinterface creation here */ + if (vrrp_autoconfig_on) + vrrp_autoconfig(ifp); return 0; } diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index a497331702..ca371186b7 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -71,6 +71,7 @@ static void zserv_encode_interface(struct stream *s, struct interface *ifp) { /* Interface information. */ + struct zebra_if *zif = ifp->info; stream_put(s, ifp->name, INTERFACE_NAMSIZ); stream_putl(s, ifp->ifindex); stream_putc(s, ifp->status); @@ -82,6 +83,7 @@ static void zserv_encode_interface(struct stream *s, struct interface *ifp) stream_putl(s, ifp->mtu); stream_putl(s, ifp->mtu6); stream_putl(s, ifp->bandwidth); + stream_putl(s, zif->link_ifindex); stream_putl(s, ifp->ll_type); stream_putl(s, ifp->hw_addr_len); if (ifp->hw_addr_len) From 1b1f3c43cfbac93eba5edb89b5d4d840718d9e09 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 8 Feb 2019 19:47:55 +0000 Subject: [PATCH 055/153] vrrpd: properly find iface in manual cfg mode As a crutch, interface search when manually configuring VRRP on an interface did a prefix match on the name of macvlan interfaces, comparing its name to the name of the interface VRRP was configured on in order to determine if the interface under question was a subinterface of the configured interface. This is obviously fragile and prone to failure. We now pass up parent interface info from Zebra so use that instead to correctly deduce parent-child relationships. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index bf804abb3d..01044b580f 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -281,9 +281,8 @@ static bool vrrp_attach_interface(struct vrrp_router *r) r->vmac.octet, sizeof(r->vmac.octet), &ifps, VRF_DEFAULT); /* - * Filter to only those interfaces whose names begin with VRRP - * interface name. E.g. if this VRRP instance was configured on eth0, - * then we filter the list to only keep interfaces matching ^eth0.* + * Filter to only those macvlan interfaces whose parent is the base + * interface this VRRP router is configured on. * * If there are still multiple interfaces we just select the first one, * as it should be functionally identical to the others. @@ -291,8 +290,8 @@ static bool vrrp_attach_interface(struct vrrp_router *r) unsigned int candidates = 0; struct interface *selection = NULL; for (unsigned int i = 0; i < ifps_cnt; i++) { - if (strncmp(ifps[i]->name, r->vr->ifp->name, - strlen(r->vr->ifp->name))) + if (ifps[i]->link_ifindex != r->vr->ifp->ifindex + || !CHECK_FLAG(ifps[i]->flags, IFF_UP)) ifps[i] = NULL; else { selection = selection ? selection : ifps[i]; From 205eb006c61e803b3fb3c6daed7b7326badb144b Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 8 Feb 2019 19:52:24 +0000 Subject: [PATCH 056/153] vrrpd: don't restart when changing priority Both master and backup should be able to react to priority changes without requiring a restart. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 3 --- vrrpd/vrrp_vty.c | 28 ---------------------------- 2 files changed, 31 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 01044b580f..6cfbd7c090 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -125,9 +125,6 @@ static bool vrrp_is_owner(struct interface *ifp, struct ipaddr *addr) void vrrp_set_priority(struct vrrp_vrouter *vr, uint8_t priority) { - if (vr->priority == priority) - return; - vr->priority = priority; vr->v4->priority = priority; vr->v6->priority = priority; diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index c73e5cb556..c32e5c2c8c 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -106,40 +106,12 @@ DEFPY(vrrp_priority, VTY_DECLVAR_CONTEXT(interface, ifp); struct vrrp_vrouter *vr; - struct vrrp_router *r; - bool nr[2] = { false, false }; - int ret = CMD_SUCCESS; uint8_t newprio = no ? VRRP_DEFAULT_PRIORITY : priority; VROUTER_GET_VTY(vty, ifp, vrid, vr); - r = vr->v4; - for (int i = 0; i < 2; i++) { - nr[i] = r->is_active && r->fsm.state != VRRP_STATE_INITIALIZE - && vr->priority != newprio; - if (nr[i]) { - vty_out(vty, - "%% WARNING: Restarting %s Virtual Router %ld to update priority\n", - family2str(r->family), vrid); - (void)vrrp_event(r, VRRP_EVENT_SHUTDOWN); - } - r = vr->v6; - } - vrrp_set_priority(vr, newprio); - r = vr->v4; - for (int i = 0; i < 2; i++) { - if (nr[i]) { - ret = vrrp_event(r, VRRP_EVENT_STARTUP); - if (ret < 0) - vty_out(vty, - "%% Failed to start Virtual Router %ld (%s)\n", - vrid, family2str(r->family)); - } - r = vr->v6; - } - return CMD_SUCCESS; } From bb95fd82bcda3fc00696c1c990a3b2d539b67339 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Sat, 9 Feb 2019 07:02:34 +0000 Subject: [PATCH 057/153] vrrpd: ingress pkt validation dbg cleanups * Use proper format specifiers for some data * Print the correct sizes when reporting IP size errors Signed-off-by: Quentin Young --- vrrpd/vrrp_packet.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index 0a569f6369..903bb3ae6c 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -255,11 +255,11 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m, size_t maxsize = (family == AF_INET) ? VRRP_MAX_PKT_SIZE_V4 : VRRP_MAX_PKT_SIZE_V6; VRRP_PKT_VCHECK(pktsize >= minsize, - "VRRP packet is undersized (%lu < %lu)", pktsize, - VRRP_MIN_PKT_SIZE); + "VRRP packet is undersized (%zu < %zu)", pktsize, + minsize); VRRP_PKT_VCHECK(pktsize <= maxsize, - "VRRP packet is oversized (%lu > %lu)", pktsize, - VRRP_MAX_PKT_SIZE); + "VRRP packet is oversized (%zu > %zu)", pktsize, + maxsize); /* Version check */ uint8_t pktver = (*pkt)->hdr.vertype >> 4; @@ -268,11 +268,11 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m, /* Checksum check */ uint16_t chksum = vrrp_pkt_checksum(*pkt, pktsize, src); VRRP_PKT_VCHECK((*pkt)->hdr.chksum == chksum, - "Bad VRRP checksum %" PRIu16 "; should be %" PRIu16 "", + "Bad VRRP checksum %" PRIx16 "; should be %" PRIx16 "", (*pkt)->hdr.chksum, chksum); /* Type check */ - VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %u", + VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %" PRIu8, (*pkt)->hdr.vertype & 0x0f); /* # addresses check */ @@ -290,7 +290,7 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m, uint8_t *p = (uint8_t *)(*pkt)->addrs; for (uint8_t i = 0; i < (*pkt)->hdr.naddr; i++) { VRRP_PKT_VCHECK(inet_ntop(family, p, vbuf, sizeof(vbuf)), - "Bad IP address, #%u", i); + "Bad IP address, #%" PRIu8, i); p += addrsz; } From 3d55d46721183994a5cbabe049b890a55c8dfef0 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 11 Feb 2019 16:36:09 +0000 Subject: [PATCH 058/153] vrrpd: allow creation of adverts with no addresses Fuzz testing revealed a crash in which VRRPD tries to create an advertisement packet with no IP addresses. Should never occur under normal use but might as well patch. Signed-off-by: Quentin Young --- vrrpd/vrrp_packet.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index 903bb3ae6c..7dcb2933f8 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -107,12 +107,17 @@ ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src, uint16_t max_adver_int, uint8_t numip, struct ipaddr **ips) { - bool v6 = IS_IPADDR_V6(ips[0]); + bool v6 = false; + size_t addrsz = 0; assert(version >= 2 && version <= 3); assert(!(version == 2 && v6)); - size_t addrsz = IPADDRSZ(ips[0]); + if (numip > 0) { + v6 = IS_IPADDR_V6(ips[0]); + addrsz = IPADDRSZ(ips[0]); + } + size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, numip); *pkt = XCALLOC(MTYPE_VRRP_PKT, pktsize); From 27fd88271034998b5ae4f333f98062a3f2f5dcdd Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 11 Feb 2019 20:44:49 +0000 Subject: [PATCH 059/153] vrrpd: autoconfig support, continued * Add support for interface up/down + address add/del events when using autoconfigure mode * Add autoconfig information to show command Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 276 +++++++++++++++++++++++++++++++++++++-------- vrrpd/vrrp.h | 147 +++++++++++++++++++++--- vrrpd/vrrp_vty.c | 19 ++-- vrrpd/vrrp_zebra.c | 13 ++- 4 files changed, 381 insertions(+), 74 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 6cfbd7c090..b01bcb939c 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -39,6 +39,11 @@ #define VRRP_LOGPFX "[CORE] " +/* statics */ +struct hash *vrrp_vrouters_hash; +bool vrrp_autoconfig_is_on; +int vrrp_autoconfig_version; + const char *vrrp_state_names[3] = { [VRRP_STATE_INITIALIZE] = "Initialize", [VRRP_STATE_MASTER] = "Master", @@ -156,6 +161,10 @@ static bool vrrp_has_ip(struct vrrp_vrouter *vr, struct ipaddr *ip) int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip, bool activate) { + int af = (ip->ipa_type == IPADDR_V6) ? AF_INET6 : AF_INET; + + assert(r->family == af); + if (vrrp_has_ip(r->vr, ip)) return 0; @@ -177,8 +186,11 @@ int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip, bool activate) bool do_activate = (activate && r->fsm.state == VRRP_STATE_INITIALIZE); int ret = 0; - if (do_activate) + if (do_activate) { ret = vrrp_event(r, VRRP_EVENT_STARTUP); + if (ret) + listnode_delete(r->addrs, new); + } else if (r->fsm.state == VRRP_STATE_MASTER) { switch (r->family) { case AF_INET: @@ -800,15 +812,6 @@ static int vrrp_socket(struct vrrp_router *r) } /* Configure sockets */ - 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); - failed = true; - goto done; - } - if (r->family == AF_INET) { /* Set Tx socket to always Tx with TTL set to 255 */ int ttl = 255; @@ -1282,6 +1285,10 @@ static int (*vrrp_event_handlers[])(struct vrrp_router *r) = { * * event * The event to spawn + * + * Returns: + * -1 on failure + * 0 otherwise */ int vrrp_event(struct vrrp_router *r, int event) { @@ -1291,7 +1298,41 @@ int vrrp_event(struct vrrp_router *r, int event) } -/* Other ------------------------------------------------------------------- */ +/* Autoconfig -------------------------------------------------------------- */ + +/* + * Set the configured addresses for this VRRP instance to exactly the addresses + * present on its macvlan subinterface(s). + * + * vr + * VRRP router to act on + */ +static void vrrp_autoconfig_autoaddrupdate(struct vrrp_vrouter *vr) +{ + list_delete_all_node(vr->v4->addrs); + list_delete_all_node(vr->v6->addrs); + + struct listnode *ln; + struct connected *c = NULL; + + if (vr->v4->mvl_ifp) + for (ALL_LIST_ELEMENTS_RO(vr->v4->mvl_ifp->connected, ln, c)) + if (c->address->family == AF_INET) + vrrp_add_ipv4(vr, c->address->u.prefix4, true); + + if (vr->v6->mvl_ifp) + for (ALL_LIST_ELEMENTS_RO(vr->v6->mvl_ifp->connected, ln, c)) + if (c->address->family == AF_INET6 + && !IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) + vrrp_add_ipv6(vr, c->address->u.prefix6, true); + + if (vr->v4->addrs->count == 0 + && vr->v4->fsm.state != VRRP_STATE_INITIALIZE) + vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN); + if (vr->v6->addrs->count == 0 + && vr->v6->fsm.state != VRRP_STATE_INITIALIZE) + vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN); +} static struct vrrp_vrouter * vrrp_autoconfig_autocreate(struct interface *mvl_ifp) @@ -1300,47 +1341,32 @@ vrrp_autoconfig_autocreate(struct interface *mvl_ifp) struct vrrp_vrouter *vr; p = if_lookup_by_index(mvl_ifp->link_ifindex, VRF_DEFAULT); + + if (!p) + return NULL; + uint8_t vrid = mvl_ifp->hw_addr[5]; zlog_info(VRRP_LOGPFX "Autoconfiguring VRRP on %s", p->name); - /* If it already exists, skip it */ - vr = vrrp_lookup(p, vrid); - if (vr) { - zlog_info(VRRP_LOGPFX "VRRP instance %" PRIu8 - "already configured on %s", - vrid, p->name); - return vr; - } - - /* create a new one */ vr = vrrp_vrouter_create(p, vrid, vrrp_autoconfig_version); - if (!vr) + if (!vr) { + zlog_warn(VRRP_LOGPFX + "Failed to autoconfigure VRRP instance %" PRIu8 + " on %s", + vrid, p->name); return NULL; + } - /* add connected addresses as vips */ - struct listnode *ln; - struct connected *c = NULL; - for (ALL_LIST_ELEMENTS_RO(mvl_ifp->connected, ln, c)) - if (c->address->family == AF_INET) - vrrp_add_ipv4(vr, c->address->u.prefix4, false); - else if (c->address->family == AF_INET6) { - if (!IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) - vrrp_add_ipv6(vr, c->address->u.prefix6, false); - } - - if (vr->v4->addrs->count) - vrrp_event(vr->v4, VRRP_EVENT_STARTUP); - if (vr->v6->addrs->count) - vrrp_event(vr->v6, VRRP_EVENT_STARTUP); + vrrp_autoconfig_autoaddrupdate(vr); vr->autoconf = true; return vr; } -static bool vrrp_ifp_is_mvl(struct interface *ifp) +static bool vrrp_ifp_has_vrrp_mac(struct interface *ifp) { struct ethaddr vmac4; struct ethaddr vmac6; @@ -1351,23 +1377,177 @@ static bool vrrp_ifp_is_mvl(struct interface *ifp) || !memcmp(ifp->hw_addr, vmac6.octet, sizeof(vmac6.octet) - 1); } -int vrrp_autoconfig(struct interface *ifp) +static struct vrrp_vrouter *vrrp_lookup_by_mvlif(struct interface *mvl_ifp) { - if (ifp && vrrp_ifp_is_mvl(ifp)) { - vrrp_autoconfig_autocreate(ifp); + struct interface *p; + + if (!mvl_ifp || !mvl_ifp->link_ifindex + || !vrrp_ifp_has_vrrp_mac(mvl_ifp)) + return NULL; + + p = if_lookup_by_index(mvl_ifp->link_ifindex, VRF_DEFAULT); + uint8_t vrid = mvl_ifp->hw_addr[5]; + + return vrrp_lookup(p, vrid); +} + +int vrrp_autoconfig_if_add(struct interface *ifp) +{ + if (!vrrp_autoconfig_is_on) return 0; + + struct vrrp_vrouter *vr; + + if (!ifp || !ifp->link_ifindex || !vrrp_ifp_has_vrrp_mac(ifp)) + return -1; + + vr = vrrp_lookup_by_mvlif(ifp); + + if (!vr) + vr = vrrp_autoconfig_autocreate(ifp); + + if (!vr) + return -1; + + if (vr->autoconf == false) + return 0; + else { + vrrp_attach_interface(vr->v4); + vrrp_attach_interface(vr->v6); + vrrp_autoconfig_autoaddrupdate(vr); } - /* Loop through interfaces, looking for compatible macvlan devices. */ - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); - - FOR_ALL_INTERFACES (vrf, ifp) - if (vrrp_ifp_is_mvl(ifp)) - vrrp_autoconfig_autocreate(ifp); - return 0; } +int vrrp_autoconfig_if_del(struct interface *ifp) +{ + if (!vrrp_autoconfig_is_on) + return 0; + + struct vrrp_vrouter *vr = vrrp_lookup_by_mvlif(ifp); + + if (!vr) + return 0; + + if (vr && vr->autoconf == false) + return 0; + + if (vr && vr->v4->mvl_ifp == ifp) { + if (vr->v4->fsm.state != VRRP_STATE_INITIALIZE) + vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN); + vr->v4->mvl_ifp = NULL; + } + if (vr && vr->v6->mvl_ifp == ifp) { + if (vr->v6->fsm.state != VRRP_STATE_INITIALIZE) + vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN); + vr->v6->mvl_ifp = NULL; + } + + if (vr->v4->mvl_ifp == NULL && vr->v6->mvl_ifp == NULL) { + vrrp_vrouter_destroy(vr); + vr = NULL; + } + + return 0; +} + +int vrrp_autoconfig_if_up(struct interface *ifp) +{ + if (!vrrp_autoconfig_is_on) + return 0; + + struct vrrp_vrouter *vr = vrrp_lookup_by_mvlif(ifp); + + if (vr && !vr->autoconf) + return 0; + + if (!vr) { + vrrp_autoconfig_if_add(ifp); + return 0; + } + + vrrp_attach_interface(vr->v4); + vrrp_attach_interface(vr->v6); + vrrp_autoconfig_autoaddrupdate(vr); + + return 0; +} + +int vrrp_autoconfig_if_down(struct interface *ifp) +{ + if (!vrrp_autoconfig_is_on) + return 0; + + return 0; +} + +int vrrp_autoconfig_if_address_add(struct interface *ifp) +{ + if (!vrrp_autoconfig_is_on) + return 0; + + struct vrrp_vrouter *vr = vrrp_lookup_by_mvlif(ifp); + + if (vr && vr->autoconf) + vrrp_autoconfig_autoaddrupdate(vr); + + return 0; +} + +int vrrp_autoconfig_if_address_del(struct interface *ifp) +{ + if (!vrrp_autoconfig_is_on) + return 0; + + struct vrrp_vrouter *vr = vrrp_lookup_by_mvlif(ifp); + + if (vr && vr->autoconf) + vrrp_autoconfig_autoaddrupdate(vr); + + return 0; +} + +int vrrp_autoconfig(void) +{ + if (!vrrp_autoconfig_is_on) + return 0; + + struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct interface *ifp; + + FOR_ALL_INTERFACES (vrf, ifp) + vrrp_autoconfig_if_add(ifp); + + return 0; +} + +void vrrp_autoconfig_on(int version) +{ + vrrp_autoconfig_is_on = true; + vrrp_autoconfig_version = version; + + vrrp_autoconfig(); +} + +void vrrp_autoconfig_off(void) +{ + vrrp_autoconfig_is_on = false; + + struct list *ll = hash_to_list(vrrp_vrouters_hash); + + struct listnode *ln; + struct vrrp_vrouter *vr; + + for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) + if (vr->autoconf) + vrrp_vrouter_destroy(vr); + + list_delete(&ll); +} + +/* Other ------------------------------------------------------------------- */ + static unsigned int vrrp_hash_key(void *arg) { struct vrrp_vrouter *vr = arg; diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 57ec55eea4..ed68b6a812 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -51,19 +51,15 @@ extern struct thread_master *master; extern struct zebra_privs_t vrrp_privs; /* Global hash of all Virtual Routers */ -struct hash *vrrp_vrouters_hash; +extern struct hash *vrrp_vrouters_hash; -/* Whether to automatically configure VRRP instances */ -static bool vrrp_autoconfig_on; -static int vrrp_autoconfig_version; - - /* - * VRRP Router. - * - * This struct contains all state for a particular VRRP Router operating - * in a Virtual Router for either IPv4 or IPv6. - */ - struct vrrp_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_router { /* * Whether this VRRP Router is active. */ @@ -421,8 +417,7 @@ DECLARE_HOOK(vrrp_change_state_hook, (struct vrrp_router * r, int to), (r, to)); */ int vrrp_event(struct vrrp_router *r, int event); - -/* Other ------------------------------------------------------------------- */ +/* Autoconfig -------------------------------------------------------------- */ /* * Search for and automatically configure VRRP instances on interfaces. @@ -439,7 +434,129 @@ int vrrp_event(struct vrrp_router *r, int event); * -1 on failure * 0 otherwise */ -int vrrp_autoconfig(struct interface *ifp); +int vrrp_autoconfig(void); + +/* + * Enable autoconfiguration. + * + * Calling this function will cause vrrpd to automatically configure VRRP + * instances on existing compatible macvlan interfaces. These instances will + * react to interface up/down and address add/delete events to keep themselves + * in sync with the available interfaces. + * + * version + * VRRP version to use for autoconfigured instances. Must be 2 or 3. + */ +void vrrp_autoconfig_on(int version); + +/* + * Disable autoconfiguration. + * + * Calling this function will delete all existing autoconfigured VRRP instances. + */ +void vrrp_autoconfig_off(void); + +/* + * Callback to notify autoconfig of interface add. + * + * If the interface is a VRRP-compatible device, and there is no existing VRRP + * router running on it, one is created. All addresses on the interface are + * added to the router. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + */ +int vrrp_autoconfig_if_add(struct interface *ifp); + +/* + * Callback to notify autoconfig of interface delete. + * + * If the interface is a VRRP-compatible device, and a VRRP router is running + * on it, and that VRRP router was automatically configured, it will be + * deleted. If that was the last router for the corresponding VRID (i.e., if + * this interface was a v4 VRRP interface and no v6 router is configured for + * the same VRID) then the entire virtual router is deleted. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + */ +int vrrp_autoconfig_if_del(struct interface *ifp); + +/* + * Callback to notify autoconfig of interface up. + * + * Roughly equivalent to vrrp_autoconfig_if_add, except that addresses are + * refreshed if an autoconfigured virtual router already exists. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + */ +int vrrp_autoconfig_if_up(struct interface *ifp); + +/* + * Callback to notify autoconfig of interface down. + * + * Does nothing. An interface down event is accompanied by address deletion + * events for all the addresses on the interface; if an autoconfigured VRRP + * router exists on this interface, then it will have all its addresses deleted + * and end up in Initialize. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + */ +int vrrp_autoconfig_if_down(struct interface *ifp); + +/* + * Callback to notify autoconfig of a new interface address. + * + * If a VRRP router exists on this interface, its address list is updated to + * match the new address list. If no addresses remain, a Shutdown event is + * issued to the VRRP router. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + * + */ +int vrrp_autoconfig_if_address_add(struct interface *ifp); + +/* + * Callback to notify autoconfig of a removed interface address. + * + * If a VRRP router exists on this interface, its address list is updated to + * match the new address list. If no addresses remain, a Shutdown event is + * issued to the VRRP router. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + * + */ +int vrrp_autoconfig_if_address_del(struct interface *ifp); + +/* Other ------------------------------------------------------------------- */ /* * Find VRRP Virtual Router by Virtual Router ID diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index c32e5c2c8c..0a91026d73 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -259,22 +259,21 @@ DEFPY(vrrp_preempt, return CMD_SUCCESS; } -DEFPY(vrrp_autoconf, - vrrp_autoconf_cmd, - "[no] vrrp autoconfig [version (2-3)]", +DEFPY(vrrp_autoconfigure, + vrrp_autoconfigure_cmd, + "[no] vrrp autoconfigure [version (2-3)]", NO_STR VRRP_STR "Automatically set up VRRP instances on VRRP-compatible interfaces\n" "Version for automatically configured instances\n" VRRP_VERSION_STR) { - vrrp_autoconfig_on = !no; version = version ? version : 3; - if (vrrp_autoconfig_on) - vrrp_autoconfig(NULL); - - vrrp_autoconfig_version = !no ? version : vrrp_autoconfig_version; + if (!no) + vrrp_autoconfig_on(version); + else + vrrp_autoconfig_off(); return CMD_SUCCESS; } @@ -293,6 +292,8 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) ttable_add_row(tt, "%s|%" PRIu32, "Virtual Router ID", vr->vrid); ttable_add_row(tt, "%s|%" PRIu8, "Protocol Version", vr->version); + ttable_add_row(tt, "%s|%s", "Autoconfigured", + vr->autoconf ? "Yes" : "No"); ttable_add_row(tt, "%s|%s", "Interface", vr->ifp->name); prefix_mac2str(&vr->v4->vmac, ethstr4, sizeof(ethstr4)); prefix_mac2str(&vr->v6->vmac, ethstr6, sizeof(ethstr6)); @@ -393,7 +394,7 @@ void vrrp_vty_init(void) if_cmd_init(); install_element(VIEW_NODE, &show_debugging_vrrpd_cmd); install_element(VIEW_NODE, &vrrp_vrid_show_cmd); - install_element(CONFIG_NODE, &vrrp_autoconf_cmd); + install_element(CONFIG_NODE, &vrrp_autoconfigure_cmd); install_element(INTERFACE_NODE, &vrrp_vrid_cmd); install_element(INTERFACE_NODE, &vrrp_priority_cmd); install_element(INTERFACE_NODE, &vrrp_advertisement_interval_cmd); diff --git a/vrrpd/vrrp_zebra.c b/vrrpd/vrrp_zebra.c index e7967f9326..a4e1158010 100644 --- a/vrrpd/vrrp_zebra.c +++ b/vrrpd/vrrp_zebra.c @@ -61,8 +61,7 @@ static int vrrp_zebra_if_add(int command, struct zclient *zclient, if (!ifp) return 0; - if (vrrp_autoconfig_on) - vrrp_autoconfig(ifp); + vrrp_autoconfig_if_add(ifp); return 0; } @@ -76,6 +75,8 @@ static int vrrp_zebra_if_del(int command, struct zclient *zclient, if (!ifp) return 0; + vrrp_autoconfig_if_del(ifp); + #if 0 if (VRRP_DEBUG_ZEBRA) { zlog_debug( @@ -103,6 +104,8 @@ static int vrrp_zebra_if_state_up(int command, struct zclient *zclient, if (!ifp) return 0; + vrrp_autoconfig_if_up(ifp); + #if 0 if (VRRP_DEBUG_ZEBRA) { zlog_debug( @@ -129,6 +132,8 @@ static int vrrp_zebra_if_state_down(int command, struct zclient *zclient, if (!ifp) return 0; + vrrp_autoconfig_if_down(ifp); + #if 0 if (VRRP_DEBUG_ZEBRA) { zlog_debug( @@ -184,6 +189,8 @@ static int vrrp_zebra_if_address_add(int command, struct zclient *zclient, if (!c) return 0; + vrrp_autoconfig_if_address_add(c->ifp); + #if 0 if (VRRP_DEBUG_ZEBRA) { char buf[BUFSIZ]; @@ -218,6 +225,8 @@ static int vrrp_zebra_if_address_del(int command, struct zclient *client, if (!c) return 0; + vrrp_autoconfig_if_address_del(c->ifp); + return 0; } From 1af0eb1f2923b211bf1efdd206d8a4fc08de56cb Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 11 Feb 2019 21:21:49 +0000 Subject: [PATCH 060/153] vrrpd: fix missing \n in cli Signed-off-by: Quentin Young --- vrrpd/vrrp_vty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 0a91026d73..05695f7942 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -170,7 +170,7 @@ DEFPY(vrrp_ip, if (deactivated) vty_out(vty, "%% Deactivated IPv4 Virtual Router %ld\n", vrid); if (failed) { - vty_out(vty, "%% Failed to %s virtual IP", + vty_out(vty, "%% Failed to %s virtual IP\n", no ? "remove" : "add"); ret = CMD_WARNING_CONFIG_FAILED; if (will_activate && !activated) { From 1b5e2a229dada21c07829e41275114f3c927f360 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 11 Feb 2019 21:22:05 +0000 Subject: [PATCH 061/153] vrrpd: set sockets to -1 after closing This also fixes a bug where assigning the same address as a VIP twice would succeed the second time even if it actually failed both times. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index b01bcb939c..14347c239f 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1001,10 +1001,14 @@ done: zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID "Failed to initialize VRRP %s router", r->vr->vrid, family2str(r->family)); - if (r->sock_rx >= 0) + if (r->sock_rx >= 0) { close(r->sock_rx); - if (r->sock_tx >= 0) + r->sock_rx = -1; + } + if (r->sock_tx >= 0) { close(r->sock_tx); + r->sock_tx = -1; + } ret = -1; } From 92c399a47c3c515ad9a8c3496eef29aba0c9796a Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 12 Feb 2019 17:34:00 +0000 Subject: [PATCH 062/153] vrrpd: minor cosmetic fix for sh vrrp Signed-off-by: Quentin Young --- vrrpd/vrrp_vty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 05695f7942..8362a966f8 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -328,7 +328,7 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) vr->v6->master_down_interval); ttable_add_row(tt, "%s|%u", "IPv4 Addresses", vr->v4->addrs->count); - char fill[37]; + char fill[35]; memset(fill, '.', sizeof(fill)); fill[sizeof(fill) - 1] = 0x00; if (vr->v4->addrs->count) { From 78fb3dbe3fc2ccec8612ac78d9b3261335227696 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 12 Feb 2019 20:39:13 +0000 Subject: [PATCH 063/153] vrrpd: add debugging knobs * Add control structures for debugging * Add CLI commands for debugging Signed-off-by: Quentin Young --- vrrpd/subdir.am | 2 + vrrpd/vrrp_debug.c | 131 +++++++++++++++++++++++++++++++++++++++++++++ vrrpd/vrrp_debug.h | 87 ++++++++++++++++++++++++++++++ vrrpd/vrrp_main.c | 2 + vrrpd/vrrp_vty.c | 63 +++++++++++++++++----- 5 files changed, 271 insertions(+), 14 deletions(-) create mode 100644 vrrpd/vrrp_debug.c create mode 100644 vrrpd/vrrp_debug.h diff --git a/vrrpd/subdir.am b/vrrpd/subdir.am index 633a4a8e73..17c8fc2792 100644 --- a/vrrpd/subdir.am +++ b/vrrpd/subdir.am @@ -13,6 +13,7 @@ endif vrrpd_libvrrp_a_SOURCES = \ vrrpd/vrrp.c \ vrrpd/vrrp_arp.c \ + vrrpd/vrrp_debug.c \ vrrpd/vrrp_memory.c \ vrrpd/vrrp_ndisc.c \ vrrpd/vrrp_packet.c \ @@ -23,6 +24,7 @@ vrrpd_libvrrp_a_SOURCES = \ noinst_HEADERS += \ vrrpd/vrrp.h \ vrrpd/vrrp_arp.h \ + vrrpd/vrrp_debug.h \ vrrpd/vrrp_memory.h \ vrrpd/vrrp_ndisc.h \ vrrpd/vrrp_vty.h \ diff --git a/vrrpd/vrrp_debug.c b/vrrpd/vrrp_debug.c new file mode 100644 index 0000000000..cea2bbff79 --- /dev/null +++ b/vrrpd/vrrp_debug.c @@ -0,0 +1,131 @@ +/* + * VRRP debugging. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "lib/command.h" +#include "lib/debug.h" +#include "lib/vector.h" + +#include "vrrp_debug.h" + +/* clang-format off */ +struct debug vrrp_dbg_arp = {0, "VRRP ARP"}; +struct debug vrrp_dbg_auto = {0, "VRRP autoconfiguration events"}; +struct debug vrrp_dbg_ndisc = {0, "VRRP Neighbor Discovery"}; +struct debug vrrp_dbg_pkt = {0, "VRRP packets"}; +struct debug vrrp_dbg_proto = {0, "VRRP protocol events"}; +struct debug vrrp_dbg_sock = {0, "VRRP sockets"}; +struct debug vrrp_dbg_zebra = {0, "VRRP Zebra events"}; + +struct debug *vrrp_debugs[] = { + &vrrp_dbg_arp, + &vrrp_dbg_auto, + &vrrp_dbg_ndisc, + &vrrp_dbg_pkt, + &vrrp_dbg_proto, + &vrrp_dbg_sock, + &vrrp_dbg_zebra +}; + +const char *vrrp_debugs_conflines[] = { + "debug vrrp arp", + "debug vrrp autoconfigure", + "debug vrrp ndisc", + "debug vrrp packets", + "debug vrrp protocol", + "debug vrrp sockets", + "debug vrrp zebra", +}; +/* clang-format on */ + +/* + * Set or unset flags on all debugs for vrrpd. + * + * flags + * The flags to set + * + * set + * Whether to set or unset the specified flags + */ +static void vrrp_debug_set_all(uint32_t flags, bool set) +{ + for (unsigned int i = 0; i < array_size(vrrp_debugs); i++) { + DEBUG_FLAGS_SET(vrrp_debugs[i], flags, set); + + /* if all modes have been turned off, don't preserve options */ + if (!DEBUG_MODE_CHECK(vrrp_debugs[i], DEBUG_MODE_ALL)) + DEBUG_CLEAR(vrrp_debugs[i]); + } +} + +static int vrrp_debug_config_write_helper(struct vty *vty, bool config) +{ + uint32_t mode = DEBUG_MODE_ALL; + + if (config) + mode = DEBUG_MODE_CONF; + + for (unsigned int i = 0; i < array_size(vrrp_debugs); i++) + if (DEBUG_MODE_CHECK(vrrp_debugs[i], mode)) + vty_out(vty, "%s\n", vrrp_debugs_conflines[i]); + + return 0; +} + +int vrrp_debug_config_write(struct vty *vty) +{ + return vrrp_debug_config_write_helper(vty, true); +} + +int vrrp_debug_status_write(struct vty *vty) +{ + return vrrp_debug_config_write_helper(vty, false); +} + +void vrrp_debug_set(struct interface *ifp, uint8_t vrid, int vtynode, + bool onoff, bool proto, bool autoconf, bool pkt, bool sock, + bool ndisc, bool arp, bool zebra) +{ + uint32_t mode = DEBUG_NODE2MODE(vtynode); + + if (proto) + DEBUG_MODE_SET(&vrrp_dbg_proto, mode, onoff); + if (autoconf) + DEBUG_MODE_SET(&vrrp_dbg_auto, mode, onoff); + if (pkt) + DEBUG_MODE_SET(&vrrp_dbg_pkt, mode, onoff); + if (sock) + DEBUG_MODE_SET(&vrrp_dbg_sock, mode, onoff); + if (ndisc) + DEBUG_MODE_SET(&vrrp_dbg_ndisc, mode, onoff); + if (arp) + DEBUG_MODE_SET(&vrrp_dbg_arp, mode, onoff); + if (zebra) + DEBUG_MODE_SET(&vrrp_dbg_zebra, mode, onoff); +} + +/* ------------------------------------------------------------------------- */ + +struct debug_callbacks vrrp_dbg_cbs = {.debug_set_all = vrrp_debug_set_all}; + +void vrrp_debug_init(void) +{ + debug_init(&vrrp_dbg_cbs); +} diff --git a/vrrpd/vrrp_debug.h b/vrrpd/vrrp_debug.h new file mode 100644 index 0000000000..c54b20e5b8 --- /dev/null +++ b/vrrpd/vrrp_debug.h @@ -0,0 +1,87 @@ +/* + * VRRP debugging. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Quentin Young + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#ifndef __VRRP_DEBUG_H__ +#define __VRRP_DEBUG_H__ + +#include + +#include "lib/debug.h" + +/* VRRP debugging records */ +struct debug vrrp_dbg_arp; +struct debug vrrp_dbg_auto; +struct debug vrrp_dbg_ndisc; +struct debug vrrp_dbg_pkt; +struct debug vrrp_dbg_proto; +struct debug vrrp_dbg_sock; +struct debug vrrp_dbg_zebra; + +/* + * Initialize VRRP debugging. + * + * Installs VTY commands and registers callbacks. + */ +void vrrp_debug_init(void); + +/* + * Print VRRP debugging configuration. + * + * vty + * VTY to print debugging configuration to. + */ +int vrrp_debug_config_write(struct vty *vty); + +/* + * Print VRRP debugging configuration, human readable form. + * + * vty + * VTY to print debugging configuration to. + */ +int vrrp_debug_status_write(struct vty *vty); + +/* + * Set debugging status. + * + * ifp + * Interface to set status on + * + * vrid + * VRID of instance to set status on + * + * vtynode + * vty->node + * + * onoff + * Whether to turn the specified debugs on or off + * + * proto + * Turn protocol debugging on or off + * + * autoconf + * Turn autoconfiguration debugging on or off + * + * pkt + * Turn packet debugging on or off + */ +void vrrp_debug_set(struct interface *ifp, uint8_t vrid, int vtynode, + bool onoff, bool proto, bool autoconf, bool pkt, bool sock, + bool ndisc, bool arp, bool zebra); + +#endif /* __VRRP_DEBUG_H__ */ diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c index daaadffab5..0117497590 100644 --- a/vrrpd/vrrp_main.c +++ b/vrrpd/vrrp_main.c @@ -35,6 +35,7 @@ #include "lib/vrf.h" #include "vrrp.h" +#include "vrrp_debug.h" #include "vrrp_vty.h" #include "vrrp_zebra.h" @@ -139,6 +140,7 @@ int main(int argc, char **argv, char **envp) master = frr_init(); + vrrp_debug_init(); vrrp_zebra_init(); vrrp_vty_init(); vrrp_init(); diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 8362a966f8..3183315bf4 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -27,8 +27,9 @@ #include "lib/vty.h" #include "vrrp.h" -#include "vrrp_vty.h" +#include "vrrp_debug.h" #include "vrrp_memory.h" +#include "vrrp_vty.h" #ifndef VTYSH_EXTRACT_PL #include "vrrpd/vrrp_vty_clippy.c" #endif @@ -52,18 +53,6 @@ } \ } while (0); -DEFUN_NOSH (show_debugging_vrrpd, - show_debugging_vrrpd_cmd, - "show debugging [vrrp]", - SHOW_STR - DEBUG_STR - "VRRP information\n") -{ - vty_out(vty, "VRRP debugging status\n"); - - return CMD_SUCCESS; -} - DEFPY(vrrp_vrid, vrrp_vrid_cmd, "[no] vrrp (1-255)$vrid [version (2-3)]", @@ -383,17 +372,63 @@ DEFPY(vrrp_vrid_show, return CMD_SUCCESS; } + +DEFPY(debug_vrrp, + debug_vrrp_cmd, + "[no] debug vrrp [{protocol$proto|autoconfigure$ac|packets$pkt|sockets$sock|ndisc$ndisc|arp$arp|zebra$zebra}]", + NO_STR + DEBUG_STR + VRRP_STR + "Debug protocol state\n" + "Debug autoconfiguration\n" + "Debug sent and received packets\n" + "Debug socket creation and configuration\n" + "Debug Neighbor Discovery\n" + "Debug ARP\n" + "Debug Zebra events\n") +{ + /* If no specific are given on/off them all */ + if (strmatch(argv[argc - 1]->text, "vrrp")) + vrrp_debug_set(NULL, 0, vty->node, !no, true, true, true, true, + true, true, true); + else + vrrp_debug_set(NULL, 0, vty->node, !no, !!proto, !!ac, !!pkt, + !!sock, !!ndisc, !!arp, !!zebra); + + return CMD_SUCCESS; +} + +DEFUN_NOSH (show_debugging_vrrp, + show_debugging_vrrp_cmd, + "show debugging [vrrp]", + SHOW_STR + DEBUG_STR + "VRRP information\n") +{ + vty_out(vty, "VRRP debugging status:\n"); + + vrrp_debug_status_write(vty); + + return CMD_SUCCESS; +} + static struct cmd_node interface_node = { INTERFACE_NODE, "%s(config-if)# ", 1 }; +static struct cmd_node debug_node = {DEBUG_NODE, "", 1}; + void vrrp_vty_init(void) { + install_node(&debug_node, vrrp_debug_config_write); install_node(&interface_node, NULL); if_cmd_init(); - install_element(VIEW_NODE, &show_debugging_vrrpd_cmd); + install_element(VIEW_NODE, &vrrp_vrid_show_cmd); + install_element(VIEW_NODE, &show_debugging_vrrp_cmd); + install_element(VIEW_NODE, &debug_vrrp_cmd); + install_element(CONFIG_NODE, &debug_vrrp_cmd); install_element(CONFIG_NODE, &vrrp_autoconfigure_cmd); install_element(INTERFACE_NODE, &vrrp_vrid_cmd); install_element(INTERFACE_NODE, &vrrp_priority_cmd); From b637bcd4456f86f4e2a230fbe765e5a212b77c3f Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 12 Feb 2019 20:39:55 +0000 Subject: [PATCH 064/153] vrrpd: use debugging knobs * Gate all debugging messages with the debugging system * Simplify a bit of debugging where it was easy to do inline Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 226 +++++++++++++++++++++++++++----------------- vrrpd/vrrp_arp.c | 27 ++++-- vrrpd/vrrp_ndisc.c | 32 ++++--- vrrpd/vrrp_packet.c | 1 + vrrpd/vrrp_vty.c | 4 + 5 files changed, 183 insertions(+), 107 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 14347c239f..85b8435a77 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -32,6 +32,7 @@ #include "vrrp.h" #include "vrrp_arp.h" +#include "vrrp_debug.h" #include "vrrp_memory.h" #include "vrrp_ndisc.h" #include "vrrp_packet.h" @@ -443,10 +444,8 @@ static void vrrp_send_advertisement(struct vrrp_router *r) r->priority, r->vr->advertisement_interval, r->addrs->count, (struct ipaddr **)&addrs); - if (pktsz > 0) - zlog_hexdump(pkt, (size_t) pktsz); - else - zlog_warn("Could not build VRRP packet"); + if (DEBUG_MODE_CHECK(&vrrp_dbg_pkt, DEBUG_MODE_ALL)) + zlog_hexdump(pkt, (size_t)pktsz); const char *group = r->family == AF_INET ? VRRP_MCASTV4_GROUP_STR : VRRP_MCASTV6_GROUP_STR; @@ -499,26 +498,27 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, char dumpbuf[BUFSIZ]; vrrp_pkt_adver_dump(dumpbuf, sizeof(dumpbuf), pkt); - zlog_debug(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Received VRRP Advertisement from %s:\n%s", - r->vr->vrid, sipstr, dumpbuf); + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Received VRRP Advertisement from %s:\n%s", + r->vr->vrid, sipstr, dumpbuf); /* Check that VRID matches our configured VRID */ if (pkt->hdr.vrid != r->vr->vrid) { - zlog_warn( - VRRP_LOGPFX VRRP_LOGPFX_VRID - "%s datagram invalid: Advertisement contains VRID %" PRIu8 - " which does not match our instance", - r->vr->vrid, family2str(r->family), pkt->hdr.vrid); + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "%s datagram invalid: Advertisement contains VRID %" PRIu8 + " which does not match our instance", + r->vr->vrid, family2str(r->family), pkt->hdr.vrid); return -1; } /* Verify that we are not the IPvX address owner */ if (r->is_owner) { - zlog_warn( - VRRP_LOGPFX VRRP_LOGPFX_VRID - "%s datagram invalid: Received advertisement but we are the address owner", - r->vr->vrid, family2str(r->family)); + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "%s datagram invalid: Received advertisement but we are the address owner", + r->vr->vrid, family2str(r->family)); return -1; } @@ -526,25 +526,25 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, bool adveq = (pkt->hdr.v2.adver_int == MAX(r->vr->advertisement_interval / 100, 1)); if (r->vr->version == 2 && !adveq) { - zlog_warn( - VRRP_LOGPFX VRRP_LOGPFX_VRID - "%s datagram invalid: Received advertisement with advertisement interval %" PRIu8 - " unequal to our configured value %u", - r->vr->vrid, family2str(r->family), - pkt->hdr.v2.adver_int, - MAX(r->vr->advertisement_interval / 100, 1)); + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "%s datagram invalid: Received advertisement with advertisement interval %" PRIu8 + " unequal to our configured value %u", + r->vr->vrid, family2str(r->family), + pkt->hdr.v2.adver_int, + MAX(r->vr->advertisement_interval / 100, 1)); return -1; } /* Check that # IPs received matches our # configured IPs */ - if (pkt->hdr.naddr != r->addrs->count) { - zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID - "%s datagram has %" PRIu8 - " addresses, but this VRRP instance has %u", - r->vr->vrid, family2str(r->family), pkt->hdr.naddr, - r->addrs->count); - } + if (pkt->hdr.naddr != r->addrs->count) + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "%s datagram has %" PRIu8 + " addresses, but this VRRP instance has %u", + r->vr->vrid, family2str(r->family), pkt->hdr.naddr, + r->addrs->count); int addrcmp; @@ -580,9 +580,10 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, vrrp_change_state(r, VRRP_STATE_BACKUP); } else { /* Discard advertisement */ - zlog_debug(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Discarding advertisement from %s", - r->vr->vrid, sipstr); + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Discarding advertisement from %s", + r->vr->vrid, sipstr); } break; case VRRP_STATE_BACKUP: @@ -606,9 +607,10 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, } else if (r->vr->preempt_mode == true && pkt->hdr.priority < r->priority) { /* Discard advertisement */ - zlog_debug(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Discarding advertisement from %s", - r->vr->vrid, sipstr); + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Discarding advertisement from %s", + r->vr->vrid, sipstr); } break; case VRRP_STATE_INITIALIZE: @@ -658,20 +660,24 @@ static int vrrp_read(struct thread *thread) goto done; } - zlog_debug(VRRP_LOGPFX VRRP_LOGPFX_VRID "Received %s datagram: ", - r->vr->vrid, family2str(r->family)); - zlog_hexdump(r->ibuf, nbytes); + if (DEBUG_MODE_CHECK(&vrrp_dbg_pkt, DEBUG_MODE_ALL)) { + DEBUGD(&vrrp_dbg_pkt, + VRRP_LOGPFX VRRP_LOGPFX_VRID "Received %s datagram: ", + r->vr->vrid, family2str(r->family)); + zlog_hexdump(r->ibuf, nbytes); + } pktsize = vrrp_pkt_parse_datagram(r->family, r->vr->version, &m, nbytes, &src, &pkt, errbuf, sizeof(errbuf)); if (pktsize < 0) { - zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID - "%s datagram invalid: %s", - r->vr->vrid, family2str(r->family), errbuf); + DEBUGD(&vrrp_dbg_pkt, + VRRP_LOGPFX VRRP_LOGPFX_VRID "%s datagram invalid: %s", + r->vr->vrid, family2str(r->family), errbuf); } else { - zlog_debug(VRRP_LOGPFX VRRP_LOGPFX_VRID "Packet looks good", - r->vr->vrid); + DEBUGD(&vrrp_dbg_pkt, + VRRP_LOGPFX VRRP_LOGPFX_VRID "Packet looks good", + r->vr->vrid); vrrp_recv_advertisement(r, &src, pkt, pktsize); } @@ -755,12 +761,12 @@ static int vrrp_bind_to_primary_connected(struct vrrp_router *r) safe_strerror(errno)); return -1; } else { - zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Bound Tx socket to primary IP address %s", - r->vr->vrid, - inet_ntop(r->family, - (const void *)&c->address->u.prefix, ipstr, - sizeof(ipstr))); + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Bound Tx socket to primary IP address %s", + r->vr->vrid, + inet_ntop(r->family, (const void *)&c->address->u.prefix, + ipstr, sizeof(ipstr))); } return 0; @@ -843,8 +849,9 @@ static int vrrp_socket(struct vrrp_router *r) failed = true; goto done; } - zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Bound Rx socket to %s", - r->vr->vrid, r->vr->ifp->name); + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID "Bound Rx socket to %s", + r->vr->vrid, r->vr->ifp->name); /* Bind Rx socket to v4 multicast address */ struct sockaddr_in sa = {0}; @@ -859,9 +866,10 @@ static int vrrp_socket(struct vrrp_router *r) failed = true; goto done; } - zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Bound Rx socket to VRRP %s multicast group", - r->vr->vrid, family2str(r->family)); + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Bound Rx socket to VRRP %s multicast group", + r->vr->vrid, family2str(r->family)); /* Join Rx socket to VRRP IPv4 multicast group */ struct connected *c = listhead(r->vr->ifp->connected)->data; @@ -876,9 +884,10 @@ static int vrrp_socket(struct vrrp_router *r) failed = true; goto done; } - zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Joined %s VRRP multicast group", - r->vr->vrid, family2str(r->family)); + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Joined %s VRRP multicast group", + r->vr->vrid, family2str(r->family)); /* Set outgoing interface for advertisements */ struct ip_mreqn mreqn = {}; @@ -893,9 +902,10 @@ static int vrrp_socket(struct vrrp_router *r) failed = true; goto done; } - zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Set %s as outgoing multicast interface", - r->vr->vrid, r->mvl_ifp->name); + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Set %s as outgoing multicast interface", + r->vr->vrid, r->mvl_ifp->name); } else if (r->family == AF_INET6) { /* Always transmit IPv6 packets with hop limit set to 255 */ ret = setsockopt_ipv6_multicast_hops(r->sock_tx, 255); @@ -935,8 +945,9 @@ static int vrrp_socket(struct vrrp_router *r) failed = true; goto done; } - zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Bound Rx socket to %s", - r->vr->vrid, r->vr->ifp->name); + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID "Bound Rx socket to %s", + r->vr->vrid, r->vr->ifp->name); /* Bind Rx socket to v6 multicast address */ struct sockaddr_in6 sa = {0}; @@ -951,9 +962,10 @@ static int vrrp_socket(struct vrrp_router *r) failed = true; goto done; } - zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Bound Rx socket to VRRP %s multicast group", - r->vr->vrid, family2str(r->family)); + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Bound Rx socket to VRRP %s multicast group", + r->vr->vrid, family2str(r->family)); /* Join VRRP IPv6 multicast group */ struct ipv6_mreq mreq; @@ -969,9 +981,10 @@ static int vrrp_socket(struct vrrp_router *r) failed = true; goto done; } - zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Joined %s VRRP multicast group", - r->vr->vrid, family2str(r->family)); + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Joined %s VRRP multicast group", + r->vr->vrid, family2str(r->family)); /* Set outgoing interface for advertisements */ ret = setsockopt(r->sock_tx, IPPROTO_IPV6, IPV6_MULTICAST_IF, @@ -984,9 +997,10 @@ static int vrrp_socket(struct vrrp_router *r) failed = true; goto done; } - zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Set %s as outgoing multicast interface", - r->vr->vrid, r->mvl_ifp->name); + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Set %s as outgoing multicast interface", + r->vr->vrid, r->mvl_ifp->name); } /* Bind Tx socket to link-local address */ @@ -1106,8 +1120,8 @@ static int vrrp_adver_timer_expire(struct thread *thread) { struct vrrp_router *r = thread->arg; - zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Adver_Timer expired", - r->vr->vrid); + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID "Adver_Timer expired", r->vr->vrid); if (r->fsm.state == VRRP_STATE_MASTER) { /* Send an ADVERTISEMENT */ @@ -1118,9 +1132,9 @@ static int vrrp_adver_timer_expire(struct thread *thread) 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", - r->vr->vrid, vrrp_state_names[r->fsm.state]); + zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Adver_Timer expired in state '%s'; this is a bug", + r->vr->vrid, vrrp_state_names[r->fsm.state]); } return 0; @@ -1261,10 +1275,11 @@ static int vrrp_shutdown(struct vrrp_router *r) 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]); + DEBUGD(&vrrp_dbg_proto, + 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; } @@ -1319,23 +1334,43 @@ static void vrrp_autoconfig_autoaddrupdate(struct vrrp_vrouter *vr) struct listnode *ln; struct connected *c = NULL; - if (vr->v4->mvl_ifp) + if (vr->v4->mvl_ifp) { + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Setting IPv4 Virtual IP list to match IPv4 addresses on %s", + vr->vrid, vr->v4->mvl_ifp->name); for (ALL_LIST_ELEMENTS_RO(vr->v4->mvl_ifp->connected, ln, c)) if (c->address->family == AF_INET) vrrp_add_ipv4(vr, c->address->u.prefix4, true); + } - if (vr->v6->mvl_ifp) + if (vr->v6->mvl_ifp) { + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Setting IPv6 Virtual IP list to match IPv6 addresses on %s", + vr->vrid, vr->v6->mvl_ifp->name); for (ALL_LIST_ELEMENTS_RO(vr->v6->mvl_ifp->connected, ln, c)) if (c->address->family == AF_INET6 && !IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) vrrp_add_ipv6(vr, c->address->u.prefix6, true); + } if (vr->v4->addrs->count == 0 - && vr->v4->fsm.state != VRRP_STATE_INITIALIZE) + && vr->v4->fsm.state != VRRP_STATE_INITIALIZE) { + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "IPv4 Virtual IP list is empty; shutting down", + vr->vrid); vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN); + } if (vr->v6->addrs->count == 0 - && vr->v6->fsm.state != VRRP_STATE_INITIALIZE) + && vr->v6->fsm.state != VRRP_STATE_INITIALIZE) { + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "IPv6 Virtual IP list is empty; shutting down", + vr->vrid); vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN); + } } static struct vrrp_vrouter * @@ -1351,7 +1386,8 @@ vrrp_autoconfig_autocreate(struct interface *mvl_ifp) uint8_t vrid = mvl_ifp->hw_addr[5]; - zlog_info(VRRP_LOGPFX "Autoconfiguring VRRP on %s", p->name); + DEBUGD(&vrrp_dbg_auto, VRRP_LOGPFX "Autoconfiguring VRRP on %s", + p->name); vr = vrrp_vrouter_create(p, vrid, vrrp_autoconfig_version); @@ -1438,17 +1474,31 @@ int vrrp_autoconfig_if_del(struct interface *ifp) return 0; if (vr && vr->v4->mvl_ifp == ifp) { - if (vr->v4->fsm.state != VRRP_STATE_INITIALIZE) + if (vr->v4->fsm.state != VRRP_STATE_INITIALIZE) { + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Interface %s deleted; shutting down IPv4 VRRP router", + vr->vrid, ifp->name); vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN); + } vr->v4->mvl_ifp = NULL; } if (vr && vr->v6->mvl_ifp == ifp) { - if (vr->v6->fsm.state != VRRP_STATE_INITIALIZE) + if (vr->v6->fsm.state != VRRP_STATE_INITIALIZE) { + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Interface %s deleted; shutting down IPv6 VRRP router", + vr->vrid, ifp->name); vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN); + } vr->v6->mvl_ifp = NULL; } if (vr->v4->mvl_ifp == NULL && vr->v6->mvl_ifp == NULL) { + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "All VRRP interfaces for instance deleted; destroying autoconfigured VRRP router", + vr->vrid); vrrp_vrouter_destroy(vr); vr = NULL; } diff --git a/vrrpd/vrrp_arp.c b/vrrpd/vrrp_arp.c index 7e77d7df13..018f0d7696 100644 --- a/vrrpd/vrrp_arp.c +++ b/vrrpd/vrrp_arp.c @@ -33,6 +33,7 @@ #include "vrrp.h" #include "vrrp_arp.h" +#include "vrrp_debug.h" #define VRRP_LOGPFX "[ARP] " @@ -132,9 +133,14 @@ void vrrp_garp_send(struct vrrp_router *r, struct in_addr *v4) /* Send garp */ inet_ntop(AF_INET, v4, astr, sizeof(astr)); - zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Sending gratuitous ARP on %s for %s", - r->vr->vrid, ifp->name, astr); + + DEBUGD(&vrrp_dbg_arp, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Sending gratuitous ARP on %s for %s", + r->vr->vrid, ifp->name, astr); + if (DEBUG_MODE_CHECK(&vrrp_dbg_arp, DEBUG_MODE_ALL)) + zlog_hexdump(garpbuf, garpbuf_len); + sent_len = vrrp_send_garp(ifp, garpbuf, garpbuf_len); if (sent_len < 0) @@ -176,12 +182,14 @@ void vrrp_garp_init(void) htons(ETH_P_RARP)); } - if (garp_fd > 0) - zlog_info(VRRP_LOGPFX "Initialized gratuitous ARP socket"); - else { + if (garp_fd > 0) { + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX "Initialized gratuitous ARP socket"); + DEBUGD(&vrrp_dbg_arp, + VRRP_LOGPFX "Initialized gratuitous ARP subsystem"); + } else { zlog_err(VRRP_LOGPFX - "Error initializing gratuitous ARP socket"); - return; + "Error initializing gratuitous ARP subsystem"); } } @@ -189,6 +197,9 @@ void vrrp_garp_fini(void) { close(garp_fd); garp_fd = -1; + + DEBUGD(&vrrp_dbg_arp, + VRRP_LOGPFX "Deinitialized gratuitous ARP subsystem"); } bool vrrp_garp_is_init(void) diff --git a/vrrpd/vrrp_ndisc.c b/vrrpd/vrrp_ndisc.c index 73ee172aa8..7b3fdde7c4 100644 --- a/vrrpd/vrrp_ndisc.c +++ b/vrrpd/vrrp_ndisc.c @@ -32,6 +32,7 @@ #include "lib/ipaddr.h" #include "lib/log.h" +#include "vrrp_debug.h" #include "vrrp_ndisc.h" #define VRRP_LOGPFX "[NDISC] " @@ -168,9 +169,14 @@ int vrrp_ndisc_una_send(struct vrrp_router *r, struct ipaddr *ip) char ipbuf[INET6_ADDRSTRLEN]; ipaddr2str(ip, ipbuf, sizeof(ipbuf)); - zlog_debug(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Sending unsolicited Neighbor Advertisement on %s for %s", - r->vr->vrid, ifp->name, ipbuf); + DEBUGD(&vrrp_dbg_ndisc, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Sending unsolicited Neighbor Advertisement on %s for %s", + r->vr->vrid, ifp->name, ipbuf); + + if (DEBUG_MODE_CHECK(&vrrp_dbg_ndisc, DEBUG_MODE_ALL) + && DEBUG_MODE_CHECK(&vrrp_dbg_pkt, DEBUG_MODE_ALL)) + zlog_hexdump(buf, VRRP_NDISC_SIZE); len = sendto(ndisc_fd, buf, VRRP_NDISC_SIZE, 0, (struct sockaddr *)&sll, sizeof(sll)); @@ -207,20 +213,24 @@ void vrrp_ndisc_init(void) } vrrp_privs.change(ZPRIVS_LOWER); - if (ndisc_fd > 0) - zlog_info( - VRRP_LOGPFX - "Initialized unsolicited neighbor advertisement socket"); - else - zlog_err( - VRRP_LOGPFX - "Error initializing unsolicited neighbor advertisement socket"); + if (ndisc_fd > 0) { + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX "Initialized Neighbor Discovery socket"); + DEBUGD(&vrrp_dbg_ndisc, + VRRP_LOGPFX "Initialized Neighbor Discovery subsystem"); + } else { + zlog_err(VRRP_LOGPFX + "Error initializing Neighbor Discovery socket"); + } } void vrrp_ndisc_fini(void) { close(ndisc_fd); ndisc_fd = -1; + + DEBUGD(&vrrp_dbg_ndisc, + VRRP_LOGPFX "Deinitialized Neighbor Discovery subsystem"); } bool vrrp_ndisc_is_init(void) diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index 7dcb2933f8..16613226dd 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -27,6 +27,7 @@ #include "lib/memory.h" #include "vrrp.h" +#include "vrrp_debug.h" #include "vrrp_memory.h" #include "vrrp_packet.h" diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 3183315bf4..df21e7da9a 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -53,6 +53,8 @@ } \ } while (0); +/* clang-format off */ + DEFPY(vrrp_vrid, vrrp_vrid_cmd, "[no] vrrp (1-255)$vrid [version (2-3)]", @@ -412,6 +414,8 @@ DEFUN_NOSH (show_debugging_vrrp, return CMD_SUCCESS; } +/* clang-format on */ + static struct cmd_node interface_node = { INTERFACE_NODE, "%s(config-if)# ", 1 From 9e006c64eb4c687cfd15a2d0765943b7ea624142 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 12 Feb 2019 20:41:43 +0000 Subject: [PATCH 065/153] vrrpd: fix bug in v6 autoconfiguration Typo caused the IPv4 VRRP router to be shutdown instead of the IPv6 one, and sometimes a crash. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 85b8435a77..7e8ce50c13 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1369,7 +1369,7 @@ static void vrrp_autoconfig_autoaddrupdate(struct vrrp_vrouter *vr) VRRP_LOGPFX VRRP_LOGPFX_VRID "IPv6 Virtual IP list is empty; shutting down", vr->vrid); - vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN); + vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN); } } From fa211f1c51abbeb5e99b10ded520b15a5f2727b8 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 12 Feb 2019 21:22:20 +0000 Subject: [PATCH 066/153] vrrpd: properly retrieve pkt src address * Fix null dereference when retrieving IPv6 source address * Change IPv4 code path to use system-specified source address instead of the one delivered in the IPv4 raw header Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 5 +++-- vrrpd/vrrp_packet.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 7e8ce50c13..9c2b980eaf 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -635,6 +635,7 @@ static int vrrp_read(struct thread *thread) ssize_t nbytes; bool resched; char errbuf[BUFSIZ]; + struct sockaddr_storage sa; uint8_t control[64]; struct ipaddr src = {}; @@ -642,8 +643,8 @@ static int vrrp_read(struct thread *thread) struct iovec iov; iov.iov_base = r->ibuf; iov.iov_len = sizeof(r->ibuf); - m.msg_name = NULL; - m.msg_namelen = 0; + m.msg_name = &sa; + m.msg_namelen = sizeof(sa); m.msg_iov = &iov; m.msg_iovlen = 1; m.msg_control = control; diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index 16613226dd..db31d163bc 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -226,8 +226,9 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m, VRRP_PKT_VCHECK(pktsize > 0, "IPv4 packet has no payload"); /* Extract source address */ + struct sockaddr_in *sa = m->msg_name; src->ipa_type = IPADDR_V4; - src->ipaddr_v4 = ip->ip_src; + src->ipaddr_v4 = sa->sin_addr; } else if (family == AF_INET6) { struct cmsghdr *c; for (c = CMSG_FIRSTHDR(m); c != NULL; CMSG_NXTHDR(m, c)) { @@ -247,8 +248,8 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m, pktsize = read; /* Extract source address */ - src->ipa_type = IPADDR_V6; struct sockaddr_in6 *sa = m->msg_name; + src->ipa_type = IPADDR_V6; memcpy(&src->ipaddr_v6, &sa->sin6_addr, sizeof(struct in6_addr)); } else { From 6ad94d3abde04d0e6363bea724f23fd0c7a5d9ac Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 12 Feb 2019 22:47:48 +0000 Subject: [PATCH 067/153] vrrpd: set DSCP byte on adverts to CS6 Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 9c2b980eaf..b773bdf870 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -831,6 +831,9 @@ static int vrrp_socket(struct vrrp_router *r) r->vr->vrid); } + /* Set Tx socket DSCP byte */ + setsockopt_ipv4_tos(r->sock_tx, IPTOS_PREC_INTERNETCONTROL); + /* Turn off multicast loop on Tx */ setsockopt_ipv4_multicast_loop(r->sock_tx, 0); @@ -917,6 +920,9 @@ static int vrrp_socket(struct vrrp_router *r) r->vr->vrid); } + /* Set Tx socket DSCP byte */ + setsockopt_ipv6_tclass(r->sock_tx, IPTOS_PREC_INTERNETCONTROL); + /* Request hop limit delivery */ setsockopt_ipv6_hoplimit(r->sock_rx, 1); if (ret < 0) { From fc278f75f73da4f0f24653977d7e1470d7bed77e Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 13 Feb 2019 19:54:03 +0000 Subject: [PATCH 068/153] vrrpd: remove ifindex from hash key computation Ifindexes apparently change more often than one might expect and so are not suitable for use in hash keys. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index b773bdf870..92caaeff3b 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1614,8 +1614,7 @@ static unsigned int vrrp_hash_key(void *arg) struct vrrp_vrouter *vr = arg; char key[IFNAMSIZ + 64]; - snprintf(key, sizeof(key), "%d%s%u", vr->ifp->ifindex, vr->ifp->name, - vr->vrid); + snprintf(key, sizeof(key), "%s@%" PRIu8, vr->ifp->name, vr->vrid); return string_hash_make(key); } From f828842a69edee4f43e7ef2a298fdb9f7fe48df4 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 13 Feb 2019 19:56:40 +0000 Subject: [PATCH 069/153] vrrpd: add support for configuration writing Signed-off-by: Quentin Young --- lib/command.c | 1 + lib/command.h | 1 + vrrpd/vrrp.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++ vrrpd/vrrp.h | 23 +++++++++++++++++ vrrpd/vrrp_debug.c | 2 +- vrrpd/vrrp_debug.h | 2 +- vrrpd/vrrp_vty.c | 12 ++++----- 7 files changed, 96 insertions(+), 9 deletions(-) diff --git a/lib/command.c b/lib/command.c index b7a323e358..73bb9580cd 100644 --- a/lib/command.c +++ b/lib/command.c @@ -149,6 +149,7 @@ const char *node_names[] = { "bfd", /* BFD_NODE */ "bfd peer", /* BFD_PEER_NODE */ "openfabric", // OPENFABRIC_NODE + "vrrp", // VRRP_NODE }; /* clang-format on */ diff --git a/lib/command.h b/lib/command.h index a5f9616dbf..d96ec97e67 100644 --- a/lib/command.h +++ b/lib/command.h @@ -147,6 +147,7 @@ enum node_type { BFD_NODE, /* BFD protocol mode. */ BFD_PEER_NODE, /* BFD peer configuration mode. */ OPENFABRIC_NODE, /* OpenFabric router configuration node */ + VRRP_NODE, /* VRRP node */ NODE_TYPE_MAX, /* maximum */ }; diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 92caaeff3b..658dc098a2 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -29,6 +29,7 @@ #include "lib/sockopt.h" #include "lib/sockunion.h" #include "lib/vrf.h" +#include "lib/vty.h" #include "vrrp.h" #include "vrrp_arp.h" @@ -1609,6 +1610,69 @@ void vrrp_autoconfig_off(void) /* Other ------------------------------------------------------------------- */ +int vrrp_config_write_interface(struct vty *vty) +{ + struct list *vrs = hash_to_list(vrrp_vrouters_hash); + struct listnode *ln; + struct vrrp_vrouter *vr; + int writes = 0; + + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) { + vty_frame(vty, "interface %s\n", vr->ifp->name); + ++writes; + + vty_out(vty, " vrrp %" PRIu8 "%s\n", vr->vrid, + vr->version == 2 ? " version 2" : ""); + ++writes; + + if (!vr->preempt_mode && ++writes) + vty_out(vty, " no vrrp %" PRIu8 " preempt\n", vr->vrid); + + if (vr->accept_mode && ++writes) + vty_out(vty, " vrrp %" PRIu8 " accept\n", vr->vrid); + + if (vr->advertisement_interval != VRRP_DEFAULT_ADVINT + && ++writes) + vty_out(vty, + " vrrp %" PRIu8 + " advertisement-interval %" PRIu16 "\n", + vr->vrid, vr->advertisement_interval); + + if (vr->priority != VRRP_DEFAULT_PRIORITY && ++writes) + vty_out(vty, " vrrp %" PRIu8 " priority %" PRIu8 "\n", + vr->vrid, vr->priority); + + ln = NULL; + struct ipaddr *ip; + + for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ln, ip)) { + char ipbuf[INET6_ADDRSTRLEN]; + ipaddr2str(ip, ipbuf, sizeof(ipbuf)); + vty_out(vty, " vrrp %" PRIu8 " ip %s\n", vr->vrid, + ipbuf); + ++writes; + } + for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ln, ip)) { + char ipbuf[INET6_ADDRSTRLEN]; + ipaddr2str(ip, ipbuf, sizeof(ipbuf)); + vty_out(vty, " vrrp %" PRIu8 " ipv6 %s\n", vr->vrid, + ipbuf); + ++writes; + } + } + + return writes; +} + +int vrrp_config_write_global(struct vty *vty) +{ + if (vrrp_autoconfig_is_on) + vty_out(vty, "vrrp autoconfigure%s\n", + vrrp_autoconfig_version == 2 ? " version 2" : ""); + + return 1; +} + static unsigned int vrrp_hash_key(void *arg) { struct vrrp_vrouter *vr = arg; diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index ed68b6a812..eabb23fe75 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -30,6 +30,7 @@ #include "lib/privs.h" #include "lib/stream.h" #include "lib/thread.h" +#include "lib/vty.h" /* Global definitions */ #define VRRP_DEFAULT_ADVINT 100 @@ -558,6 +559,28 @@ int vrrp_autoconfig_if_address_del(struct interface *ifp); /* Other ------------------------------------------------------------------- */ +/* + * Write interface block-level configuration to vty. + * + * vty + * vty to write config to + * + * Returns: + * # of lines written + */ +int vrrp_config_write_interface(struct vty *vty); + +/* + * Write global level configuration to vty. + * + * vty + * vty to write config to + * + * Returns: + * # of lines written + */ +int vrrp_config_write_global(struct vty *vty); + /* * Find VRRP Virtual Router by Virtual Router ID */ diff --git a/vrrpd/vrrp_debug.c b/vrrpd/vrrp_debug.c index cea2bbff79..b841bca78a 100644 --- a/vrrpd/vrrp_debug.c +++ b/vrrpd/vrrp_debug.c @@ -89,7 +89,7 @@ static int vrrp_debug_config_write_helper(struct vty *vty, bool config) return 0; } -int vrrp_debug_config_write(struct vty *vty) +int vrrp_config_write_debug(struct vty *vty) { return vrrp_debug_config_write_helper(vty, true); } diff --git a/vrrpd/vrrp_debug.h b/vrrpd/vrrp_debug.h index c54b20e5b8..20f9930955 100644 --- a/vrrpd/vrrp_debug.h +++ b/vrrpd/vrrp_debug.h @@ -46,7 +46,7 @@ void vrrp_debug_init(void); * vty * VTY to print debugging configuration to. */ -int vrrp_debug_config_write(struct vty *vty); +int vrrp_config_write_debug(struct vty *vty); /* * Print VRRP debugging configuration, human readable form. diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index df21e7da9a..d19f9adebc 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -416,17 +416,15 @@ DEFUN_NOSH (show_debugging_vrrp, /* clang-format on */ -static struct cmd_node interface_node = { - INTERFACE_NODE, - "%s(config-if)# ", 1 -}; - +static struct cmd_node interface_node = {INTERFACE_NODE, "%s(config-if)# ", 1}; static struct cmd_node debug_node = {DEBUG_NODE, "", 1}; +static struct cmd_node vrrp_node = {VRRP_NODE, "", 1}; void vrrp_vty_init(void) { - install_node(&debug_node, vrrp_debug_config_write); - install_node(&interface_node, NULL); + install_node(&debug_node, vrrp_config_write_debug); + install_node(&interface_node, vrrp_config_write_interface); + install_node(&vrrp_node, vrrp_config_write_global); if_cmd_init(); install_element(VIEW_NODE, &vrrp_vrid_show_cmd); From d5a6ff434e164ced08f882c15081c0f0956db26f Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 13 Feb 2019 20:02:54 +0000 Subject: [PATCH 070/153] vrrpd: fix err messaging for ipv6 addr add * Add newline to message * Change pasted IPv4 to IPv6 Signed-off-by: Quentin Young --- vrrpd/vrrp_vty.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index d19f9adebc..eacf4a14c3 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -218,12 +218,12 @@ DEFPY(vrrp_ip6, if (deactivated) vty_out(vty, "%% Deactivated IPv6 Virtual Router %ld\n", vrid); if (failed) { - vty_out(vty, "%% Failed to %s virtual IP", + vty_out(vty, "%% Failed to %s virtual IP\n", no ? "remove" : "add"); ret = CMD_WARNING_CONFIG_FAILED; if (will_activate && !activated) { vty_out(vty, - "%% Failed to activate IPv4 Virtual Router %ld\n", + "%% Failed to activate IPv6 Virtual Router %ld\n", vrid); } } From 97b5f22bcdb7c527725de15dcc3d37eef3340026 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 13 Feb 2019 20:24:56 +0000 Subject: [PATCH 071/153] vrrpd: update auto*, init scripts, etc Add vrrpd to all the usual places daemons need to go Signed-off-by: Quentin Young --- configure.ac | 2 ++ redhat/frr.spec.in | 20 +++++++++++++++++++- tools/etc/frr/daemons | 2 ++ tools/etc/rsyslog.d/45-frr.conf | 2 ++ tools/frr.in | 2 +- 5 files changed, 26 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 69f489dbc5..c228ff0c91 100755 --- a/configure.ac +++ b/configure.ac @@ -443,6 +443,8 @@ AC_ARG_ENABLE([fabricd], AS_HELP_STRING([--disable-fabricd], [do not build fabricd])) AC_ARG_ENABLE([bgp-announce], AS_HELP_STRING([--disable-bgp-announce,], [turn off BGP route announcement])) +AC_ARG_ENABLE([vrrpd], + AS_HELP_STRING([--disable-vrrpd], [do not build vrrpd])) AC_ARG_ENABLE([bgp-vnc], AS_HELP_STRING([--disable-bgp-vnc],[turn off BGP VNC support])) AC_ARG_ENABLE([snmp], diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index 36f9259865..ebd9ac3f47 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -24,6 +24,7 @@ %{!?with_pam: %global with_pam 0 } %{!?with_pbrd: %global with_pbrd 1 } %{!?with_pimd: %global with_pimd 1 } +%{!?with_vrrpd: %global with_vrrpd 1 } %{!?with_rpki: %global with_rpki 0 } %{!?with_rtadv: %global with_rtadv 1 } %{!?with_watchfrr: %global with_watchfrr 1 } @@ -124,6 +125,12 @@ %define daemon_babeld "" %endif +%if %{with_vrrpd} + %define daemon_vrrpd vrrpd +%else + %define daemon_vrrpd "" +%endif + %if %{with_watchfrr} %define daemon_watchfrr watchfrr %else @@ -136,7 +143,7 @@ %define daemon_bfdd "" %endif -%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr} %{daemon_pbrd} %{daemon_bfdd} +%define all_daemons %{daemon_list} %{daemon_ldpd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_eigrpd} %{daemon_babeld} %{daemon_watchfrr} %{daemon_pbrd} %{daemon_bfdd} %{daemon_vrrpd} #release sub-revision (the two digits after the CONFDATE) %{!?release_rev: %global release_rev 01 } @@ -306,6 +313,11 @@ developing OSPF-API and frr applications. %else --disable-babeld \ %endif +%if %{with_vrrpd} + --enable-vrrpd \ +%else + --disable-vrrpd \ +%endif %if %{with_pam} --with-libpam \ %endif @@ -461,6 +473,9 @@ zebra_spec_add_service isisd 2608/tcp "ISISd vty" zebra_spec_add_service bfdd 2617/tcp "BFDd vty" %endif zebra_spec_add_service fabricd 2618/tcp "Fabricd vty" +%if %{with_vrrpd} + zebra_spec_add_service vrrpd 2619/tcp "VRRPd vty" +%endif %if "%{initsystem}" == "systemd" for daemon in %all_daemons ; do @@ -596,6 +611,9 @@ fi %if %{with_pbrd} %{_sbindir}/pbrd %endif +%if %{with_vrrpd} + %{_sbindir}/vrrpd +%endif %{_sbindir}/isisd %{_sbindir}/fabricd %if %{with_ldpd} diff --git a/tools/etc/frr/daemons b/tools/etc/frr/daemons index 2abff422c9..b920621d70 100644 --- a/tools/etc/frr/daemons +++ b/tools/etc/frr/daemons @@ -29,6 +29,7 @@ sharpd=no pbrd=no bfdd=no fabricd=no +vrrpd=no # # If this option is set the /etc/init.d/frr script automatically loads @@ -53,6 +54,7 @@ pbrd_options=" -A 127.0.0.1" staticd_options="-A 127.0.0.1" bfdd_options=" -A 127.0.0.1" fabricd_options="-A 127.0.0.1" +vrrpd_options=" -A 127.0.0.1" # The list of daemons to watch is automatically generated by the init script. #watchfrr_options="" diff --git a/tools/etc/rsyslog.d/45-frr.conf b/tools/etc/rsyslog.d/45-frr.conf index 4612e8beaf..feeeb13f13 100644 --- a/tools/etc/rsyslog.d/45-frr.conf +++ b/tools/etc/rsyslog.d/45-frr.conf @@ -16,6 +16,7 @@ if $programname == 'babeld' or $programname == 'pimd' or $programname == 'ripd' or $programname == 'ripngd' or + $programname == 'vrrpd' or $programname == 'watchfrr' or $programname == 'zebra' then :omfile:$frr_log @@ -33,6 +34,7 @@ if $programname == 'babeld' or $programname == 'pimd' or $programname == 'ripd' or $programname == 'ripngd' or + $programname == 'vrrpd' or $programname == 'watchfrr' or $programname == 'zebra' then stop diff --git a/tools/frr.in b/tools/frr.in index 2e3a094589..d871afa42b 100755 --- a/tools/frr.in +++ b/tools/frr.in @@ -25,7 +25,7 @@ FRR_VTY_GROUP="@enable_vty_group@" # frrvty # Local Daemon selection may be done by using /etc/frr/daemons. # See /usr/share/doc/frr/README.Debian.gz for further information. # Keep zebra first and do not list watchfrr! -DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd" +DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd" MAX_INSTANCES=5 RELOAD_SCRIPT="$D_PATH/frr-reload.py" From dfed4e22bf82ca8827f12887ab5349196afa2538 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 13 Feb 2019 21:40:37 +0000 Subject: [PATCH 072/153] vrrpd: fix incorrect index for vrrp event names Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 658dc098a2..8071944d99 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1320,7 +1320,7 @@ static int (*vrrp_event_handlers[])(struct vrrp_router *r) = { int vrrp_event(struct vrrp_router *r, int event) { zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "'%s' event", r->vr->vrid, - vrrp_event_names[r->fsm.state]); + vrrp_event_names[event]); return vrrp_event_handlers[event](r); } From 00984df75ad2cb581d3acffac4aa2d1eb58b648d Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 13 Feb 2019 21:40:54 +0000 Subject: [PATCH 073/153] vrrpd: log addresses for autoconfig When adding and removing addreses. log them. Also include a VRID tag when autoconfiguring new instances. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 8071944d99..5dde98a082 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1341,6 +1341,7 @@ static void vrrp_autoconfig_autoaddrupdate(struct vrrp_vrouter *vr) struct listnode *ln; struct connected *c = NULL; + char ipbuf[INET6_ADDRSTRLEN]; if (vr->v4->mvl_ifp) { DEBUGD(&vrrp_dbg_auto, @@ -1348,8 +1349,14 @@ static void vrrp_autoconfig_autoaddrupdate(struct vrrp_vrouter *vr) "Setting IPv4 Virtual IP list to match IPv4 addresses on %s", vr->vrid, vr->v4->mvl_ifp->name); for (ALL_LIST_ELEMENTS_RO(vr->v4->mvl_ifp->connected, ln, c)) - if (c->address->family == AF_INET) + if (c->address->family == AF_INET) { + inet_ntop(AF_INET, &c->address->u.prefix4, ipbuf, + sizeof(ipbuf)); + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID "Adding %s", + vr->vrid, ipbuf); vrrp_add_ipv4(vr, c->address->u.prefix4, true); + } } if (vr->v6->mvl_ifp) { @@ -1359,8 +1366,14 @@ static void vrrp_autoconfig_autoaddrupdate(struct vrrp_vrouter *vr) vr->vrid, vr->v6->mvl_ifp->name); for (ALL_LIST_ELEMENTS_RO(vr->v6->mvl_ifp->connected, ln, c)) if (c->address->family == AF_INET6 - && !IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) + && !IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) { + inet_ntop(AF_INET6, &c->address->u.prefix6, + ipbuf, sizeof(ipbuf)); + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID "Adding %s", + vr->vrid, ipbuf); vrrp_add_ipv6(vr, c->address->u.prefix6, true); + } } if (vr->v4->addrs->count == 0 @@ -1394,7 +1407,8 @@ vrrp_autoconfig_autocreate(struct interface *mvl_ifp) uint8_t vrid = mvl_ifp->hw_addr[5]; - DEBUGD(&vrrp_dbg_auto, VRRP_LOGPFX "Autoconfiguring VRRP on %s", + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID "Autoconfiguring VRRP on %s", vrid, p->name); vr = vrrp_vrouter_create(p, vrid, vrrp_autoconfig_version); From b7dc1bbb31e015ada8ca23617dda5b61888d0a4f Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 13 Feb 2019 22:15:37 +0000 Subject: [PATCH 074/153] vrrpd: close sockets on shutdown When shutting down a VRRP router, kill the sockets as well. Too dangerous to try to reuse them. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 5dde98a082..cf0ab3d54b 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1291,7 +1291,15 @@ static int vrrp_shutdown(struct vrrp_router *r) break; } - /* Transition to the Initialize state */ + if (r->sock_rx > 0) { + close(r->sock_rx); + r->sock_rx = -1; + } + if (r->sock_tx > 0) { + close(r->sock_tx); + r->sock_tx = -1; + } + vrrp_change_state(r, VRRP_STATE_INITIALIZE); r->is_active = false; From 2198a5bbc90bb3fc4ec4626ff627a6df5b1081e4 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 13 Feb 2019 22:16:56 +0000 Subject: [PATCH 075/153] vrrpd: skip binding interface after create When automatically creating new VRRP instances, we don't need to try to bind them to macvlan interfaces again. We only need to do that when we got notified that a new interface came up and want an existing VRRP instance to update its interface bindings. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index cf0ab3d54b..140a373087 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1463,25 +1463,28 @@ static struct vrrp_vrouter *vrrp_lookup_by_mvlif(struct interface *mvl_ifp) int vrrp_autoconfig_if_add(struct interface *ifp) { + bool created = false; + struct vrrp_vrouter *vr; + if (!vrrp_autoconfig_is_on) return 0; - struct vrrp_vrouter *vr; - if (!ifp || !ifp->link_ifindex || !vrrp_ifp_has_vrrp_mac(ifp)) return -1; vr = vrrp_lookup_by_mvlif(ifp); - if (!vr) + if (!vr) { vr = vrrp_autoconfig_autocreate(ifp); + created = true; + } if (!vr) return -1; if (vr->autoconf == false) return 0; - else { + else if (!created) { vrrp_attach_interface(vr->v4); vrrp_attach_interface(vr->v6); vrrp_autoconfig_autoaddrupdate(vr); From e6341d212bb52b4f1f8238b51894733b8593855b Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 13 Feb 2019 22:37:41 +0000 Subject: [PATCH 076/153] vrrpd: better messaging for interface binds Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 140a373087..bb24bc9510 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -320,15 +320,15 @@ static bool vrrp_attach_interface(struct vrrp_router *r) if (candidates == 0) zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID - "No interface found w/ MAC %s", - r->vr->vrid, ethstr); + "%s interface: None (no interface found w/ MAC %s)", + r->vr->vrid, family2str(r->family), ethstr); else if (candidates > 1) zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Multiple VRRP interfaces found; using %s", - r->vr->vrid, selection->name); + "%s interface: Multiple interfaces found; using %s", + r->vr->vrid, family2str(r->family), selection->name); else - zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Selected %s", - r->vr->vrid, selection->name); + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "%s interface: %s", + r->vr->vrid, family2str(r->family), selection->name); r->mvl_ifp = selection; From 6e93585e6f4f837e3f1912e1877b72d7ca7544f9 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 14 Feb 2019 22:28:51 +0000 Subject: [PATCH 077/153] vrrpd: interface tracking * Dynamically bind interfaces when they become available * Automatically start VRRP sessions when their interfaces are added or come up * Automatically shut down VRRP sessions when their interfaces are deleted or go down * Automatically unbind interfaces when they are deleted Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 510 ++++++++++++++++++++++++++++++++++++--------- vrrpd/vrrp.h | 128 +++--------- vrrpd/vrrp_vty.c | 10 +- vrrpd/vrrp_zebra.c | 12 +- 4 files changed, 444 insertions(+), 216 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index bb24bc9510..bf4f0f1ec9 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -128,8 +128,146 @@ static bool vrrp_is_owner(struct interface *ifp, struct ipaddr *addr) return !!connected_lookup_prefix_exact(ifp, &p); } +/* + * Whether an interface has a MAC address that matches the VRRP RFC. + * + * ifp + * Interface to check + * + * Returns: + * Whether the interface has a VRRP mac or not + */ +static bool vrrp_ifp_has_vrrp_mac(struct interface *ifp) +{ + struct ethaddr vmac4; + struct ethaddr vmac6; + vrrp_mac_set(&vmac4, 0, 0x00); + vrrp_mac_set(&vmac6, 1, 0x00); + + return !memcmp(ifp->hw_addr, vmac4.octet, sizeof(vmac4.octet) - 1) + || !memcmp(ifp->hw_addr, vmac6.octet, sizeof(vmac6.octet) - 1); +} + +/* + * Lookup a Virtual Router instance given a macvlan subinterface. + * + * The VRID is extracted from the interface MAC and the 2-tuple (iface, vrid) + * is used to look up any existing instances that match the interface. It does + * not matter whether the instance is already bound to the interface or not. + * + * mvl_ifp + * Interface pointer to use to lookup. Should be a macvlan device. + * + * Returns: + * Virtual Router, if found + * NULL otherwise + */ +static struct vrrp_vrouter *vrrp_lookup_by_if_mvl(struct interface *mvl_ifp) +{ + struct interface *p; + + if (!mvl_ifp || !mvl_ifp->link_ifindex + || !vrrp_ifp_has_vrrp_mac(mvl_ifp)) + return NULL; + + p = if_lookup_by_index(mvl_ifp->link_ifindex, VRF_DEFAULT); + uint8_t vrid = mvl_ifp->hw_addr[5]; + + return vrrp_lookup(p, vrid); +} + +/* + * Lookup the Virtual Router instances configured on a particular interface. + * + * ifp + * Interface pointer to use to lookup. Should not be a macvlan device. + * + * Returns: + * List of virtual routers found + */ +static struct list *vrrp_lookup_by_if(struct interface *ifp) +{ + struct list *l = hash_to_list(vrrp_vrouters_hash); + struct listnode *ln, *nn; + struct vrrp_vrouter *vr; + + for (ALL_LIST_ELEMENTS(l, ln, nn, vr)) + if (vr->ifp != ifp) + list_delete_node(l, ln); + + return l; +} + +/* + * Lookup any Virtual Router instances associated with a particular interface. + * This is a combination of the results from vrrp_lookup_by_if_mvl and + * vrrp_lookup_by_if. + * + * Suppose the system interface list looks like the following: + * + * eth0 + * \- eth0-v0 00:00:5e:00:01:01 + * \- eth0-v1 00:00:5e:00:02:01 + * \- eth0-v2 00:00:5e:00:01:0a + * + * Passing eth0-v2 to this function will give you the VRRP instance configured + * on eth0 with VRID 10. Passing eth0-v0 or eth0-v1 will give you the VRRP + * instance configured on eth0 with VRID 1. Passing eth0 will give you both. + * + * ifp + * Interface pointer to use to lookup. Can be any interface. + * + * Returns: + * List of virtual routers found + */ +static struct list *vrrp_lookup_by_if_any(struct interface *ifp) +{ + struct vrrp_vrouter *vr; + struct list *vrs; + + vr = vrrp_lookup_by_if_mvl(ifp); + vrs = vr ? list_new() : vrrp_lookup_by_if(ifp); + + if (vr) + listnode_add(vrs, vr); + + return vrs; +} + /* Configuration controllers ----------------------------------------------- */ +void vrrp_check_start(struct vrrp_vrouter *vr) +{ + struct vrrp_router *r; + bool start; + + if (vr->shutdown || vr->ifp == NULL) + return; + + r = vr->v4; + start = r->fsm.state == VRRP_STATE_INITIALIZE; + start = start && (vr->ifp != NULL); + start = start && (CHECK_FLAG(vr->ifp->flags, IFF_UP)); + start = start && vr->ifp->connected->count > 0; + start = start && (r->mvl_ifp != NULL); + start = start && (CHECK_FLAG(r->mvl_ifp->flags, IFF_UP)); + start = start && r->addrs->count > 0; + if (start) + vrrp_event(r, VRRP_EVENT_STARTUP); + + r = vr->v6; + start = r->fsm.state == VRRP_STATE_INITIALIZE; + start = start && (vr->ifp != NULL); + start = start && (CHECK_FLAG(vr->ifp->flags, IFF_UP)); + start = start && vr->ifp->connected->count; + start = start && (r->mvl_ifp != NULL); + start = start && (CHECK_FLAG(r->mvl_ifp->flags, IFF_UP)); + start = start && (r->mvl_ifp->connected->count > 0); + start = start && r->addrs->count > 0; + if (start) + vrrp_event(r, VRRP_EVENT_STARTUP); +} + void vrrp_set_priority(struct vrrp_vrouter *vr, uint8_t priority) { vr->priority = priority; @@ -161,7 +299,7 @@ static bool vrrp_has_ip(struct vrrp_vrouter *vr, struct ipaddr *ip) return false; } -int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip, bool activate) +int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip) { int af = (ip->ipa_type == IPADDR_V6) ? AF_INET6 : AF_INET; @@ -185,15 +323,7 @@ int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip, bool activate) *new = *ip; listnode_add(r->addrs, new); - bool do_activate = (activate && r->fsm.state == VRRP_STATE_INITIALIZE); - int ret = 0; - - if (do_activate) { - ret = vrrp_event(r, VRRP_EVENT_STARTUP); - if (ret) - listnode_delete(r->addrs, new); - } - else if (r->fsm.state == VRRP_STATE_MASTER) { + if (r->fsm.state == VRRP_STATE_MASTER) { switch (r->family) { case AF_INET: vrrp_garp_send(r, &new->ipaddr_v4); @@ -204,26 +334,26 @@ int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip, bool activate) } } - return ret; + return 0; } -int vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4, bool activate) +int vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4) { struct ipaddr ip; ip.ipa_type = IPADDR_V4; ip.ipaddr_v4 = v4; - return vrrp_add_ip(vr->v4, &ip, activate); + return vrrp_add_ip(vr->v4, &ip); } -int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6, bool activate) +int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6) { struct ipaddr ip; ip.ipa_type = IPADDR_V6; ip.ipaddr_v6 = v6; - return vrrp_add_ip(vr->v6, &ip, activate); + return vrrp_add_ip(vr->v6, &ip); } -int vrrp_del_ip(struct vrrp_router *r, struct ipaddr *ip, bool deactivate) +int vrrp_del_ip(struct vrrp_router *r, struct ipaddr *ip) { struct listnode *ln, *nn; struct ipaddr *iter; @@ -232,37 +362,36 @@ int vrrp_del_ip(struct vrrp_router *r, struct ipaddr *ip, bool deactivate) if (!vrrp_has_ip(r->vr, ip)) return 0; - if (deactivate && r->addrs->count == 1 - && r->fsm.state != VRRP_STATE_INITIALIZE) - ret = vrrp_event(r, VRRP_EVENT_SHUTDOWN); + for (ALL_LIST_ELEMENTS(r->addrs, ln, nn, iter)) + if (!memcmp(&iter->ip, &ip->ip, IPADDRSZ(ip))) + list_delete_node(r->addrs, ln); /* - * Don't delete IP if we failed to deactivate, otherwise we'll run into - * issues later trying to build a VRRP advertisement with no IPs + * NB: Deleting the last address and then issuing a shutdown will cause + * transmission of a priority 0 VRRP Advertisement - as per the RFC - + * but it will have no addresses. This is not forbidden in the RFC but + * might confuse other implementations. */ - if (ret == 0) { - for (ALL_LIST_ELEMENTS(r->addrs, ln, nn, iter)) - if (!memcmp(&iter->ip, &ip->ip, IPADDRSZ(ip))) - list_delete_node(r->addrs, ln); - } + if (r->addrs->count == 0 && r->fsm.state != VRRP_STATE_INITIALIZE) + ret = vrrp_event(r, VRRP_EVENT_SHUTDOWN); return ret; } -int vrrp_del_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6, bool deactivate) +int vrrp_del_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6) { struct ipaddr ip; ip.ipa_type = IPADDR_V6; ip.ipaddr_v6 = v6; - return vrrp_del_ip(vr->v6, &ip, deactivate); + return vrrp_del_ip(vr->v6, &ip); } -int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4, bool deactivate) +int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4) { struct ipaddr ip; ip.ipa_type = IPADDR_V4; ip.ipaddr_v4 = v4; - return vrrp_del_ip(vr->v4, &ip, deactivate); + return vrrp_del_ip(vr->v4, &ip); } @@ -1363,7 +1492,7 @@ static void vrrp_autoconfig_autoaddrupdate(struct vrrp_vrouter *vr) DEBUGD(&vrrp_dbg_auto, VRRP_LOGPFX VRRP_LOGPFX_VRID "Adding %s", vr->vrid, ipbuf); - vrrp_add_ipv4(vr, c->address->u.prefix4, true); + vrrp_add_ipv4(vr, c->address->u.prefix4); } } @@ -1380,10 +1509,12 @@ static void vrrp_autoconfig_autoaddrupdate(struct vrrp_vrouter *vr) DEBUGD(&vrrp_dbg_auto, VRRP_LOGPFX VRRP_LOGPFX_VRID "Adding %s", vr->vrid, ipbuf); - vrrp_add_ipv6(vr, c->address->u.prefix6, true); + vrrp_add_ipv6(vr, c->address->u.prefix6); } } + vrrp_check_start(vr); + if (vr->v4->addrs->count == 0 && vr->v4->fsm.state != VRRP_STATE_INITIALIZE) { DEBUGD(&vrrp_dbg_auto, @@ -1436,32 +1567,21 @@ vrrp_autoconfig_autocreate(struct interface *mvl_ifp) return vr; } -static bool vrrp_ifp_has_vrrp_mac(struct interface *ifp) -{ - struct ethaddr vmac4; - struct ethaddr vmac6; - vrrp_mac_set(&vmac4, 0, 0x00); - vrrp_mac_set(&vmac6, 1, 0x00); - - return !memcmp(ifp->hw_addr, vmac4.octet, sizeof(vmac4.octet) - 1) - || !memcmp(ifp->hw_addr, vmac6.octet, sizeof(vmac6.octet) - 1); -} - -static struct vrrp_vrouter *vrrp_lookup_by_mvlif(struct interface *mvl_ifp) -{ - struct interface *p; - - if (!mvl_ifp || !mvl_ifp->link_ifindex - || !vrrp_ifp_has_vrrp_mac(mvl_ifp)) - return NULL; - - p = if_lookup_by_index(mvl_ifp->link_ifindex, VRF_DEFAULT); - uint8_t vrid = mvl_ifp->hw_addr[5]; - - return vrrp_lookup(p, vrid); -} - -int vrrp_autoconfig_if_add(struct interface *ifp) +/* + * Callback to notify autoconfig of interface add. + * + * If the interface is a VRRP-compatible device, and there is no existing VRRP + * router running on it, one is created. All addresses on the interface are + * added to the router. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + */ +static int vrrp_autoconfig_if_add(struct interface *ifp) { bool created = false; struct vrrp_vrouter *vr; @@ -1472,7 +1592,7 @@ int vrrp_autoconfig_if_add(struct interface *ifp) if (!ifp || !ifp->link_ifindex || !vrrp_ifp_has_vrrp_mac(ifp)) return -1; - vr = vrrp_lookup_by_mvlif(ifp); + vr = vrrp_lookup_by_if_mvl(ifp); if (!vr) { vr = vrrp_autoconfig_autocreate(ifp); @@ -1485,66 +1605,73 @@ int vrrp_autoconfig_if_add(struct interface *ifp) if (vr->autoconf == false) return 0; else if (!created) { - vrrp_attach_interface(vr->v4); - vrrp_attach_interface(vr->v6); vrrp_autoconfig_autoaddrupdate(vr); } return 0; } -int vrrp_autoconfig_if_del(struct interface *ifp) +/* + * Callback to notify autoconfig of interface delete. + * + * If the interface is a VRRP-compatible device, and a VRRP router is running + * on it, and that VRRP router was automatically configured, it will be + * deleted. If that was the last router for the corresponding VRID (i.e., if + * this interface was a v4 VRRP interface and no v6 router is configured for + * the same VRID) then the entire virtual router is deleted. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + */ +static int vrrp_autoconfig_if_del(struct interface *ifp) { if (!vrrp_autoconfig_is_on) return 0; - struct vrrp_vrouter *vr = vrrp_lookup_by_mvlif(ifp); + struct vrrp_vrouter *vr; + struct listnode *ln; + struct list *vrs; - if (!vr) - return 0; + vrs = vrrp_lookup_by_if_any(ifp); - if (vr && vr->autoconf == false) - return 0; - - if (vr && vr->v4->mvl_ifp == ifp) { - if (vr->v4->fsm.state != VRRP_STATE_INITIALIZE) { + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) + if (vr->autoconf + && (!vr->ifp || (!vr->v4->mvl_ifp && !vr->v6->mvl_ifp))) { DEBUGD(&vrrp_dbg_auto, VRRP_LOGPFX VRRP_LOGPFX_VRID - "Interface %s deleted; shutting down IPv4 VRRP router", - vr->vrid, ifp->name); - vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN); + "All VRRP interfaces for instance deleted; destroying autoconfigured VRRP router", + vr->vrid); + vrrp_vrouter_destroy(vr); } - vr->v4->mvl_ifp = NULL; - } - if (vr && vr->v6->mvl_ifp == ifp) { - if (vr->v6->fsm.state != VRRP_STATE_INITIALIZE) { - DEBUGD(&vrrp_dbg_auto, - VRRP_LOGPFX VRRP_LOGPFX_VRID - "Interface %s deleted; shutting down IPv6 VRRP router", - vr->vrid, ifp->name); - vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN); - } - vr->v6->mvl_ifp = NULL; - } - if (vr->v4->mvl_ifp == NULL && vr->v6->mvl_ifp == NULL) { - DEBUGD(&vrrp_dbg_auto, - VRRP_LOGPFX VRRP_LOGPFX_VRID - "All VRRP interfaces for instance deleted; destroying autoconfigured VRRP router", - vr->vrid); - vrrp_vrouter_destroy(vr); - vr = NULL; - } + list_delete(&vrs); return 0; } -int vrrp_autoconfig_if_up(struct interface *ifp) +/* + * Callback to notify autoconfig of interface up. + * + * Roughly equivalent to vrrp_autoconfig_if_add, except that addresses are + * refreshed if an autoconfigured virtual router already exists. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + */ +static int vrrp_autoconfig_if_up(struct interface *ifp) { if (!vrrp_autoconfig_is_on) return 0; - struct vrrp_vrouter *vr = vrrp_lookup_by_mvlif(ifp); + struct vrrp_vrouter *vr = vrrp_lookup_by_if_mvl(ifp); if (vr && !vr->autoconf) return 0; @@ -1554,14 +1681,27 @@ int vrrp_autoconfig_if_up(struct interface *ifp) return 0; } - vrrp_attach_interface(vr->v4); - vrrp_attach_interface(vr->v6); vrrp_autoconfig_autoaddrupdate(vr); return 0; } -int vrrp_autoconfig_if_down(struct interface *ifp) +/* + * Callback to notify autoconfig of interface down. + * + * Does nothing. An interface down event is accompanied by address deletion + * events for all the addresses on the interface; if an autoconfigured VRRP + * router exists on this interface, then it will have all its addresses deleted + * and end up in Initialize. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + */ +static int vrrp_autoconfig_if_down(struct interface *ifp) { if (!vrrp_autoconfig_is_on) return 0; @@ -1569,12 +1709,27 @@ int vrrp_autoconfig_if_down(struct interface *ifp) return 0; } -int vrrp_autoconfig_if_address_add(struct interface *ifp) +/* + * Callback to notify autoconfig of a new interface address. + * + * If a VRRP router exists on this interface, its address list is updated to + * match the new address list. If no addresses remain, a Shutdown event is + * issued to the VRRP router. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + * + */ +static int vrrp_autoconfig_if_address_add(struct interface *ifp) { if (!vrrp_autoconfig_is_on) return 0; - struct vrrp_vrouter *vr = vrrp_lookup_by_mvlif(ifp); + struct vrrp_vrouter *vr = vrrp_lookup_by_if_mvl(ifp); if (vr && vr->autoconf) vrrp_autoconfig_autoaddrupdate(vr); @@ -1582,12 +1737,27 @@ int vrrp_autoconfig_if_address_add(struct interface *ifp) return 0; } -int vrrp_autoconfig_if_address_del(struct interface *ifp) +/* + * Callback to notify autoconfig of a removed interface address. + * + * If a VRRP router exists on this interface, its address list is updated to + * match the new address list. If no addresses remain, a Shutdown event is + * issued to the VRRP router. + * + * ifp + * Interface to operate on + * + * Returns: + * -1 on failure + * 0 otherwise + * + */ +static int vrrp_autoconfig_if_address_del(struct interface *ifp) { if (!vrrp_autoconfig_is_on) return 0; - struct vrrp_vrouter *vr = vrrp_lookup_by_mvlif(ifp); + struct vrrp_vrouter *vr = vrrp_lookup_by_if_mvl(ifp); if (vr && vr->autoconf) vrrp_autoconfig_autoaddrupdate(vr); @@ -1633,6 +1803,142 @@ void vrrp_autoconfig_off(void) list_delete(&ll); } +/* Interface tracking ------------------------------------------------------ */ + +/* + * Bind any pending interfaces. + * + * mvl_ifp + * macvlan interface that some VRRP instances might want to bind to + */ +static void vrrp_bind_pending(struct interface *mvl_ifp) +{ + struct vrrp_vrouter *vr; + + vr = vrrp_lookup_by_if_mvl(mvl_ifp); + + if (vr) { + if (mvl_ifp->hw_addr[4] == 0x01 && !vr->v4->mvl_ifp) + vrrp_attach_interface(vr->v4); + else if (mvl_ifp->hw_addr[4] == 0x02 && !vr->v6->mvl_ifp) + vrrp_attach_interface(vr->v6); + } +} + +void vrrp_if_up(struct interface *ifp) +{ + struct vrrp_vrouter *vr; + struct listnode *ln; + struct list *vrs; + + vrrp_bind_pending(ifp); + + vrs = vrrp_lookup_by_if_any(ifp); + + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) + vrrp_check_start(vr); + + list_delete(&vrs); + + vrrp_autoconfig_if_up(ifp); +} + +void vrrp_if_down(struct interface *ifp) +{ + struct vrrp_vrouter *vr; + struct listnode *ln; + struct list *vrs; + + vrs = vrrp_lookup_by_if_any(ifp); + + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) { + if (vr->v4->mvl_ifp == ifp || vr->ifp == ifp) { + if (vr->v4->fsm.state != VRRP_STATE_INITIALIZE) { + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Interface %s down; shutting down IPv4 VRRP router", + vr->vrid, ifp->name); + vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN); + } + } + + if (vr->v6->mvl_ifp == ifp || vr->ifp == ifp) { + if (vr->v6->fsm.state != VRRP_STATE_INITIALIZE) { + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Interface %s down; shutting down IPv6 VRRP router", + vr->vrid, ifp->name); + vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN); + } + } + } + + list_delete(&vrs); + + vrrp_autoconfig_if_down(ifp); +} + +void vrrp_if_add(struct interface *ifp) +{ + vrrp_bind_pending(ifp); + + /* thanks, zebra */ + if (CHECK_FLAG(ifp->flags, IFF_UP)) + vrrp_if_up(ifp); + + vrrp_autoconfig_if_add(ifp); +} + +void vrrp_if_del(struct interface *ifp) +{ + struct listnode *ln; + struct vrrp_vrouter *vr; + struct list *vrs = vrrp_lookup_by_if_any(ifp); + + vrrp_if_down(ifp); + + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) { + if (vr->ifp == ifp) + vr->ifp = NULL; + else if (vr->v4->mvl_ifp == ifp) + vr->v4->mvl_ifp = NULL; + else if (vr->v6->mvl_ifp == ifp) + vr->v6->mvl_ifp = NULL; + } + + list_delete(&vrs); + + vrrp_autoconfig_if_del(ifp); +} + +void vrrp_if_address_add(struct interface *ifp) +{ + struct vrrp_vrouter *vr; + struct listnode *ln; + struct list *vrs; + + /* + * We have to do a wide search here, because we need to know when a v6 + * macvlan device gets a new address. This is because the macvlan link + * local is used as the source address for v6 advertisements, and hence + * "do I have a link local" constitutes an activation condition for v6 + * virtual routers. + */ + vrs = vrrp_lookup_by_if_any(ifp); + + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) + vrrp_check_start(vr); + + list_delete(&vrs); + + vrrp_autoconfig_if_address_add(ifp); +} + +void vrrp_if_address_del(struct interface *ifp) +{ + vrrp_autoconfig_if_address_del(ifp); +} + /* Other ------------------------------------------------------------------- */ int vrrp_config_write_interface(struct vty *vty) diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index eabb23fe75..45f4e4ecb7 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -168,6 +168,9 @@ struct vrrp_vrouter { /* Whether this instance was automatically configured */ bool autoconf; + /* Whether this VRRP router is in administrative shutdown */ + bool shutdown; + /* Interface */ struct interface *ifp; @@ -234,6 +237,14 @@ void vrrp_vrouter_destroy(struct vrrp_vrouter *vr); /* Configuration controllers ----------------------------------------------- */ +/* + * Check if a Virtual Router ought to be started, and if so, start it. + * + * vr + * Virtual Router to checkstart + */ +void vrrp_check_start(struct vrrp_vrouter *vr); + /* * Change the configured priority of a VRRP Virtual Router. * @@ -279,7 +290,7 @@ void vrrp_set_advertisement_interval(struct vrrp_vrouter *vr, * -1 on error * 0 otherwise */ -int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip, bool activate); +int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip); /* * Add an IPv4 address to a VRRP Virtual Router. @@ -298,7 +309,7 @@ int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip, bool activate); * -1 on error * 0 otherwise */ -int vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4, bool activate); +int vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4); /* * Add an IPv6 address to a VRRP Virtual Router. @@ -317,7 +328,7 @@ int vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4, bool activate); * -1 on error * 0 otherwise */ -int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6, bool activate); +int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6); /* * Remove an IP address from a VRRP Virtual Router. @@ -338,7 +349,7 @@ int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6, bool activate); * -1 on error * 0 otherwise */ -int vrrp_del_ip(struct vrrp_router *r, struct ipaddr *ip, bool deactivate); +int vrrp_del_ip(struct vrrp_router *r, struct ipaddr *ip); /* * Remove an IPv4 address from a VRRP Virtual Router. @@ -359,7 +370,7 @@ int vrrp_del_ip(struct vrrp_router *r, struct ipaddr *ip, bool deactivate); * -1 on error * 0 otherwise */ -int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4, bool deactivate); +int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4); /* * Remove an IPv6 address from a VRRP Virtual Router. @@ -380,7 +391,7 @@ int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4, bool deactivate); * -1 on error * 0 otherwise */ -int vrrp_del_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6, bool deactivate); +int vrrp_del_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6); /* State machine ----------------------------------------------------------- */ @@ -457,105 +468,14 @@ void vrrp_autoconfig_on(int version); */ void vrrp_autoconfig_off(void); -/* - * Callback to notify autoconfig of interface add. - * - * If the interface is a VRRP-compatible device, and there is no existing VRRP - * router running on it, one is created. All addresses on the interface are - * added to the router. - * - * ifp - * Interface to operate on - * - * Returns: - * -1 on failure - * 0 otherwise - */ -int vrrp_autoconfig_if_add(struct interface *ifp); +/* Interface Tracking ------------------------------------------------------ */ -/* - * Callback to notify autoconfig of interface delete. - * - * If the interface is a VRRP-compatible device, and a VRRP router is running - * on it, and that VRRP router was automatically configured, it will be - * deleted. If that was the last router for the corresponding VRID (i.e., if - * this interface was a v4 VRRP interface and no v6 router is configured for - * the same VRID) then the entire virtual router is deleted. - * - * ifp - * Interface to operate on - * - * Returns: - * -1 on failure - * 0 otherwise - */ -int vrrp_autoconfig_if_del(struct interface *ifp); - -/* - * Callback to notify autoconfig of interface up. - * - * Roughly equivalent to vrrp_autoconfig_if_add, except that addresses are - * refreshed if an autoconfigured virtual router already exists. - * - * ifp - * Interface to operate on - * - * Returns: - * -1 on failure - * 0 otherwise - */ -int vrrp_autoconfig_if_up(struct interface *ifp); - -/* - * Callback to notify autoconfig of interface down. - * - * Does nothing. An interface down event is accompanied by address deletion - * events for all the addresses on the interface; if an autoconfigured VRRP - * router exists on this interface, then it will have all its addresses deleted - * and end up in Initialize. - * - * ifp - * Interface to operate on - * - * Returns: - * -1 on failure - * 0 otherwise - */ -int vrrp_autoconfig_if_down(struct interface *ifp); - -/* - * Callback to notify autoconfig of a new interface address. - * - * If a VRRP router exists on this interface, its address list is updated to - * match the new address list. If no addresses remain, a Shutdown event is - * issued to the VRRP router. - * - * ifp - * Interface to operate on - * - * Returns: - * -1 on failure - * 0 otherwise - * - */ -int vrrp_autoconfig_if_address_add(struct interface *ifp); - -/* - * Callback to notify autoconfig of a removed interface address. - * - * If a VRRP router exists on this interface, its address list is updated to - * match the new address list. If no addresses remain, a Shutdown event is - * issued to the VRRP router. - * - * ifp - * Interface to operate on - * - * Returns: - * -1 on failure - * 0 otherwise - * - */ -int vrrp_autoconfig_if_address_del(struct interface *ifp); +void vrrp_if_add(struct interface *ifp); +void vrrp_if_del(struct interface *ifp); +void vrrp_if_up(struct interface *ifp); +void vrrp_if_down(struct interface *ifp); +void vrrp_if_address_add(struct interface *ifp); +void vrrp_if_address_del(struct interface *ifp); /* Other ------------------------------------------------------------------- */ diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index eacf4a14c3..4f1d5dfa08 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -146,12 +146,14 @@ DEFPY(vrrp_ip, if (no) { int oldstate = vr->v4->fsm.state; - failed = vrrp_del_ipv4(vr, ip, true); + failed = vrrp_del_ipv4(vr, ip); + vrrp_check_start(vr); deactivated = (vr->v4->fsm.state == VRRP_STATE_INITIALIZE && oldstate != VRRP_STATE_INITIALIZE); } else { int oldstate = vr->v4->fsm.state; - failed = vrrp_add_ipv4(vr, ip, true); + failed = vrrp_add_ipv4(vr, ip); + vrrp_check_start(vr); activated = (vr->v4->fsm.state != VRRP_STATE_INITIALIZE && oldstate == VRRP_STATE_INITIALIZE); } @@ -203,12 +205,12 @@ DEFPY(vrrp_ip6, if (no) { int oldstate = vr->v6->fsm.state; - failed = vrrp_del_ipv6(vr, ipv6, true); + failed = vrrp_del_ipv6(vr, ipv6); deactivated = (vr->v6->fsm.state == VRRP_STATE_INITIALIZE && oldstate != VRRP_STATE_INITIALIZE); } else { int oldstate = vr->v6->fsm.state; - failed = vrrp_add_ipv6(vr, ipv6, true); + failed = vrrp_add_ipv6(vr, ipv6); activated = (vr->v6->fsm.state != VRRP_STATE_INITIALIZE && oldstate == VRRP_STATE_INITIALIZE); } diff --git a/vrrpd/vrrp_zebra.c b/vrrpd/vrrp_zebra.c index a4e1158010..1baeb41ba4 100644 --- a/vrrpd/vrrp_zebra.c +++ b/vrrpd/vrrp_zebra.c @@ -61,7 +61,7 @@ static int vrrp_zebra_if_add(int command, struct zclient *zclient, if (!ifp) return 0; - vrrp_autoconfig_if_add(ifp); + vrrp_if_add(ifp); return 0; } @@ -75,7 +75,7 @@ static int vrrp_zebra_if_del(int command, struct zclient *zclient, if (!ifp) return 0; - vrrp_autoconfig_if_del(ifp); + vrrp_if_del(ifp); #if 0 if (VRRP_DEBUG_ZEBRA) { @@ -104,7 +104,7 @@ static int vrrp_zebra_if_state_up(int command, struct zclient *zclient, if (!ifp) return 0; - vrrp_autoconfig_if_up(ifp); + vrrp_if_up(ifp); #if 0 if (VRRP_DEBUG_ZEBRA) { @@ -132,7 +132,7 @@ static int vrrp_zebra_if_state_down(int command, struct zclient *zclient, if (!ifp) return 0; - vrrp_autoconfig_if_down(ifp); + vrrp_if_down(ifp); #if 0 if (VRRP_DEBUG_ZEBRA) { @@ -189,7 +189,7 @@ static int vrrp_zebra_if_address_add(int command, struct zclient *zclient, if (!c) return 0; - vrrp_autoconfig_if_address_add(c->ifp); + vrrp_if_address_add(c->ifp); #if 0 if (VRRP_DEBUG_ZEBRA) { @@ -225,7 +225,7 @@ static int vrrp_zebra_if_address_del(int command, struct zclient *client, if (!c) return 0; - vrrp_autoconfig_if_address_del(c->ifp); + vrrp_if_address_del(c->ifp); return 0; } From 22e4b6a73dd604c65a66a634481e4929a444b6f4 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 14 Feb 2019 23:40:20 +0000 Subject: [PATCH 078/153] vrrpd: only bind to link locals for v6 adverts Was missing a check to make sure that the v6 address we select as the source IP for advertisements was a link local address Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index bf4f0f1ec9..113e42490d 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -851,8 +851,13 @@ static int vrrp_bind_to_primary_connected(struct vrrp_router *r) struct listnode *ln; struct connected *c = NULL; for (ALL_LIST_ELEMENTS_RO(ifp->connected, ln, c)) - if (c->address->family == r->family) - break; + if (c->address->family == r->family) { + if (r->family == AF_INET6 + && IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) + break; + else if (r->family == AF_INET) + break; + } if (c == NULL) { zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID From 45505f63c5ff0e05e49e55393fa7b557a697b48c Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 15 Feb 2019 19:32:08 +0000 Subject: [PATCH 079/153] vrrpd: tweak interface tracking on ifdown Change the interface tracking code to react to an interface down by automatically transitioning to Backup, instead of shutting down the session. This is because we get ZEBRA_INTERFACE_DOWN messages when we set an interface to protodown as part of transitioning to Backup; if we shut down the session in response to these messages, we end up shutting ourselves down every time we try to transition to Backup. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 113e42490d..3c8f3fbfb3 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1203,6 +1203,9 @@ static void vrrp_change_state_backup(struct vrrp_router *r) if (r->family == AF_INET6) vrrp_zebra_radv_set(r, false); + /* Disable Adver_Timer */ + THREAD_OFF(r->t_adver_timer); + vrrp_zclient_send_interface_protodown(r->mvl_ifp, true); } @@ -1402,19 +1405,17 @@ static int vrrp_startup(struct vrrp_router *r) */ static int vrrp_shutdown(struct vrrp_router *r) { + uint8_t saved_prio; + 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; + 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: DEBUGD(&vrrp_dbg_proto, @@ -1425,6 +1426,10 @@ static int vrrp_shutdown(struct vrrp_router *r) break; } + /* Cancel all timers */ + THREAD_OFF(r->t_adver_timer); + THREAD_OFF(r->t_master_down_timer); + if (r->sock_rx > 0) { close(r->sock_rx); r->sock_rx = -1; @@ -1858,22 +1863,22 @@ void vrrp_if_down(struct interface *ifp) for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) { if (vr->v4->mvl_ifp == ifp || vr->ifp == ifp) { - if (vr->v4->fsm.state != VRRP_STATE_INITIALIZE) { + if (vr->v4->fsm.state == VRRP_STATE_MASTER) { DEBUGD(&vrrp_dbg_auto, VRRP_LOGPFX VRRP_LOGPFX_VRID - "Interface %s down; shutting down IPv4 VRRP router", + "Interface %s down; transitioning IPv4 VRRP router to Backup", vr->vrid, ifp->name); - vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN); + vrrp_change_state(vr->v4, VRRP_STATE_BACKUP); } } if (vr->v6->mvl_ifp == ifp || vr->ifp == ifp) { - if (vr->v6->fsm.state != VRRP_STATE_INITIALIZE) { + if (vr->v6->fsm.state == VRRP_STATE_MASTER) { DEBUGD(&vrrp_dbg_auto, VRRP_LOGPFX VRRP_LOGPFX_VRID - "Interface %s down; shutting down IPv6 VRRP router", + "Interface %s down; transitioning IPv6 VRRP router to Backup", vr->vrid, ifp->name); - vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN); + vrrp_change_state(vr->v6, VRRP_STATE_BACKUP); } } } From 114a413efa7bca86645e514cf568a8ac30023bb9 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 15 Feb 2019 22:40:31 +0000 Subject: [PATCH 080/153] vrrpd: check for v6 link-local before starting Having a v6 link-local is a precondition for starting a v6 VRRP router; check that we do. Also add some helpful comments to the check-start function because good lord that thing is getting unwieldy. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 3c8f3fbfb3..5d97199f40 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -245,24 +245,39 @@ void vrrp_check_start(struct vrrp_vrouter *vr) return; r = vr->v4; + /* Must not already be started */ start = r->fsm.state == VRRP_STATE_INITIALIZE; + /* Must have a parent interface */ start = start && (vr->ifp != NULL); + /* Parent interface must be up */ start = start && (CHECK_FLAG(vr->ifp->flags, IFF_UP)); - start = start && vr->ifp->connected->count > 0; + /* Parent interface must have at least one v4 */ + start = start && vr->ifp->connected->count > 1; + /* Must have a macvlan interface */ start = start && (r->mvl_ifp != NULL); + /* Macvlan interface must be up */ start = start && (CHECK_FLAG(r->mvl_ifp->flags, IFF_UP)); + /* Must have at least one VIP configured */ start = start && r->addrs->count > 0; if (start) vrrp_event(r, VRRP_EVENT_STARTUP); r = vr->v6; + /* Must not already be started */ start = r->fsm.state == VRRP_STATE_INITIALIZE; + /* Must have a parent interface */ start = start && (vr->ifp != NULL); + /* Parent interface must be up */ start = start && (CHECK_FLAG(vr->ifp->flags, IFF_UP)); - start = start && vr->ifp->connected->count; + /* Must have a macvlan interface */ start = start && (r->mvl_ifp != NULL); + /* Macvlan interface must be up */ start = start && (CHECK_FLAG(r->mvl_ifp->flags, IFF_UP)); - start = start && (r->mvl_ifp->connected->count > 0); + /* Macvlan interface must have at least two v6 */ + start = start && (r->mvl_ifp->connected->count >= 2); + /* Macvlan interface must have a link local */ + start = start && connected_get_linklocal(r->mvl_ifp); + /* Must have at least one VIP configured */ start = start && r->addrs->count > 0; if (start) vrrp_event(r, VRRP_EVENT_STARTUP); From 89f3420409aa72b0053f5cd0cd2ffd647432bcc7 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 19 Feb 2019 20:11:04 +0000 Subject: [PATCH 081/153] vrrpd: ignore address deletion if iface is down See code comment for explanation Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 5d97199f40..d22c245afe 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1961,7 +1961,42 @@ void vrrp_if_address_add(struct interface *ifp) void vrrp_if_address_del(struct interface *ifp) { - vrrp_autoconfig_if_address_del(ifp); + /* + * Zebra is stupid and sends us address deletion notifications + * when any of the following condition sets are met: + * + * - IFF_UP && address deleted + * - IFF_UP -> !IFF_UP + * + * Note that the second one is nonsense, because Zebra behaves as + * though an interface going down means all the addresses on that + * interface got deleted. Which is a problem for autoconfig because all + * the addresses on an interface going away means the VRRP session goes + * to Initialize. However interfaces go down whenever we transition to + * Backup, so this effectively means that for autoconfigured instances + * we actually end up in Initialize whenever we try to go into Backup. + * + * Also, Zebra does NOT send us notifications when: + * - !IFF_UP && address deleted + * + * Which means if we're in backup and an address is deleted out from + * under us, we won't even know. + * + * The only solution here is to only resynchronize our address list + * when: + * + * - An interfaces comes up + * - An interface address is added + * - An interface address is deleted AND the interface is up + * + * Even though this is only a problem with autoconfig at the moment I'm + * papering over Zebra's braindead semantics here. Every piece of code + * in this function should be protected by a check that the interface + * is up. + */ + if (CHECK_FLAG(ifp->flags, IFF_UP)) { + vrrp_autoconfig_if_address_del(ifp); + } } /* Other ------------------------------------------------------------------- */ From dca8cfccd21a89eaa786da92cbe866537eaf542b Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 19 Feb 2019 21:08:36 +0000 Subject: [PATCH 082/153] vrrpd: enable vrrp zebra debugging Signed-off-by: Quentin Young --- vrrpd/vrrp_zebra.c | 133 +++++++++++++++++++++------------------------ 1 file changed, 63 insertions(+), 70 deletions(-) diff --git a/vrrpd/vrrp_zebra.c b/vrrpd/vrrp_zebra.c index 1baeb41ba4..7a68f1bbcd 100644 --- a/vrrpd/vrrp_zebra.c +++ b/vrrpd/vrrp_zebra.c @@ -27,13 +27,46 @@ #include "lib/zclient.h" #include "vrrp.h" +#include "vrrp_debug.h" #include "vrrp_zebra.h" +#define VRRP_LOGPFX "[ZEBRA] " + static struct zclient *zclient = NULL; +static void vrrp_zebra_debug_if_state(struct interface *ifp, vrf_id_t vrf_id, + const char *func) +{ + DEBUGD(&vrrp_dbg_zebra, + "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", + func, ifp->name, ifp->ifindex, vrf_id, (long)ifp->flags, + ifp->metric, ifp->mtu, if_is_operative(ifp)); +} + +static void vrrp_zebra_debug_if_dump_address(struct interface *ifp, + const char *func) +{ + struct connected *ifc; + struct listnode *node; + + DEBUGD(&vrrp_dbg_zebra, "%s: interface %s addresses:", func, ifp->name); + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + struct prefix *p = ifc->address; + + if (p->family != AF_INET) + continue; + + DEBUGD(&vrrp_dbg_zebra, "%s: interface %s address %s %s", func, + ifp->name, inet_ntoa(p->u.prefix4), + CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? "secondary" + : "primary"); + } +} + + static void vrrp_zebra_connected(struct zclient *zclient) { - fprintf(stderr, "Zclient connected\n"); zclient_send_reg_requests(zclient, VRF_DEFAULT); } @@ -58,9 +91,12 @@ static int vrrp_zebra_if_add(int command, struct zclient *zclient, * interface_add_read below, see comments in lib/zclient.c */ ifp = zebra_interface_add_read(zclient->ibuf, vrf_id); + if (!ifp) return 0; + vrrp_zebra_debug_if_state(ifp, vrf_id, __func__); + vrrp_if_add(ifp); return 0; @@ -72,20 +108,13 @@ static int vrrp_zebra_if_del(int command, struct zclient *zclient, struct interface *ifp; ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); + if (!ifp) return 0; - vrrp_if_del(ifp); + vrrp_zebra_debug_if_state(ifp, vrf_id, __func__); -#if 0 - if (VRRP_DEBUG_ZEBRA) { - zlog_debug( - "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", - __PRETTY_FUNCTION__, ifp->name, ifp->ifindex, vrf_id, - (long)ifp->flags, ifp->metric, ifp->mtu, - if_is_operative(ifp)); - } -#endif + vrrp_if_del(ifp); return 0; } @@ -101,20 +130,13 @@ static int vrrp_zebra_if_state_up(int command, struct zclient *zclient, * zebra_interface_state_read(zclient->ibuf, vrf_id); */ ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); + if (!ifp) return 0; - vrrp_if_up(ifp); + vrrp_zebra_debug_if_state(ifp, vrf_id, __func__); -#if 0 - if (VRRP_DEBUG_ZEBRA) { - zlog_debug( - "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", - __PRETTY_FUNCTION__, ifp->name, ifp->ifindex, vrf_id, - (long)ifp->flags, ifp->metric, ifp->mtu, - if_is_operative(ifp)); - } -#endif + vrrp_if_up(ifp); return 0; } @@ -129,49 +151,17 @@ static int vrrp_zebra_if_state_down(int command, struct zclient *zclient, * zebra_interface_state_read below, see comments in lib/zclient.c */ ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); + if (!ifp) return 0; - vrrp_if_down(ifp); + vrrp_zebra_debug_if_state(ifp, vrf_id, __func__); -#if 0 - if (VRRP_DEBUG_ZEBRA) { - zlog_debug( - "%s: %s index %d(%u) flags %ld metric %d mtu %d operative %d", - __PRETTY_FUNCTION__, ifp->name, ifp->ifindex, vrf_id, - (long)ifp->flags, ifp->metric, ifp->mtu, - if_is_operative(ifp)); - } -#endif + vrrp_if_down(ifp); return 0; } -#ifdef VRRP_DEBUG_IFADDR_DUMP -static void dump_if_address(struct interface *ifp) -{ - struct connected *ifc; - struct listnode *node; - - zlog_debug("%s %s: interface %s addresses:", __FILE__, - __PRETTY_FUNCTION__, ifp->name); - - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { - struct prefix *p = ifc->address; - - if (p->family != AF_INET) - continue; - - zlog_debug("%s %s: interface %s address %s %s", __FILE__, - __PRETTY_FUNCTION__, ifp->name, - inet_ntoa(p->u.prefix4), - CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) - ? "secondary" - : "primary"); - } -} -#endif - static int vrrp_zebra_if_address_add(int command, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id) { @@ -186,25 +176,15 @@ static int vrrp_zebra_if_address_add(int command, struct zclient *zclient, * connected_add_by_prefix() */ c = zebra_interface_address_read(command, zclient->ibuf, vrf_id); + if (!c) return 0; + vrrp_zebra_debug_if_state(c->ifp, vrf_id, __func__); + vrrp_zebra_debug_if_dump_address(c->ifp, __func__); + vrrp_if_address_add(c->ifp); -#if 0 - if (VRRP_DEBUG_ZEBRA) { - char buf[BUFSIZ]; - prefix2str(p, buf, BUFSIZ); - zlog_debug("%s: %s(%u) connected IP address %s flags %u %s", - __PRETTY_FUNCTION__, c->ifp->name, vrf_id, buf, - c->flags, - CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY) - ? "secondary" - : "primary"); - - } -#endif - return 0; } @@ -222,9 +202,13 @@ static int vrrp_zebra_if_address_del(int command, struct zclient *client, * connected_delete_by_prefix() */ c = zebra_interface_address_read(command, client->ibuf, vrf_id); + if (!c) return 0; + vrrp_zebra_debug_if_state(c->ifp, vrf_id, __func__); + vrrp_zebra_debug_if_dump_address(c->ifp, __func__); + vrrp_if_address_del(c->ifp); return 0; @@ -232,12 +216,21 @@ static int vrrp_zebra_if_address_del(int command, struct zclient *client, void vrrp_zebra_radv_set(struct vrrp_router *r, bool enable) { + DEBUGD(&vrrp_dbg_zebra, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Requesting Zebra to turn router advertisements %s for %s", + r->vr->vrid, enable ? "on" : "off", r->mvl_ifp->name); + zclient_send_interface_radv_req(zclient, VRF_DEFAULT, r->mvl_ifp, enable, VRRP_RADV_INT); } int vrrp_zclient_send_interface_protodown(struct interface *ifp, bool down) { + DEBUGD(&vrrp_dbg_zebra, + VRRP_LOGPFX "Requesting Zebra to set %s protodown %s", ifp->name, + down ? "on" : "off"); + return zclient_send_interface_protodown(zclient, VRF_DEFAULT, ifp, down); } From b0ec34c8fd86dc68a969146e3474ae3117c5a156 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 19 Feb 2019 22:01:35 +0000 Subject: [PATCH 083/153] vrrpd: use if_is_operative() Checks for interface usability instead of admin state, which is what I wanted anyway. Also removes the operstate check when binding interfaces. This way we can bind currently inoperative interfaces, won't start until they're at least admin up, but *will* start if they're carrier down, because we can fix that (and probably caused it :) Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index d22c245afe..28ff2fa3c7 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -250,13 +250,13 @@ void vrrp_check_start(struct vrrp_vrouter *vr) /* Must have a parent interface */ start = start && (vr->ifp != NULL); /* Parent interface must be up */ - start = start && (CHECK_FLAG(vr->ifp->flags, IFF_UP)); + start = start && if_is_operative(vr->ifp); /* Parent interface must have at least one v4 */ start = start && vr->ifp->connected->count > 1; /* Must have a macvlan interface */ start = start && (r->mvl_ifp != NULL); - /* Macvlan interface must be up */ - start = start && (CHECK_FLAG(r->mvl_ifp->flags, IFF_UP)); + /* Macvlan interface must be admin up */ + start = start && CHECK_FLAG(r->mvl_ifp->flags, IFF_UP); /* Must have at least one VIP configured */ start = start && r->addrs->count > 0; if (start) @@ -268,11 +268,11 @@ void vrrp_check_start(struct vrrp_vrouter *vr) /* Must have a parent interface */ start = start && (vr->ifp != NULL); /* Parent interface must be up */ - start = start && (CHECK_FLAG(vr->ifp->flags, IFF_UP)); + start = start && if_is_operative(vr->ifp); /* Must have a macvlan interface */ start = start && (r->mvl_ifp != NULL); - /* Macvlan interface must be up */ - start = start && (CHECK_FLAG(r->mvl_ifp->flags, IFF_UP)); + /* Macvlan interface must be admin up */ + start = start && CHECK_FLAG(r->mvl_ifp->flags, IFF_UP); /* Macvlan interface must have at least two v6 */ start = start && (r->mvl_ifp->connected->count >= 2); /* Macvlan interface must have a link local */ @@ -445,8 +445,7 @@ static bool vrrp_attach_interface(struct vrrp_router *r) unsigned int candidates = 0; struct interface *selection = NULL; for (unsigned int i = 0; i < ifps_cnt; i++) { - if (ifps[i]->link_ifindex != r->vr->ifp->ifindex - || !CHECK_FLAG(ifps[i]->flags, IFF_UP)) + if (ifps[i]->link_ifindex != r->vr->ifp->ifindex) ifps[i] = NULL; else { selection = selection ? selection : ifps[i]; @@ -1965,8 +1964,8 @@ void vrrp_if_address_del(struct interface *ifp) * Zebra is stupid and sends us address deletion notifications * when any of the following condition sets are met: * - * - IFF_UP && address deleted - * - IFF_UP -> !IFF_UP + * - if_is_operative && address deleted + * - if_is_operative -> !if_is_operative * * Note that the second one is nonsense, because Zebra behaves as * though an interface going down means all the addresses on that @@ -1977,7 +1976,7 @@ void vrrp_if_address_del(struct interface *ifp) * we actually end up in Initialize whenever we try to go into Backup. * * Also, Zebra does NOT send us notifications when: - * - !IFF_UP && address deleted + * - !if_is_operative && address deleted * * Which means if we're in backup and an address is deleted out from * under us, we won't even know. @@ -1994,7 +1993,7 @@ void vrrp_if_address_del(struct interface *ifp) * in this function should be protected by a check that the interface * is up. */ - if (CHECK_FLAG(ifp->flags, IFF_UP)) { + if (if_is_operative(ifp)) { vrrp_autoconfig_if_address_del(ifp); } } From 8bceffc7c2da33a670723967cf525ab734b124f4 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 19 Feb 2019 22:36:34 +0000 Subject: [PATCH 084/153] vrrpd: don't update interface addrs on ifup Updating interface addresses on autoconfigured VRRP instances when we receive notification that an interface is up will cause us to delete that VRRP instance because Zebra deletes all interface addresses when an interfaces goes down so when it comes back up it has no addresses which causes us to delete the instance, then Zebra subsequently sends us the addresses which causes the instance to get recreated, however in a non-owner scenario this will merely cause us to start in Backup, wait a while, transition to Master, protodown off our interface, get an interface up notification, delete all our ip addresses, destroy ourselves, receive address notifications, recreate ourselves, reenter Backup and cycle through it all over again. So we just have to assume that no addresses went away since this interface was last up. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 6 ++---- vrrpd/vrrp_zebra.c | 3 --- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 28ff2fa3c7..449b2aca7d 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1680,8 +1680,8 @@ static int vrrp_autoconfig_if_del(struct interface *ifp) /* * Callback to notify autoconfig of interface up. * - * Roughly equivalent to vrrp_autoconfig_if_add, except that addresses are - * refreshed if an autoconfigured virtual router already exists. + * Creates VRRP instance on interface if it does not exist. Otherwise does + * nothing. * * ifp * Interface to operate on @@ -1705,8 +1705,6 @@ static int vrrp_autoconfig_if_up(struct interface *ifp) return 0; } - vrrp_autoconfig_autoaddrupdate(vr); - return 0; } diff --git a/vrrpd/vrrp_zebra.c b/vrrpd/vrrp_zebra.c index 7a68f1bbcd..223d61001e 100644 --- a/vrrpd/vrrp_zebra.c +++ b/vrrpd/vrrp_zebra.c @@ -54,9 +54,6 @@ static void vrrp_zebra_debug_if_dump_address(struct interface *ifp, for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { struct prefix *p = ifc->address; - if (p->family != AF_INET) - continue; - DEBUGD(&vrrp_dbg_zebra, "%s: interface %s address %s %s", func, ifp->name, inet_ntoa(p->u.prefix4), CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? "secondary" From f96a183be2ae87b63e5453b0cdea33319b83a9cc Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 19 Feb 2019 23:39:42 +0000 Subject: [PATCH 085/153] vrrpd: add administrative shutdown option Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 3 +++ vrrpd/vrrp_vty.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 449b2aca7d..3a9746d5a9 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -2013,6 +2013,9 @@ int vrrp_config_write_interface(struct vty *vty) vr->version == 2 ? " version 2" : ""); ++writes; + if (vr->shutdown && ++writes) + vty_out(vty, " vrrp %" PRIu8 " shutdown\n", vr->vrid); + if (!vr->preempt_mode && ++writes) vty_out(vty, " no vrrp %" PRIu8 " preempt\n", vr->vrid); diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 4f1d5dfa08..7dcae8d005 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -85,6 +85,34 @@ DEFPY(vrrp_vrid, return CMD_SUCCESS; } +DEFPY(vrrp_shutdown, + vrrp_shutdown_cmd, + "[no] vrrp (1-255)$vrid shutdown", + NO_STR + VRRP_STR + VRRP_VRID_STR + "Force VRRP router into administrative shutdown\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + + struct vrrp_vrouter *vr; + + VROUTER_GET_VTY(vty, ifp, vrid, vr); + + if (!no) { + if (vr->v4->fsm.state != VRRP_STATE_INITIALIZE) + vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN); + if (vr->v6->fsm.state != VRRP_STATE_INITIALIZE) + vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN); + vr->shutdown = true; + } else { + vr->shutdown = false; + vrrp_check_start(vr); + } + + return CMD_SUCCESS; +} + DEFPY(vrrp_priority, vrrp_priority_cmd, "[no] vrrp (1-255)$vrid priority (1-254)", @@ -287,6 +315,7 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) ttable_add_row(tt, "%s|%" PRIu8, "Protocol Version", vr->version); ttable_add_row(tt, "%s|%s", "Autoconfigured", vr->autoconf ? "Yes" : "No"); + ttable_add_row(tt, "%s|%s", "Shutdown", vr->shutdown ? "Yes" : "No"); ttable_add_row(tt, "%s|%s", "Interface", vr->ifp->name); prefix_mac2str(&vr->v4->vmac, ethstr4, sizeof(ethstr4)); prefix_mac2str(&vr->v6->vmac, ethstr6, sizeof(ethstr6)); @@ -435,6 +464,7 @@ void vrrp_vty_init(void) install_element(CONFIG_NODE, &debug_vrrp_cmd); install_element(CONFIG_NODE, &vrrp_autoconfigure_cmd); install_element(INTERFACE_NODE, &vrrp_vrid_cmd); + install_element(INTERFACE_NODE, &vrrp_shutdown_cmd); install_element(INTERFACE_NODE, &vrrp_priority_cmd); install_element(INTERFACE_NODE, &vrrp_advertisement_interval_cmd); install_element(INTERFACE_NODE, &vrrp_ip_cmd); From 929c5fb33ae40ccb30eb986e74716aa9ac130959 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 19 Feb 2019 23:42:06 +0000 Subject: [PATCH 086/153] vrrpd: default to accept mode = true We have no facilities to enforce accept mode = false yet so this is updated to reflect the actual state of the system. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 3a9746d5a9..e0f9b5286b 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -533,7 +533,7 @@ struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid, vr->vrid = vrid; vr->priority = VRRP_DEFAULT_PRIORITY; vr->preempt_mode = true; - vr->accept_mode = false; + vr->accept_mode = true; vr->v4 = vrrp_router_create(vr, AF_INET); vr->v6 = vrrp_router_create(vr, AF_INET6); From e91c9247eb43ea5b743ead19edc323bd679c0cd5 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 20 Feb 2019 19:04:34 +0000 Subject: [PATCH 087/153] lib: add more convenient boolean adder for json Signed-off-by: Quentin Young --- lib/json.c | 5 +++++ lib/json.h | 1 + 2 files changed, 6 insertions(+) diff --git a/lib/json.c b/lib/json.c index 4ea20ba178..efc3794040 100644 --- a/lib/json.c +++ b/lib/json.c @@ -64,6 +64,11 @@ void json_object_boolean_true_add(struct json_object *obj, const char *key) json_object_object_add(obj, key, json_object_new_boolean(1)); } +void json_object_boolean_add(struct json_object *obj, const char *key, bool val) +{ + json_object_object_add(obj, key, json_object_new_boolean(val)); +} + struct json_object *json_object_lock(struct json_object *obj) { return json_object_get(obj); diff --git a/lib/json.h b/lib/json.h index a5251662be..b35f221b99 100644 --- a/lib/json.h +++ b/lib/json.h @@ -61,6 +61,7 @@ extern void json_object_string_add(struct json_object *obj, const char *key, const char *s); extern void json_object_int_add(struct json_object *obj, const char *key, int64_t i); +void json_object_boolean_add(struct json_object *obj, const char *key, bool val); extern void json_object_boolean_false_add(struct json_object *obj, const char *key); extern void json_object_boolean_true_add(struct json_object *obj, From 6e21b5ae8ca31fd103a9e31e041780eb1f4d46df Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 20 Feb 2019 19:06:49 +0000 Subject: [PATCH 088/153] vrrpd: add json support to show command Signed-off-by: Quentin Young --- vrrpd/vrrp_vty.c | 101 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 4 deletions(-) diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 7dcae8d005..f382c44190 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -22,6 +22,7 @@ #include "lib/command.h" #include "lib/if.h" #include "lib/ipaddr.h" +#include "lib/json.h" #include "lib/prefix.h" #include "lib/termtable.h" #include "lib/vty.h" @@ -299,6 +300,87 @@ DEFPY(vrrp_autoconfigure, return CMD_SUCCESS; } +/* + * Build JSON representation of VRRP instance. + * + * vr + * VRRP router to build json object from + * + * Returns: + * JSON representation of VRRP instance. Must be freed by caller. + */ +static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr) +{ + char ethstr4[ETHER_ADDR_STRLEN]; + char ethstr6[ETHER_ADDR_STRLEN]; + char ipstr[INET6_ADDRSTRLEN]; + const char *stastr4 = vrrp_state_names[vr->v4->fsm.state]; + const char *stastr6 = vrrp_state_names[vr->v6->fsm.state]; + struct listnode *ln; + struct ipaddr *ip; + struct json_object *j = json_object_new_object(); + struct json_object *v4 = json_object_new_object(); + struct json_object *v4_addrs = json_object_new_array(); + struct json_object *v6 = json_object_new_object(); + struct json_object *v6_addrs = json_object_new_array(); + + prefix_mac2str(&vr->v4->vmac, ethstr4, sizeof(ethstr4)); + prefix_mac2str(&vr->v6->vmac, ethstr6, sizeof(ethstr6)); + + json_object_int_add(j, "vrid", vr->vrid); + json_object_int_add(j, "version", vr->version); + json_object_boolean_add(j, "autoconfigured", vr->autoconf); + json_object_boolean_add(j, "shutdown", vr->shutdown); + json_object_boolean_add(j, "preempt_mode", vr->preempt_mode); + json_object_boolean_add(j, "accept_mode", vr->accept_mode); + json_object_string_add(j, "interface", vr->ifp->name); + /* v4 */ + json_object_string_add(v4, "interface", + vr->v4->mvl_ifp ? vr->v4->mvl_ifp->name : ""); + json_object_string_add(v4, "vmac", ethstr4); + json_object_string_add(v4, "status", stastr4); + json_object_int_add(v4, "effective_priority", vr->v4->priority); + json_object_int_add(v4, "master_adver_interval", + vr->v4->master_adver_interval); + json_object_int_add(v4, "skew_time", vr->v4->skew_time); + json_object_int_add(v4, "master_down_interval", + vr->v4->master_down_interval); + if (vr->v4->addrs->count) { + for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ln, ip)) { + inet_ntop(vr->v4->family, &ip->ipaddr_v4, ipstr, + sizeof(ipstr)); + json_object_array_add(v4_addrs, + json_object_new_string(ipstr)); + } + } + json_object_object_add(v4, "addresses", v4_addrs); + json_object_object_add(j, "v4", v4); + + /* v6 */ + json_object_string_add(v6, "interface", + vr->v6->mvl_ifp ? vr->v6->mvl_ifp->name : ""); + json_object_string_add(v6, "vmac", ethstr6); + json_object_string_add(v6, "status", stastr6); + json_object_int_add(v6, "effective_priority", vr->v6->priority); + json_object_int_add(v6, "master_adver_interval", + vr->v6->master_adver_interval); + json_object_int_add(v6, "skew_time", vr->v6->skew_time); + json_object_int_add(v6, "master_down_interval", + vr->v6->master_down_interval); + if (vr->v6->addrs->count) { + for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ln, ip)) { + inet_ntop(vr->v6->family, &ip->ipaddr_v6, ipstr, + sizeof(ipstr)); + json_object_array_add(v6_addrs, + json_object_new_string(ipstr)); + } + } + json_object_object_add(v6, "addresses", v6_addrs); + json_object_object_add(j, "v6", v6); + + return j; +} + static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) { char ethstr4[ETHER_ADDR_STRLEN]; @@ -375,21 +457,22 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) vty_out(vty, "\n%s\n", table); XFREE(MTYPE_TMP, table); ttable_del(tt); - } DEFPY(vrrp_vrid_show, vrrp_vrid_show_cmd, - "show vrrp [interface INTERFACE$ifn] [(1-255)$vrid]", + "show vrrp [interface INTERFACE$ifn] [(1-255)$vrid] [json$json]", SHOW_STR VRRP_STR INTERFACE_STR "Only show VRRP instances on this interface\n" - VRRP_VRID_STR) + VRRP_VRID_STR + JSON_STR) { struct vrrp_vrouter *vr; struct listnode *ln; struct list *ll = hash_to_list(vrrp_vrouters_hash); + struct json_object *j = json_object_new_array(); for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) { if (ifn && !strmatch(ifn, vr->ifp->name)) @@ -397,9 +480,19 @@ DEFPY(vrrp_vrid_show, if (vrid && vrid != vr->vrid) continue; - vrrp_show(vty, vr); + if (!json) + vrrp_show(vty, vr); + else + json_object_array_add(j, vrrp_build_json(vr)); } + if (json) + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + j, JSON_C_TO_STRING_PRETTY)); + + json_object_free(j); + list_delete(&ll); return CMD_SUCCESS; From 789ce3af2c479acb28d483e197f8604f65d6e449 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 20 Feb 2019 19:07:13 +0000 Subject: [PATCH 089/153] vrrpd: cleanup formatting for vty show cmd Signed-off-by: Quentin Young --- vrrpd/vrrp_vty.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index f382c44190..45a1373e23 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -381,6 +381,15 @@ static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr) return j; } +/* + * Dump VRRP instance status to VTY. + * + * vty + * vty to dump to + * + * vr + * VRRP router to dump + */ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) { char ethstr4[ETHER_ADDR_STRLEN]; @@ -418,17 +427,21 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) vr->preempt_mode ? "Yes" : "No"); ttable_add_row(tt, "%s|%s", "Accept Mode", vr->accept_mode ? "Yes" : "No"); - ttable_add_row(tt, "%s|%" PRIu16" cs", "Advertisement Interval", + ttable_add_row(tt, "%s|%" PRIu16 " cs", "Advertisement Interval", vr->advertisement_interval); - ttable_add_row(tt, "%s|%" PRIu16" cs", "Master Advertisement Interval (v4)", + 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)", + 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)", + 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)", + 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); From 62475ecd43b5394aef378ac09949512235bf0c17 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 21 Feb 2019 16:36:58 +0000 Subject: [PATCH 090/153] vrrpd: stop session before nulling iface When an interface is deleted from the system, stop any attached VRRP sessions before nulling out the interface fields. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index e0f9b5286b..92ed12e239 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1920,12 +1920,15 @@ void vrrp_if_del(struct interface *ifp) vrrp_if_down(ifp); for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) { - if (vr->ifp == ifp) - vr->ifp = NULL; - else if (vr->v4->mvl_ifp == ifp) + if ((vr->v4->mvl_ifp == ifp || vr->ifp == ifp) + && vr->v4->fsm.state != VRRP_STATE_INITIALIZE) { + vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN); vr->v4->mvl_ifp = NULL; - else if (vr->v6->mvl_ifp == ifp) + } else if ((vr->v6->mvl_ifp == ifp || vr->ifp == ifp) + && vr->v6->fsm.state != VRRP_STATE_INITIALIZE) { + vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN); vr->v6->mvl_ifp = NULL; + } } list_delete(&vrs); From c4485ad5aaade4b6f68b932a9be35b7f911b50cf Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 21 Feb 2019 22:42:55 +0000 Subject: [PATCH 091/153] vrrpd: do not transition to backup on ifdown Transitioning to backup on an interface down causes all sorts of problems when it comes back up, not least of which is breaking preempt mode. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 92ed12e239..da6f5ba4d9 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1874,24 +1874,11 @@ void vrrp_if_down(struct interface *ifp) vrs = vrrp_lookup_by_if_any(ifp); for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) { - if (vr->v4->mvl_ifp == ifp || vr->ifp == ifp) { - if (vr->v4->fsm.state == VRRP_STATE_MASTER) { - DEBUGD(&vrrp_dbg_auto, - VRRP_LOGPFX VRRP_LOGPFX_VRID - "Interface %s down; transitioning IPv4 VRRP router to Backup", - vr->vrid, ifp->name); - vrrp_change_state(vr->v4, VRRP_STATE_BACKUP); - } - } - - if (vr->v6->mvl_ifp == ifp || vr->ifp == ifp) { - if (vr->v6->fsm.state == VRRP_STATE_MASTER) { - DEBUGD(&vrrp_dbg_auto, - VRRP_LOGPFX VRRP_LOGPFX_VRID - "Interface %s down; transitioning IPv6 VRRP router to Backup", - vr->vrid, ifp->name); - vrrp_change_state(vr->v6, VRRP_STATE_BACKUP); - } + if (vr->ifp == ifp || vr->v4->mvl_ifp == ifp + || vr->v6->mvl_ifp == ifp) { + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID "Interface %s down", + vr->vrid, ifp->name); } } From b1d7f513aeaf8af37f533d877c7369c30ff3a6e6 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 22 Feb 2019 18:51:10 +0000 Subject: [PATCH 092/153] vrrpd: follow frrouting json conventions Use camelCase for json keys Signed-off-by: Quentin Young --- vrrpd/vrrp_vty.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 45a1373e23..4fe65b2ceb 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -331,19 +331,19 @@ static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr) json_object_int_add(j, "version", vr->version); json_object_boolean_add(j, "autoconfigured", vr->autoconf); json_object_boolean_add(j, "shutdown", vr->shutdown); - json_object_boolean_add(j, "preempt_mode", vr->preempt_mode); - json_object_boolean_add(j, "accept_mode", vr->accept_mode); + json_object_boolean_add(j, "preemptMode", vr->preempt_mode); + json_object_boolean_add(j, "acceptMode", vr->accept_mode); json_object_string_add(j, "interface", vr->ifp->name); /* v4 */ json_object_string_add(v4, "interface", vr->v4->mvl_ifp ? vr->v4->mvl_ifp->name : ""); json_object_string_add(v4, "vmac", ethstr4); json_object_string_add(v4, "status", stastr4); - json_object_int_add(v4, "effective_priority", vr->v4->priority); - json_object_int_add(v4, "master_adver_interval", + json_object_int_add(v4, "effectivePriority", vr->v4->priority); + json_object_int_add(v4, "masterAdverInterval", vr->v4->master_adver_interval); - json_object_int_add(v4, "skew_time", vr->v4->skew_time); - json_object_int_add(v4, "master_down_interval", + json_object_int_add(v4, "skewTime", vr->v4->skew_time); + json_object_int_add(v4, "masterDownInterval", vr->v4->master_down_interval); if (vr->v4->addrs->count) { for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ln, ip)) { @@ -361,11 +361,11 @@ static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr) vr->v6->mvl_ifp ? vr->v6->mvl_ifp->name : ""); json_object_string_add(v6, "vmac", ethstr6); json_object_string_add(v6, "status", stastr6); - json_object_int_add(v6, "effective_priority", vr->v6->priority); - json_object_int_add(v6, "master_adver_interval", + json_object_int_add(v6, "effectivePriority", vr->v6->priority); + json_object_int_add(v6, "masterAdverInterval", vr->v6->master_adver_interval); - json_object_int_add(v6, "skew_time", vr->v6->skew_time); - json_object_int_add(v6, "master_down_interval", + json_object_int_add(v6, "skewTime", vr->v6->skew_time); + json_object_int_add(v6, "masterDownInterval", vr->v6->master_down_interval); if (vr->v6->addrs->count) { for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ln, ip)) { From 6332c77f98481d94c9243cbe6ffb57d131cf9d6a Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 22 Feb 2019 18:51:38 +0000 Subject: [PATCH 093/153] vrrpd: add statistics collection Collect and display the following: - Advertisement Tx/Rx - GARP Tx/Rx - NDISC Tx/Rx - # transitions Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 6 ++++++ vrrpd/vrrp.h | 12 ++++++++++++ vrrpd/vrrp_arp.c | 2 ++ vrrpd/vrrp_ndisc.c | 2 ++ vrrpd/vrrp_vty.c | 37 +++++++++++++++++++++++++++++++++++++ 5 files changed, 59 insertions(+) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index da6f5ba4d9..89de0525dc 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -604,6 +604,8 @@ static void vrrp_send_advertisement(struct vrrp_router *r) zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID "Failed to send VRRP Advertisement: %s", r->vr->vrid, safe_strerror(errno)); + } else { + ++r->stats.adver_tx_cnt; } } @@ -690,6 +692,8 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, r->vr->vrid, family2str(r->family), pkt->hdr.naddr, r->addrs->count); + ++r->stats.adver_rx_cnt; + int addrcmp; switch (r->fsm.state) { @@ -1270,6 +1274,8 @@ static void vrrp_change_state(struct vrrp_router *r, int 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; + + ++r->stats.trans_cnt; } /* diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 45f4e4ecb7..94cd1ca3f6 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -141,6 +141,18 @@ struct vrrp_router { int state; } fsm; + struct { + /* Total number of advertisements sent and received */ + uint32_t adver_tx_cnt; + uint32_t adver_rx_cnt; + /* Total number of gratuitous ARPs sent */ + uint32_t garp_tx_cnt; + /* Total number of unsolicited Neighbor Advertisements sent */ + uint32_t una_tx_cnt; + /* Total number of state transitions */ + uint32_t trans_cnt; + } stats; + struct thread *t_master_down_timer; struct thread *t_adver_timer; struct thread *t_read; diff --git a/vrrpd/vrrp_arp.c b/vrrpd/vrrp_arp.c index 018f0d7696..006f31a95b 100644 --- a/vrrpd/vrrp_arp.c +++ b/vrrpd/vrrp_arp.c @@ -147,6 +147,8 @@ void vrrp_garp_send(struct vrrp_router *r, struct in_addr *v4) zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID "Error sending gratuitous ARP on %s for %s", r->vr->vrid, ifp->name, astr); + else + ++r->stats.garp_tx_cnt; } void vrrp_garp_send_all(struct vrrp_router *r) diff --git a/vrrpd/vrrp_ndisc.c b/vrrpd/vrrp_ndisc.c index 7b3fdde7c4..6813506d1f 100644 --- a/vrrpd/vrrp_ndisc.c +++ b/vrrpd/vrrp_ndisc.c @@ -187,6 +187,8 @@ int vrrp_ndisc_una_send(struct vrrp_router *r, struct ipaddr *ip) "Error sending unsolicited Neighbor Advertisement on %s for %s", r->vr->vrid, ifp->name, ipbuf); ret = -1; + } else { + ++r->stats.una_tx_cnt; } return ret; diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 4fe65b2ceb..426aac4cd2 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -300,6 +300,8 @@ DEFPY(vrrp_autoconfigure, return CMD_SUCCESS; } +/* clang-format on */ + /* * Build JSON representation of VRRP instance. * @@ -320,8 +322,10 @@ static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr) struct ipaddr *ip; struct json_object *j = json_object_new_object(); struct json_object *v4 = json_object_new_object(); + struct json_object *v4_stats = json_object_new_object(); struct json_object *v4_addrs = json_object_new_array(); struct json_object *v6 = json_object_new_object(); + struct json_object *v6_stats = json_object_new_object(); struct json_object *v6_addrs = json_object_new_array(); prefix_mac2str(&vr->v4->vmac, ethstr4, sizeof(ethstr4)); @@ -345,6 +349,13 @@ static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr) json_object_int_add(v4, "skewTime", vr->v4->skew_time); json_object_int_add(v4, "masterDownInterval", vr->v4->master_down_interval); + /* v4 stats */ + json_object_int_add(v4_stats, "adverTx", vr->v4->stats.adver_tx_cnt); + json_object_int_add(v4_stats, "adverRx", vr->v4->stats.adver_rx_cnt); + json_object_int_add(v4_stats, "garpTx", vr->v4->stats.garp_tx_cnt); + json_object_int_add(v4_stats, "transitions", vr->v4->stats.trans_cnt); + json_object_object_add(v4, "stats", v4_stats); + /* v4 addrs */ if (vr->v4->addrs->count) { for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ln, ip)) { inet_ntop(vr->v4->family, &ip->ipaddr_v4, ipstr, @@ -367,6 +378,14 @@ static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr) json_object_int_add(v6, "skewTime", vr->v6->skew_time); json_object_int_add(v6, "masterDownInterval", vr->v6->master_down_interval); + /* v6 stats */ + json_object_int_add(v6_stats, "adverTx", vr->v6->stats.adver_tx_cnt); + json_object_int_add(v6_stats, "adverRx", vr->v6->stats.adver_rx_cnt); + json_object_int_add(v6_stats, "neighborAdverTx", + vr->v6->stats.una_tx_cnt); + json_object_int_add(v6_stats, "transitions", vr->v6->stats.trans_cnt); + json_object_object_add(v6, "stats", v6_stats); + /* v6 addrs */ if (vr->v6->addrs->count) { for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ln, ip)) { inet_ntop(vr->v6->family, &ip->ipaddr_v6, ipstr, @@ -435,6 +454,22 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) ttable_add_row(tt, "%s|%" PRIu16 " cs", "Master Advertisement Interval (v6)", vr->v6->master_adver_interval); + ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Tx (v4)", + vr->v4->stats.adver_tx_cnt); + ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Tx (v6)", + vr->v6->stats.adver_tx_cnt); + ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Rx (v4)", + vr->v4->stats.adver_rx_cnt); + ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Rx (v6)", + vr->v6->stats.adver_rx_cnt); + ttable_add_row(tt, "%s|%" PRIu32, "Gratuitous ARP Tx (v4)", + vr->v4->stats.garp_tx_cnt); + ttable_add_row(tt, "%s|%" PRIu32, "Neigh. Adverts Tx (v6)", + vr->v6->stats.una_tx_cnt); + ttable_add_row(tt, "%s|%" PRIu32, "State transitions (v4)", + vr->v4->stats.trans_cnt); + ttable_add_row(tt, "%s|%" PRIu32, "State transitions (v6)", + vr->v6->stats.trans_cnt); ttable_add_row(tt, "%s|%" PRIu16 " cs", "Skew Time (v4)", vr->v4->skew_time); ttable_add_row(tt, "%s|%" PRIu16 " cs", "Skew Time (v6)", @@ -472,6 +507,8 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) ttable_del(tt); } +/* clang-format off */ + DEFPY(vrrp_vrid_show, vrrp_vrid_show_cmd, "show vrrp [interface INTERFACE$ifn] [(1-255)$vrid] [json$json]", From 8cd1d2779da8c2188e91c518fcbc18740086e300 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 25 Feb 2019 21:43:36 +0000 Subject: [PATCH 094/153] vrrpd: allow configuring global defaults Allow configuring the following as global defaults: - Priority - Advertisement interval - Preempt mode - Administrative shutdown Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 63 +++++++++++++++++++++++++++++++++++++----------- vrrpd/vrrp.h | 20 +++++++++++++-- vrrpd/vrrp_vty.c | 31 ++++++++++++++++++++++-- 3 files changed, 96 insertions(+), 18 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 89de0525dc..06c3bbfd10 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -46,6 +46,8 @@ struct hash *vrrp_vrouters_hash; bool vrrp_autoconfig_is_on; int vrrp_autoconfig_version; +struct vrrp_defaults vd; + const char *vrrp_state_names[3] = { [VRRP_STATE_INITIALIZE] = "Initialize", [VRRP_STATE_MASTER] = "Master", @@ -531,14 +533,15 @@ struct vrrp_vrouter *vrrp_vrouter_create(struct interface *ifp, uint8_t vrid, vr->ifp = ifp; vr->version = version; vr->vrid = vrid; - vr->priority = VRRP_DEFAULT_PRIORITY; - vr->preempt_mode = true; - vr->accept_mode = true; + vr->priority = vd.priority; + vr->preempt_mode = vd.preempt_mode; + vr->accept_mode = vd.accept_mode; + vr->shutdown = vd.shutdown; 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_set_advertisement_interval(vr, vd.advertisement_interval); hash_get(vrrp_vrouters_hash, vr, hash_alloc_intern); @@ -2009,23 +2012,26 @@ int vrrp_config_write_interface(struct vty *vty) vr->version == 2 ? " version 2" : ""); ++writes; - if (vr->shutdown && ++writes) - vty_out(vty, " vrrp %" PRIu8 " shutdown\n", vr->vrid); + if (vr->shutdown != vd.shutdown && ++writes) + vty_out(vty, " %svrrp %" PRIu8 " shutdown\n", + vr->shutdown ? "" : "no ", vr->vrid); - if (!vr->preempt_mode && ++writes) - vty_out(vty, " no vrrp %" PRIu8 " preempt\n", vr->vrid); + if (vr->preempt_mode != vd.preempt_mode && ++writes) + vty_out(vty, " %svrrp %" PRIu8 " preempt\n", + vr->preempt_mode ? "" : "no ", vr->vrid); - if (vr->accept_mode && ++writes) - vty_out(vty, " vrrp %" PRIu8 " accept\n", vr->vrid); + if (vr->accept_mode != vd.accept_mode && ++writes) + vty_out(vty, " %svrrp %" PRIu8 " accept\n", + vr->accept_mode ? "" : "no ", vr->vrid); - if (vr->advertisement_interval != VRRP_DEFAULT_ADVINT + if (vr->advertisement_interval != vd.advertisement_interval && ++writes) vty_out(vty, " vrrp %" PRIu8 " advertisement-interval %" PRIu16 "\n", vr->vrid, vr->advertisement_interval); - if (vr->priority != VRRP_DEFAULT_PRIORITY && ++writes) + if (vr->priority != vd.priority && ++writes) vty_out(vty, " vrrp %" PRIu8 " priority %" PRIu8 "\n", vr->vrid, vr->priority); @@ -2053,11 +2059,33 @@ int vrrp_config_write_interface(struct vty *vty) int vrrp_config_write_global(struct vty *vty) { - if (vrrp_autoconfig_is_on) + unsigned int writes = 0; + + if (vrrp_autoconfig_is_on && ++writes) vty_out(vty, "vrrp autoconfigure%s\n", vrrp_autoconfig_version == 2 ? " version 2" : ""); - return 1; + if (vd.priority != VRRP_DEFAULT_PRIORITY && ++writes) + vty_out(vty, "vrrp default priority %" PRIu8 "\n", vd.priority); + + if (vd.advertisement_interval != VRRP_DEFAULT_ADVINT && ++writes) + vty_out(vty, + "vrrp default advertisement-interval %" PRIu16 "\n", + vd.advertisement_interval); + + if (vd.preempt_mode != VRRP_DEFAULT_PREEMPT && ++writes) + vty_out(vty, "%svrrp default preempt\n", + !vd.preempt_mode ? "no " : ""); + + if (vd.accept_mode != VRRP_DEFAULT_ACCEPT && ++writes) + vty_out(vty, "%svrrp default accept\n", + !vd.accept_mode ? "no " : ""); + + if (vd.shutdown != VRRP_DEFAULT_SHUTDOWN && ++writes) + vty_out(vty, "%svrrp default shutdown\n", + !vd.shutdown ? "no " : ""); + + return writes; } static unsigned int vrrp_hash_key(void *arg) @@ -2085,6 +2113,13 @@ static bool vrrp_hash_cmp(const void *arg1, const void *arg2) void vrrp_init(void) { + /* Set default defaults */ + vd.priority = VRRP_DEFAULT_PRIORITY; + vd.advertisement_interval = VRRP_DEFAULT_ADVINT; + vd.preempt_mode = VRRP_DEFAULT_PREEMPT; + vd.accept_mode = VRRP_DEFAULT_ACCEPT; + vd.shutdown = VRRP_DEFAULT_SHUTDOWN; + vrrp_autoconfig_version = 3; vrrp_vrouters_hash = hash_create(&vrrp_hash_key, vrrp_hash_cmp, "VRRP virtual router hash"); diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 94cd1ca3f6..4ea1a37377 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -33,8 +33,6 @@ #include "lib/vty.h" /* Global definitions */ -#define VRRP_DEFAULT_ADVINT 100 -#define VRRP_DEFAULT_PRIORITY 100 #define VRRP_RADV_INT 16 #define VRRP_PRIO_MASTER 255 #define VRRP_MCASTV4_GROUP_STR "224.0.0.18" @@ -45,6 +43,24 @@ #define VRRP_LOGPFX_VRID "[VRID: %u] " +/* Default defaults */ +#define VRRP_DEFAULT_PRIORITY 100 +#define VRRP_DEFAULT_ADVINT 100 +#define VRRP_DEFAULT_PREEMPT true +#define VRRP_DEFAULT_ACCEPT true +#define VRRP_DEFAULT_SHUTDOWN false + +/* Configured defaults */ +struct vrrp_defaults { + uint8_t priority; + uint16_t advertisement_interval; + bool preempt_mode; + bool accept_mode; + bool shutdown; +}; + +extern struct vrrp_defaults vd; + /* threadmaster */ extern struct thread_master *master; diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 426aac4cd2..daf373d394 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -126,7 +126,7 @@ DEFPY(vrrp_priority, VTY_DECLVAR_CONTEXT(interface, ifp); struct vrrp_vrouter *vr; - uint8_t newprio = no ? VRRP_DEFAULT_PRIORITY : priority; + uint8_t newprio = no ? vd.priority : priority; VROUTER_GET_VTY(vty, ifp, vrid, vr); @@ -144,7 +144,8 @@ DEFPY(vrrp_advertisement_interval, VTY_DECLVAR_CONTEXT(interface, ifp); struct vrrp_vrouter *vr; - uint16_t newadvint = no ? VRRP_DEFAULT_ADVINT : advertisement_interval; + uint16_t newadvint = no ? vd.advertisement_interval : + advertisement_interval; VROUTER_GET_VTY(vty, ifp, vrid, vr); vrrp_set_advertisement_interval(vr, newadvint); @@ -300,6 +301,31 @@ DEFPY(vrrp_autoconfigure, return CMD_SUCCESS; } +DEFPY(vrrp_default, + vrrp_default_cmd, + "[no] vrrp default ", + NO_STR + VRRP_STR + "Configure defaults for new VRRP instances\n" + VRRP_ADVINT_STR + "Advertisement interval in centiseconds\n" + "Preempt mode\n" + VRRP_PRIORITY_STR + "Priority value\n" + "Force VRRP router into administrative shutdown\n") +{ + if (adv) + vd.advertisement_interval = no ? VRRP_DEFAULT_ADVINT : advint; + if (p) + vd.preempt_mode = !no; + if (prio) + vd.priority = no ? VRRP_DEFAULT_PRIORITY : prioval; + if (s) + vd.shutdown = !no; + + return CMD_SUCCESS; +} + /* clang-format on */ /* @@ -606,6 +632,7 @@ void vrrp_vty_init(void) install_element(VIEW_NODE, &debug_vrrp_cmd); install_element(CONFIG_NODE, &debug_vrrp_cmd); install_element(CONFIG_NODE, &vrrp_autoconfigure_cmd); + install_element(CONFIG_NODE, &vrrp_default_cmd); install_element(INTERFACE_NODE, &vrrp_vrid_cmd); install_element(INTERFACE_NODE, &vrrp_shutdown_cmd); install_element(INTERFACE_NODE, &vrrp_priority_cmd); From 354b49d6d9433c3e5b57fb76e8ce8b72c9388d26 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 26 Feb 2019 18:49:11 +0000 Subject: [PATCH 095/153] vrrpd: style cleanup Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 12 ++++++------ vrrpd/vrrp_arp.c | 9 +++++---- vrrpd/vrrp_main.c | 2 +- vrrpd/vrrp_ndisc.c | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 06c3bbfd10..20cec70063 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -478,7 +478,6 @@ static bool vrrp_attach_interface(struct vrrp_router *r) r->mvl_ifp = selection; return !!r->mvl_ifp; - } static struct vrrp_router *vrrp_router_create(struct vrrp_vrouter *vr, @@ -594,8 +593,8 @@ static void vrrp_send_advertisement(struct vrrp_router *r) if (DEBUG_MODE_CHECK(&vrrp_dbg_pkt, DEBUG_MODE_ALL)) zlog_hexdump(pkt, (size_t)pktsz); - const char *group = - r->family == AF_INET ? VRRP_MCASTV4_GROUP_STR : VRRP_MCASTV6_GROUP_STR; + const char *group = r->family == AF_INET ? VRRP_MCASTV4_GROUP_STR + : VRRP_MCASTV6_GROUP_STR; str2sockunion(group, &dest); ssize_t sent = sendto(r->sock_tx, pkt, (size_t)pktsz, 0, &dest.sa, @@ -711,7 +710,8 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, r->vr->advertisement_interval * 10, &r->t_adver_timer); } else if (pkt->hdr.priority > r->priority - || ((pkt->hdr.priority == r->priority) && addrcmp > 0)) { + || ((pkt->hdr.priority == r->priority) + && addrcmp > 0)) { zlog_info( VRRP_LOGPFX VRRP_LOGPFX_VRID "Received advertisement from %s w/ priority %" PRIu8 @@ -1520,8 +1520,8 @@ static void vrrp_autoconfig_autoaddrupdate(struct vrrp_vrouter *vr) vr->vrid, vr->v4->mvl_ifp->name); for (ALL_LIST_ELEMENTS_RO(vr->v4->mvl_ifp->connected, ln, c)) if (c->address->family == AF_INET) { - inet_ntop(AF_INET, &c->address->u.prefix4, ipbuf, - sizeof(ipbuf)); + inet_ntop(AF_INET, &c->address->u.prefix4, + ipbuf, sizeof(ipbuf)); DEBUGD(&vrrp_dbg_auto, VRRP_LOGPFX VRRP_LOGPFX_VRID "Adding %s", vr->vrid, ipbuf); diff --git a/vrrpd/vrrp_arp.c b/vrrpd/vrrp_arp.c index 006f31a95b..dd3c635beb 100644 --- a/vrrpd/vrrp_arp.c +++ b/vrrpd/vrrp_arp.c @@ -50,7 +50,8 @@ static int garp_fd = -1; /* Send the gratuitous ARP message */ -static ssize_t vrrp_send_garp(struct interface *ifp, uint8_t *buf, ssize_t pack_len) +static ssize_t vrrp_send_garp(struct interface *ifp, uint8_t *buf, + ssize_t pack_len) { struct sockaddr_ll sll; ssize_t len; @@ -59,7 +60,7 @@ static ssize_t vrrp_send_garp(struct interface *ifp, uint8_t *buf, ssize_t pack_ memset(&sll, 0, sizeof(sll)); sll.sll_family = AF_PACKET; sll.sll_protocol = ETH_P_ARP; - sll.sll_ifindex = (int) ifp->ifindex; + sll.sll_ifindex = (int)ifp->ifindex; sll.sll_halen = ifp->hw_addr_len; memset(sll.sll_addr, 0xFF, ETH_ALEN); @@ -80,14 +81,14 @@ static ssize_t vrrp_build_garp(uint8_t *buf, struct interface *ifp, return -1; /* Build Ethernet header */ - struct ether_header *eth = (struct ether_header *) buf; + struct ether_header *eth = (struct ether_header *)buf; memset(eth->ether_dhost, 0xFF, ETH_ALEN); memcpy(eth->ether_shost, ifp->hw_addr, ETH_ALEN); eth->ether_type = htons(ETHERTYPE_ARP); /* Build ARP payload */ - struct arphdr *arph = (struct arphdr *) (buf + ETHER_HDR_LEN); + struct arphdr *arph = (struct arphdr *)(buf + ETHER_HDR_LEN); arph->ar_hrd = htons(HWTYPE_ETHER); arph->ar_pro = htons(ETHERTYPE_IP); diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c index 0117497590..0ac77350eb 100644 --- a/vrrpd/vrrp_main.c +++ b/vrrpd/vrrp_main.c @@ -57,7 +57,7 @@ struct zebra_privs_t vrrp_privs = { .cap_num_p = array_size(_caps_p), .cap_num_i = 0}; -struct option longopts[] = { { 0 } }; +struct option longopts[] = {{0}}; /* Master of threads. */ struct thread_master *master; diff --git a/vrrpd/vrrp_ndisc.c b/vrrpd/vrrp_ndisc.c index 6813506d1f..0f59b23007 100644 --- a/vrrpd/vrrp_ndisc.c +++ b/vrrpd/vrrp_ndisc.c @@ -67,7 +67,7 @@ static int ndisc_fd = -1; * 0 otherwise */ static int vrrp_ndisc_una_build(struct interface *ifp, struct ipaddr *ip, - uint8_t *buf, size_t bufsiz) + uint8_t *buf, size_t bufsiz) { if (bufsiz < VRRP_NDISC_SIZE) return -1; From c16fb34051998600039c5f57dbd230d237e6eced Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 27 Feb 2019 20:46:35 +0000 Subject: [PATCH 096/153] vrrpd: be less smart about interface state Stop caring about interface state so much. It's screwing up autoconfig because Zebra's message semantics are pretty much absolute nonsense when it comes to indicating interface state. This change will cause us to do things like attempt to transmit advertisements on a down interface, but I'd rather have the user see those error messages in the log file than force them to fight vrrpd to convince it that, yes, they actually do want a VRRP instance created. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 20cec70063..bec47a0e2a 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -251,14 +251,18 @@ void vrrp_check_start(struct vrrp_vrouter *vr) start = r->fsm.state == VRRP_STATE_INITIALIZE; /* Must have a parent interface */ start = start && (vr->ifp != NULL); +#if 0 /* Parent interface must be up */ start = start && if_is_operative(vr->ifp); +#endif /* Parent interface must have at least one v4 */ start = start && vr->ifp->connected->count > 1; /* Must have a macvlan interface */ start = start && (r->mvl_ifp != NULL); +#if 0 /* Macvlan interface must be admin up */ start = start && CHECK_FLAG(r->mvl_ifp->flags, IFF_UP); +#endif /* Must have at least one VIP configured */ start = start && r->addrs->count > 0; if (start) @@ -269,12 +273,16 @@ void vrrp_check_start(struct vrrp_vrouter *vr) start = r->fsm.state == VRRP_STATE_INITIALIZE; /* Must have a parent interface */ start = start && (vr->ifp != NULL); +#if 0 /* Parent interface must be up */ start = start && if_is_operative(vr->ifp); +#endif /* Must have a macvlan interface */ start = start && (r->mvl_ifp != NULL); +#if 0 /* Macvlan interface must be admin up */ start = start && CHECK_FLAG(r->mvl_ifp->flags, IFF_UP); +#endif /* Macvlan interface must have at least two v6 */ start = start && (r->mvl_ifp->connected->count >= 2); /* Macvlan interface must have a link local */ From 6309f71c9b20ba35b8afb3bc58f2b9555a5b42bb Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 27 Feb 2019 20:56:33 +0000 Subject: [PATCH 097/153] vrrpd: log why vrouter could not be started If we do a checkstart and cannot start the VRRP router, log the reason why for debugging purposes. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index bec47a0e2a..b9c7b3d2c0 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -242,6 +242,7 @@ void vrrp_check_start(struct vrrp_vrouter *vr) { struct vrrp_router *r; bool start; + const char *whynot = NULL; if (vr->shutdown || vr->ifp == NULL) return; @@ -251,46 +252,62 @@ void vrrp_check_start(struct vrrp_vrouter *vr) start = r->fsm.state == VRRP_STATE_INITIALIZE; /* Must have a parent interface */ start = start && (vr->ifp != NULL); + whynot = (!start && !whynot) ? "No base interface" : NULL; #if 0 /* Parent interface must be up */ start = start && if_is_operative(vr->ifp); #endif /* Parent interface must have at least one v4 */ start = start && vr->ifp->connected->count > 1; + whynot = (!start && !whynot) ? "No primary IPv4 address" : NULL; /* Must have a macvlan interface */ start = start && (r->mvl_ifp != NULL); + whynot = (!start && !whynot) ? "No VRRP interface" : NULL; #if 0 /* Macvlan interface must be admin up */ start = start && CHECK_FLAG(r->mvl_ifp->flags, IFF_UP); #endif /* Must have at least one VIP configured */ start = start && r->addrs->count > 0; + whynot = (!start && !whynot) ? "No Virtual IP address configured" : NULL; if (start) vrrp_event(r, VRRP_EVENT_STARTUP); + else if (whynot) + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Refusing to start IPv4 Virtual Router: %s", + vr->vrid, whynot); r = vr->v6; /* Must not already be started */ start = r->fsm.state == VRRP_STATE_INITIALIZE; /* Must have a parent interface */ start = start && (vr->ifp != NULL); + whynot = (!start && !whynot) ? "No base interface" : NULL; #if 0 /* Parent interface must be up */ start = start && if_is_operative(vr->ifp); #endif /* Must have a macvlan interface */ start = start && (r->mvl_ifp != NULL); + whynot = (!start && !whynot) ? "No VRRP interface" : NULL; #if 0 /* Macvlan interface must be admin up */ start = start && CHECK_FLAG(r->mvl_ifp->flags, IFF_UP); #endif - /* Macvlan interface must have at least two v6 */ - start = start && (r->mvl_ifp->connected->count >= 2); /* Macvlan interface must have a link local */ start = start && connected_get_linklocal(r->mvl_ifp); + whynot = (!start && !whynot) ? "No link local address configured" : NULL; + /* Macvlan interface must have a v6 IP besides the link local */ + start = start && (r->mvl_ifp->connected->count >= 2); + whynot = (!start && !whynot) ? "No Virtual IP address configured" : NULL; /* Must have at least one VIP configured */ start = start && r->addrs->count > 0; if (start) vrrp_event(r, VRRP_EVENT_STARTUP); + else if (whynot) + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + "Refusing to start IPv6 Virtual Router: %s", + vr->vrid, whynot); } void vrrp_set_priority(struct vrrp_vrouter *vr, uint8_t priority) From 7e9fee6a662d93863a99bf967ac820ad26d23a37 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 27 Feb 2019 22:07:58 +0000 Subject: [PATCH 098/153] vrrpd: fix bad fmt specifiers in dgram validator They used a %u where they should have used a PRIu16, and a %lu where they wanted a %zu. Shame! Signed-off-by: Quentin Young --- vrrpd/vrrp_packet.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index db31d163bc..551e142e54 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -211,7 +211,8 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m, /* IP total length check */ VRRP_PKT_VCHECK( ntohs(ip->ip_len) == read, - "IPv4 packet length field does not match # received bytes; %u != %lu", + "IPv4 packet length field does not match # received bytes; %" PRIu16 + "!= %zu", ntohs(ip->ip_len), read); /* TTL check */ From 14eb6274e804d75c2cd0d4c117bef28a263ff97c Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 27 Feb 2019 22:46:24 +0000 Subject: [PATCH 099/153] vrrpd: set autoconfed VRRP ifaces protodown off If we just detected a macvlan and used it to automatically create an interface, set that interface into protodown off. This way users don't have to manually bring the interface back up in order to get autoconfig to work again. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index b9c7b3d2c0..44885f2e55 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1654,7 +1654,10 @@ static int vrrp_autoconfig_if_add(struct interface *ifp) if (!vr) { vr = vrrp_autoconfig_autocreate(ifp); - created = true; + if (vr) { + created = true; + vrrp_zclient_send_interface_protodown(ifp, false); + } } if (!vr) From ac1429b9ef8da6d5d8744be7f6cc7f284d5b9b5a Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 28 Feb 2019 18:25:39 +0000 Subject: [PATCH 100/153] vrrpd: only update one vrrp_router list at a time When using an autoconfigured VRRP instance, when an interface address was added or deleted we were trying to update the address list for both v4 and v6 vrrp_router's which sometimes would cause all the addresses to get deleted off of one of them and result in an automatic shutdown. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 101 +++++++++++++++++++++++---------------------------- 1 file changed, 46 insertions(+), 55 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 44885f2e55..58a046fefa 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1529,65 +1529,44 @@ int vrrp_event(struct vrrp_router *r, int event) * vr * VRRP router to act on */ -static void vrrp_autoconfig_autoaddrupdate(struct vrrp_vrouter *vr) +static void vrrp_autoconfig_autoaddrupdate(struct vrrp_router *r) { - list_delete_all_node(vr->v4->addrs); - list_delete_all_node(vr->v6->addrs); - struct listnode *ln; struct connected *c = NULL; + bool is_v6_ll; char ipbuf[INET6_ADDRSTRLEN]; - if (vr->v4->mvl_ifp) { - DEBUGD(&vrrp_dbg_auto, - VRRP_LOGPFX VRRP_LOGPFX_VRID - "Setting IPv4 Virtual IP list to match IPv4 addresses on %s", - vr->vrid, vr->v4->mvl_ifp->name); - for (ALL_LIST_ELEMENTS_RO(vr->v4->mvl_ifp->connected, ln, c)) - if (c->address->family == AF_INET) { - inet_ntop(AF_INET, &c->address->u.prefix4, - ipbuf, sizeof(ipbuf)); - DEBUGD(&vrrp_dbg_auto, - VRRP_LOGPFX VRRP_LOGPFX_VRID "Adding %s", - vr->vrid, ipbuf); - vrrp_add_ipv4(vr, c->address->u.prefix4); - } + if (!r->mvl_ifp) + return; + + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Setting %s Virtual IP list to match IPv4 addresses on %s", + r->vr->vrid, family2str(r->family), r->mvl_ifp->name); + for (ALL_LIST_ELEMENTS_RO(r->mvl_ifp->connected, ln, c)) { + is_v6_ll = (c->address->family == AF_INET6 + && IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)); + if (c->address->family == r->family && !is_v6_ll) { + inet_ntop(r->family, &c->address->u.prefix, ipbuf, + sizeof(ipbuf)); + DEBUGD(&vrrp_dbg_auto, + VRRP_LOGPFX VRRP_LOGPFX_VRID "Adding %s", + r->vr->vrid, ipbuf); + if (r->family == AF_INET) + vrrp_add_ipv4(r->vr, c->address->u.prefix4); + else + vrrp_add_ipv6(r->vr, c->address->u.prefix6); + } } - if (vr->v6->mvl_ifp) { - DEBUGD(&vrrp_dbg_auto, - VRRP_LOGPFX VRRP_LOGPFX_VRID - "Setting IPv6 Virtual IP list to match IPv6 addresses on %s", - vr->vrid, vr->v6->mvl_ifp->name); - for (ALL_LIST_ELEMENTS_RO(vr->v6->mvl_ifp->connected, ln, c)) - if (c->address->family == AF_INET6 - && !IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) { - inet_ntop(AF_INET6, &c->address->u.prefix6, - ipbuf, sizeof(ipbuf)); - DEBUGD(&vrrp_dbg_auto, - VRRP_LOGPFX VRRP_LOGPFX_VRID "Adding %s", - vr->vrid, ipbuf); - vrrp_add_ipv6(vr, c->address->u.prefix6); - } - } + vrrp_check_start(r->vr); - vrrp_check_start(vr); - - if (vr->v4->addrs->count == 0 - && vr->v4->fsm.state != VRRP_STATE_INITIALIZE) { + if (r->addrs->count == 0 && r->fsm.state != VRRP_STATE_INITIALIZE) { DEBUGD(&vrrp_dbg_auto, VRRP_LOGPFX VRRP_LOGPFX_VRID - "IPv4 Virtual IP list is empty; shutting down", - vr->vrid); - vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN); - } - if (vr->v6->addrs->count == 0 - && vr->v6->fsm.state != VRRP_STATE_INITIALIZE) { - DEBUGD(&vrrp_dbg_auto, - VRRP_LOGPFX VRRP_LOGPFX_VRID - "IPv6 Virtual IP list is empty; shutting down", - vr->vrid); - vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN); + "%s Virtual IP list is empty; shutting down", + r->vr->vrid, family2str(r->family)); + vrrp_event(r, VRRP_EVENT_SHUTDOWN); } } @@ -1618,7 +1597,8 @@ vrrp_autoconfig_autocreate(struct interface *mvl_ifp) return NULL; } - vrrp_autoconfig_autoaddrupdate(vr); + vrrp_autoconfig_autoaddrupdate(vr->v4); + vrrp_autoconfig_autoaddrupdate(vr->v6); vr->autoconf = true; @@ -1666,7 +1646,10 @@ static int vrrp_autoconfig_if_add(struct interface *ifp) if (vr->autoconf == false) return 0; else if (!created) { - vrrp_autoconfig_autoaddrupdate(vr); + if (vr->v4->mvl_ifp == ifp) + vrrp_autoconfig_autoaddrupdate(vr->v4); + else if (vr->v6->mvl_ifp == ifp) + vrrp_autoconfig_autoaddrupdate(vr->v6); } return 0; @@ -1790,8 +1773,12 @@ static int vrrp_autoconfig_if_address_add(struct interface *ifp) struct vrrp_vrouter *vr = vrrp_lookup_by_if_mvl(ifp); - if (vr && vr->autoconf) - vrrp_autoconfig_autoaddrupdate(vr); + if (vr && vr->autoconf) { + if (vr->v4->mvl_ifp == ifp) + vrrp_autoconfig_autoaddrupdate(vr->v4); + else if (vr->v6->mvl_ifp == ifp) + vrrp_autoconfig_autoaddrupdate(vr->v6); + } return 0; } @@ -1818,8 +1805,12 @@ static int vrrp_autoconfig_if_address_del(struct interface *ifp) struct vrrp_vrouter *vr = vrrp_lookup_by_if_mvl(ifp); - if (vr && vr->autoconf) - vrrp_autoconfig_autoaddrupdate(vr); + if (vr && vr->autoconf) { + if (vr->v4->mvl_ifp == ifp) + vrrp_autoconfig_autoaddrupdate(vr->v4); + else if (vr->v6->mvl_ifp == ifp) + vrrp_autoconfig_autoaddrupdate(vr->v6); + } return 0; } From 76c00fca685b4368dfbb4801ce6d5b630de6e7af Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 28 Feb 2019 19:00:26 +0000 Subject: [PATCH 101/153] vrrpd: use parent interface LLA for advert tx Interface MACs for v6 macvlan devices are the same, so the link local address will be the same, which breaks mastership election based on primary address comparison. Use the parent interface link local address. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 58a046fefa..84ff84b8a4 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -888,11 +888,7 @@ static int vrrp_bind_to_primary_connected(struct vrrp_router *r) char ipstr[INET6_ADDRSTRLEN]; struct interface *ifp; - /* - * A slight quirk: the RFC specifies that advertisements under IPv6 must - * be transmitted using the link local address of the source interface - */ - ifp = r->family == AF_INET ? r->vr->ifp : r->mvl_ifp; + ifp = r->vr->ifp; struct listnode *ln; struct connected *c = NULL; From 29ef66fa19f15dcc57db28aba4b048e50bd9da39 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 28 Feb 2019 20:47:04 +0000 Subject: [PATCH 102/153] vrrpd: check start for manual v6 addr add Signed-off-by: Quentin Young --- vrrpd/vrrp_vty.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index daf373d394..4c474489f4 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -236,11 +236,13 @@ DEFPY(vrrp_ip6, if (no) { int oldstate = vr->v6->fsm.state; failed = vrrp_del_ipv6(vr, ipv6); + vrrp_check_start(vr); deactivated = (vr->v6->fsm.state == VRRP_STATE_INITIALIZE && oldstate != VRRP_STATE_INITIALIZE); } else { int oldstate = vr->v6->fsm.state; failed = vrrp_add_ipv6(vr, ipv6); + vrrp_check_start(vr); activated = (vr->v6->fsm.state != VRRP_STATE_INITIALIZE && oldstate == VRRP_STATE_INITIALIZE); } From 323cc42ac190c2b087a4b19cea6e9bd580c0dff7 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 28 Feb 2019 21:41:21 +0000 Subject: [PATCH 103/153] vrrpd: fix unsigned - signed cmp For some reason this warning only shows up on armel Signed-off-by: Quentin Young --- vrrpd/vrrp_vty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 4c474489f4..3f6918539c 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -555,7 +555,7 @@ DEFPY(vrrp_vrid_show, for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) { if (ifn && !strmatch(ifn, vr->ifp->name)) continue; - if (vrid && vrid != vr->vrid) + if ((uint8_t) vrid && vrid != vr->vrid) continue; if (!json) From c7e65c4f805d2378c710836877eb8db5f8211ef6 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 28 Feb 2019 22:12:23 +0000 Subject: [PATCH 104/153] vrrpd: add more debugging info * Add reason why we are discarding adverts * Add primary IP to show vrrp output Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 14 ++++++++++---- vrrpd/vrrp_vty.c | 6 ++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 84ff84b8a4..55ae7ce7a4 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -668,6 +668,8 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, { char sipstr[INET6_ADDRSTRLEN]; ipaddr2str(src, sipstr, sizeof(sipstr)); + char dipstr[INET6_ADDRSTRLEN]; + ipaddr2str(&r->src, dipstr, sizeof(dipstr)); char dumpbuf[BUFSIZ]; vrrp_pkt_adver_dump(dumpbuf, sizeof(dumpbuf), pkt); @@ -758,8 +760,10 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, /* Discard advertisement */ DEBUGD(&vrrp_dbg_proto, VRRP_LOGPFX VRRP_LOGPFX_VRID - "Discarding advertisement from %s", - r->vr->vrid, sipstr); + "Discarding advertisement from %s (%" PRIu8 + " = %" PRIu8 " & %s <= %s)", + r->vr->vrid, sipstr, pkt->hdr.priority, + r->priority, sipstr, dipstr); } break; case VRRP_STATE_BACKUP: @@ -785,8 +789,10 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, /* Discard advertisement */ DEBUGD(&vrrp_dbg_proto, VRRP_LOGPFX VRRP_LOGPFX_VRID - "Discarding advertisement from %s", - r->vr->vrid, sipstr); + "Discarding advertisement from %s (%" PRIu8 + " < %" PRIu8 " & preempt = true)", + r->vr->vrid, sipstr, pkt->hdr.priority, + r->priority); } break; case VRRP_STATE_INITIALIZE: diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 3f6918539c..2a7a6ecfc4 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -444,6 +444,8 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) char ipstr[INET6_ADDRSTRLEN]; const char *stastr4 = vrrp_state_names[vr->v4->fsm.state]; const char *stastr6 = vrrp_state_names[vr->v6->fsm.state]; + char sipstr4[INET6_ADDRSTRLEN]; + char sipstr6[INET6_ADDRSTRLEN]; struct listnode *ln; struct ipaddr *ip; @@ -461,6 +463,10 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) vr->v4->mvl_ifp ? vr->v4->mvl_ifp->name : "None"); ttable_add_row(tt, "%s|%s", "VRRP interface (v6)", vr->v6->mvl_ifp ? vr->v6->mvl_ifp->name : "None"); + ipaddr2str(&vr->v4->src, sipstr4, sizeof(sipstr4)); + ipaddr2str(&vr->v6->src, sipstr6, sizeof(sipstr6)); + ttable_add_row(tt, "%s|%s", "Primary IP (v4)", sipstr4); + ttable_add_row(tt, "%s|%s", "Primary IP (v6)", sipstr6); ttable_add_row(tt, "%s|%s", "Virtual MAC (v4)", ethstr4); ttable_add_row(tt, "%s|%s", "Virtual MAC (v6)", ethstr6); ttable_add_row(tt, "%s|%s", "Status (v4)", stastr4); From d37281cb045a89cd2c80a9d71cfa5640b0b77079 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 28 Feb 2019 23:13:20 +0000 Subject: [PATCH 105/153] vrrpd: fix autoconfig of protodown'd interfaces When autoconfiguring VRRP, interfaces that are protodown'd should be automatically brought up. Otherwise Zebra won't send us their interface addresses and we'll sit in Initialize forever. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 62 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 55ae7ce7a4..4d4a66b6d1 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1599,11 +1599,22 @@ vrrp_autoconfig_autocreate(struct interface *mvl_ifp) return NULL; } + vr->autoconf = true; + + /* + * If these interfaces are protodown on, we need to un-protodown them + * in order to get Zebra to send us their addresses so we can + * autoconfigure them. + */ + if (vr->v4->mvl_ifp) + vrrp_zclient_send_interface_protodown(vr->v4->mvl_ifp, false); + if (vr->v6->mvl_ifp) + vrrp_zclient_send_interface_protodown(vr->v6->mvl_ifp, false); + + /* If they're not, we can go ahead and add the addresses we have */ vrrp_autoconfig_autoaddrupdate(vr->v4); vrrp_autoconfig_autoaddrupdate(vr->v6); - vr->autoconf = true; - return vr; } @@ -1636,22 +1647,43 @@ static int vrrp_autoconfig_if_add(struct interface *ifp) if (!vr) { vr = vrrp_autoconfig_autocreate(ifp); - if (vr) { - created = true; - vrrp_zclient_send_interface_protodown(ifp, false); - } + created = true; } - if (!vr) - return -1; - - if (vr->autoconf == false) + if (!vr || vr->autoconf == false) return 0; - else if (!created) { - if (vr->v4->mvl_ifp == ifp) - vrrp_autoconfig_autoaddrupdate(vr->v4); - else if (vr->v6->mvl_ifp == ifp) - vrrp_autoconfig_autoaddrupdate(vr->v6); + + if (!created) { + /* + * We didn't create it, but it has already been autoconfigured. + * Try to attach this interface to the existing instance. + */ + if (!vr->v4->mvl_ifp) { + vrrp_attach_interface(vr->v4); + /* If we just attached it, make sure it's turned on */ + if (vr->v4->mvl_ifp) { + vrrp_zclient_send_interface_protodown( + vr->v4->mvl_ifp, false); + /* + * If it's already up, we can go ahead and add + * the addresses we have + */ + vrrp_autoconfig_autoaddrupdate(vr->v4); + } + } + if (!vr->v6->mvl_ifp) { + vrrp_attach_interface(vr->v6); + /* If we just attached it, make sure it's turned on */ + if (vr->v6->mvl_ifp) { + vrrp_zclient_send_interface_protodown( + vr->v6->mvl_ifp, false); + /* + * If it's already up, we can go ahead and add + * the addresses we have + */ + vrrp_autoconfig_autoaddrupdate(vr->v6); + } + } } return 0; From 3a9c6f93d4e22651d97b595ebbc4616631751e7c Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 28 Feb 2019 23:48:09 +0000 Subject: [PATCH 106/153] vrrpd: fix interface block config writes Improper reuse of list node broke config writes of multiple instances Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 4d4a66b6d1..fbc738e308 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -2053,7 +2053,7 @@ void vrrp_if_address_del(struct interface *ifp) int vrrp_config_write_interface(struct vty *vty) { struct list *vrs = hash_to_list(vrrp_vrouters_hash); - struct listnode *ln; + struct listnode *ln, *ipln; struct vrrp_vrouter *vr; int writes = 0; @@ -2088,23 +2088,24 @@ int vrrp_config_write_interface(struct vty *vty) vty_out(vty, " vrrp %" PRIu8 " priority %" PRIu8 "\n", vr->vrid, vr->priority); - ln = NULL; struct ipaddr *ip; - for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ln, ip)) { + for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ipln, ip)) { char ipbuf[INET6_ADDRSTRLEN]; ipaddr2str(ip, ipbuf, sizeof(ipbuf)); vty_out(vty, " vrrp %" PRIu8 " ip %s\n", vr->vrid, ipbuf); ++writes; } - for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ln, ip)) { + + for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ipln, ip)) { char ipbuf[INET6_ADDRSTRLEN]; ipaddr2str(ip, ipbuf, sizeof(ipbuf)); vty_out(vty, " vrrp %" PRIu8 " ipv6 %s\n", vr->vrid, ipbuf); ++writes; } + vty_endframe(vty, "!\n"); } return writes; From 48cd8f1398d3949ccf089aa2edee0ce2a6554874 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 1 Mar 2019 17:11:06 +0000 Subject: [PATCH 107/153] vrrpd: fix sign compare on armel Signed-off-by: Quentin Young --- vrrpd/vrrp_vty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 2a7a6ecfc4..0498d3c260 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -561,7 +561,7 @@ DEFPY(vrrp_vrid_show, for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) { if (ifn && !strmatch(ifn, vr->ifp->name)) continue; - if ((uint8_t) vrid && vrid != vr->vrid) + if (vrid && ((uint8_t) vrid) != vr->vrid) continue; if (!json) From 2f1fc30fd25aeff4ff88efbab31cf31d366793ef Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 4 Mar 2019 17:27:55 +0000 Subject: [PATCH 108/153] vrrpd: allow user to set priority = 255 Too many problems with implicit ownership determination via duplicate address assignment. Will revisit that in the future. For now, allow user to specify 255 as a priority value. This is functionally no different than any other priority value; it just serves as a self-documenting way of saying you want one router to always be master. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 29 ++++++++++++++++++++++++++--- vrrpd/vrrp_vty.c | 2 +- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index fbc738e308..fc0a57b71c 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -107,12 +107,16 @@ static void vrrp_recalculate_timers(struct vrrp_router *r) * Determines if a VRRP router is the owner of the specified address. * * The determining factor for whether an interface is the address owner is - * simply whether the address is assigned to the VRRP subinterface by someone + * simply whether the address is assigned to the VRRP base interface by someone * other than vrrpd. * * This function should always return the correct answer regardless of * master/backup status. * + * ifp + * The interface to check owernship of. This should be the base interface of + * a VRRP router. + * * vr * Virtual Router * @@ -121,6 +125,23 @@ static void vrrp_recalculate_timers(struct vrrp_router *r) */ static bool vrrp_is_owner(struct interface *ifp, struct ipaddr *addr) { + /* + * This code sanity checks implicit ownership configuration. Ideally, + * the way we determine address ownership status for this VRRP router + * is by looking at whether our VIPs are also assigned to the base + * interface, and therefore count as "real" addresses. This frees the + * user from having to manually configure priority 255 to indicate + * address ownership. However, this means one of the VIPs will be used + * as the source address for VRRP advertisements, which in turn means + * that other VRRP routers will be receiving packets with a source + * address they themselves have. This causes lots of different issues + * so for now we're disabling this and forcing the user to configure + * priority 255 to indicate ownership. + */ + + return false; + +#if 0 struct prefix p; p.family = IS_IPADDR_V4(addr) ? AF_INET : AF_INET6; @@ -128,6 +149,7 @@ static bool vrrp_is_owner(struct interface *ifp, struct ipaddr *addr) memcpy(&p.u, &addr->ip, sizeof(addr->ip)); return !!connected_lookup_prefix_exact(ifp, &p); +#endif } /* @@ -1411,13 +1433,14 @@ static int vrrp_startup(struct vrrp_router *r) char ipbuf[INET6_ADDRSTRLEN]; inet_ntop(r->family, &primary->ip.addr, ipbuf, sizeof(ipbuf)); - if (vrrp_is_owner(r->vr->ifp, primary)) { + if (r->vr->priority == VRRP_PRIO_MASTER + || vrrp_is_owner(r->vr->ifp, 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", + "%s has priority set to 255 or owns primary Virtual Router IP %s; electing self as Master", r->vr->vrid, r->vr->ifp->name, ipbuf); } diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 0498d3c260..ec3a70dadb 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -116,7 +116,7 @@ DEFPY(vrrp_shutdown, DEFPY(vrrp_priority, vrrp_priority_cmd, - "[no] vrrp (1-255)$vrid priority (1-254)", + "[no] vrrp (1-255)$vrid priority (1-255)", NO_STR VRRP_STR VRRP_VRID_STR From d60b2ffdfad204b675f642e9b712034e8dcef432 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 4 Mar 2019 18:46:08 +0000 Subject: [PATCH 109/153] vrrpd: delay sending adverts/garp/una for iface up When transitioning to Master from Backup, wait until Zebra sets the macvlan device to protodown off before transmitting advertisements, gratuitous ARPs, or Unsolicited Neighbor Advertisements. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++----- vrrpd/vrrp.h | 21 ++++++++++++++ 2 files changed, 95 insertions(+), 8 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index fc0a57b71c..070bb9cc79 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -553,10 +553,12 @@ static void vrrp_router_destroy(struct vrrp_router *r) if (r->is_active) vrrp_event(r, VRRP_EVENT_SHUTDOWN); - if (r->sock_rx >= 0) + if (r->sock_rx >= 0) { close(r->sock_rx); - if (r->sock_tx >= 0) + } + if (r->sock_tx >= 0) { close(r->sock_tx); + } /* FIXME: also delete list elements */ list_delete(&r->addrs); @@ -1276,6 +1278,11 @@ static void vrrp_change_state_backup(struct vrrp_router *r) /* Disable Adver_Timer */ THREAD_OFF(r->t_adver_timer); + /* This should not be necessary, but just in case */ + r->advert_pending = false; + r->garp_pending = false; + r->ndisc_pending = false; + vrrp_zclient_send_interface_protodown(r->mvl_ifp, true); } @@ -1294,6 +1301,11 @@ static void vrrp_change_state_initialize(struct vrrp_router *r) r->master_adver_interval = 0; vrrp_recalculate_timers(r); + /* This should not be necessary, but just in case */ + r->advert_pending = false; + r->garp_pending = false; + r->ndisc_pending = false; + /* Disable ND Router Advertisements */ if (r->family == AF_INET6) vrrp_zebra_radv_set(r, false); @@ -1367,16 +1379,23 @@ static int vrrp_master_down_timer_expire(struct thread *thread) zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Master_Down_Timer expired", r->vr->vrid); - vrrp_send_advertisement(r); - if (r->family == AF_INET) - vrrp_garp_send_all(r); - if (r->family == AF_INET6) - vrrp_ndisc_una_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); + /* + * Since this implemention uses protodown to implement backup status, + * we have to wait for the interface to come up before we can send our + * initial advert and garp/ndisc packets. This will be handled in + * vrrp_if_up(). + */ + r->advert_pending = true; + if (r->family == AF_INET) + r->garp_pending = true; + if (r->family == AF_INET6) + r->ndisc_pending = true; + return 0; } @@ -1512,6 +1531,10 @@ static int vrrp_shutdown(struct vrrp_router *r) r->sock_tx = -1; } + r->advert_pending = false; + r->garp_pending = false; + r->ndisc_pending = false; + vrrp_change_state(r, VRRP_STATE_INITIALIZE); r->is_active = false; @@ -1942,9 +1965,52 @@ void vrrp_if_up(struct interface *ifp) vrs = vrrp_lookup_by_if_any(ifp); - for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) { vrrp_check_start(vr); + /* + * Handle the situation in which we performed a state + * transition on this VRRP router but needed to wait for the + * macvlan interface to come up to perform some actions + */ + if (ifp == vr->v4->mvl_ifp) { + if (vr->v4->advert_pending) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Interface up; sending pending advertisement", + vr->vrid); + vrrp_send_advertisement(vr->v4); + vr->v4->advert_pending = false; + } + if (vr->v4->garp_pending) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Interface up; sending pending gratuitous ARP", + vr->vrid); + vrrp_garp_send_all(vr->v4); + vr->v4->garp_pending = false; + } + } + if (ifp == vr->v6->mvl_ifp) { + if (vr->v6->advert_pending) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Interface up; sending pending advertisement", + vr->vrid); + vrrp_send_advertisement(vr->v6); + vr->v6->advert_pending = false; + } + if (vr->v6->ndisc_pending) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Interface up; sending pending Unsolicited Neighbor Advertisement", + vr->vrid); + vrrp_ndisc_una_send_all(vr->v6); + vr->v6->ndisc_pending = false; + } + } + } + list_delete(&vrs); vrrp_autoconfig_if_up(ifp); diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 4ea1a37377..99cebd5c8a 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -120,6 +120,27 @@ struct vrrp_router { */ struct list *addrs; + /* + * This flag says whether we are waiting on an interface up + * notification from Zebra before we send an ADVERTISEMENT. + */ + bool advert_pending; + + /* + * If this is an IPv4 VRRP router, this flag says whether we are + * waiting on an interface up notification from Zebra before we send + * gratuitous ARP packets for all our addresses. Should never be true + * if family == AF_INET6. + */ + bool garp_pending; + /* + * If this is an IPv6 VRRP router, this flag says whether we are + * waiting on an interface up notification from Zebra before we send + * Unsolicited Neighbor Advertisement packets for all our addresses. + * Should never be true if family == AF_INET. + */ + bool ndisc_pending; + /* * Effective priority * => vr->priority if we are Backup From ee5aabb6fcf84027ef7a16a49755adafc2f07183 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 4 Mar 2019 20:15:25 +0000 Subject: [PATCH 110/153] vrrpd: delay sending advert/garp/una for ifup pt 2 Pt 2: When transitioning directly into Master (because we are the address owner), wait until Zebra sets the macvlan device to protodown off before transmitting advertisements, gratuitous ARPs, or Unsolicited Neighbor Advertisements. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 62 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 070bb9cc79..a4e4a62b0a 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1260,7 +1260,41 @@ static void vrrp_change_state_master(struct vrrp_router *r) if (r->family == AF_INET6) vrrp_zebra_radv_set(r, true); + /* Set protodown off */ vrrp_zclient_send_interface_protodown(r->mvl_ifp, false); + + /* + * If protodown is already off, we can send our stuff, otherwise we + * have to delay until the interface is all the way up + */ + if (if_is_operative(r->mvl_ifp)) { + vrrp_send_advertisement(r); + + if (r->family == AF_INET) + vrrp_garp_send_all(r); + else if (r->family == AF_INET6) + vrrp_ndisc_una_send_all(r); + } else { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Delaying VRRP advertisement until interface is up", + r->vr->vrid); + r->advert_pending = true; + + if (r->family == AF_INET) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Delaying VRRP gratuitous ARPs until interface is up", + r->vr->vrid); + r->garp_pending = true; + } else if (r->family == AF_INET6) { + DEBUGD(&vrrp_dbg_proto, + VRRP_LOGPFX VRRP_LOGPFX_VRID + "Delaying VRRP unsolicited neighbor advertisement until interface is up", + r->vr->vrid); + r->ndisc_pending = true; + } + } } /* @@ -1278,7 +1312,6 @@ static void vrrp_change_state_backup(struct vrrp_router *r) /* Disable Adver_Timer */ THREAD_OFF(r->t_adver_timer); - /* This should not be necessary, but just in case */ r->advert_pending = false; r->garp_pending = false; r->ndisc_pending = false; @@ -1301,7 +1334,6 @@ static void vrrp_change_state_initialize(struct vrrp_router *r) r->master_adver_interval = 0; vrrp_recalculate_timers(r); - /* This should not be necessary, but just in case */ r->advert_pending = false; r->garp_pending = false; r->ndisc_pending = false; @@ -1384,18 +1416,6 @@ static int vrrp_master_down_timer_expire(struct thread *thread) &r->t_adver_timer); vrrp_change_state(r, VRRP_STATE_MASTER); - /* - * Since this implemention uses protodown to implement backup status, - * we have to wait for the interface to come up before we can send our - * initial advert and garp/ndisc packets. This will be handled in - * vrrp_if_up(). - */ - r->advert_pending = true; - if (r->family == AF_INET) - r->garp_pending = true; - if (r->family == AF_INET6) - r->ndisc_pending = true; - return 0; } @@ -1464,13 +1484,6 @@ static int vrrp_startup(struct vrrp_router *r) } if (r->priority == VRRP_PRIO_MASTER) { - vrrp_send_advertisement(r); - - if (r->family == AF_INET) - vrrp_garp_send_all(r); - if (r->family == AF_INET6) - vrrp_ndisc_una_send_all(r); - thread_add_timer_msec(master, vrrp_adver_timer_expire, r, r->vr->advertisement_interval * 10, &r->t_adver_timer); @@ -1531,10 +1544,6 @@ static int vrrp_shutdown(struct vrrp_router *r) r->sock_tx = -1; } - r->advert_pending = false; - r->garp_pending = false; - r->ndisc_pending = false; - vrrp_change_state(r, VRRP_STATE_INITIALIZE); r->is_active = false; @@ -1968,6 +1977,9 @@ void vrrp_if_up(struct interface *ifp) for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) { vrrp_check_start(vr); + if (!if_is_operative(ifp)) + continue; + /* * Handle the situation in which we performed a state * transition on this VRRP router but needed to wait for the From a6070d48e7c3a962aa161c43a6c3280ffcaa5552 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 5 Mar 2019 18:38:20 +0000 Subject: [PATCH 111/153] vrrpd: fix debug message = -> <= Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index a4e4a62b0a..524619dfad 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -785,7 +785,7 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, DEBUGD(&vrrp_dbg_proto, VRRP_LOGPFX VRRP_LOGPFX_VRID "Discarding advertisement from %s (%" PRIu8 - " = %" PRIu8 " & %s <= %s)", + " <= %" PRIu8 " & %s <= %s)", r->vr->vrid, sipstr, pkt->hdr.priority, r->priority, sipstr, dipstr); } From 18ca2e1ff40c86920804c7967a0a0828c2911996 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 5 Mar 2019 18:57:14 +0000 Subject: [PATCH 112/153] vtysh: don't sort vrrp interface config It is order dependent Signed-off-by: Quentin Young --- vtysh/vtysh_config.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 7ca3ed9c5e..cf94ab643a 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -257,6 +257,10 @@ void vtysh_config_parse_line(void *arg, const char *line) strlen(" exit-vrf")) == 0) { config_add_line_uniq_end(config->line, line); + } else if (!strncmp(line, " vrrp", strlen(" vrrp")) + || !strncmp(line, " no vrrp", + strlen(" no vrrp"))) { + config_add_line(config->line, line); } else if (config->index == RMAP_NODE || config->index == INTERFACE_NODE || config->index == LOGICALROUTER_NODE From a90edf08e3ccdfd466a3663f87abc59931a56e7f Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 5 Mar 2019 20:33:27 +0000 Subject: [PATCH 113/153] vrrpd: fix broken reads when reinitializing When a VRRP router was shut down - either due to an administrative event, or its interface getting deleted, or some other reason - it was forgetting to cancel its read task. When it was started again, the read task was still around, and so it wasn't getting scheduled again with the new socket fd's. This caused our socket to queue ingress packets but never read them, resulting in the restarted router always electing itself to Master (since it wasn't listening to any other advertisements, even though the kernel was delivering them). The t_write cancellation call doesn't matter here, but I'm putting it in there because it doesn't hurt and this way I won't forget about it if it becomes necessary in the future. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 524619dfad..d4a6dfc95a 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1534,6 +1534,8 @@ static int vrrp_shutdown(struct vrrp_router *r) /* Cancel all timers */ THREAD_OFF(r->t_adver_timer); THREAD_OFF(r->t_master_down_timer); + THREAD_OFF(r->t_read); + THREAD_OFF(r->t_write); if (r->sock_rx > 0) { close(r->sock_rx); From d7cfcdc0b0a962cc81824197b6c5901438b2fd7f Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 6 Mar 2019 18:34:34 +0000 Subject: [PATCH 114/153] vrrpd: disallow setting priority = 255 Assuming we fix our automatic detection method in the future, we won't be able to revert this back to disallowing 255 without breaking user configs. Let's just disallow it now, there's no functional difference still. Signed-off-by: Quentin Young --- vrrpd/vrrp_vty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index ec3a70dadb..0498d3c260 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -116,7 +116,7 @@ DEFPY(vrrp_shutdown, DEFPY(vrrp_priority, vrrp_priority_cmd, - "[no] vrrp (1-255)$vrid priority (1-255)", + "[no] vrrp (1-255)$vrid priority (1-254)", NO_STR VRRP_STR VRRP_VRID_STR From bd0934fa6dd3ea847ab993ef985786f0b52d3b96 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 6 Mar 2019 20:00:57 +0000 Subject: [PATCH 115/153] Revert "vrrpd: use parent interface LLA for advert tx" This reverts commit 23e1accb0b083713ee7b0ef7fb08a0c47d4bbc85. This bug has now been fixed by preconfiguring the VRRP macvlan interfaces to use addrgenmode random instead of eui64. --- vrrpd/vrrp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index d4a6dfc95a..cdc9e38665 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -918,7 +918,11 @@ static int vrrp_bind_to_primary_connected(struct vrrp_router *r) char ipstr[INET6_ADDRSTRLEN]; struct interface *ifp; - ifp = r->vr->ifp; + /* + * A slight quirk: the RFC specifies that advertisements under IPv6 must + * be transmitted using the link local address of the source interface + */ + ifp = r->family == AF_INET ? r->vr->ifp : r->mvl_ifp; struct listnode *ln; struct connected *c = NULL; From 613b45b008bd473704b341ce4d8247ddceeba494 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 7 Mar 2019 18:46:02 +0000 Subject: [PATCH 116/153] vrrpd: clean up logging * Always include address family when available * Log advertisement decodes on one line Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 273 +++++++++++++++++++++++--------------------- vrrpd/vrrp.h | 3 +- vrrpd/vrrp_arp.c | 16 +-- vrrpd/vrrp_ndisc.c | 8 +- vrrpd/vrrp_packet.c | 14 +-- 5 files changed, 167 insertions(+), 147 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index cdc9e38665..cba078a6c1 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -295,9 +295,9 @@ void vrrp_check_start(struct vrrp_vrouter *vr) if (start) vrrp_event(r, VRRP_EVENT_STARTUP); else if (whynot) - zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Refusing to start IPv4 Virtual Router: %s", - vr->vrid, whynot); + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Refusing to start Virtual Router: %s", + vr->vrid, family2str(r->family), whynot); r = vr->v6; /* Must not already be started */ @@ -327,9 +327,9 @@ void vrrp_check_start(struct vrrp_vrouter *vr) if (start) vrrp_event(r, VRRP_EVENT_STARTUP); else if (whynot) - zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Refusing to start IPv6 Virtual Router: %s", - vr->vrid, whynot); + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Refusing to start Virtual Router: %s", + vr->vrid, family2str(r->family), whynot); } void vrrp_set_priority(struct vrrp_vrouter *vr, uint8_t priority) @@ -376,9 +376,9 @@ int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip) char ipbuf[INET6_ADDRSTRLEN]; inet_ntop(r->family, &ip->ip, ipbuf, sizeof(ipbuf)); zlog_err( - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "This VRRP router is not the address owner of %s, but is the address owner of other addresses; this config is unsupported.", - r->vr->vrid, ipbuf); + r->vr->vrid, family2str(r->family), ipbuf); return -1; } @@ -511,15 +511,16 @@ static bool vrrp_attach_interface(struct vrrp_router *r) assert(!!selection == !!candidates); if (candidates == 0) - zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID - "%s interface: None (no interface found w/ MAC %s)", + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Interface: None (no interface found w/ MAC %s)", r->vr->vrid, family2str(r->family), ethstr); else if (candidates > 1) - zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID - "%s interface: Multiple interfaces found; using %s", + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Interface: Multiple interfaces found; using %s", r->vr->vrid, family2str(r->family), selection->name); else - zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "%s interface: %s", + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Interface: %s", r->vr->vrid, family2str(r->family), selection->name); r->mvl_ifp = selection; @@ -652,9 +653,10 @@ static void vrrp_send_advertisement(struct vrrp_router *r) XFREE(MTYPE_VRRP_PKT, pkt); if (sent < 0) { - zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Failed to send VRRP Advertisement: %s", - r->vr->vrid, safe_strerror(errno)); + r->vr->vrid, family2str(r->family), + safe_strerror(errno)); } else { ++r->stats.adver_tx_cnt; } @@ -698,15 +700,15 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, char dumpbuf[BUFSIZ]; vrrp_pkt_adver_dump(dumpbuf, sizeof(dumpbuf), pkt); DEBUGD(&vrrp_dbg_proto, - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Received VRRP Advertisement from %s:\n%s", - r->vr->vrid, sipstr, dumpbuf); + r->vr->vrid, family2str(r->family), sipstr, dumpbuf); /* Check that VRID matches our configured VRID */ if (pkt->hdr.vrid != r->vr->vrid) { DEBUGD(&vrrp_dbg_proto, - VRRP_LOGPFX VRRP_LOGPFX_VRID - "%s datagram invalid: Advertisement contains VRID %" PRIu8 + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Datagram invalid: Advertisement contains VRID %" PRIu8 " which does not match our instance", r->vr->vrid, family2str(r->family), pkt->hdr.vrid); return -1; @@ -715,8 +717,8 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, /* Verify that we are not the IPvX address owner */ if (r->is_owner) { DEBUGD(&vrrp_dbg_proto, - VRRP_LOGPFX VRRP_LOGPFX_VRID - "%s datagram invalid: Received advertisement but we are the address owner", + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Datagram invalid: Received advertisement but we are the address owner", r->vr->vrid, family2str(r->family)); return -1; } @@ -726,8 +728,8 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, == MAX(r->vr->advertisement_interval / 100, 1)); if (r->vr->version == 2 && !adveq) { DEBUGD(&vrrp_dbg_proto, - VRRP_LOGPFX VRRP_LOGPFX_VRID - "%s datagram invalid: Received advertisement with advertisement interval %" PRIu8 + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Datagram invalid: Received advertisement with advertisement interval %" PRIu8 " unequal to our configured value %u", r->vr->vrid, family2str(r->family), pkt->hdr.v2.adver_int, @@ -739,8 +741,8 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, /* Check that # IPs received matches our # configured IPs */ if (pkt->hdr.naddr != r->addrs->count) DEBUGD(&vrrp_dbg_proto, - VRRP_LOGPFX VRRP_LOGPFX_VRID - "%s datagram has %" PRIu8 + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Datagram has %" PRIu8 " addresses, but this VRRP instance has %u", r->vr->vrid, family2str(r->family), pkt->hdr.naddr, r->addrs->count); @@ -764,10 +766,11 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, || ((pkt->hdr.priority == r->priority) && addrcmp > 0)) { zlog_info( - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Received advertisement from %s w/ priority %" PRIu8 "; switching to Backup", - r->vr->vrid, sipstr, pkt->hdr.priority); + r->vr->vrid, family2str(r->family), sipstr, + pkt->hdr.priority); THREAD_OFF(r->t_adver_timer); if (r->vr->version == 3) { r->master_adver_interval = @@ -783,11 +786,11 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, } else { /* Discard advertisement */ DEBUGD(&vrrp_dbg_proto, - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Discarding advertisement from %s (%" PRIu8 " <= %" PRIu8 " & %s <= %s)", - r->vr->vrid, sipstr, pkt->hdr.priority, - r->priority, sipstr, dipstr); + r->vr->vrid, family2str(r->family), sipstr, + pkt->hdr.priority, r->priority, sipstr, dipstr); } break; case VRRP_STATE_BACKUP: @@ -812,17 +815,18 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, && pkt->hdr.priority < r->priority) { /* Discard advertisement */ DEBUGD(&vrrp_dbg_proto, - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Discarding advertisement from %s (%" PRIu8 " < %" PRIu8 " & preempt = true)", - r->vr->vrid, sipstr, pkt->hdr.priority, - r->priority); + r->vr->vrid, family2str(r->family), sipstr, + pkt->hdr.priority, r->priority); } break; case VRRP_STATE_INITIALIZE: - zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID + zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Received ADVERTISEMENT in state %s; this is a bug", - r->vr->vrid, vrrp_state_names[r->fsm.state]); + r->vr->vrid, family2str(r->family), + vrrp_state_names[r->fsm.state]); break; } @@ -869,7 +873,8 @@ static int vrrp_read(struct thread *thread) if (DEBUG_MODE_CHECK(&vrrp_dbg_pkt, DEBUG_MODE_ALL)) { DEBUGD(&vrrp_dbg_pkt, - VRRP_LOGPFX VRRP_LOGPFX_VRID "Received %s datagram: ", + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Datagram rx: ", r->vr->vrid, family2str(r->family)); zlog_hexdump(r->ibuf, nbytes); } @@ -877,16 +882,13 @@ static int vrrp_read(struct thread *thread) pktsize = vrrp_pkt_parse_datagram(r->family, r->vr->version, &m, nbytes, &src, &pkt, errbuf, sizeof(errbuf)); - if (pktsize < 0) { + if (pktsize < 0) DEBUGD(&vrrp_dbg_pkt, - VRRP_LOGPFX VRRP_LOGPFX_VRID "%s datagram invalid: %s", + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Datagram invalid: %s", r->vr->vrid, family2str(r->family), errbuf); - } else { - DEBUGD(&vrrp_dbg_pkt, - VRRP_LOGPFX VRRP_LOGPFX_VRID "Packet looks good", - r->vr->vrid); + else vrrp_recv_advertisement(r, &src, pkt, pktsize); - } resched = true; @@ -936,8 +938,8 @@ static int vrrp_bind_to_primary_connected(struct vrrp_router *r) } if (c == NULL) { - zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Failed to find %s address to bind on %s", + zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to find address to bind on %s", r->vr->vrid, family2str(r->family), ifp->name); return -1; } @@ -964,9 +966,9 @@ static int vrrp_bind_to_primary_connected(struct vrrp_router *r) sockopt_reuseaddr(r->sock_tx); if (bind(r->sock_tx, (const struct sockaddr *)&su, sizeof(su)) < 0) { zlog_err( - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Failed to bind Tx socket to primary IP address %s: %s", - r->vr->vrid, + r->vr->vrid, family2str(r->family), inet_ntop(r->family, (const void *)&c->address->u.prefix, ipstr, sizeof(ipstr)), @@ -974,9 +976,9 @@ static int vrrp_bind_to_primary_connected(struct vrrp_router *r) return -1; } else { DEBUGD(&vrrp_dbg_sock, - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Bound Tx socket to primary IP address %s", - r->vr->vrid, + r->vr->vrid, family2str(r->family), inet_ntop(r->family, (const void *)&c->address->u.prefix, ipstr, sizeof(ipstr))); } @@ -1022,8 +1024,8 @@ static int vrrp_socket(struct vrrp_router *r) if (r->sock_rx < 0 || r->sock_tx < 0) { const char *rxtx = r->sock_rx < 0 ? "Rx" : "Tx"; - zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Can't create %s VRRP %s socket", + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Can't create VRRP %s socket", r->vr->vrid, family2str(r->family), rxtx); failed = true; goto done; @@ -1037,9 +1039,9 @@ static int vrrp_socket(struct vrrp_router *r) sizeof(ttl)); if (ret < 0) { zlog_warn( - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Failed to set outgoing multicast TTL count to 255; RFC 5798 compliant implementations will drop our packets", - r->vr->vrid); + r->vr->vrid, family2str(r->family)); } /* Set Tx socket DSCP byte */ @@ -1057,16 +1059,17 @@ static int vrrp_socket(struct vrrp_router *r) } vrrp_privs.change(ZPRIVS_LOWER); if (ret) { - zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Failed to bind Rx socket to %s: %s", - r->vr->vrid, r->vr->ifp->name, - safe_strerror(errno)); + r->vr->vrid, family2str(r->family), + r->vr->ifp->name, safe_strerror(errno)); failed = true; goto done; } DEBUGD(&vrrp_dbg_sock, - VRRP_LOGPFX VRRP_LOGPFX_VRID "Bound Rx socket to %s", - r->vr->vrid, r->vr->ifp->name); + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Bound Rx socket to %s", + r->vr->vrid, family2str(r->family), r->vr->ifp->name); /* Bind Rx socket to v4 multicast address */ struct sockaddr_in sa = {0}; @@ -1074,16 +1077,16 @@ static int vrrp_socket(struct vrrp_router *r) sa.sin_addr.s_addr = htonl(VRRP_MCASTV4_GROUP); if (bind(r->sock_rx, (struct sockaddr *)&sa, sizeof(sa))) { zlog_err( - VRRP_LOGPFX VRRP_LOGPFX_VRID - "Failed to bind Rx socket to VRRP %s multicast group: %s", + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to bind Rx socket to VRRP multicast group: %s", r->vr->vrid, family2str(r->family), safe_strerror(errno)); failed = true; goto done; } DEBUGD(&vrrp_dbg_sock, - VRRP_LOGPFX VRRP_LOGPFX_VRID - "Bound Rx socket to VRRP %s multicast group", + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Bound Rx socket to VRRP multicast group", r->vr->vrid, family2str(r->family)); /* Join Rx socket to VRRP IPv4 multicast group */ @@ -1100,8 +1103,8 @@ static int vrrp_socket(struct vrrp_router *r) goto done; } DEBUGD(&vrrp_dbg_sock, - VRRP_LOGPFX VRRP_LOGPFX_VRID - "Joined %s VRRP multicast group", + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Joined VRRP multicast group", r->vr->vrid, family2str(r->family)); /* Set outgoing interface for advertisements */ @@ -1111,24 +1114,25 @@ static int vrrp_socket(struct vrrp_router *r) (void *)&mreqn, sizeof(mreqn)); if (ret < 0) { zlog_warn( - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Could not set %s as outgoing multicast interface", - r->vr->vrid, r->mvl_ifp->name); + r->vr->vrid, family2str(r->family), + r->mvl_ifp->name); failed = true; goto done; } DEBUGD(&vrrp_dbg_sock, - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Set %s as outgoing multicast interface", - r->vr->vrid, r->mvl_ifp->name); + r->vr->vrid, family2str(r->family), r->mvl_ifp->name); } else if (r->family == AF_INET6) { /* Always transmit IPv6 packets with hop limit set to 255 */ ret = setsockopt_ipv6_multicast_hops(r->sock_tx, 255); if (ret < 0) { zlog_warn( - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Failed to set outgoing multicast hop count to 255; RFC 5798 compliant implementations will drop our packets", - r->vr->vrid); + r->vr->vrid, family2str(r->family)); } /* Set Tx socket DSCP byte */ @@ -1137,9 +1141,9 @@ static int vrrp_socket(struct vrrp_router *r) /* Request hop limit delivery */ setsockopt_ipv6_hoplimit(r->sock_rx, 1); if (ret < 0) { - zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Failed to request IPv6 Hop Limit delivery", - r->vr->vrid); + r->vr->vrid, family2str(r->family)); failed = true; goto done; } @@ -1156,16 +1160,17 @@ static int vrrp_socket(struct vrrp_router *r) } vrrp_privs.change(ZPRIVS_LOWER); if (ret) { - zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Failed to bind Rx socket to %s: %s", - r->vr->vrid, r->vr->ifp->name, - safe_strerror(errno)); + r->vr->vrid, family2str(r->family), + r->vr->ifp->name, safe_strerror(errno)); failed = true; goto done; } DEBUGD(&vrrp_dbg_sock, - VRRP_LOGPFX VRRP_LOGPFX_VRID "Bound Rx socket to %s", - r->vr->vrid, r->vr->ifp->name); + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Bound Rx socket to %s", + r->vr->vrid, family2str(r->family), r->vr->ifp->name); /* Bind Rx socket to v6 multicast address */ struct sockaddr_in6 sa = {0}; @@ -1173,16 +1178,16 @@ static int vrrp_socket(struct vrrp_router *r) inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &sa.sin6_addr); if (bind(r->sock_rx, (struct sockaddr *)&sa, sizeof(sa))) { zlog_err( - VRRP_LOGPFX VRRP_LOGPFX_VRID - "Failed to bind Rx socket to VRRP %s multicast group: %s", + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to bind Rx socket to VRRP multicast group: %s", r->vr->vrid, family2str(r->family), safe_strerror(errno)); failed = true; goto done; } DEBUGD(&vrrp_dbg_sock, - VRRP_LOGPFX VRRP_LOGPFX_VRID - "Bound Rx socket to VRRP %s multicast group", + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Bound Rx socket to VRRP multicast group", r->vr->vrid, family2str(r->family)); /* Join VRRP IPv6 multicast group */ @@ -1193,15 +1198,15 @@ static int vrrp_socket(struct vrrp_router *r) ret = setsockopt(r->sock_rx, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)); if (ret < 0) { - zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Failed to join VRRP %s multicast group", + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to join VRRP multicast group", r->vr->vrid, family2str(r->family)); failed = true; goto done; } DEBUGD(&vrrp_dbg_sock, - VRRP_LOGPFX VRRP_LOGPFX_VRID - "Joined %s VRRP multicast group", + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Joined VRRP multicast group", r->vr->vrid, family2str(r->family)); /* Set outgoing interface for advertisements */ @@ -1209,16 +1214,17 @@ static int vrrp_socket(struct vrrp_router *r) &r->mvl_ifp->ifindex, sizeof(ifindex_t)); if (ret < 0) { zlog_warn( - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Could not set %s as outgoing multicast interface", - r->vr->vrid, r->mvl_ifp->name); + r->vr->vrid, family2str(r->family), + r->mvl_ifp->name); failed = true; goto done; } DEBUGD(&vrrp_dbg_sock, - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Set %s as outgoing multicast interface", - r->vr->vrid, r->mvl_ifp->name); + r->vr->vrid, family2str(r->family), r->mvl_ifp->name); } /* Bind Tx socket to link-local address */ @@ -1230,8 +1236,8 @@ static int vrrp_socket(struct vrrp_router *r) done: ret = 0; if (failed) { - zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID - "Failed to initialize VRRP %s router", + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to initialize VRRP router", r->vr->vrid, family2str(r->family)); if (r->sock_rx >= 0) { close(r->sock_rx); @@ -1280,22 +1286,22 @@ static void vrrp_change_state_master(struct vrrp_router *r) vrrp_ndisc_una_send_all(r); } else { DEBUGD(&vrrp_dbg_proto, - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Delaying VRRP advertisement until interface is up", - r->vr->vrid); + r->vr->vrid, family2str(r->family)); r->advert_pending = true; if (r->family == AF_INET) { DEBUGD(&vrrp_dbg_proto, - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Delaying VRRP gratuitous ARPs until interface is up", - r->vr->vrid); + r->vr->vrid, family2str(r->family)); r->garp_pending = true; } else if (r->family == AF_INET6) { DEBUGD(&vrrp_dbg_proto, - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Delaying VRRP unsolicited neighbor advertisement until interface is up", - r->vr->vrid); + r->vr->vrid, family2str(r->family)); r->ndisc_pending = true; } } @@ -1371,7 +1377,8 @@ static void vrrp_change_state(struct vrrp_router *r, int to) /* Call our handlers, then any subscribers */ 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, + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "%s -> %s", + r->vr->vrid, family2str(r->family), vrrp_state_names[r->fsm.state], vrrp_state_names[to]); r->fsm.state = to; @@ -1386,7 +1393,9 @@ static int vrrp_adver_timer_expire(struct thread *thread) struct vrrp_router *r = thread->arg; DEBUGD(&vrrp_dbg_proto, - VRRP_LOGPFX VRRP_LOGPFX_VRID "Adver_Timer expired", r->vr->vrid); + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Adver_Timer expired", + r->vr->vrid, family2str(r->family)); if (r->fsm.state == VRRP_STATE_MASTER) { /* Send an ADVERTISEMENT */ @@ -1397,9 +1406,10 @@ static int vrrp_adver_timer_expire(struct thread *thread) r->vr->advertisement_interval * 10, &r->t_adver_timer); } else { - zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID + zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Adver_Timer expired in state '%s'; this is a bug", - r->vr->vrid, vrrp_state_names[r->fsm.state]); + r->vr->vrid, family2str(r->family), + vrrp_state_names[r->fsm.state]); } return 0; @@ -1412,8 +1422,9 @@ static int vrrp_master_down_timer_expire(struct thread *thread) { struct vrrp_router *r = thread->arg; - zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "Master_Down_Timer expired", - r->vr->vrid); + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Master_Down_Timer expired", + r->vr->vrid, family2str(r->family)); thread_add_timer_msec(master, vrrp_adver_timer_expire, r, r->vr->advertisement_interval * 10, @@ -1448,8 +1459,8 @@ static int vrrp_startup(struct vrrp_router *r) /* Must have a valid macvlan interface available */ if (r->mvl_ifp == NULL && !vrrp_attach_interface(r)) { - zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID - "No appropriate interface for %s VRRP found", + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "No appropriate interface found", r->vr->vrid, family2str(r->family)); return -1; } @@ -1482,9 +1493,10 @@ static int vrrp_startup(struct vrrp_router *r) vrrp_recalculate_timers(r); zlog_info( - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "%s has priority set to 255 or owns primary Virtual Router IP %s; electing self as Master", - r->vr->vrid, r->vr->ifp->name, ipbuf); + r->vr->vrid, family2str(r->family), r->vr->ifp->name, + ipbuf); } if (r->priority == VRRP_PRIO_MASTER) { @@ -1528,9 +1540,10 @@ static int vrrp_shutdown(struct vrrp_router *r) break; case VRRP_STATE_INITIALIZE: DEBUGD(&vrrp_dbg_proto, - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Received '%s' event in '%s' state; ignoring", - r->vr->vrid, vrrp_event_names[VRRP_EVENT_SHUTDOWN], + r->vr->vrid, family2str(r->family), + vrrp_event_names[VRRP_EVENT_SHUTDOWN], vrrp_state_names[VRRP_STATE_INITIALIZE]); break; } @@ -1577,8 +1590,8 @@ static int (*vrrp_event_handlers[])(struct vrrp_router *r) = { */ int vrrp_event(struct vrrp_router *r, int event) { - zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID "'%s' event", r->vr->vrid, - vrrp_event_names[event]); + zlog_info(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "'%s' event", + r->vr->vrid, family2str(r->family), vrrp_event_names[event]); return vrrp_event_handlers[event](r); } @@ -1603,8 +1616,8 @@ static void vrrp_autoconfig_autoaddrupdate(struct vrrp_router *r) return; DEBUGD(&vrrp_dbg_auto, - VRRP_LOGPFX VRRP_LOGPFX_VRID - "Setting %s Virtual IP list to match IPv4 addresses on %s", + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Setting Virtual IP list to match IPv4 addresses on %s", r->vr->vrid, family2str(r->family), r->mvl_ifp->name); for (ALL_LIST_ELEMENTS_RO(r->mvl_ifp->connected, ln, c)) { is_v6_ll = (c->address->family == AF_INET6 @@ -1613,8 +1626,9 @@ static void vrrp_autoconfig_autoaddrupdate(struct vrrp_router *r) inet_ntop(r->family, &c->address->u.prefix, ipbuf, sizeof(ipbuf)); DEBUGD(&vrrp_dbg_auto, - VRRP_LOGPFX VRRP_LOGPFX_VRID "Adding %s", - r->vr->vrid, ipbuf); + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Adding %s", + r->vr->vrid, family2str(r->family), ipbuf); if (r->family == AF_INET) vrrp_add_ipv4(r->vr, c->address->u.prefix4); else @@ -1626,8 +1640,8 @@ static void vrrp_autoconfig_autoaddrupdate(struct vrrp_router *r) if (r->addrs->count == 0 && r->fsm.state != VRRP_STATE_INITIALIZE) { DEBUGD(&vrrp_dbg_auto, - VRRP_LOGPFX VRRP_LOGPFX_VRID - "%s Virtual IP list is empty; shutting down", + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Virtual IP list is empty; shutting down", r->vr->vrid, family2str(r->family)); vrrp_event(r, VRRP_EVENT_SHUTDOWN); } @@ -1645,18 +1659,19 @@ vrrp_autoconfig_autocreate(struct interface *mvl_ifp) return NULL; uint8_t vrid = mvl_ifp->hw_addr[5]; + uint8_t fam = mvl_ifp->hw_addr[4]; DEBUGD(&vrrp_dbg_auto, - VRRP_LOGPFX VRRP_LOGPFX_VRID "Autoconfiguring VRRP on %s", vrid, - p->name); + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Autoconfiguring VRRP on %s", + vrid, family2str(fam), p->name); vr = vrrp_vrouter_create(p, vrid, vrrp_autoconfig_version); if (!vr) { - zlog_warn(VRRP_LOGPFX - "Failed to autoconfigure VRRP instance %" PRIu8 - " on %s", - vrid, p->name); + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to autoconfigure VRRP on %s", + vrid, family2str(fam), p->name); return NULL; } @@ -1995,16 +2010,18 @@ void vrrp_if_up(struct interface *ifp) if (vr->v4->advert_pending) { DEBUGD(&vrrp_dbg_proto, VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX_FAM "Interface up; sending pending advertisement", - vr->vrid); + vr->vrid, family2str(vr->v4->family)); vrrp_send_advertisement(vr->v4); vr->v4->advert_pending = false; } if (vr->v4->garp_pending) { DEBUGD(&vrrp_dbg_proto, VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX_FAM "Interface up; sending pending gratuitous ARP", - vr->vrid); + vr->vrid, family2str(vr->v4->family)); vrrp_garp_send_all(vr->v4); vr->v4->garp_pending = false; } @@ -2013,16 +2030,18 @@ void vrrp_if_up(struct interface *ifp) if (vr->v6->advert_pending) { DEBUGD(&vrrp_dbg_proto, VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX_FAM "Interface up; sending pending advertisement", - vr->vrid); + vr->vrid, family2str(vr->v6->family)); vrrp_send_advertisement(vr->v6); vr->v6->advert_pending = false; } if (vr->v6->ndisc_pending) { DEBUGD(&vrrp_dbg_proto, VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX_FAM "Interface up; sending pending Unsolicited Neighbor Advertisement", - vr->vrid); + vr->vrid, family2str(vr->v6->family)); vrrp_ndisc_una_send_all(vr->v6); vr->v6->ndisc_pending = false; } diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 99cebd5c8a..9513009e70 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -41,7 +41,8 @@ #define VRRP_MCASTV6_GROUP 0xff020000000000000000000000000012 #define IPPROTO_VRRP 112 -#define VRRP_LOGPFX_VRID "[VRID: %u] " +#define VRRP_LOGPFX_VRID "[VRID %u] " +#define VRRP_LOGPFX_FAM "[%s] " /* Default defaults */ #define VRRP_DEFAULT_PRIORITY 100 diff --git a/vrrpd/vrrp_arp.c b/vrrpd/vrrp_arp.c index dd3c635beb..8e903e137f 100644 --- a/vrrpd/vrrp_arp.c +++ b/vrrpd/vrrp_arp.c @@ -123,9 +123,9 @@ void vrrp_garp_send(struct vrrp_router *r, struct in_addr *v4) /* If the interface doesn't support ARP, don't try sending */ if (ifp->flags & IFF_NOARP) { zlog_warn( - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Unable to send gratuitous ARP on %s; has IFF_NOARP\n", - r->vr->vrid, ifp->name); + r->vr->vrid, family2str(r->family), ifp->name); return; } @@ -136,18 +136,18 @@ void vrrp_garp_send(struct vrrp_router *r, struct in_addr *v4) inet_ntop(AF_INET, v4, astr, sizeof(astr)); DEBUGD(&vrrp_dbg_arp, - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Sending gratuitous ARP on %s for %s", - r->vr->vrid, ifp->name, astr); + r->vr->vrid, family2str(r->family), ifp->name, astr); if (DEBUG_MODE_CHECK(&vrrp_dbg_arp, DEBUG_MODE_ALL)) zlog_hexdump(garpbuf, garpbuf_len); sent_len = vrrp_send_garp(ifp, garpbuf, garpbuf_len); if (sent_len < 0) - zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Error sending gratuitous ARP on %s for %s", - r->vr->vrid, ifp->name, astr); + r->vr->vrid, family2str(r->family), ifp->name, astr); else ++r->stats.garp_tx_cnt; } @@ -161,9 +161,9 @@ void vrrp_garp_send_all(struct vrrp_router *r) /* If the interface doesn't support ARP, don't try sending */ if (ifp->flags & IFF_NOARP) { zlog_warn( - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Unable to send gratuitous ARP on %s; has IFF_NOARP\n", - r->vr->vrid, ifp->name); + r->vr->vrid, family2str(r->family), ifp->name); return; } diff --git a/vrrpd/vrrp_ndisc.c b/vrrpd/vrrp_ndisc.c index 0f59b23007..a9b15a1d16 100644 --- a/vrrpd/vrrp_ndisc.c +++ b/vrrpd/vrrp_ndisc.c @@ -170,9 +170,9 @@ int vrrp_ndisc_una_send(struct vrrp_router *r, struct ipaddr *ip) ipaddr2str(ip, ipbuf, sizeof(ipbuf)); DEBUGD(&vrrp_dbg_ndisc, - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Sending unsolicited Neighbor Advertisement on %s for %s", - r->vr->vrid, ifp->name, ipbuf); + r->vr->vrid, family2str(r->family), ifp->name, ipbuf); if (DEBUG_MODE_CHECK(&vrrp_dbg_ndisc, DEBUG_MODE_ALL) && DEBUG_MODE_CHECK(&vrrp_dbg_pkt, DEBUG_MODE_ALL)) @@ -183,9 +183,9 @@ int vrrp_ndisc_una_send(struct vrrp_router *r, struct ipaddr *ip) if (len < 0) { zlog_err( - VRRP_LOGPFX VRRP_LOGPFX_VRID + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Error sending unsolicited Neighbor Advertisement on %s for %s", - r->vr->vrid, ifp->name, ipbuf); + r->vr->vrid, family2str(r->family), ifp->name, ipbuf); ret = -1; } else { ++r->stats.una_tx_cnt; diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index 551e142e54..970c8d7d7e 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -156,22 +156,22 @@ size_t vrrp_pkt_adver_dump(char *buf, size_t buflen, struct vrrp_pkt *pkt) struct vrrp_hdr *hdr = &pkt->hdr; buf[0] = 0x00; - snprintf(tmpbuf, sizeof(tmpbuf), "Version: %u\n", (hdr->vertype >> 4)); + snprintf(tmpbuf, sizeof(tmpbuf), "version %u, ", (hdr->vertype >> 4)); rs += strlcat(buf, tmpbuf, buflen); - snprintf(tmpbuf, sizeof(tmpbuf), "Type: %u (%s)\n", + snprintf(tmpbuf, sizeof(tmpbuf), "type %u (%s), ", (hdr->vertype & 0x0F), vrrp_packet_names[(hdr->vertype & 0x0F)]); rs += strlcat(buf, tmpbuf, buflen); - snprintf(tmpbuf, sizeof(tmpbuf), "VRID: %u\n", hdr->vrid); + snprintf(tmpbuf, sizeof(tmpbuf), "vrid %u, ", hdr->vrid); rs += strlcat(buf, tmpbuf, buflen); - snprintf(tmpbuf, sizeof(tmpbuf), "Priority: %u\n", hdr->priority); + snprintf(tmpbuf, sizeof(tmpbuf), "priority %u, ", hdr->priority); rs += strlcat(buf, tmpbuf, buflen); - snprintf(tmpbuf, sizeof(tmpbuf), "Count IPvX: %u\n", hdr->naddr); + snprintf(tmpbuf, sizeof(tmpbuf), "#%u addresses, ", hdr->naddr); rs += strlcat(buf, tmpbuf, buflen); - snprintf(tmpbuf, sizeof(tmpbuf), "Max Adver Int: %u\n", + snprintf(tmpbuf, sizeof(tmpbuf), "max adver int %u, ", ntohs(hdr->v3.adver_int)); rs += strlcat(buf, tmpbuf, buflen); - snprintf(tmpbuf, sizeof(tmpbuf), "Checksum: %x\n", ntohs(hdr->chksum)); + snprintf(tmpbuf, sizeof(tmpbuf), "checksum %x", ntohs(hdr->chksum)); rs += strlcat(buf, tmpbuf, buflen); return rs; From e352b62527312f7beb3e74fb8487b314e3213e04 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 7 Mar 2019 19:14:14 +0000 Subject: [PATCH 117/153] vrrpd: protodown mvlans when shutting down This way VMACs get uninstalled from any lower-layer hardware. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index cba078a6c1..5f57dd7faa 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1554,6 +1554,9 @@ static int vrrp_shutdown(struct vrrp_router *r) THREAD_OFF(r->t_read); THREAD_OFF(r->t_write); + /* Protodown macvlan */ + vrrp_zclient_send_interface_protodown(r->mvl_ifp, true); + if (r->sock_rx > 0) { close(r->sock_rx); r->sock_rx = -1; From 52650486d7dbd8236a05e6c0ebd4baa0a0630dee Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 7 Mar 2019 21:18:03 +0000 Subject: [PATCH 118/153] Revert "vrrpd: protodown mvlans when shutting down" This reverts commit 9ed05814d5451379d37b6b3382b5b886afa1230f --- vrrpd/vrrp.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 5f57dd7faa..cba078a6c1 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1554,9 +1554,6 @@ static int vrrp_shutdown(struct vrrp_router *r) THREAD_OFF(r->t_read); THREAD_OFF(r->t_write); - /* Protodown macvlan */ - vrrp_zclient_send_interface_protodown(r->mvl_ifp, true); - if (r->sock_rx > 0) { close(r->sock_rx); r->sock_rx = -1; From 1760ce424b642c8ac9e7d4db72f9346533da1f98 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 8 Mar 2019 18:30:03 +0000 Subject: [PATCH 119/153] vrrpd: allow OS to choose VRRP IPv6 src address Under IPv6, Linux will look at our destination address and select the source address with the smallest scope that covers the destination. For the VRRP multicast address ff02::12, Linux will always select a link local address. We can take advantage of this behavior to avoid a subtle and complicated class of bugs caused by Zebra's semantics around inoperative interfaces. As far as Zebra is concerned, an inoperative interface has no addresses assigned to it. This is a real bummer for VRRP because it's quite possible that the IPv6 VRRP macvlan device is down when we attach to it, and we would really like to be able to know in advance which address we will be transmitting IPv6 advertisements from without having to bring an interface up to convince Zebra to tell us its address list. In the future, though, it would be better to be explicit about selecting the link local we want. This will require adding the ability to Zebra to read and send us address information for inoperative interfaces. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index cba078a6c1..6262cd692f 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -315,10 +315,10 @@ void vrrp_check_start(struct vrrp_vrouter *vr) #if 0 /* Macvlan interface must be admin up */ start = start && CHECK_FLAG(r->mvl_ifp->flags, IFF_UP); -#endif /* Macvlan interface must have a link local */ start = start && connected_get_linklocal(r->mvl_ifp); whynot = (!start && !whynot) ? "No link local address configured" : NULL; +#endif /* Macvlan interface must have a v6 IP besides the link local */ start = start && (r->mvl_ifp->connected->count >= 2); whynot = (!start && !whynot) ? "No Virtual IP address configured" : NULL; @@ -1125,6 +1125,13 @@ static int vrrp_socket(struct vrrp_router *r) VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Set %s as outgoing multicast interface", r->vr->vrid, family2str(r->family), r->mvl_ifp->name); + + /* Select and bind source address */ + if (vrrp_bind_to_primary_connected(r) < 0) { + failed = true; + goto done; + } + } else if (r->family == AF_INET6) { /* Always transmit IPv6 packets with hop limit set to 255 */ ret = setsockopt_ipv6_multicast_hops(r->sock_tx, 255); @@ -1227,12 +1234,6 @@ static int vrrp_socket(struct vrrp_router *r) r->vr->vrid, family2str(r->family), r->mvl_ifp->name); } - /* Bind Tx socket to link-local address */ - if (vrrp_bind_to_primary_connected(r) < 0) { - failed = true; - goto done; - } - done: ret = 0; if (failed) { From 4d2ea6bf2860039123aca3437365fe9f65e4ba7d Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 8 Mar 2019 18:35:04 +0000 Subject: [PATCH 120/153] Revert "Revert "vrrpd: protodown mvlans when shutting down"" Now that the requirement of knowing our VRRP IPv6 primary address in advance is lifted, it's no problem for us that the macvlans could be down when we get them. We can handle this in both the v4 and v6 case now, so we don't need to behave as if they should always be left up. This reverts commit 6eae67dabcbc31a2117ce3847c18ac52b3b76b1e. --- vrrpd/vrrp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 6262cd692f..cba79b544e 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1555,6 +1555,9 @@ static int vrrp_shutdown(struct vrrp_router *r) THREAD_OFF(r->t_read); THREAD_OFF(r->t_write); + /* Protodown macvlan */ + vrrp_zclient_send_interface_protodown(r->mvl_ifp, true); + if (r->sock_rx > 0) { close(r->sock_rx); r->sock_rx = -1; From 23804b4f048caeef3e7a10d9f957eced5908321a Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 8 Mar 2019 18:52:56 +0000 Subject: [PATCH 121/153] vrrpd: display primary address in json output And also, fill in the non-json output with a :: for the v6 primary since we're letting the operating system select which one it wants to use and we don't actually know what our primary address is. Another thing to revisit in the future... Signed-off-by: Quentin Young --- vrrpd/vrrp_vty.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 0498d3c260..e1f9d29b08 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -346,6 +346,8 @@ static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr) char ipstr[INET6_ADDRSTRLEN]; const char *stastr4 = vrrp_state_names[vr->v4->fsm.state]; const char *stastr6 = vrrp_state_names[vr->v6->fsm.state]; + char sipstr4[INET6_ADDRSTRLEN] = {}; + char sipstr6[INET6_ADDRSTRLEN] = {}; struct listnode *ln; struct ipaddr *ip; struct json_object *j = json_object_new_object(); @@ -370,6 +372,8 @@ static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr) json_object_string_add(v4, "interface", vr->v4->mvl_ifp ? vr->v4->mvl_ifp->name : ""); json_object_string_add(v4, "vmac", ethstr4); + ipaddr2str(&vr->v4->src, sipstr4, sizeof(sipstr4)); + json_object_string_add(v4, "primaryAddress", sipstr4); json_object_string_add(v4, "status", stastr4); json_object_int_add(v4, "effectivePriority", vr->v4->priority); json_object_int_add(v4, "masterAdverInterval", @@ -399,6 +403,10 @@ static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr) json_object_string_add(v6, "interface", vr->v6->mvl_ifp ? vr->v6->mvl_ifp->name : ""); json_object_string_add(v6, "vmac", ethstr6); + ipaddr2str(&vr->v6->src, sipstr6, sizeof(sipstr6)); + if (strlen(sipstr6) == 0 && vr->v6->src.ip.addr == 0x00) + strlcat(sipstr6, "::", sizeof(sipstr6)); + json_object_string_add(v6, "primaryAddress", sipstr6); json_object_string_add(v6, "status", stastr6); json_object_int_add(v6, "effectivePriority", vr->v6->priority); json_object_int_add(v6, "masterAdverInterval", @@ -444,8 +452,8 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) char ipstr[INET6_ADDRSTRLEN]; const char *stastr4 = vrrp_state_names[vr->v4->fsm.state]; const char *stastr6 = vrrp_state_names[vr->v6->fsm.state]; - char sipstr4[INET6_ADDRSTRLEN]; - char sipstr6[INET6_ADDRSTRLEN]; + char sipstr4[INET6_ADDRSTRLEN] = {}; + char sipstr6[INET6_ADDRSTRLEN] = {}; struct listnode *ln; struct ipaddr *ip; @@ -465,6 +473,8 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) vr->v6->mvl_ifp ? vr->v6->mvl_ifp->name : "None"); ipaddr2str(&vr->v4->src, sipstr4, sizeof(sipstr4)); ipaddr2str(&vr->v6->src, sipstr6, sizeof(sipstr6)); + if (strlen(sipstr6) == 0 && vr->v6->src.ip.addr == 0x00) + strlcat(sipstr6, "::", sizeof(sipstr6)); ttable_add_row(tt, "%s|%s", "Primary IP (v4)", sipstr4); ttable_add_row(tt, "%s|%s", "Primary IP (v6)", sipstr6); ttable_add_row(tt, "%s|%s", "Virtual MAC (v4)", ethstr4); From 8b28e459a17c661972362723ada9bf15e3957601 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 12 Mar 2019 17:31:39 +0000 Subject: [PATCH 122/153] vrrpd: allow centisecond precision for vrrpv2 The RFC is not clear about how precise the skew time calculation should be in VRRPv2. The advertisement interval is given in seconds, and the field in the advertisement packet only supports non-fractional seconds, so I was following this for calculating skew time as well. However the skew time formula in vrrpv2 always yields a fractional amount of seconds in the range (0-1), which right now means we always truncate to 0 seconds. I doubt this is what the RFC wanted so I'm allowing centisecond precision for skew time. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index cba79b544e..3f93ac441c 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -97,7 +97,7 @@ static void vrrp_mac_set(struct ethaddr *mac, bool v6, uint8_t vrid) */ static void vrrp_recalculate_timers(struct vrrp_router *r) { - uint16_t skm = (r->vr->version == 3) ? r->master_adver_interval : 1; + uint16_t skm = (r->vr->version == 3) ? r->master_adver_interval : 100; r->skew_time = ((256 - r->vr->priority) * skm) / 256; r->master_down_interval = (3 * r->master_adver_interval); r->master_down_interval += r->skew_time; From c2034b2550eb29a1564994838c94720e1a78ba8e Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 13 Mar 2019 17:18:36 +0000 Subject: [PATCH 123/153] vrrpd: include auth fields in v2 packet Based on looking at other vendors, seems I misinterpreted the RFC - type 0 auth (no authentication) still requires the authentication fields to be present, just set to all zero. This should fix VRRPv2 interop with other vendors. Signed-off-by: Quentin Young --- vrrpd/vrrp_packet.c | 9 +++++---- vrrpd/vrrp_packet.h | 17 +++++++++++------ 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index 970c8d7d7e..102750eafe 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -119,7 +119,7 @@ ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src, addrsz = IPADDRSZ(ips[0]); } - size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, numip); + size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, version, numip); *pkt = XCALLOC(MTYPE_VRRP_PKT, pktsize); (*pkt)->hdr.vertype |= version << 4; @@ -283,9 +283,10 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m, VRRP_PKT_VCHECK(((*pkt)->hdr.vertype & 0x0F) == 1, "Bad type %" PRIu8, (*pkt)->hdr.vertype & 0x0f); - /* # addresses check */ - size_t ves = VRRP_PKT_SIZE(family, (*pkt)->hdr.naddr); - VRRP_PKT_VCHECK(pktsize == ves, "Packet has incorrect # addresses"); + /* Exact size check */ + size_t ves = VRRP_PKT_SIZE(family, pktver, (*pkt)->hdr.naddr); + VRRP_PKT_VCHECK(pktsize == ves, "Packet has incorrect # addresses%s", + pktver == 2 ? " or missing auth fields" : ""); /* auth type check */ if (version == 2) diff --git a/vrrpd/vrrp_packet.h b/vrrpd/vrrp_packet.h index 2061b63c61..475e4780d5 100644 --- a/vrrpd/vrrp_packet.h +++ b/vrrpd/vrrp_packet.h @@ -70,6 +70,10 @@ struct vrrp_pkt { * When used, this is actually an array of one or the other, not an * array of union. If N v4 addresses are stored then * sizeof(addrs) == N * sizeof(struct in_addr). + * + * Under v2, the last 2 entries in this array are the authentication + * data fields. We don't support auth in v2 so these are always just 8 + * bytes of 0x00. */ union { struct in_addr v4; @@ -77,17 +81,18 @@ struct vrrp_pkt { } addrs[]; } __attribute__((packed)); -#define VRRP_PKT_SIZE(_f, _naddr) \ +#define VRRP_PKT_SIZE(_f, _ver, _naddr) \ ({ \ size_t _asz = ((_f) == AF_INET) ? sizeof(struct in_addr) \ : sizeof(struct in6_addr); \ - sizeof(struct vrrp_hdr) + (_asz * (_naddr)); \ + size_t _auth = 2 * sizeof(uint32_t) * (3 - (_ver)); \ + sizeof(struct vrrp_hdr) + (_asz * (_naddr)) + _auth; \ }) -#define VRRP_MIN_PKT_SIZE_V4 VRRP_PKT_SIZE(AF_INET, 1) -#define VRRP_MAX_PKT_SIZE_V4 VRRP_PKT_SIZE(AF_INET, 255) -#define VRRP_MIN_PKT_SIZE_V6 VRRP_PKT_SIZE(AF_INET6, 1) -#define VRRP_MAX_PKT_SIZE_V6 VRRP_PKT_SIZE(AF_INET6, 255) +#define VRRP_MIN_PKT_SIZE_V4 VRRP_PKT_SIZE(AF_INET, 3, 1) +#define VRRP_MAX_PKT_SIZE_V4 VRRP_PKT_SIZE(AF_INET, 2, 255) +#define VRRP_MIN_PKT_SIZE_V6 VRRP_PKT_SIZE(AF_INET6, 3, 1) +#define VRRP_MAX_PKT_SIZE_V6 VRRP_PKT_SIZE(AF_INET6, 3, 255) #define VRRP_MIN_PKT_SIZE VRRP_MIN_PKT_SIZE_V4 #define VRRP_MAX_PKT_SIZE VRRP_MAX_PKT_SIZE_V6 From 08671293e2d2017c91d66496c0a92033107d5adc Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 13 Mar 2019 18:37:48 +0000 Subject: [PATCH 124/153] vrrpd: fix v2 master_down_interval computation VRRPv2 uses the configured advertisement interval to compute the master down timer, whereas VRRPv3 uses the one advertised by the master. Fix computation to use the configured in in v2. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 3f93ac441c..4ab6605918 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -97,9 +97,11 @@ static void vrrp_mac_set(struct ethaddr *mac, bool v6, uint8_t vrid) */ static void vrrp_recalculate_timers(struct vrrp_router *r) { + uint16_t mdiadv = r->vr->version == 3 ? r->master_adver_interval + : r->vr->advertisement_interval; uint16_t skm = (r->vr->version == 3) ? r->master_adver_interval : 100; r->skew_time = ((256 - r->vr->priority) * skm) / 256; - r->master_down_interval = (3 * r->master_adver_interval); + r->master_down_interval = 3 * mdiadv; r->master_down_interval += r->skew_time; } From 958b1487639ea67a5c2acefe8ac79fd118c9dba5 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 14 Mar 2019 15:18:20 +0000 Subject: [PATCH 125/153] vrrpd: late bind to Tx address Stupid stupid stupid. I can just bind to the Tx address right before I Tx, since if I've gotten there I know my link is up. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 179 +++++++++++++++++++++++++++------------------------ 1 file changed, 94 insertions(+), 85 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 4ab6605918..3ffa2fbf68 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -623,6 +623,92 @@ static void vrrp_change_state(struct vrrp_router *r, int to); static int vrrp_adver_timer_expire(struct thread *thread); static int vrrp_master_down_timer_expire(struct thread *thread); +/* + * Finds the first connected address of the appropriate family on a VRRP + * router's interface and binds the Tx socket of the VRRP router to that + * address. + * + * Also sets src field of vrrp_router. + * + * r + * VRRP router to operate on + * + * Returns: + * 0 on success + * -1 on failure + */ +static int vrrp_bind_to_primary_connected(struct vrrp_router *r) +{ + char ipstr[INET6_ADDRSTRLEN]; + struct interface *ifp; + + /* + * A slight quirk: the RFC specifies that advertisements under IPv6 must + * be transmitted using the link local address of the source interface + */ + ifp = r->family == AF_INET ? r->vr->ifp : r->mvl_ifp; + + struct listnode *ln; + struct connected *c = NULL; + for (ALL_LIST_ELEMENTS_RO(ifp->connected, ln, c)) + if (c->address->family == r->family) { + if (r->family == AF_INET6 + && IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) + break; + else if (r->family == AF_INET) + break; + } + + if (c == NULL) { + zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to find address to bind on %s", + r->vr->vrid, family2str(r->family), ifp->name); + return -1; + } + + union sockunion su; + memset(&su, 0x00, sizeof(su)); + + switch (r->family) { + case AF_INET: + r->src.ipa_type = IPADDR_V4; + r->src.ipaddr_v4 = c->address->u.prefix4; + su.sin.sin_family = AF_INET; + su.sin.sin_addr = c->address->u.prefix4; + break; + case AF_INET6: + r->src.ipa_type = IPADDR_V6; + r->src.ipaddr_v6 = c->address->u.prefix6; + su.sin6.sin6_family = AF_INET6; + su.sin6.sin6_scope_id = ifp->ifindex; + su.sin6.sin6_addr = c->address->u.prefix6; + break; + } + + sockopt_reuseaddr(r->sock_tx); + if (bind(r->sock_tx, (const struct sockaddr *)&su, sizeof(su)) < 0) { + zlog_err( + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to bind Tx socket to primary IP address %s: %s", + r->vr->vrid, family2str(r->family), + inet_ntop(r->family, + (const void *)&c->address->u.prefix, ipstr, + sizeof(ipstr)), + safe_strerror(errno)); + return -1; + } else { + DEBUGD(&vrrp_dbg_sock, + VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Bound Tx socket to primary IP address %s", + r->vr->vrid, family2str(r->family), + inet_ntop(r->family, (const void *)&c->address->u.prefix, + ipstr, sizeof(ipstr))); + } + + return 0; +} + + /* * Create and multicast a VRRP ADVERTISEMENT message. * @@ -636,6 +722,10 @@ static void vrrp_send_advertisement(struct vrrp_router *r) struct ipaddr *addrs[r->addrs->count]; union sockunion dest; + if (r->src.ipa_type == IPADDR_NONE + && vrrp_bind_to_primary_connected(r) < 0) + return; + list_to_array(r->addrs, (void **)addrs, r->addrs->count); pktsz = vrrp_pkt_adver_build(&pkt, &r->src, r->vr->version, r->vr->vrid, @@ -903,91 +993,6 @@ done: return 0; } -/* - * Finds the first connected address of the appropriate family on a VRRP - * router's interface and binds the Tx socket of the VRRP router to that - * address. - * - * Also sets src field of vrrp_router. - * - * r - * VRRP router to operate on - * - * Returns: - * 0 on success - * -1 on failure - */ -static int vrrp_bind_to_primary_connected(struct vrrp_router *r) -{ - char ipstr[INET6_ADDRSTRLEN]; - struct interface *ifp; - - /* - * A slight quirk: the RFC specifies that advertisements under IPv6 must - * be transmitted using the link local address of the source interface - */ - ifp = r->family == AF_INET ? r->vr->ifp : r->mvl_ifp; - - struct listnode *ln; - struct connected *c = NULL; - for (ALL_LIST_ELEMENTS_RO(ifp->connected, ln, c)) - if (c->address->family == r->family) { - if (r->family == AF_INET6 - && IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) - break; - else if (r->family == AF_INET) - break; - } - - if (c == NULL) { - zlog_err(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM - "Failed to find address to bind on %s", - r->vr->vrid, family2str(r->family), ifp->name); - return -1; - } - - union sockunion su; - memset(&su, 0x00, sizeof(su)); - - switch (r->family) { - case AF_INET: - r->src.ipa_type = IPADDR_V4; - r->src.ipaddr_v4 = c->address->u.prefix4; - su.sin.sin_family = AF_INET; - su.sin.sin_addr = c->address->u.prefix4; - break; - case AF_INET6: - r->src.ipa_type = IPADDR_V6; - r->src.ipaddr_v6 = c->address->u.prefix6; - su.sin6.sin6_family = AF_INET6; - su.sin6.sin6_scope_id = ifp->ifindex; - su.sin6.sin6_addr = c->address->u.prefix6; - break; - } - - sockopt_reuseaddr(r->sock_tx); - if (bind(r->sock_tx, (const struct sockaddr *)&su, sizeof(su)) < 0) { - zlog_err( - VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM - "Failed to bind Tx socket to primary IP address %s: %s", - r->vr->vrid, family2str(r->family), - inet_ntop(r->family, - (const void *)&c->address->u.prefix, ipstr, - sizeof(ipstr)), - safe_strerror(errno)); - return -1; - } else { - DEBUGD(&vrrp_dbg_sock, - VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM - "Bound Tx socket to primary IP address %s", - r->vr->vrid, family2str(r->family), - inet_ntop(r->family, (const void *)&c->address->u.prefix, - ipstr, sizeof(ipstr))); - } - - return 0; -} - /* * Creates and configures VRRP router sockets. * @@ -1328,6 +1333,7 @@ static void vrrp_change_state_backup(struct vrrp_router *r) r->advert_pending = false; r->garp_pending = false; r->ndisc_pending = false; + memset(&r->src, 0x00, sizeof(r->src)); vrrp_zclient_send_interface_protodown(r->mvl_ifp, true); } @@ -1560,6 +1566,9 @@ static int vrrp_shutdown(struct vrrp_router *r) /* Protodown macvlan */ vrrp_zclient_send_interface_protodown(r->mvl_ifp, true); + /* Throw away our source address */ + memset(&r->src, 0x00, sizeof(r->src)); + if (r->sock_rx > 0) { close(r->sock_rx); r->sock_rx = -1; From e1a32d76375fb992d21be9b47742a7a3736dc1ca Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 14 Mar 2019 15:43:22 +0000 Subject: [PATCH 126/153] vrrpd: make useless assert useful Assert would always come back true due to improper placement. Signed-off-by: Quentin Young --- vrrpd/vrrp_packet.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index 102750eafe..fcbea8f695 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -112,13 +112,14 @@ ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src, size_t addrsz = 0; assert(version >= 2 && version <= 3); - assert(!(version == 2 && v6)); if (numip > 0) { v6 = IS_IPADDR_V6(ips[0]); addrsz = IPADDRSZ(ips[0]); } + assert(!(version == 2 && v6)); + size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, version, numip); *pkt = XCALLOC(MTYPE_VRRP_PKT, pktsize); From 26c7454b7fc6e2c408a453fc0de1a05a5f557bf6 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 14 Mar 2019 15:54:49 +0000 Subject: [PATCH 127/153] vrrpd: do not start v6 router if using VRRPv2 v2 doesn't support IPv6. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 3ffa2fbf68..ff39d78ed3 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -304,6 +304,9 @@ void vrrp_check_start(struct vrrp_vrouter *vr) r = vr->v6; /* Must not already be started */ start = r->fsm.state == VRRP_STATE_INITIALIZE; + /* Must not be v2 */ + start = vr->version != 2; + whynot = (!start && !whynot) ? "VRRPv2 does not support v6" : NULL; /* Must have a parent interface */ start = start && (vr->ifp != NULL); whynot = (!start && !whynot) ? "No base interface" : NULL; From 359c72baadf246d8a596ae5e80d4e6bc2aa6b7c3 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 14 Mar 2019 15:56:39 +0000 Subject: [PATCH 128/153] vrrpd: add asserts for VRRPv2 and IPv6 Disallow adding IPv6 addresses to VRRPv2 routers. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index ff39d78ed3..8f54bcf7c3 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -373,6 +373,7 @@ int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip) int af = (ip->ipa_type == IPADDR_V6) ? AF_INET6 : AF_INET; assert(r->family == af); + assert(!(r->vr->version == 2 && ip->ipa_type == IPADDR_V6)); if (vrrp_has_ip(r->vr, ip)) return 0; @@ -416,6 +417,8 @@ int vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4) int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6) { + assert(vr->version != 2); + struct ipaddr ip; ip.ipa_type = IPADDR_V6; ip.ipaddr_v6 = v6; @@ -1649,7 +1652,7 @@ static void vrrp_autoconfig_autoaddrupdate(struct vrrp_router *r) r->vr->vrid, family2str(r->family), ipbuf); if (r->family == AF_INET) vrrp_add_ipv4(r->vr, c->address->u.prefix4); - else + else if (r->vr->version == 3) vrrp_add_ipv6(r->vr, c->address->u.prefix6); } } From 65dc7dd387744967153a45cb8fe3040a6572727e Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 14 Mar 2019 19:51:32 +0000 Subject: [PATCH 129/153] zebra: don't protodown a NULL interface We were running into some problems where VRRP is trying to protodown interfaces that no longer exist. While this is a minor bug in its own right, this was crashing Zebra because Zebra was not doing a null check after its ifindex lookup. Signed-off-by: Quentin Young --- zebra/zapi_msg.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index ca371186b7..6a478560ab 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -1352,10 +1352,17 @@ static void zread_interface_set_protodown(ZAPI_HANDLER_ARGS) /* set ifdown */ ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), ifindex); - zlog_info("Setting interface %s (%u): protodown %s", ifp->name, ifindex, - down ? "on" : "off"); - zebra_if_set_protodown(ifp, down); + if (ifp) { + zlog_info("Setting interface %s (%u): protodown %s", ifp->name, + ifindex, down ? "on" : "off"); + zebra_if_set_protodown(ifp, down); + } else { + zlog_warn( + "Cannot set protodown %s for interface %u; does not exist", + down ? "on" : "off", ifindex); + } + stream_failure: return; From d5dc62c40bfd2bc3a0cf5603930379d45d236f51 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 15 Mar 2019 19:54:37 +0000 Subject: [PATCH 130/153] vrrpd: tweak startup criteria for v6 VRRP router * Remove check for having at least 2 IPv6 addresses on the macvlan device; this was only taking place in v6, and breaking the ability to start VRRP on an IPv6 macvlan that was already set to protodown on * Improve log messaging indicating that we cannot start because we haven't got any VIPs configured Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 8f54bcf7c3..f2271278cc 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -323,12 +323,13 @@ void vrrp_check_start(struct vrrp_vrouter *vr) /* Macvlan interface must have a link local */ start = start && connected_get_linklocal(r->mvl_ifp); whynot = (!start && !whynot) ? "No link local address configured" : NULL; -#endif /* Macvlan interface must have a v6 IP besides the link local */ start = start && (r->mvl_ifp->connected->count >= 2); - whynot = (!start && !whynot) ? "No Virtual IP address configured" : NULL; + whynot = (!start && !whynot) ? "No Virtual IP configured on macvlan device" : NULL; +#endif /* Must have at least one VIP configured */ start = start && r->addrs->count > 0; + whynot = (!start && !whynot) ? "No Virtual IP address configured" : NULL; if (start) vrrp_event(r, VRRP_EVENT_STARTUP); else if (whynot) From cb44d47667d3d6dfff9d4b76d19948d66606a45d Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 20 Mar 2019 04:50:22 +0000 Subject: [PATCH 131/153] vrrpd: change all user facing times to ms Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 4 ++-- vrrpd/vrrp.h | 3 +++ vrrpd/vrrp_vty.c | 52 ++++++++++++++++++++++++++++-------------------- 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index f2271278cc..47f5c59d14 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -2229,8 +2229,8 @@ int vrrp_config_write_interface(struct vty *vty) && ++writes) vty_out(vty, " vrrp %" PRIu8 - " advertisement-interval %" PRIu16 "\n", - vr->vrid, vr->advertisement_interval); + " advertisement-interval %d\n", + vr->vrid, vr->advertisement_interval * CS2MS); if (vr->priority != vd.priority && ++writes) vty_out(vty, " vrrp %" PRIu8 " priority %" PRIu8 "\n", diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 9513009e70..08ef84ec16 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -51,6 +51,9 @@ #define VRRP_DEFAULT_ACCEPT true #define VRRP_DEFAULT_SHUTDOWN false +/* User compatibility constant */ +#define CS2MS 10 + /* Configured defaults */ struct vrrp_defaults { uint8_t priority; diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index e1f9d29b08..2040d0e8b2 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -137,9 +137,9 @@ DEFPY(vrrp_priority, DEFPY(vrrp_advertisement_interval, vrrp_advertisement_interval_cmd, - "[no] vrrp (1-255)$vrid advertisement-interval (1-4096)", + "[no] vrrp (1-255)$vrid advertisement-interval (10-40950)", NO_STR VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR - "Advertisement interval in centiseconds") + "Advertisement interval in milliseconds; must be multiple of 10") { VTY_DECLVAR_CONTEXT(interface, ifp); @@ -147,6 +147,14 @@ DEFPY(vrrp_advertisement_interval, uint16_t newadvint = no ? vd.advertisement_interval : advertisement_interval; + if (newadvint % 10 != 0) { + vty_out(vty, "%% Value must be a multiple of 10\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* all internal computations are in centiseconds */ + newadvint /= CS2MS; + VROUTER_GET_VTY(vty, ifp, vrid, vr); vrrp_set_advertisement_interval(vr, newadvint); @@ -377,10 +385,10 @@ static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr) json_object_string_add(v4, "status", stastr4); json_object_int_add(v4, "effectivePriority", vr->v4->priority); json_object_int_add(v4, "masterAdverInterval", - vr->v4->master_adver_interval); - json_object_int_add(v4, "skewTime", vr->v4->skew_time); + vr->v4->master_adver_interval * CS2MS); + json_object_int_add(v4, "skewTime", vr->v4->skew_time * CS2MS); json_object_int_add(v4, "masterDownInterval", - vr->v4->master_down_interval); + vr->v4->master_down_interval * CS2MS); /* v4 stats */ json_object_int_add(v4_stats, "adverTx", vr->v4->stats.adver_tx_cnt); json_object_int_add(v4_stats, "adverRx", vr->v4->stats.adver_rx_cnt); @@ -410,10 +418,10 @@ static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr) json_object_string_add(v6, "status", stastr6); json_object_int_add(v6, "effectivePriority", vr->v6->priority); json_object_int_add(v6, "masterAdverInterval", - vr->v6->master_adver_interval); - json_object_int_add(v6, "skewTime", vr->v6->skew_time); + vr->v6->master_adver_interval * CS2MS); + json_object_int_add(v6, "skewTime", vr->v6->skew_time * CS2MS); json_object_int_add(v6, "masterDownInterval", - vr->v6->master_down_interval); + vr->v6->master_down_interval * CS2MS); /* v6 stats */ json_object_int_add(v6_stats, "adverTx", vr->v6->stats.adver_tx_cnt); json_object_int_add(v6_stats, "adverRx", vr->v6->stats.adver_rx_cnt); @@ -490,14 +498,14 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) vr->preempt_mode ? "Yes" : "No"); ttable_add_row(tt, "%s|%s", "Accept Mode", vr->accept_mode ? "Yes" : "No"); - ttable_add_row(tt, "%s|%" PRIu16 " cs", "Advertisement Interval", - vr->advertisement_interval); - ttable_add_row(tt, "%s|%" PRIu16 " cs", + ttable_add_row(tt, "%s|%d ms", "Advertisement Interval", + vr->advertisement_interval * CS2MS); + ttable_add_row(tt, "%s|%d ms", "Master Advertisement Interval (v4)", - vr->v4->master_adver_interval); - ttable_add_row(tt, "%s|%" PRIu16 " cs", + vr->v4->master_adver_interval * CS2MS); + ttable_add_row(tt, "%s|%d ms", "Master Advertisement Interval (v6)", - vr->v6->master_adver_interval); + vr->v6->master_adver_interval * CS2MS); ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Tx (v4)", vr->v4->stats.adver_tx_cnt); ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Tx (v6)", @@ -514,14 +522,14 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) vr->v4->stats.trans_cnt); ttable_add_row(tt, "%s|%" PRIu32, "State transitions (v6)", vr->v6->stats.trans_cnt); - ttable_add_row(tt, "%s|%" PRIu16 " cs", "Skew Time (v4)", - vr->v4->skew_time); - ttable_add_row(tt, "%s|%" PRIu16 " cs", "Skew Time (v6)", - vr->v6->skew_time); - ttable_add_row(tt, "%s|%" PRIu16 " cs", "Master Down Interval (v4)", - vr->v4->master_down_interval); - ttable_add_row(tt, "%s|%" PRIu16 " cs", "Master Down Interval (v6)", - vr->v6->master_down_interval); + ttable_add_row(tt, "%s|%d ms", "Skew Time (v4)", + vr->v4->skew_time * CS2MS); + ttable_add_row(tt, "%s|%d ms", "Skew Time (v6)", + vr->v6->skew_time * CS2MS); + ttable_add_row(tt, "%s|%d ms", "Master Down Interval (v4)", + vr->v4->master_down_interval * CS2MS); + ttable_add_row(tt, "%s|%d ms", "Master Down Interval (v6)", + vr->v6->master_down_interval * CS2MS); ttable_add_row(tt, "%s|%u", "IPv4 Addresses", vr->v4->addrs->count); char fill[35]; From 0a7b203ea0cb9ad23d2adc6482fe1a1d83dc0398 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 20 Mar 2019 04:55:35 +0000 Subject: [PATCH 132/153] vrrpd: display configured adv int in json output Signed-off-by: Quentin Young --- vrrpd/vrrp_vty.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 2040d0e8b2..01cc5b651e 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -376,6 +376,8 @@ static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr) json_object_boolean_add(j, "preemptMode", vr->preempt_mode); json_object_boolean_add(j, "acceptMode", vr->accept_mode); json_object_string_add(j, "interface", vr->ifp->name); + json_object_int_add(j, "advertisementInterval", + vr->advertisement_interval * CS2MS); /* v4 */ json_object_string_add(v4, "interface", vr->v4->mvl_ifp ? vr->v4->mvl_ifp->name : ""); From 0fb0aeba64c6845d841fc2f70b4cfa2870024dfe Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 25 Mar 2019 21:18:06 +0000 Subject: [PATCH 133/153] vrrpd: include vrrp_packet.h in subdir.am Signed-off-by: Quentin Young --- vrrpd/subdir.am | 1 + 1 file changed, 1 insertion(+) diff --git a/vrrpd/subdir.am b/vrrpd/subdir.am index 17c8fc2792..a328f969d6 100644 --- a/vrrpd/subdir.am +++ b/vrrpd/subdir.am @@ -27,6 +27,7 @@ noinst_HEADERS += \ vrrpd/vrrp_debug.h \ vrrpd/vrrp_memory.h \ vrrpd/vrrp_ndisc.h \ + vrrpd/vrrp_packet.h \ vrrpd/vrrp_vty.h \ vrrpd/vrrp_zebra.h \ # end From 8261c2e3c7eed3a3fbe058043dfe088ab0257592 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 15 Apr 2019 22:15:28 +0000 Subject: [PATCH 134/153] doc: change vrrp to listen on port 2619 2617 was taken by BFD while VRRP was indev. Signed-off-by: Quentin Young --- vrrpd/vrrp_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c index 0ac77350eb..8e2b6249ff 100644 --- a/vrrpd/vrrp_main.c +++ b/vrrpd/vrrp_main.c @@ -105,7 +105,7 @@ static const struct frr_yang_module_info *vrrp_yang_modules[] = { &frr_interface_info, }; -#define VRRP_VTY_PORT 2617 +#define VRRP_VTY_PORT 2619 FRR_DAEMON_INFO(vrrpd, VRRP, .vty_port = VRRP_VTY_PORT, .proghelp = "Virtual Router Redundancy Protocol", From 4edac1f75f2c8e3366feacf17482169547108bba Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 15 Apr 2019 22:03:43 +0000 Subject: [PATCH 135/153] doc: add vrrpd(8) Signed-off-by: Quentin Young --- doc/manpages/common-options.rst | 1 + doc/manpages/conf.py | 1 + doc/manpages/defines.rst | 2 +- doc/manpages/index.rst | 1 + doc/manpages/subdir.am | 1 + doc/manpages/vrrpd.rst | 40 +++++++++++++++++++++++++++++++++ 6 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 doc/manpages/vrrpd.rst diff --git a/doc/manpages/common-options.rst b/doc/manpages/common-options.rst index a5977a6ebb..a47a233c08 100644 --- a/doc/manpages/common-options.rst +++ b/doc/manpages/common-options.rst @@ -126,6 +126,7 @@ These following options control the daemon's VTY (interactive command line) inte staticd 2616 bfdd 2617 fabricd 2618 + vrrpd 2619 Port 2607 is used for ospfd's Opaque LSA API. diff --git a/doc/manpages/conf.py b/doc/manpages/conf.py index 46240de1c0..e7813d8176 100644 --- a/doc/manpages/conf.py +++ b/doc/manpages/conf.py @@ -334,6 +334,7 @@ man_pages = [ ('frr', 'frr', 'a systemd interaction script', [], 1), ('bfdd', 'bfdd', fwfrr.format("a bfd"), [], 8), ('fabricd', 'fabricd', fwfrr.format("an OpenFabric "), [], 8), + ('vrrpd', 'vrrpd', fwfrr.format("a VRRP"), [], 8), ] # -- Options for Texinfo output ------------------------------------------- diff --git a/doc/manpages/defines.rst b/doc/manpages/defines.rst index cdf5e1967e..2a6a9fd1bd 100644 --- a/doc/manpages/defines.rst +++ b/doc/manpages/defines.rst @@ -1,3 +1,3 @@ .. |synopsis-options| replace:: [-d|-t|-dt] [-C] [-f config-file] [-i pid-file] [-z zclient-path] [-u user] [-g group] [-A vty-addr] [-P vty-port] [-M module[:options]] [-N pathspace] [--vty_socket vty-path] [--moduledir module-path] .. |synopsis-options-hv| replace:: [-h] [-v] -.. |seealso-programs| replace:: zebra(8), vtysh(1), ripd(8), ripngd(8), ospfd(8), ospf6d(8), bgpd(8), isisd(8), babeld(8), nhrpd(8), pimd(8), pbrd(8), ldpd(8), eigrpd(8), staticd(8), fabricd(8), mtracebis(8) +.. |seealso-programs| replace:: zebra(8), vtysh(1), ripd(8), ripngd(8), ospfd(8), ospf6d(8), bgpd(8), isisd(8), babeld(8), nhrpd(8), pimd(8), pbrd(8), ldpd(8), eigrpd(8), staticd(8), fabricd(8), vrrpd(8), mtracebis(8) diff --git a/doc/manpages/index.rst b/doc/manpages/index.rst index 053555c4e4..40f06efdfe 100644 --- a/doc/manpages/index.rst +++ b/doc/manpages/index.rst @@ -26,4 +26,5 @@ watchfrr zebra vtysh + vrrpd frr diff --git a/doc/manpages/subdir.am b/doc/manpages/subdir.am index a4457c9c45..19d2d8d6ae 100644 --- a/doc/manpages/subdir.am +++ b/doc/manpages/subdir.am @@ -30,6 +30,7 @@ man_RSTFILES = \ doc/manpages/zebra.rst \ doc/manpages/bfdd.rst \ doc/manpages/bfd-options.rst \ + doc/manpages/vrrpd.rst \ # end EXTRA_DIST += $(man_RSTFILES) diff --git a/doc/manpages/vrrpd.rst b/doc/manpages/vrrpd.rst new file mode 100644 index 0000000000..3670916050 --- /dev/null +++ b/doc/manpages/vrrpd.rst @@ -0,0 +1,40 @@ +***** +VRRPD +***** + +.. include:: defines.rst +.. |DAEMON| replace:: vrrpd + +SYNOPSIS +======== +|DAEMON| |synopsis-options-hv| + +|DAEMON| |synopsis-options| + +DESCRIPTION +=========== +|DAEMON| is a routing component that works with the FRRouting routing engine. +It implements the Virtual Router Redundancy Protocol. Support for both VRRPv2 +and VRRPv3 is present. + +OPTIONS +======= +OPTIONS available for the |DAEMON| command: + +.. include:: common-options.rst + +FILES +===== + +|INSTALL_PREFIX_SBIN|/|DAEMON| + The default location of the |DAEMON| binary. + +|INSTALL_PREFIX_ETC|/|DAEMON|.conf + The default location of the |DAEMON| config file. + +$(PWD)/|DAEMON|.log + If the |DAEMON| process is configured to output logs to a file, then you + will find this file in the directory where you started |DAEMON|. + +.. include:: epilogue.rst + From 2fff50ec018fb6ea64329399b103be7f4359f021 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 18 Apr 2019 20:03:35 +0000 Subject: [PATCH 136/153] vrrpd, lib: style fixes Fixup: * Blank lines after declarations * Trailing whitespace * Braces and parentheses Signed-off-by: Quentin Young --- lib/command.c | 2 +- lib/ipaddr.h | 2 +- lib/json.h | 3 ++- vrrpd/vrrp.c | 57 ++++++++++++++++++++++++++++++++++----------- vrrpd/vrrp.h | 2 +- vrrpd/vrrp_debug.c | 2 +- vrrpd/vrrp_main.c | 2 +- vrrpd/vrrp_ndisc.c | 6 +++-- vrrpd/vrrp_packet.c | 12 ++++++++++ vrrpd/vrrp_vty.c | 14 +++++++---- vrrpd/vrrp_zebra.c | 2 +- zebra/if_netlink.c | 6 ++--- 12 files changed, 79 insertions(+), 31 deletions(-) diff --git a/lib/command.c b/lib/command.c index 73bb9580cd..18426e0c51 100644 --- a/lib/command.c +++ b/lib/command.c @@ -149,7 +149,7 @@ const char *node_names[] = { "bfd", /* BFD_NODE */ "bfd peer", /* BFD_PEER_NODE */ "openfabric", // OPENFABRIC_NODE - "vrrp", // VRRP_NODE + "vrrp", /* VRRP_NODE */ }; /* clang-format on */ diff --git a/lib/ipaddr.h b/lib/ipaddr.h index 503431a7c0..1c2399fdd3 100644 --- a/lib/ipaddr.h +++ b/lib/ipaddr.h @@ -57,7 +57,7 @@ struct ipaddr { #define SET_IPADDR_V6(p) (p)->ipa_type = IPADDR_V6 #define IPADDRSZ(p) \ - IS_IPADDR_V4((p)) ? sizeof(struct in_addr) : sizeof(struct in6_addr) + (IS_IPADDR_V4((p)) ? sizeof(struct in_addr) : sizeof(struct in6_addr)) static inline int str2ipaddr(const char *str, struct ipaddr *ip) { diff --git a/lib/json.h b/lib/json.h index b35f221b99..c4d566b318 100644 --- a/lib/json.h +++ b/lib/json.h @@ -61,7 +61,8 @@ extern void json_object_string_add(struct json_object *obj, const char *key, const char *s); extern void json_object_int_add(struct json_object *obj, const char *key, int64_t i); -void json_object_boolean_add(struct json_object *obj, const char *key, bool val); +void json_object_boolean_add(struct json_object *obj, const char *key, + bool val); extern void json_object_boolean_false_add(struct json_object *obj, const char *key); extern void json_object_boolean_true_add(struct json_object *obj, diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 47f5c59d14..d24f918b12 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -100,6 +100,7 @@ static void vrrp_recalculate_timers(struct vrrp_router *r) uint16_t mdiadv = r->vr->version == 3 ? r->master_adver_interval : r->vr->advertisement_interval; uint16_t skm = (r->vr->version == 3) ? r->master_adver_interval : 100; + r->skew_time = ((256 - r->vr->priority) * skm) / 256; r->master_down_interval = 3 * mdiadv; r->master_down_interval += r->skew_time; @@ -167,6 +168,7 @@ static bool vrrp_ifp_has_vrrp_mac(struct interface *ifp) { struct ethaddr vmac4; struct ethaddr vmac6; + vrrp_mac_set(&vmac4, 0, 0x00); vrrp_mac_set(&vmac6, 1, 0x00); @@ -293,7 +295,8 @@ void vrrp_check_start(struct vrrp_vrouter *vr) #endif /* Must have at least one VIP configured */ start = start && r->addrs->count > 0; - whynot = (!start && !whynot) ? "No Virtual IP address configured" : NULL; + whynot = + (!start && !whynot) ? "No Virtual IP address configured" : NULL; if (start) vrrp_event(r, VRRP_EVENT_STARTUP); else if (whynot) @@ -322,14 +325,18 @@ void vrrp_check_start(struct vrrp_vrouter *vr) start = start && CHECK_FLAG(r->mvl_ifp->flags, IFF_UP); /* Macvlan interface must have a link local */ start = start && connected_get_linklocal(r->mvl_ifp); - whynot = (!start && !whynot) ? "No link local address configured" : NULL; + whynot = + (!start && !whynot) ? "No link local address configured" : NULL; /* Macvlan interface must have a v6 IP besides the link local */ start = start && (r->mvl_ifp->connected->count >= 2); - whynot = (!start && !whynot) ? "No Virtual IP configured on macvlan device" : NULL; + whynot = (!start && !whynot) + ? "No Virtual IP configured on macvlan device" + : NULL; #endif /* Must have at least one VIP configured */ start = start && r->addrs->count > 0; - whynot = (!start && !whynot) ? "No Virtual IP address configured" : NULL; + whynot = + (!start && !whynot) ? "No Virtual IP address configured" : NULL; if (start) vrrp_event(r, VRRP_EVENT_STARTUP); else if (whynot) @@ -381,6 +388,7 @@ int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip) if (!vrrp_is_owner(r->vr->ifp, ip) && r->is_owner) { char ipbuf[INET6_ADDRSTRLEN]; + inet_ntop(r->family, &ip->ip, ipbuf, sizeof(ipbuf)); zlog_err( VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM @@ -411,6 +419,7 @@ int vrrp_add_ip(struct vrrp_router *r, struct ipaddr *ip) int vrrp_add_ipv4(struct vrrp_vrouter *vr, struct in_addr v4) { struct ipaddr ip; + ip.ipa_type = IPADDR_V4; ip.ipaddr_v4 = v4; return vrrp_add_ip(vr->v4, &ip); @@ -421,6 +430,7 @@ int vrrp_add_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6) assert(vr->version != 2); struct ipaddr ip; + ip.ipa_type = IPADDR_V6; ip.ipaddr_v6 = v6; return vrrp_add_ip(vr->v6, &ip); @@ -454,6 +464,7 @@ int vrrp_del_ip(struct vrrp_router *r, struct ipaddr *ip) int vrrp_del_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6) { struct ipaddr ip; + ip.ipa_type = IPADDR_V6; ip.ipaddr_v6 = v6; return vrrp_del_ip(vr->v6, &ip); @@ -462,6 +473,7 @@ int vrrp_del_ipv6(struct vrrp_vrouter *vr, struct in6_addr v6) int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4) { struct ipaddr ip; + ip.ipa_type = IPADDR_V4; ip.ipaddr_v4 = v4; return vrrp_del_ip(vr->v4, &ip); @@ -473,6 +485,7 @@ int vrrp_del_ipv4(struct vrrp_vrouter *vr, struct in_addr v4) static void vrrp_router_addr_list_del_cb(void *val) { struct ipaddr *ip = val; + XFREE(MTYPE_VRRP_IP, ip); } @@ -490,6 +503,7 @@ static bool vrrp_attach_interface(struct vrrp_router *r) { /* Search for existing interface with computed MAC address */ struct interface **ifps; + size_t ifps_cnt = if_lookup_by_hwaddr( r->vmac.octet, sizeof(r->vmac.octet), &ifps, VRF_DEFAULT); @@ -502,6 +516,7 @@ static bool vrrp_attach_interface(struct vrrp_router *r) */ unsigned int candidates = 0; struct interface *selection = NULL; + for (unsigned int i = 0; i < ifps_cnt; i++) { if (ifps[i]->link_ifindex != r->vr->ifp->ifindex) ifps[i] = NULL; @@ -515,6 +530,7 @@ static bool vrrp_attach_interface(struct vrrp_router *r) XFREE(MTYPE_TMP, ifps); char ethstr[ETHER_ADDR_STRLEN]; + prefix_mac2str(&r->vmac, ethstr, sizeof(ethstr)); assert(!!selection == !!candidates); @@ -563,12 +579,10 @@ static void vrrp_router_destroy(struct vrrp_router *r) if (r->is_active) vrrp_event(r, VRRP_EVENT_SHUTDOWN); - if (r->sock_rx >= 0) { + if (r->sock_rx >= 0) close(r->sock_rx); - } - if (r->sock_tx >= 0) { + if (r->sock_tx >= 0) close(r->sock_tx); - } /* FIXME: also delete list elements */ list_delete(&r->addrs); @@ -617,6 +631,7 @@ void vrrp_vrouter_destroy(struct vrrp_vrouter *vr) struct vrrp_vrouter *vrrp_lookup(struct interface *ifp, uint8_t vrid) { struct vrrp_vrouter vr; + vr.vrid = vrid; vr.ifp = ifp; @@ -657,6 +672,7 @@ static int vrrp_bind_to_primary_connected(struct vrrp_router *r) struct listnode *ln; struct connected *c = NULL; + for (ALL_LIST_ELEMENTS_RO(ifp->connected, ln, c)) if (c->address->family == r->family) { if (r->family == AF_INET6 @@ -674,6 +690,7 @@ static int vrrp_bind_to_primary_connected(struct vrrp_router *r) } union sockunion su; + memset(&su, 0x00, sizeof(su)); switch (r->family) { @@ -792,11 +809,13 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, struct vrrp_pkt *pkt, size_t pktsize) { char sipstr[INET6_ADDRSTRLEN]; - ipaddr2str(src, sipstr, sizeof(sipstr)); char dipstr[INET6_ADDRSTRLEN]; + + ipaddr2str(src, sipstr, sizeof(sipstr)); ipaddr2str(&r->src, dipstr, sizeof(dipstr)); char dumpbuf[BUFSIZ]; + vrrp_pkt_adver_dump(dumpbuf, sizeof(dumpbuf), pkt); DEBUGD(&vrrp_dbg_proto, VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM @@ -950,6 +969,7 @@ static int vrrp_read(struct thread *thread) struct msghdr m; struct iovec iov; + iov.iov_base = r->ibuf; iov.iov_len = sizeof(r->ibuf); m.msg_name = &sa; @@ -1038,6 +1058,7 @@ static int vrrp_socket(struct vrrp_router *r) if (r->sock_rx < 0 || r->sock_tx < 0) { const char *rxtx = r->sock_rx < 0 ? "Rx" : "Tx"; + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Can't create VRRP %s socket", r->vr->vrid, family2str(r->family), rxtx); @@ -1049,6 +1070,7 @@ static int vrrp_socket(struct vrrp_router *r) if (r->family == AF_INET) { /* Set Tx socket to always Tx with TTL set to 255 */ int ttl = 255; + ret = setsockopt(r->sock_tx, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); if (ret < 0) { @@ -1087,6 +1109,7 @@ static int vrrp_socket(struct vrrp_router *r) /* Bind Rx socket to v4 multicast address */ struct sockaddr_in sa = {0}; + sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl(VRRP_MCASTV4_GROUP); if (bind(r->sock_rx, (struct sockaddr *)&sa, sizeof(sa))) { @@ -1106,6 +1129,7 @@ static int vrrp_socket(struct vrrp_router *r) /* Join Rx socket to VRRP IPv4 multicast group */ struct connected *c = listhead(r->vr->ifp->connected)->data; struct in_addr v4 = c->address->u.prefix4; + ret = setsockopt_ipv4_multicast(r->sock_rx, IP_ADD_MEMBERSHIP, v4, htonl(VRRP_MCASTV4_GROUP), r->vr->ifp->ifindex); @@ -1123,6 +1147,7 @@ static int vrrp_socket(struct vrrp_router *r) /* Set outgoing interface for advertisements */ struct ip_mreqn mreqn = {}; + mreqn.imr_ifindex = r->mvl_ifp->ifindex; ret = setsockopt(r->sock_tx, IPPROTO_IP, IP_MULTICAST_IF, (void *)&mreqn, sizeof(mreqn)); @@ -1195,6 +1220,7 @@ static int vrrp_socket(struct vrrp_router *r) /* Bind Rx socket to v6 multicast address */ struct sockaddr_in6 sa = {0}; + sa.sin6_family = AF_INET6; inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &sa.sin6_addr); if (bind(r->sock_rx, (struct sockaddr *)&sa, sizeof(sa))) { @@ -1213,6 +1239,7 @@ static int vrrp_socket(struct vrrp_router *r) /* Join VRRP IPv6 multicast group */ struct ipv6_mreq mreq; + inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &mreq.ipv6mr_multiaddr); mreq.ipv6mr_interface = r->vr->ifp->ifindex; @@ -1271,7 +1298,7 @@ done: /* State machine ----------------------------------------------------------- */ -DEFINE_HOOK(vrrp_change_state_hook, (struct vrrp_router * r, int to), (r, 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. @@ -1490,6 +1517,7 @@ static int vrrp_startup(struct vrrp_router *r) /* Create socket */ if (r->sock_rx < 0 || r->sock_tx < 0) { int ret = vrrp_socket(r); + if (ret < 0 || r->sock_tx < 0 || r->sock_rx < 0) return ret; } @@ -1499,8 +1527,8 @@ static int vrrp_startup(struct vrrp_router *r) /* Configure effective priority */ struct ipaddr *primary = (struct ipaddr *)listhead(r->addrs)->data; - char ipbuf[INET6_ADDRSTRLEN]; + inet_ntop(r->family, &primary->ip.addr, ipbuf, sizeof(ipbuf)); if (r->vr->priority == VRRP_PRIO_MASTER @@ -2191,9 +2219,8 @@ void vrrp_if_address_del(struct interface *ifp) * in this function should be protected by a check that the interface * is up. */ - if (if_is_operative(ifp)) { + if (if_is_operative(ifp)) vrrp_autoconfig_if_address_del(ifp); - } } /* Other ------------------------------------------------------------------- */ @@ -2240,6 +2267,7 @@ int vrrp_config_write_interface(struct vty *vty) for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ipln, ip)) { char ipbuf[INET6_ADDRSTRLEN]; + ipaddr2str(ip, ipbuf, sizeof(ipbuf)); vty_out(vty, " vrrp %" PRIu8 " ip %s\n", vr->vrid, ipbuf); @@ -2248,6 +2276,7 @@ int vrrp_config_write_interface(struct vty *vty) for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ipln, ip)) { char ipbuf[INET6_ADDRSTRLEN]; + ipaddr2str(ip, ipbuf, sizeof(ipbuf)); vty_out(vty, " vrrp %" PRIu8 " ipv6 %s\n", vr->vrid, ipbuf); @@ -2293,8 +2322,8 @@ int vrrp_config_write_global(struct vty *vty) static unsigned int vrrp_hash_key(void *arg) { struct vrrp_vrouter *vr = arg; - char key[IFNAMSIZ + 64]; + snprintf(key, sizeof(key), "%s@%" PRIu8, vr->ifp->name, vr->vrid); return string_hash_make(key); diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 08ef84ec16..ee2a8249a5 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -464,7 +464,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_router * r, int to), (r, to)); +DECLARE_HOOK(vrrp_change_state_hook, (struct vrrp_router *r, int to), (r, to)); /* * Trigger a VRRP event on a given Virtual Router.. diff --git a/vrrpd/vrrp_debug.c b/vrrpd/vrrp_debug.c index b841bca78a..09d5e780cf 100644 --- a/vrrpd/vrrp_debug.c +++ b/vrrpd/vrrp_debug.c @@ -35,7 +35,7 @@ struct debug vrrp_dbg_sock = {0, "VRRP sockets"}; struct debug vrrp_dbg_zebra = {0, "VRRP Zebra events"}; struct debug *vrrp_debugs[] = { - &vrrp_dbg_arp, + &vrrp_dbg_arp, &vrrp_dbg_auto, &vrrp_dbg_ndisc, &vrrp_dbg_pkt, diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c index 8e2b6249ff..d6a43be8e8 100644 --- a/vrrpd/vrrp_main.c +++ b/vrrpd/vrrp_main.c @@ -57,7 +57,7 @@ struct zebra_privs_t vrrp_privs = { .cap_num_p = array_size(_caps_p), .cap_num_i = 0}; -struct option longopts[] = {{0}}; +struct option longopts[] = {}; /* Master of threads. */ struct thread_master *master; diff --git a/vrrpd/vrrp_ndisc.c b/vrrpd/vrrp_ndisc.c index a9b15a1d16..8a439e97ca 100644 --- a/vrrpd/vrrp_ndisc.c +++ b/vrrpd/vrrp_ndisc.c @@ -4,7 +4,7 @@ * Quentin Young * * Portions: - * Copyright (C) 2001-2017 Alexandre Cassen + * Copyright (C) 2001-2017 Alexandre Cassen * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -134,6 +134,7 @@ static int vrrp_ndisc_una_build(struct interface *ifp, struct ipaddr *ip, uint32_t len = sizeof(struct nd_neighbor_advert) + sizeof(struct nd_opt_hdr) + ETH_ALEN; struct ipv6_ph ph = {}; + ph.src = ip6h->ip6_src; ph.dst = ip6h->ip6_dst; ph.ulpl = htonl(len); @@ -149,8 +150,8 @@ int vrrp_ndisc_una_send(struct vrrp_router *r, struct ipaddr *ip) int ret = 0; struct interface *ifp = r->mvl_ifp; - uint8_t buf[VRRP_NDISC_SIZE]; + ret = vrrp_ndisc_una_build(ifp, ip, buf, sizeof(buf)); if (ret == -1) @@ -167,6 +168,7 @@ int vrrp_ndisc_una_send(struct vrrp_router *r, struct ipaddr *ip) sll.sll_ifindex = (int)ifp->ifindex; char ipbuf[INET6_ADDRSTRLEN]; + ipaddr2str(ip, ipbuf, sizeof(ipbuf)); DEBUGD(&vrrp_dbg_ndisc, diff --git a/vrrpd/vrrp_packet.c b/vrrpd/vrrp_packet.c index fcbea8f695..c3f2afba4c 100644 --- a/vrrpd/vrrp_packet.c +++ b/vrrpd/vrrp_packet.c @@ -76,10 +76,12 @@ static uint16_t vrrp_pkt_checksum(struct vrrp_pkt *pkt, size_t pktsize, bool v6 = (src->ipa_type == IPADDR_V6); uint16_t chksum_pre = pkt->hdr.chksum; + pkt->hdr.chksum = 0; if (v6) { struct ipv6_ph ph = {}; + ph.src = src->ipaddr_v6; inet_pton(AF_INET6, VRRP_MCASTV6_GROUP_STR, &ph.dst); ph.ulpl = htons(pktsize); @@ -87,6 +89,7 @@ static uint16_t vrrp_pkt_checksum(struct vrrp_pkt *pkt, size_t pktsize, chksum = in_cksum_with_ph6(&ph, pkt, pktsize); } else if (!v6 && ((pkt->hdr.vertype >> 4) == 3)) { struct ipv4_ph ph = {}; + ph.src = src->ipaddr_v4; inet_pton(AF_INET, VRRP_MCASTV4_GROUP_STR, &ph.dst); ph.proto = 112; @@ -121,6 +124,7 @@ ssize_t vrrp_pkt_adver_build(struct vrrp_pkt **pkt, struct ipaddr *src, assert(!(version == 2 && v6)); size_t pktsize = VRRP_PKT_SIZE(v6 ? AF_INET6 : AF_INET, version, numip); + *pkt = XCALLOC(MTYPE_VRRP_PKT, pktsize); (*pkt)->hdr.vertype |= version << 4; @@ -229,10 +233,12 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m, /* Extract source address */ struct sockaddr_in *sa = m->msg_name; + src->ipa_type = IPADDR_V4; src->ipaddr_v4 = sa->sin_addr; } else if (family == AF_INET6) { struct cmsghdr *c; + for (c = CMSG_FIRSTHDR(m); c != NULL; CMSG_NXTHDR(m, c)) { if (c->cmsg_level == IPPROTO_IPV6 && c->cmsg_type == IPV6_HOPLIMIT) @@ -242,6 +248,7 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m, VRRP_PKT_VCHECK(!!c, "IPv6 Hop Limit not received"); uint8_t *hoplimit = CMSG_DATA(c); + VRRP_PKT_VCHECK(*hoplimit == 255, "IPv6 Hop Limit is %" PRIu8 "; should be 255", *hoplimit); @@ -251,6 +258,7 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m, /* Extract source address */ struct sockaddr_in6 *sa = m->msg_name; + src->ipa_type = IPADDR_V6; memcpy(&src->ipaddr_v6, &sa->sin6_addr, sizeof(struct in6_addr)); @@ -272,10 +280,12 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m, /* Version check */ uint8_t pktver = (*pkt)->hdr.vertype >> 4; + VRRP_PKT_VCHECK(pktver == version, "Bad version %u", pktver); /* Checksum check */ uint16_t chksum = vrrp_pkt_checksum(*pkt, pktsize, src); + VRRP_PKT_VCHECK((*pkt)->hdr.chksum == chksum, "Bad VRRP checksum %" PRIx16 "; should be %" PRIx16 "", (*pkt)->hdr.chksum, chksum); @@ -286,6 +296,7 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m, /* Exact size check */ size_t ves = VRRP_PKT_SIZE(family, pktver, (*pkt)->hdr.naddr); + VRRP_PKT_VCHECK(pktsize == ves, "Packet has incorrect # addresses%s", pktver == 2 ? " or missing auth fields" : ""); @@ -298,6 +309,7 @@ ssize_t vrrp_pkt_parse_datagram(int family, int version, struct msghdr *m, /* Addresses check */ char vbuf[INET6_ADDRSTRLEN]; uint8_t *p = (uint8_t *)(*pkt)->addrs; + for (uint8_t i = 0; i < (*pkt)->hdr.naddr; i++) { VRRP_PKT_VCHECK(inet_ntop(family, p, vbuf, sizeof(vbuf)), "Bad IP address, #%" PRIu8, i); diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 01cc5b651e..0cb33bffed 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -52,7 +52,7 @@ (unsigned int)_vrid); \ return CMD_WARNING_CONFIG_FAILED; \ } \ - } while (0); + } while (0) /* clang-format off */ @@ -177,19 +177,20 @@ DEFPY(vrrp_ip, bool activated = false; bool failed = false; int ret = CMD_SUCCESS; + int oldstate; VROUTER_GET_VTY(vty, ifp, vrid, vr); bool will_activate = (vr->v4->fsm.state == VRRP_STATE_INITIALIZE); if (no) { - int oldstate = vr->v4->fsm.state; + oldstate = vr->v4->fsm.state; failed = vrrp_del_ipv4(vr, ip); vrrp_check_start(vr); deactivated = (vr->v4->fsm.state == VRRP_STATE_INITIALIZE && oldstate != VRRP_STATE_INITIALIZE); } else { - int oldstate = vr->v4->fsm.state; + oldstate = vr->v4->fsm.state; failed = vrrp_add_ipv4(vr, ip); vrrp_check_start(vr); activated = (vr->v4->fsm.state != VRRP_STATE_INITIALIZE @@ -230,6 +231,7 @@ DEFPY(vrrp_ip6, bool activated = false; bool failed = false; int ret = CMD_SUCCESS; + int oldstate; VROUTER_GET_VTY(vty, ifp, vrid, vr); @@ -242,13 +244,13 @@ DEFPY(vrrp_ip6, bool will_activate = (vr->v6->fsm.state == VRRP_STATE_INITIALIZE); if (no) { - int oldstate = vr->v6->fsm.state; + oldstate = vr->v6->fsm.state; failed = vrrp_del_ipv6(vr, ipv6); vrrp_check_start(vr); deactivated = (vr->v6->fsm.state == VRRP_STATE_INITIALIZE && oldstate != VRRP_STATE_INITIALIZE); } else { - int oldstate = vr->v6->fsm.state; + oldstate = vr->v6->fsm.state; failed = vrrp_add_ipv6(vr, ipv6); vrrp_check_start(vr); activated = (vr->v6->fsm.state != VRRP_STATE_INITIALIZE @@ -535,6 +537,7 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) ttable_add_row(tt, "%s|%u", "IPv4 Addresses", vr->v4->addrs->count); char fill[35]; + memset(fill, '.', sizeof(fill)); fill[sizeof(fill) - 1] = 0x00; if (vr->v4->addrs->count) { @@ -556,6 +559,7 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) } char *table = ttable_dump(tt, "\n"); + vty_out(vty, "\n%s\n", table); XFREE(MTYPE_TMP, table); ttable_del(tt); diff --git a/vrrpd/vrrp_zebra.c b/vrrpd/vrrp_zebra.c index 223d61001e..8d5a6453e1 100644 --- a/vrrpd/vrrp_zebra.c +++ b/vrrpd/vrrp_zebra.c @@ -32,7 +32,7 @@ #define VRRP_LOGPFX "[ZEBRA] " -static struct zclient *zclient = NULL; +static struct zclient *zclient; static void vrrp_zebra_debug_if_state(struct interface *ifp, vrf_id_t vrf_id, const char *func) diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index e7d988cd9f..df8d4bfe15 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -1406,7 +1406,7 @@ int netlink_protodown(struct interface *ifp, bool down) char buf[NL_PKT_BUF_SIZE]; } req; - memset(&req, 0, sizeof req); + memset(&req, 0, sizeof(req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); req.n.nlmsg_flags = NLM_F_REQUEST; @@ -1415,8 +1415,8 @@ int netlink_protodown(struct interface *ifp, bool down) req.ifa.ifi_index = ifp->ifindex; - addattr_l(&req.n, sizeof req, IFLA_PROTO_DOWN, &down, 4); - addattr_l(&req.n, sizeof req, IFLA_LINK, &ifp->ifindex, 4); + addattr_l(&req.n, sizeof(req), IFLA_PROTO_DOWN, &down, 4); + addattr_l(&req.n, sizeof(req), IFLA_LINK, &ifp->ifindex, 4); return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, 0); From b58ab00f720cd1f43d69672c6aa2bb18a53859dd Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 18 Apr 2019 21:59:24 +0000 Subject: [PATCH 137/153] doc: add VRRP documentation Signed-off-by: Quentin Young --- doc/extra/spelling_wordlist.txt | 6 + doc/user/index.rst | 1 + doc/user/subdir.am | 1 + doc/user/vrrp.rst | 506 ++++++++++++++++++++++++++++++++ 4 files changed, 514 insertions(+) create mode 100644 doc/user/vrrp.rst diff --git a/doc/extra/spelling_wordlist.txt b/doc/extra/spelling_wordlist.txt index 2944592962..271f5e49f1 100644 --- a/doc/extra/spelling_wordlist.txt +++ b/doc/extra/spelling_wordlist.txt @@ -80,6 +80,9 @@ IP iptables ipv IPv +IPvX +IPv4 +IPv6 isis isisd lan @@ -99,6 +102,8 @@ LSAs Masaki Mbit Mbits +macvlan +macvlans mib motd mpls @@ -227,6 +232,7 @@ VN VNC vrf vrfs +vrrp vty Vty vtysh diff --git a/doc/user/index.rst b/doc/user/index.rst index 4c218c6580..4e14de6737 100644 --- a/doc/user/index.rst +++ b/doc/user/index.rst @@ -56,6 +56,7 @@ Protocols sharp static vnc + vrrp ######## Appendix diff --git a/doc/user/subdir.am b/doc/user/subdir.am index 08b5dc954c..1e4d86c722 100644 --- a/doc/user/subdir.am +++ b/doc/user/subdir.am @@ -37,6 +37,7 @@ user_RSTFILES = \ doc/user/snmptrap.rst \ doc/user/static.rst \ doc/user/vnc.rst \ + doc/user/vrrp.rst \ doc/user/vtysh.rst \ doc/user/zebra.rst \ doc/user/bfd.rst \ diff --git a/doc/user/vrrp.rst b/doc/user/vrrp.rst new file mode 100644 index 0000000000..a2dd950987 --- /dev/null +++ b/doc/user/vrrp.rst @@ -0,0 +1,506 @@ +.. _vrrp: + +**** +VRRP +**** + +:abbr:`VRRP` stands for Virtual Router Redundancy Protocol. This protocol is +used to allow multiple backup routers on the same segment to take over +operation of each others' IP addresses if the primary router fails. This is +typically used to provide fault-tolerant gateways to hosts on the segment. + +FRR implements VRRPv2 (:rfc:`3768`) and VRRPv3 (:rfc:`5798`). For VRRPv2, no +authentication methods are supported; these are deprecated in the VRRPv2 +specification as they do not provide any additional security over the base +protocol. + +.. note:: + + - VRRP is supported on Linux 5.1+ + - VRRP does not implement Accept_Mode + +.. _vrrp-starting: + +Starting VRRP +============= + +The configuration file for *vrrpd* is :file:`vrrpd.conf`. The typical location +of :file:`vrrpd.conf` is |INSTALL_PREFIX_ETC|/vrrpd.conf. + +If using integrated config, then :file:`vrrpd.conf` need not be present and +:file:`frr.conf` is read instead. + +.. program:: vrrpd + +:abbr:`VRRP` supports all the common FRR daemon start options which are +documented elsewhere. + +.. _vrrp-protocol-overview: + +Protocol Overview +================= + +From :rfc:`5798`: + + VRRP specifies an election protocol that dynamically assigns responsibility + for a virtual router to one of the VRRP routers on a LAN. The VRRP router + controlling the IPv4 or IPv6 address(es) associated with a virtual router is + called the Master, and it forwards packets sent to these IPv4 or IPv6 + addresses. VRRP Master routers are configured with virtual IPv4 or IPv6 + addresses, and VRRP Backup routers infer the address family of the virtual + addresses being carried based on the transport protocol. 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. The election process + provides dynamic failover in the forwarding responsibility should the Master + become unavailable. For IPv4, the advantage gained from using VRRP is a + higher-availability default path without requiring configuration of dynamic + routing or router discovery protocols on every end-host. For IPv6, the + advantage gained from using VRRP for IPv6 is a quicker switchover to Backup + routers than can be obtained with standard IPv6 Neighbor Discovery + mechanisms. + +VRRP accomplishes these goals primarily by using a virtual MAC address shared +between the physical routers participating in a VRRP virtual router. This +reduces churn in the neighbor tables of hosts and downstream switches and makes +router failover theoretically transparent to these devices. + +FRR implements the election protocol and handles changing the operating system +interface configuration in response to protocol state changes. + +As a consequence of the shared virtual MAC requirement, VRRP is currently +supported only on Linux, as Linux is the only operating system that provides +the necessary features in its network stack to make implementing this protocol +feasible. + +When a VRRP router is acting as the Master router, FRR allows the interface(s) +with the backed-up IP addresses to remain up and functional. When the router +transitions to Backup state, these interfaces are set into ``protodown`` mode. +This is an interface mode that is functionally equivalent to ``NO-CARRIER``. +Physical drivers typically use this state indication to drop traffic on an +interface. In the case of VRRP, the interfaces in question are macvlan devices, +which are virtual interfaces. Since the IP addresses managed by VRRP are on +these interfaces, this has the same effect as removing these addresses from the +interface, but is implemented as a state flag. + +.. _vrrp-configuration: + +Configuring VRRP +================ + +VRRP is configured on a per-interface basis, with some global defaults +accessible outside the interface context. + +.. _vrrp-system-configuration: + +System Configuration +-------------------- + +FRR's VRRP implementation uses Linux macvlan devices to to implement the shared +virtual MAC feature of the protocol. Currently, it does not create those system +interfaces - they must be configured outside of FRR before VRRP can be enabled +on them. + +Each interface on which VRRP will be enabled must have at least one macvlan +device configured with the virtual MAC and placed in the proper operation mode. +The addresses backed up by VRRP are assigned to these interfaces. + +Suppose you have an interface ``eth0`` with the following configuration: + +.. code-block:: console + + $ ip link show eth0 + 2: eth0: mtu 1500 qdisc fq_codel state UP group default qlen 1000 + link/ether 02:17:45:00:aa:aa brd ff:ff:ff:ff:ff:ff + inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic eth0 + valid_lft 72532sec preferred_lft 72532sec + inet 10.0.2.16/24 brd 10.0.2.255 scope global dynamic eth0 + valid_lft 72532sec preferred_lft 72532sec + inet6 fe80::17:45ff:fe00:aaaa/64 scope link + valid_lft forever preferred_lft forever + +Suppose the address you want to back up is ``10.0.2.16``, and will be managed +by the virtual router with id ``5``. A macvlan device with the appropriate MAC +address must be created before VRRP can begin to operate. + +If you are using ``ifupdown2``, the configuration is as follows: + +.. code-block:: console + + iface eth0 + ... + vrrp 5 10.0.2.16/24 2001:0db8::0370:7334/64 + +Applying this configuration with ``ifreload -a`` will create the appropriate +macvlan device. If you are using ``iproute2``, the equivalent configuration is: + +.. code-block:: console + + ip link add vrrp4-2-1 link eth0 addrgenmode random type macvlan mode bridge + ip link set dev vrrp4-2-1 address 00:00:5e:00:01:05 + ip addr add 10.0.2.16/24 dev vrrp4-2-1 + ip link set dev vrrp4-2-1 up + + ip link add vrrp6-2-1 link eth0 addrgenmode random type macvlan mode bridge + ip link set dev vrrp4-2-1 address 00:00:5e:00:02:05 + ip addr add 2001:db8::370:7334/64 dev vrrp6-2-1 + ip link set dev vrrp6-2-1 up + +In either case, the created interfaces will look like this: + +.. code-block:: console + + $ ip addr show vrrp4-2-1 + 5: vrrp4-2-1@eth0: mtu 1500 qdisc noqueue state UP group default qlen 1000 + link/ether 00:00:5e:00:01:05 brd ff:ff:ff:ff:ff:ff + inet 10.0.2.16/24 scope global vrrp4-2-1 + valid_lft forever preferred_lft forever + inet6 fe80::dc56:d11a:e69d:ea72/64 scope link stable-privacy + valid_lft forever preferred_lft forever + + $ ip addr show vrrp6-2-1 + 8: vrrp6-2-1@eth0: mtu 1500 qdisc noqueue state UP group default qlen 1000 + link/ether 00:00:5e:00:02:05 brd ff:ff:ff:ff:ff:ff + inet6 2001:db8::370:7334/64 scope global + valid_lft forever preferred_lft forever + inet6 fe80::f8b7:c9dd:a1e8:9844/64 scope link stable-privacy + valid_lft forever preferred_lft forever + +Using ``vrrp4-2-1`` as an example, a few things to note about this interface: + +- It is slaved to ``eth0``; any packets transmitted on this interface will + egress via ``eth0`` +- Its MAC address is set to the VRRP IPv4 virtual MAC specified by the RFC for + :abbr:`VRID (Virtual Router ID)` ``5`` +- The link local address on the interface is not derived from the interface + MAC + +First to note is that packets transmitted on this interface will egress via +``eth0``, but with their Ethernet source MAC set to the VRRP virtual MAC. This +is how FRR's VRRP implementation accomplishes the virtual MAC requirement on +real hardware. + +Ingress traffic is a more complicated matter. Macvlan devices have multiple +operating modes that change how ingress traffic is handled. Of relevance to +FRR's implementation are the ``bridge`` and ``private`` modes. In ``private`` +mode, any ingress traffic on ``eth0`` (in our example) with a source MAC +address equal to the MAC address on any of ``eth0``'s macvlan devices will be +placed *only* on that macvlan device. This curious behavior is undesirable, +since FRR's implementation of VRRP needs to be able to receive advertisements +from neighbors while in Backup mode - i.e., while its macvlan devices are in +``protodown on``. If the macvlan devices are instead set to ``bridge`` mode, +all ingress traffic shows up on all interfaces - including ``eth0`` - +regardless of source MAC or any other factor. Consequently, macvlans used by +FRR for VRRP must be set to ``bridge`` mode or the protocol will not function +correctly. + +As for the MAC address assigned to this interface, the last byte of the address +holds the :abbr:`VRID (Virtual Router Identifier)`, in this case ``0x05``. The +second to last byte is ``0x01``, as specified by the RFC for IPv4 operation. +The IPv6 MAC address is be identical except that the second to last byte is +defined to be ``0x02``. Two things to note from this arrangement: + +1. There can only be up to 255 unique Virtual Routers on an interface (only 1 + byte is available for the VRID) +2. IPv4 and IPv6 addresses must be assigned to different macvlan devices, + because they have different MAC addresses + +Finally, take note of the generated IPv6 link local address on the interface. +For interfaces on which VRRP will operate in IPv6 mode, this link local +*cannot* be derived using the usual EUI-64 method. This is because VRRP +advertisements are sent from the link local address of this interface, and VRRP +uses the source address of received advertisements as part of its election +algorithm. If the IPv6 link local of a router is equivalent to the IPv6 link +local in a received advertisement, this can cause both routers to assume the +Master role (very bad). ``ifupdown`` knows to set the ``addrgenmode`` of the +interface properly, but when using ``iproute2`` to create the macvlan devices, +you must be careful to manually specify ``addrgenmode random``. + +A brief note on the Backup state +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +It is worth noting here that an alternate choice for the implementation of the +Backup state, such as removing all the IP addresses assigned to the macvlan +device or deleting their local routes instead of setting the device into +``protodown on``, would allow the protocol to function regardless of whether +the macvlan device(s) are set to ``private`` or ``bridge`` mode. Indeed, the +strange behavior of the kernel macvlan driver in ``private`` mode, whereby it +performs what may be thought of as a sort of interface-level layer 2 "NAT" +based on source MAC, can be traced back to a patch clearly designed to +accommodate a VRRP implementation from a different vendor. However, the +``protodown`` based implementation allows for a configuration model in which +FRR does not dynamically manage the addresses assigned on a system, but instead +just manages interface state. Such a scenario was in mind when this protocol +implementation was initially built, which is why the other choices are not +currently present. Since support for placing macvlan devices into ``protodown`` +was not added to Linux until version 5.1, this also explains the relatively +restrictive kernel versioning requirement. + +In the future other methods of implementing Backup state may be added along +with a configuration knob to choose between them. + +.. _vrrp-interface-configuration: + +Interface Configuration +----------------------- + +Continuing with the example from the previous section, we assume the macvlan +interfaces have been properly configured with the proper MAC addresses and the +IPvX addresses assigned. + +In FRR, a possible VRRPv3 configuration for this interface is: + +.. code-block:: frr + + interface eth0 + vrrp 5 version 3 + vrrp 5 priority 200 + vrrp 5 advertisement-interval 1500 + vrrp 5 ip 10.0.2.16 + vrrp 5 ipv6 2001:0db8::0370:7334 + +VRRP will activate as soon as the first IPvX address configuration line is +encountered. If you do not want this behavior, use the :clicmd:`vrrp (1-255) +shutdown` command, and apply the ``no`` form when you are ready to activate +VRRP. + +At this point executing ``show vrrp`` will display the following: + +.. code-block:: console + + ubuntu-bionic# show vrrp + + Virtual Router ID 5 + Protocol Version 3 + Autoconfigured Yes + Shutdown No + Interface eth0 + VRRP interface (v4) vrrp4-2-5 + VRRP interface (v6) vrrp6-2-5 + Primary IP (v4) 10.0.2.15 + Primary IP (v6) fe80::9b91:7155:bf6a:d386 + Virtual MAC (v4) 00:00:5e:00:01:05 + Virtual MAC (v6) 00:00:5e:00:02:05 + Status (v4) Master + Status (v6) Master + Priority 200 + Effective Priority (v4) 200 + Effective Priority (v6) 200 + Preempt Mode Yes + Accept Mode Yes + Advertisement Interval 1500 ms + Master Advertisement Interval (v4) 1000 ms + Master Advertisement Interval (v6) 1000 ms + Advertisements Tx (v4) 14 + Advertisements Tx (v6) 14 + Advertisements Rx (v4) 0 + Advertisements Rx (v6) 0 + Gratuitous ARP Tx (v4) 1 + Neigh. Adverts Tx (v6) 1 + State transitions (v4) 2 + State transitions (v6) 2 + Skew Time (v4) 210 ms + Skew Time (v6) 210 ms + Master Down Interval (v4) 3210 ms + Master Down Interval (v6) 3210 ms + IPv4 Addresses 1 + .................................. 10.0.2.16 + IPv6 Addresses 1 + .................................. 2001:db8::370:7334 + +At this point, VRRP has sent gratuitous ARP requests for the IPv4 address, +Unsolicited Neighbor Advertisements for the IPv6 address, and has asked Zebra +to send Router Advertisements on its behalf. It is also transmitting VRRPv3 +advertisements on the macvlan interfaces. + +The Primary IP fields are of some interest, as the behavior may be +counterintuitive. These fields show the source address used for VRRP +advertisements. Although VRRPv3 advertisements are always transmitted on the +macvlan interfaces, in the IPv4 case the source address is set to the primary +IPv4 address on the base interface, ``eth0`` in this case. This is a protocol +requirement, and IPv4 VRRP will not function unless the base interface has an +IPv4 address assigned. In the IPv6 case the link local of the macvlan interface +is used. + +If any misconfiguration errors are detected, VRRP for the misconfigured address +family will not come up and the configuration issue will be logged to FRR's +configured logging destination. + +Per the RFC, IPv4 and IPv6 virtual routers are independent of each other. For +instance, it is possible for the IPv4 router to be in Backup state while the +IPv6 router is in Master state; or for either to be completely inoperative +while the other is operative, etc. Instances sharing the same base interface +and VRID are shown together in the show output for conceptual convenience. + +To complete your VRRP deployment, configure other routers on the segment with +the exact same system and FRR configuration as shown above. Provided each +router receives the others' VRRP advertisements, the Master election protocol +will run, one Master will be elected, and the other routers will place their +macvlan interfaces into ``protodown on`` until Master fails or priority values +are changed to favor another router. + +Switching the protocol version to VRRPv2 is accomplished simply by changing +``version 3`` to ``version 2`` in the VRID configuration line. Note that VRRPv2 +does not support IPv6, so any IPv6 configuration will be rejected by FRR when +using VRRPv2. + +.. note:: + + All VRRP routers initially start in Backup state, and wait for the + calculated Master Down Interval to pass before they assume Master status. + This prevents downstream neighbor table churn if another router is already + Master with higher priority, meaning this box will ultimately assume Backup + status once the first advertisement is received. However, if the calculated + Master Down Interval is high and this router is configured such that it will + ultimately assume Master status, then it will take a while for this to + happen. This is a known issue. + + +All interface configuration commands are documented below. + +.. index:: [no] vrrp (1-255) [version (2-3)] +.. clicmd:: [no] vrrp (1-255) [version (2-3)] + + Create a VRRP router with the specified VRID on the interface. Optionally + specify the protocol version. If the protocol version is not specified, the + default is VRRPv3. + +.. index:: [no] vrrp (1-255) advertisement-interval (10-40950) +.. clicmd:: [no] vrrp (1-255) advertisement-interval (10-40950) + + Set the advertisement interval. This is the interval at which VRRP + advertisements will be sent. Values are given in milliseconds, but must be + multiples of 10, as VRRP itself uses centiseconds. + +.. index:: [no] vrrp (1-255) ip A.B.C.D +.. clicmd:: [no] vrrp (1-255) ip A.B.C.D + + Add an IPv4 address to the router. This address must already be configured + on the appropriate macvlan device. Adding an IP address to the router will + implicitly activate the router; see :clicmd:`[no] vrrp (1-255) shutdown` to + override this behavior. + +.. index:: [no] vrrp (1-255) ipv6 X:X::X:X +.. clicmd:: [no] vrrp (1-255) ipv6 X:X::X:X + + Add an IPv6 address to the router. This address must already be configured + on the appropriate macvlan device. Adding an IP address to the router will + implicitly activate the router; see :clicmd:`[no] vrrp (1-255) shutdown` to + override this behavior. + + This command will fail if the protocol version is set to VRRPv2, as VRRPv2 + does not support IPv6. + +.. index:: [no] vrrp (1-255) preempt +.. clicmd:: [no] vrrp (1-255) preempt + + Toggle preempt mode. When enabled, preemption allows Backup routers with + higher priority to take over Master status from the existing Master. Enabled + by default. + +.. index:: [no] vrrp (1-255) priority (1-254) +.. clicmd:: [no] vrrp (1-255) priority (1-254) + + Set the router priority. The router with the highest priority is elected as + the Master. If all routers in the VRRP virtual router are configured with + the same priority, the router with the highest primary IP address is elected + as the Master. Priority value 255 is reserved for the acting Master router. + +.. index:: [no] vrrp (1-255) shutdown +.. clicmd:: [no] vrrp (1-255) shutdown + + Place the router into administrative shutdown. VRRP will not activate for + this router until this command is removed with the ``no`` form. + +.. _vrrp-global-configuration: + +Global Configuration +-------------------- + +Show commands, global defaults and debugging configuration commands. + +.. index:: show vrrp [interface INTERFACE] [(1-255)] [json] +.. clicmd:: show vrrp [interface INTERFACE] [(1-255)] [json] + + Shows VRRP status for some or all configured VRRP routers. Specifying an + interface will only show routers configured on that interface. Specifying a + VRID will only show routers with that VRID. Specifying ``json`` will dump + each router state in a JSON array. + +.. index:: debug vrrp [{protocol|autoconfigure|packets|sockets|ndisc|arp|zebra}] +.. clicmd:: debug vrrp [{protocol|autoconfigure|packets|sockets|ndisc|arp|zebra}] + + Toggle debugging logs for some or all components of VRRP. + + protocol + Logs state changes, election protocol decisions, and interface status + changes. + + autoconfigure + Logs actions taken by the autoconfiguration procedures. See + :ref:`vrrp-autoconfiguration`. + + packets + Logs details of ingress and egress packets. Includes packet decodes and + hex dumps. + + sockets + Logs details of socket configuration and initialization. + + ndisc + Logs actions taken by the Neighbor Discovery component of VRRP. + + arp + Logs actions taken by the ARP component of VRRP. + + zebra + Logs communications with Zebra. + +.. index:: [no] vrrp default +.. clicmd:: [no] vrrp default + + Configure defaults for new VRRP routers. These values will not affect + already configured VRRP routers, but will be applied to newly configured + ones. + +.. _vrrp-autoconfiguration: + +Autoconfiguration +----------------- + +In light of the complicated configuration required on the base system before +VRRP can be enabled, FRR has the ability to automatically configure VRRP +sessions by inspecting the interfaces present on the system. Since it is quite +unlikely that macvlan devices with VRRP virtual MACs will exist on systems not +using VRRP, this can be a convenient shortcut to automatically generate FRR +configuration. + +After configuring the interfaces as described in +:ref:`vrrp-system-configuration`, and configuring any defaults you may want, +execute the following command: + +.. index:: [no] vrrp autoconfigure [version (2-3)] +.. clicmd:: [no] vrrp autoconfigure [version (2-3)] + + Generates VRRP configuration based on the interface configuration on the + base system. Any existing interfaces that are configured properly for VRRP - + i.e. have the correct MAC address, link local address (when required), IPv4 + and IPv6 addresses - are used to create a VRRP router on their parent + interfaces, with VRRP IPvX addresses taken from the addresses assigned to + the macvlan devices. The generated configuration appears in the output of + ``show run``, which can then be modified as needed and written to the config + file. The ``version`` parameter controls the protocol version; if using + VRRPv2, keep in mind that IPv6 is not supported and will not be configured. + +The following configuration is then generated for you: + +.. code-block:: frr + + interface eth0 + vrrp 5 + vrrp 5 ip 10.0.2.16 + vrrp 5 ipv6 2001:db8::370:7334 + +VRRP is automatically activated. Global defaults, if set, are applied. + +You can then edit this configuration with **vtysh** as needed, and commit it by +writing to the configuration file. From c94d18955e8a1e239c063c62ddde00559c91840c Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Sat, 20 Apr 2019 00:41:30 +0000 Subject: [PATCH 138/153] vrrpd: use frr_elevate_privs Missed a few in the uplift. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 6 ++---- vrrpd/vrrp_ndisc.c | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index d24f918b12..f98b230c62 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1087,13 +1087,12 @@ static int vrrp_socket(struct vrrp_router *r) setsockopt_ipv4_multicast_loop(r->sock_tx, 0); /* Bind Rx socket to exact interface */ - vrrp_privs.change(ZPRIVS_RAISE); + frr_elevate_privs(&vrrp_privs) { ret = setsockopt(r->sock_rx, SOL_SOCKET, SO_BINDTODEVICE, r->vr->ifp->name, strlen(r->vr->ifp->name)); } - vrrp_privs.change(ZPRIVS_LOWER); if (ret) { zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Failed to bind Rx socket to %s: %s", @@ -1198,13 +1197,12 @@ static int vrrp_socket(struct vrrp_router *r) setsockopt_ipv6_multicast_loop(r->sock_tx, 0); /* Bind Rx socket to exact interface */ - vrrp_privs.change(ZPRIVS_RAISE); + frr_elevate_privs(&vrrp_privs) { ret = setsockopt(r->sock_rx, SOL_SOCKET, SO_BINDTODEVICE, r->vr->ifp->name, strlen(r->vr->ifp->name)); } - vrrp_privs.change(ZPRIVS_LOWER); if (ret) { zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM "Failed to bind Rx socket to %s: %s", diff --git a/vrrpd/vrrp_ndisc.c b/vrrpd/vrrp_ndisc.c index 8a439e97ca..8081533ebc 100644 --- a/vrrpd/vrrp_ndisc.c +++ b/vrrpd/vrrp_ndisc.c @@ -211,11 +211,10 @@ int vrrp_ndisc_una_send_all(struct vrrp_router *r) void vrrp_ndisc_init(void) { - vrrp_privs.change(ZPRIVS_RAISE); + frr_elevate_privs(&vrrp_privs) { ndisc_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IPV6)); } - vrrp_privs.change(ZPRIVS_LOWER); if (ndisc_fd > 0) { DEBUGD(&vrrp_dbg_sock, From 4f576e7575f9c1f4b017374a990906130d00dfdd Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 22 Apr 2019 18:03:30 +0000 Subject: [PATCH 139/153] lib, vrrpd: define & use ZEBRA_ROUTE_VRRP Allow Zebra to know our protocol name. Signed-off-by: Quentin Young --- lib/route_types.txt | 1 + vrrpd/vrrp_zebra.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/route_types.txt b/lib/route_types.txt index c5eff44ca7..6c46fa89e3 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -83,6 +83,7 @@ ZEBRA_ROUTE_SHARP, sharp, sharpd, 'D', 1, 1, 1, "SHARP" ZEBRA_ROUTE_PBR, pbr, pbrd, 'F', 1, 1, 0, "PBR" ZEBRA_ROUTE_BFD, bfd, bfdd, '-', 0, 0, 0, "BFD" ZEBRA_ROUTE_OPENFABRIC, openfabric, fabricd, 'f', 1, 1, 1, "OpenFabric" +ZEBRA_ROUTE_VRRP, vrrp, vrrpd, '-', 0, 0, 0, "VRRP" ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-" diff --git a/vrrpd/vrrp_zebra.c b/vrrpd/vrrp_zebra.c index 8d5a6453e1..7503034de3 100644 --- a/vrrpd/vrrp_zebra.c +++ b/vrrpd/vrrp_zebra.c @@ -246,7 +246,7 @@ void vrrp_zebra_init(void) zclient->interface_address_add = vrrp_zebra_if_address_add; zclient->interface_address_delete = vrrp_zebra_if_address_del; - zclient_init(zclient, 0, 0, &vrrp_privs); + zclient_init(zclient, ZEBRA_ROUTE_VRRP, 0, &vrrp_privs); zlog_notice("%s: zclient socket initialized", __PRETTY_FUNCTION__); } From f1175ba9314200821a47dcffab1fd01c18ee701f Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 22 Apr 2019 18:04:56 +0000 Subject: [PATCH 140/153] vrrpd: gracefully shutdown on SIGTERM / SIGINT Handle kill signals by gracefully destroying all of our VRRP instances. If any of them are in Master state, send an advert with 0 priority to notify Backup routers we are going down. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 18 ++++++++++++++++++ vrrpd/vrrp.h | 8 ++++++++ vrrpd/vrrp_main.c | 4 +++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index f98b230c62..7b66492c11 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -2354,3 +2354,21 @@ void vrrp_init(void) "VRRP virtual router hash"); vrf_init(NULL, NULL, NULL, NULL, NULL); } + +void vrrp_fini(void) +{ + /* Destroy all instances */ + struct list *vrs = hash_to_list(vrrp_vrouters_hash); + + struct listnode *ln; + struct vrrp_vrouter *vr; + + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) { + vrrp_vrouter_destroy(vr); + } + + list_delete(&vrs); + + hash_clean(vrrp_vrouters_hash, NULL); + hash_free(vrrp_vrouters_hash); +} diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index ee2a8249a5..fd4901fe22 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -265,6 +265,14 @@ struct vrrp_vrouter { */ void vrrp_init(void); +/* + * Destroy all VRRP instances and gracefully shutdown. + * + * For instances in Master state, VRRP advertisements with 0 priority will be + * sent if possible to notify Backup routers that we are going away. + */ +void vrrp_fini(void); + /* Creation and destruction ------------------------------------------------ */ diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c index d6a43be8e8..98b907d013 100644 --- a/vrrpd/vrrp_main.c +++ b/vrrpd/vrrp_main.c @@ -69,10 +69,12 @@ static void sighup(void) } /* SIGINT / SIGTERM handler. */ -static void sigint(void) +static void __attribute__((noreturn)) sigint(void) { zlog_notice("Terminating on signal"); + vrrp_fini(); + exit(0); } From d32bee97f93805cab0909424f729affd2f2cafa9 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Mon, 22 Apr 2019 18:20:47 +0000 Subject: [PATCH 141/153] vrrpd: fix global buffer overflow from style fix I knew I had done that for a reason Signed-off-by: Quentin Young --- vrrpd/vrrp_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c index 98b907d013..bb3cc595d8 100644 --- a/vrrpd/vrrp_main.c +++ b/vrrpd/vrrp_main.c @@ -57,7 +57,7 @@ struct zebra_privs_t vrrp_privs = { .cap_num_p = array_size(_caps_p), .cap_num_i = 0}; -struct option longopts[] = {}; +struct option longopts[] = {{0}}; /* Master of threads. */ struct thread_master *master; From f9d31f6f6c2b2ad88629ec533861ee57b657b11f Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Tue, 16 Apr 2019 18:24:01 +0000 Subject: [PATCH 142/153] tools: fix vrrp autoconfigure reload Ticket: CM-24375 Signed-off-by: Quentin Young --- tools/frr-reload.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/frr-reload.py b/tools/frr-reload.py index ce1db770d2..23e8f3000d 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -410,7 +410,8 @@ end "service ", "table ", "username ", - "zebra ") + "zebra ", + "vrrp autoconfigure") for line in self.lines: From 33b010a97693cef8bbe120d8205099b581e573fe Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 24 Apr 2019 17:02:14 +0000 Subject: [PATCH 143/153] vrrpd: convert defaults command to milliseconds Missed this in the conversion from centiseconds to milliseconds. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 2 +- vrrpd/vrrp_vty.c | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 7b66492c11..1e01307ed1 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -2300,7 +2300,7 @@ int vrrp_config_write_global(struct vty *vty) if (vd.advertisement_interval != VRRP_DEFAULT_ADVINT && ++writes) vty_out(vty, "vrrp default advertisement-interval %" PRIu16 "\n", - vd.advertisement_interval); + vd.advertisement_interval * CS2MS); if (vd.preempt_mode != VRRP_DEFAULT_PREEMPT && ++writes) vty_out(vty, "%svrrp default preempt\n", diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 0cb33bffed..296a8a67dd 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -315,19 +315,26 @@ DEFPY(vrrp_autoconfigure, DEFPY(vrrp_default, vrrp_default_cmd, - "[no] vrrp default ", + "[no] vrrp default ", NO_STR VRRP_STR "Configure defaults for new VRRP instances\n" VRRP_ADVINT_STR - "Advertisement interval in centiseconds\n" + "Advertisement interval in milliseconds\n" "Preempt mode\n" VRRP_PRIORITY_STR "Priority value\n" "Force VRRP router into administrative shutdown\n") { - if (adv) + if (adv) { + if (advint % 10 != 0) { + vty_out(vty, "%% Value must be a multiple of 10\n"); + return CMD_WARNING_CONFIG_FAILED; + } + /* all internal computations are in centiseconds */ + advint /= CS2MS; vd.advertisement_interval = no ? VRRP_DEFAULT_ADVINT : advint; + } if (p) vd.preempt_mode = !no; if (prio) From 181232b555bb8074c7509976ca18618f7e6a7aaf Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Wed, 24 Apr 2019 17:02:35 +0000 Subject: [PATCH 144/153] vrrpd: fix magnitude error when removing adver_int When resetting advertisement interval back to the default, we were dividing centiseconds by 10 instead of milliseconds. Signed-off-by: Quentin Young --- vrrpd/vrrp_vty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 296a8a67dd..9cd1be245e 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -144,7 +144,7 @@ DEFPY(vrrp_advertisement_interval, VTY_DECLVAR_CONTEXT(interface, ifp); struct vrrp_vrouter *vr; - uint16_t newadvint = no ? vd.advertisement_interval : + uint16_t newadvint = no ? vd.advertisement_interval * 10: advertisement_interval; if (newadvint % 10 != 0) { From a881aecde741f26dba164be00abc105eef2aa9b4 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 2 May 2019 15:54:25 +0000 Subject: [PATCH 145/153] vrrpd: fix memleak during config write Forgot to free a list created in the course of writing our config. Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 1e01307ed1..d50fb4f2ec 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -2283,6 +2283,8 @@ int vrrp_config_write_interface(struct vty *vty) vty_endframe(vty, "!\n"); } + list_delete(&vrs); + return writes; } From 30a1595df8cff4eb66b6dcb8fd4093d04225c13f Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 2 May 2019 15:53:58 +0000 Subject: [PATCH 146/153] vrrpd: add 'show vrrp summary' command Shows a brief summary table of all VRRP routers Signed-off-by: Quentin Young --- vrrpd/vrrp_vty.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 9cd1be245e..b14efc33e7 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -572,6 +572,23 @@ static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) ttable_del(tt); } +/* + * Sort comparator, used when sorting VRRP instances for display purposes. + * + * Sorts by interface name first, then by VRID ascending. + */ +static int vrrp_instance_display_sort_cmp(const void **d1, const void **d2) +{ + const struct vrrp_vrouter *vr1 = *d1; + const struct vrrp_vrouter *vr2 = *d2; + int result; + + result = strcmp(vr1->ifp->name, vr2->ifp->name); + result += !result * (vr1->vrid - vr2->vrid); + + return result; +} + /* clang-format off */ DEFPY(vrrp_vrid_show, @@ -589,6 +606,8 @@ DEFPY(vrrp_vrid_show, struct list *ll = hash_to_list(vrrp_vrouters_hash); struct json_object *j = json_object_new_array(); + list_sort(ll, vrrp_instance_display_sort_cmp); + for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) { if (ifn && !strmatch(ifn, vr->ifp->name)) continue; @@ -613,6 +632,55 @@ DEFPY(vrrp_vrid_show, return CMD_SUCCESS; } +DEFPY(vrrp_vrid_show_summary, + vrrp_vrid_show_summary_cmd, + "show vrrp [interface INTERFACE$ifn] [(1-255)$vrid] summary", + SHOW_STR + VRRP_STR + INTERFACE_STR + "Only show VRRP instances on this interface\n" + VRRP_VRID_STR + "Summarize all VRRP instances\n") +{ + struct vrrp_vrouter *vr; + struct listnode *ln; + struct list *ll = hash_to_list(vrrp_vrouters_hash); + + list_sort(ll, vrrp_instance_display_sort_cmp); + + struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + + ttable_add_row( + tt, "Interface|VRID|Priority|IPv4|IPv6|State (v4)|State (v6)"); + ttable_rowseps(tt, 0, BOTTOM, true, '-'); + + for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) { + if (ifn && !strmatch(ifn, vr->ifp->name)) + continue; + if (vrid && ((uint8_t)vrid) != vr->vrid) + continue; + + ttable_add_row( + tt, "%s|%" PRIu8 "|%" PRIu8 "|%d|%d|%s|%s", + vr->ifp->name, vr->vrid, vr->priority, + vr->v4->addrs->count, vr->v6->addrs->count, + vr->v4->fsm.state == VRRP_STATE_MASTER ? "Master" + : "Backup", + vr->v6->fsm.state == VRRP_STATE_MASTER ? "Master" + : "Backup"); + } + + char *table = ttable_dump(tt, "\n"); + + vty_out(vty, "\n%s\n", table); + XFREE(MTYPE_TMP, table); + ttable_del(tt); + + list_delete(&ll); + + return CMD_SUCCESS; +} + DEFPY(debug_vrrp, debug_vrrp_cmd, @@ -667,6 +735,7 @@ void vrrp_vty_init(void) if_cmd_init(); install_element(VIEW_NODE, &vrrp_vrid_show_cmd); + install_element(VIEW_NODE, &vrrp_vrid_show_summary_cmd); install_element(VIEW_NODE, &show_debugging_vrrp_cmd); install_element(VIEW_NODE, &debug_vrrp_cmd); install_element(CONFIG_NODE, &debug_vrrp_cmd); From 303b93cdee4c2f2d7966b4bf92fb46ca0feb35d7 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Thu, 2 May 2019 17:22:42 +0000 Subject: [PATCH 147/153] zebra: update zebra_rib for vrrp VRRP doesn't install any routes, but should still have an array entry. Also add a help string for VRRP to route_types.txt Signed-off-by: Quentin Young --- lib/route_types.txt | 1 + zebra/zebra_rib.c | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/route_types.txt b/lib/route_types.txt index 6c46fa89e3..59f3a91cf0 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -111,4 +111,5 @@ ZEBRA_ROUTE_BABEL, "Babel routing protocol (Babel)" ZEBRA_ROUTE_SHARP, "Super Happy Advanced Routing Protocol (sharpd)" ZEBRA_ROUTE_PBR, "Policy Based Routing (PBR)" ZEBRA_ROUTE_BFD, "Bidirectional Fowarding Detection (BFD)" +ZEBRA_ROUTE_VRRP, "Virtual Router Redundancy Protocol (VRRP)" ZEBRA_ROUTE_OPENFABRIC, "OpenFabric Routing Protocol" diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 8d4f49e3ee..3b305f6e3d 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -101,6 +101,7 @@ static const struct { [ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 4}, [ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, 4}, [ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, 115, 2}, + [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 4} /* Any new route type added to zebra, should be mirrored here */ /* no entry/default: 150 */ From 5ebcbd1578657987d160347b0446379e063fc181 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 17 May 2019 00:18:50 +0000 Subject: [PATCH 148/153] doc: remove trailing whitespace in vrrp manpage Signed-off-by: Quentin Young --- doc/manpages/vrrpd.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manpages/vrrpd.rst b/doc/manpages/vrrpd.rst index 3670916050..0e73b07cda 100644 --- a/doc/manpages/vrrpd.rst +++ b/doc/manpages/vrrpd.rst @@ -3,7 +3,7 @@ VRRPD ***** .. include:: defines.rst -.. |DAEMON| replace:: vrrpd +.. |DAEMON| replace:: vrrpd SYNOPSIS ======== From 19c38250041783559ba9021373ee748222cdc848 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 17 May 2019 00:21:52 +0000 Subject: [PATCH 149/153] vrrpd, zebra: fix checkpatch warnings Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 9 +++++---- vrrpd/vrrp_main.c | 2 +- vrrpd/vrrp_vty.c | 4 ++-- zebra/zapi_msg.c | 1 + 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index d50fb4f2ec..02652d6abf 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -709,6 +709,8 @@ static int vrrp_bind_to_primary_connected(struct vrrp_router *r) break; } + int ret = 0; + sockopt_reuseaddr(r->sock_tx); if (bind(r->sock_tx, (const struct sockaddr *)&su, sizeof(su)) < 0) { zlog_err( @@ -719,7 +721,7 @@ static int vrrp_bind_to_primary_connected(struct vrrp_router *r) (const void *)&c->address->u.prefix, ipstr, sizeof(ipstr)), safe_strerror(errno)); - return -1; + ret = -1; } else { DEBUGD(&vrrp_dbg_sock, VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM @@ -729,7 +731,7 @@ static int vrrp_bind_to_primary_connected(struct vrrp_router *r) ipstr, sizeof(ipstr))); } - return 0; + return ret; } @@ -2365,9 +2367,8 @@ void vrrp_fini(void) struct listnode *ln; struct vrrp_vrouter *vr; - for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) { + for (ALL_LIST_ELEMENTS_RO(vrs, ln, vr)) vrrp_vrouter_destroy(vr); - } list_delete(&vrs); diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c index bb3cc595d8..46a92d936a 100644 --- a/vrrpd/vrrp_main.c +++ b/vrrpd/vrrp_main.c @@ -57,7 +57,7 @@ struct zebra_privs_t vrrp_privs = { .cap_num_p = array_size(_caps_p), .cap_num_i = 0}; -struct option longopts[] = {{0}}; +struct option longopts[] = { {0} }; /* Master of threads. */ struct thread_master *master; diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index b14efc33e7..48d81b0258 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -144,8 +144,8 @@ DEFPY(vrrp_advertisement_interval, VTY_DECLVAR_CONTEXT(interface, ifp); struct vrrp_vrouter *vr; - uint16_t newadvint = no ? vd.advertisement_interval * 10: - advertisement_interval; + uint16_t newadvint = + no ? vd.advertisement_interval * 10 : advertisement_interval; if (newadvint % 10 != 0) { vty_out(vty, "%% Value must be a multiple of 10\n"); diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 6a478560ab..94bfa34b38 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -72,6 +72,7 @@ static void zserv_encode_interface(struct stream *s, struct interface *ifp) { /* Interface information. */ struct zebra_if *zif = ifp->info; + stream_put(s, ifp->name, INTERFACE_NAMSIZ); stream_putl(s, ifp->ifindex); stream_putc(s, ifp->status); From d8d78e2ca9d83b10e602d19e9ce4791b1927f3b1 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 17 May 2019 00:26:24 +0000 Subject: [PATCH 150/153] zebra: gracefully fail to protodown on bsd Signed-off-by: Quentin Young --- zebra/interface.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zebra/interface.c b/zebra/interface.c index 2ed8a82000..13582008a7 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1066,7 +1066,11 @@ void zebra_if_update_all_links(void) void zebra_if_set_protodown(struct interface *ifp, bool down) { +#ifdef HAVE_NETLINK netlink_protodown(ifp, down); +#else + zlog_warn("Protodown is not supported on this platform"); +#endif } /* Output prefix string to vty. */ From bdf1666144f84b0b00d5d0572db27ff581e91622 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 17 May 2019 16:25:57 +0000 Subject: [PATCH 151/153] vrrpd: fix some clang-analyze warnings Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 02652d6abf..a5d4ef3f44 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -308,7 +308,7 @@ void vrrp_check_start(struct vrrp_vrouter *vr) /* Must not already be started */ start = r->fsm.state == VRRP_STATE_INITIALIZE; /* Must not be v2 */ - start = vr->version != 2; + start = start && vr->version != 2; whynot = (!start && !whynot) ? "VRRPv2 does not support v6" : NULL; /* Must have a parent interface */ start = start && (vr->ifp != NULL); @@ -1128,6 +1128,7 @@ static int vrrp_socket(struct vrrp_router *r) r->vr->vrid, family2str(r->family)); /* Join Rx socket to VRRP IPv4 multicast group */ + assert(listhead(r->vr->ifp->connected)); struct connected *c = listhead(r->vr->ifp->connected)->data; struct in_addr v4 = c->address->u.prefix4; @@ -1526,6 +1527,7 @@ static int vrrp_startup(struct vrrp_router *r) thread_add_read(master, vrrp_read, r, r->sock_rx, &r->t_read); /* Configure effective priority */ + assert(listhead(r->addrs)); struct ipaddr *primary = (struct ipaddr *)listhead(r->addrs)->data; char ipbuf[INET6_ADDRSTRLEN]; From 1f1896fc6e2902eb0abdc5ff78859eb779409660 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 17 May 2019 16:26:26 +0000 Subject: [PATCH 152/153] tools: add vrrpd to frrcommon.sh Signed-off-by: Quentin Young --- tools/frrcommon.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/frrcommon.sh.in b/tools/frrcommon.sh.in index 897e6d6558..3fc38d4bed 100644 --- a/tools/frrcommon.sh.in +++ b/tools/frrcommon.sh.in @@ -29,7 +29,7 @@ FRR_VTY_GROUP="@enable_vty_group@" # frrvty # - keep zebra first # - watchfrr does NOT belong in this list -DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd" +DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd" RELOAD_SCRIPT="$D_PATH/frr-reload.py" # From 53ca01e52c487954f68ab7a76f163cf465fb9209 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 17 May 2019 16:33:44 +0000 Subject: [PATCH 153/153] vrrpd: const vrrp_hash_key Signed-off-by: Quentin Young --- vrrpd/vrrp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index a5d4ef3f44..ad09caf359 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -2323,9 +2323,9 @@ int vrrp_config_write_global(struct vty *vty) return writes; } -static unsigned int vrrp_hash_key(void *arg) +static unsigned int vrrp_hash_key(const void *arg) { - struct vrrp_vrouter *vr = arg; + const struct vrrp_vrouter *vr = arg; char key[IFNAMSIZ + 64]; snprintf(key, sizeof(key), "%s@%" PRIu8, vr->ifp->name, vr->vrid);