summaryrefslogtreecommitdiffstats
path: root/src/plugins/vrrp/vrrp_periodic.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/vrrp/vrrp_periodic.c')
-rw-r--r--src/plugins/vrrp/vrrp_periodic.c228
1 files changed, 228 insertions, 0 deletions
diff --git a/src/plugins/vrrp/vrrp_periodic.c b/src/plugins/vrrp/vrrp_periodic.c
new file mode 100644
index 00000000000..d37347701fe
--- /dev/null
+++ b/src/plugins/vrrp/vrrp_periodic.c
@@ -0,0 +1,228 @@
+/*
+ * vrrp_periodic.c - vrrp plug-in periodic function
+ *
+ * Copyright 2019-2020 Rubicon Communications, LLC (Netgate)
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+#include <vlib/vlib.h>
+#include <vppinfra/error.h>
+#include <vrrp/vrrp.h>
+#include <vrrp/vrrp_packet.h>
+
+static int
+vrrp_vr_timer_compare (const void *v1, const void *v2)
+{
+ vrrp_main_t *vmp = &vrrp_main;
+ const u32 *idx1, *idx2;
+ vrrp_vr_timer_t *timer1, *timer2;
+
+ idx1 = v1;
+ idx2 = v2;
+
+ timer1 = pool_elt_at_index (vmp->vr_timers, *idx1);
+ timer2 = pool_elt_at_index (vmp->vr_timers, *idx2);
+
+ /* don't check equality, they are unlikely to be exactly equal and
+ * if it occurs, it won't matter what order they were in.
+ * sort the list in reverse so we can pick the next timer off the end */
+ if (timer1->expire_time > timer2->expire_time)
+ return -1;
+ else
+ return 1;
+}
+
+static u32
+vrrp_vr_timer_get_next (void)
+{
+ vrrp_main_t *vmp = &vrrp_main;
+ int n_timers;
+
+ n_timers = vec_len (vmp->pending_timers);
+
+ if (!n_timers)
+ return ~0;
+
+ return vec_elt (vmp->pending_timers, n_timers - 1);
+}
+
+/* cancel an existing timer. This could happen because:
+ * - adv timer expired on master. another adv should be scheduled.
+ * - a shutdown event is received
+ * - a master is preempted by a higher priority master
+ * - adv received on backup. master down timer should be rescheduled.
+ */
+void
+vrrp_vr_timer_cancel (vrrp_vr_t * vr)
+{
+ vrrp_main_t *vmp = &vrrp_main;
+ u32 *t;
+
+ /* don't search for a timer that was already canceled or never set */
+ if (vr->runtime.timer_index == ~0)
+ return;
+
+ /* timers stored in descending order, start at the end of the list */
+ /* vec_foreach_backwards does not deal with 0 pointers, check first */
+ if (vmp->pending_timers)
+ vec_foreach_backwards (t, vmp->pending_timers)
+ {
+ if (*t == vr->runtime.timer_index)
+ {
+ vec_delete (vmp->pending_timers, 1, t - vmp->pending_timers);
+ break;
+ }
+ }
+
+ if (!pool_is_free_index (vmp->vr_timers, vr->runtime.timer_index))
+ pool_put_index (vmp->vr_timers, vr->runtime.timer_index);
+
+ vr->runtime.timer_index = ~0;
+
+ vlib_process_signal_event (vmp->vlib_main, vrrp_periodic_node.index,
+ VRRP_EVENT_VR_TIMER_UPDATE, 0);
+}
+
+void
+vrrp_vr_timer_set (vrrp_vr_t * vr, vrrp_vr_timer_type_t type)
+{
+ vrrp_main_t *vmp = &vrrp_main;
+ vlib_main_t *vm = vlib_get_main ();
+ vrrp_vr_timer_t *timer;
+ f64 now;
+
+ /* Each VR should be waiting on at most 1 timer at any given time.
+ * If there is already a timer set for this VR, cancel it.
+ */
+ if (vr->runtime.timer_index != ~0)
+ vrrp_vr_timer_cancel (vr);
+
+ pool_get (vmp->vr_timers, timer);
+ vr->runtime.timer_index = timer - vmp->vr_timers;
+
+ timer->vr_index = vr - vmp->vrs;
+ timer->type = type;
+
+ now = vlib_time_now (vm);
+
+ /* RFC 5798 specifies that timers are in centiseconds, so x / 100.0 */
+ switch (type)
+ {
+ case VRRP_VR_TIMER_ADV:
+ timer->expire_time = now + (vr->config.adv_interval / 100.0);
+ break;
+ case VRRP_VR_TIMER_MASTER_DOWN:
+ timer->expire_time = now + (vr->runtime.master_down_int / 100.0);
+ break;
+ default:
+ /* should never reach here */
+ clib_warning ("Unrecognized VRRP timer type (%d)", type);
+ return;
+ }
+
+ vec_add1 (vmp->pending_timers, vr->runtime.timer_index);
+
+ vec_sort_with_function (vmp->pending_timers, vrrp_vr_timer_compare);
+
+ vlib_process_signal_event (vmp->vlib_main, vrrp_periodic_node.index,
+ VRRP_EVENT_VR_TIMER_UPDATE, 0);
+}
+
+void
+vrrp_vr_timer_timeout (u32 timer_index)
+{
+ vrrp_main_t *vmp = &vrrp_main;
+ vrrp_vr_timer_t *timer;
+ vrrp_vr_t *vr;
+
+ if (pool_is_free_index (vmp->vr_timers, timer_index))
+ {
+ clib_warning ("Timeout on free timer index %u", timer_index);
+ return;
+ }
+
+ timer = pool_elt_at_index (vmp->vr_timers, timer_index);
+ vr = pool_elt_at_index (vmp->vrs, timer->vr_index);
+
+ switch (timer->type)
+ {
+ case VRRP_VR_TIMER_ADV:
+ vrrp_adv_send (vr, 0);
+ vrrp_vr_timer_set (vr, VRRP_VR_TIMER_ADV);
+ break;
+ case VRRP_VR_TIMER_MASTER_DOWN:
+ vrrp_vr_transition (vr, VRRP_VR_STATE_MASTER, NULL);
+ break;
+ default:
+ clib_warning ("Unrecognized timer type %d", timer->type);
+ return;
+ }
+
+}
+
+static uword
+vrrp_periodic_process (vlib_main_t * vm,
+ vlib_node_runtime_t * rt, vlib_frame_t * f)
+{
+ vrrp_main_t *pm = &vrrp_main;
+ f64 now;
+ f64 timeout = 10.0;
+ uword *event_data = 0;
+ uword event_type;
+ u32 next_timer = ~0;
+ vrrp_vr_timer_t *timer;
+
+ while (1)
+ {
+ now = vlib_time_now (vm);
+
+ if (next_timer == ~0)
+ {
+ vlib_process_wait_for_event (vm);
+ }
+ else
+ {
+ timer = pool_elt_at_index (pm->vr_timers, next_timer);
+ timeout = timer->expire_time - now;
+
+ vlib_process_wait_for_event_or_clock (vm, timeout);
+ }
+
+ event_type = vlib_process_get_events (vm, (uword **) & event_data);
+
+ switch (event_type)
+ {
+ /* Handle VRRP_EVENT_VR_TIMER_UPDATE */
+ case VRRP_EVENT_VR_TIMER_UPDATE:
+ next_timer = vrrp_vr_timer_get_next ();
+ break;
+
+ /* Handle periodic timeouts */
+ case ~0:
+ vrrp_vr_timer_timeout (next_timer);
+ next_timer = vrrp_vr_timer_get_next ();
+ break;
+ }
+ vec_reset_length (event_data);
+ }
+ return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (vrrp_periodic_node) =
+{
+ .function = vrrp_periodic_process,
+ .type = VLIB_NODE_TYPE_PROCESS,
+ .name = "vrrp-periodic-process",
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */