vrrp: add plugin providing vrrp support
[vpp.git] / src / plugins / vrrp / vrrp_periodic.c
1 /*
2  * vrrp_periodic.c - vrrp plug-in periodic function
3  *
4  * Copyright 2019-2020 Rubicon Communications, LLC (Netgate)
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  *
8  */
9
10 #include <vlib/vlib.h>
11 #include <vppinfra/error.h>
12 #include <vrrp/vrrp.h>
13 #include <vrrp/vrrp_packet.h>
14
15 static int
16 vrrp_vr_timer_compare (const void *v1, const void *v2)
17 {
18   vrrp_main_t *vmp = &vrrp_main;
19   const u32 *idx1, *idx2;
20   vrrp_vr_timer_t *timer1, *timer2;
21
22   idx1 = v1;
23   idx2 = v2;
24
25   timer1 = pool_elt_at_index (vmp->vr_timers, *idx1);
26   timer2 = pool_elt_at_index (vmp->vr_timers, *idx2);
27
28   /* don't check equality, they are unlikely to be exactly equal and
29    * if it occurs, it won't matter what order they were in.
30    * sort the list in reverse so we can pick the next timer off the end */
31   if (timer1->expire_time > timer2->expire_time)
32     return -1;
33   else
34     return 1;
35 }
36
37 static u32
38 vrrp_vr_timer_get_next (void)
39 {
40   vrrp_main_t *vmp = &vrrp_main;
41   int n_timers;
42
43   n_timers = vec_len (vmp->pending_timers);
44
45   if (!n_timers)
46     return ~0;
47
48   return vec_elt (vmp->pending_timers, n_timers - 1);
49 }
50
51 /* cancel an existing timer. This could happen because:
52  * - adv timer expired on master. another adv should be scheduled.
53  * - a shutdown event is received
54  * - a master is preempted by a higher priority master
55  * - adv received on backup. master down timer should be rescheduled.
56  */
57 void
58 vrrp_vr_timer_cancel (vrrp_vr_t * vr)
59 {
60   vrrp_main_t *vmp = &vrrp_main;
61   u32 *t;
62
63   /* don't search for a timer that was already canceled or never set */
64   if (vr->runtime.timer_index == ~0)
65     return;
66
67   /* timers stored in descending order, start at the end of the list */
68   /* vec_foreach_backwards does not deal with 0 pointers, check first */
69   if (vmp->pending_timers)
70     vec_foreach_backwards (t, vmp->pending_timers)
71     {
72       if (*t == vr->runtime.timer_index)
73         {
74           vec_delete (vmp->pending_timers, 1, t - vmp->pending_timers);
75           break;
76         }
77     }
78
79   if (!pool_is_free_index (vmp->vr_timers, vr->runtime.timer_index))
80     pool_put_index (vmp->vr_timers, vr->runtime.timer_index);
81
82   vr->runtime.timer_index = ~0;
83
84   vlib_process_signal_event (vmp->vlib_main, vrrp_periodic_node.index,
85                              VRRP_EVENT_VR_TIMER_UPDATE, 0);
86 }
87
88 void
89 vrrp_vr_timer_set (vrrp_vr_t * vr, vrrp_vr_timer_type_t type)
90 {
91   vrrp_main_t *vmp = &vrrp_main;
92   vlib_main_t *vm = vlib_get_main ();
93   vrrp_vr_timer_t *timer;
94   f64 now;
95
96   /* Each VR should be waiting on at most 1 timer at any given time.
97    * If there is already a timer set for this VR, cancel it.
98    */
99   if (vr->runtime.timer_index != ~0)
100     vrrp_vr_timer_cancel (vr);
101
102   pool_get (vmp->vr_timers, timer);
103   vr->runtime.timer_index = timer - vmp->vr_timers;
104
105   timer->vr_index = vr - vmp->vrs;
106   timer->type = type;
107
108   now = vlib_time_now (vm);
109
110   /* RFC 5798 specifies that timers are in centiseconds, so x / 100.0 */
111   switch (type)
112     {
113     case VRRP_VR_TIMER_ADV:
114       timer->expire_time = now + (vr->config.adv_interval / 100.0);
115       break;
116     case VRRP_VR_TIMER_MASTER_DOWN:
117       timer->expire_time = now + (vr->runtime.master_down_int / 100.0);
118       break;
119     default:
120       /* should never reach here */
121       clib_warning ("Unrecognized VRRP timer type (%d)", type);
122       return;
123     }
124
125   vec_add1 (vmp->pending_timers, vr->runtime.timer_index);
126
127   vec_sort_with_function (vmp->pending_timers, vrrp_vr_timer_compare);
128
129   vlib_process_signal_event (vmp->vlib_main, vrrp_periodic_node.index,
130                              VRRP_EVENT_VR_TIMER_UPDATE, 0);
131 }
132
133 void
134 vrrp_vr_timer_timeout (u32 timer_index)
135 {
136   vrrp_main_t *vmp = &vrrp_main;
137   vrrp_vr_timer_t *timer;
138   vrrp_vr_t *vr;
139
140   if (pool_is_free_index (vmp->vr_timers, timer_index))
141     {
142       clib_warning ("Timeout on free timer index %u", timer_index);
143       return;
144     }
145
146   timer = pool_elt_at_index (vmp->vr_timers, timer_index);
147   vr = pool_elt_at_index (vmp->vrs, timer->vr_index);
148
149   switch (timer->type)
150     {
151     case VRRP_VR_TIMER_ADV:
152       vrrp_adv_send (vr, 0);
153       vrrp_vr_timer_set (vr, VRRP_VR_TIMER_ADV);
154       break;
155     case VRRP_VR_TIMER_MASTER_DOWN:
156       vrrp_vr_transition (vr, VRRP_VR_STATE_MASTER, NULL);
157       break;
158     default:
159       clib_warning ("Unrecognized timer type %d", timer->type);
160       return;
161     }
162
163 }
164
165 static uword
166 vrrp_periodic_process (vlib_main_t * vm,
167                        vlib_node_runtime_t * rt, vlib_frame_t * f)
168 {
169   vrrp_main_t *pm = &vrrp_main;
170   f64 now;
171   f64 timeout = 10.0;
172   uword *event_data = 0;
173   uword event_type;
174   u32 next_timer = ~0;
175   vrrp_vr_timer_t *timer;
176
177   while (1)
178     {
179       now = vlib_time_now (vm);
180
181       if (next_timer == ~0)
182         {
183           vlib_process_wait_for_event (vm);
184         }
185       else
186         {
187           timer = pool_elt_at_index (pm->vr_timers, next_timer);
188           timeout = timer->expire_time - now;
189
190           vlib_process_wait_for_event_or_clock (vm, timeout);
191         }
192
193       event_type = vlib_process_get_events (vm, (uword **) & event_data);
194
195       switch (event_type)
196         {
197           /* Handle VRRP_EVENT_VR_TIMER_UPDATE */
198         case VRRP_EVENT_VR_TIMER_UPDATE:
199           next_timer = vrrp_vr_timer_get_next ();
200           break;
201
202           /* Handle periodic timeouts */
203         case ~0:
204           vrrp_vr_timer_timeout (next_timer);
205           next_timer = vrrp_vr_timer_get_next ();
206           break;
207         }
208       vec_reset_length (event_data);
209     }
210   return 0;
211 }
212
213 /* *INDENT-OFF* */
214 VLIB_REGISTER_NODE (vrrp_periodic_node) =
215 {
216   .function = vrrp_periodic_process,
217   .type = VLIB_NODE_TYPE_PROCESS,
218   .name = "vrrp-periodic-process",
219 };
220 /* *INDENT-ON* */
221
222 /*
223  * fd.io coding-style-patch-verification: ON
224  *
225  * Local Variables:
226  * eval: (c-set-style "gnu")
227  * End:
228  */