Line data Source code
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 0 : vrrp_vr_timer_compare (const void *v1, const void *v2)
17 : {
18 0 : vrrp_main_t *vmp = &vrrp_main;
19 : const u32 *idx1, *idx2;
20 : vrrp_vr_timer_t *timer1, *timer2;
21 :
22 0 : idx1 = v1;
23 0 : idx2 = v2;
24 :
25 0 : timer1 = pool_elt_at_index (vmp->vr_timers, *idx1);
26 0 : 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 0 : if (timer1->expire_time > timer2->expire_time)
32 0 : return -1;
33 : else
34 0 : return 1;
35 : }
36 :
37 : static u32
38 4 : vrrp_vr_timer_get_next (void)
39 : {
40 4 : vrrp_main_t *vmp = &vrrp_main;
41 : int n_timers;
42 :
43 4 : n_timers = vec_len (vmp->pending_timers);
44 :
45 4 : if (!n_timers)
46 2 : return ~0;
47 :
48 2 : 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 4 : vrrp_vr_timer_cancel (vrrp_vr_t * vr)
59 : {
60 4 : 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 4 : if (vr->runtime.timer_index == ~0)
65 2 : 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 2 : if (vmp->pending_timers)
70 2 : vec_foreach_backwards (t, vmp->pending_timers)
71 : {
72 2 : if (*t == vr->runtime.timer_index)
73 : {
74 2 : vec_delete (vmp->pending_timers, 1, t - vmp->pending_timers);
75 2 : break;
76 : }
77 : }
78 :
79 2 : if (!pool_is_free_index (vmp->vr_timers, vr->runtime.timer_index))
80 2 : pool_put_index (vmp->vr_timers, vr->runtime.timer_index);
81 :
82 2 : vr->runtime.timer_index = ~0;
83 :
84 2 : vlib_process_signal_event (vmp->vlib_main, vrrp_periodic_node.index,
85 : VRRP_EVENT_VR_TIMER_UPDATE, 0);
86 : }
87 :
88 : void
89 2 : vrrp_vr_timer_set (vrrp_vr_t * vr, vrrp_vr_timer_type_t type)
90 : {
91 2 : vrrp_main_t *vmp = &vrrp_main;
92 2 : 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 2 : if (vr->runtime.timer_index != ~0)
100 0 : vrrp_vr_timer_cancel (vr);
101 :
102 2 : pool_get (vmp->vr_timers, timer);
103 2 : vr->runtime.timer_index = timer - vmp->vr_timers;
104 :
105 2 : timer->vr_index = vr - vmp->vrs;
106 2 : timer->type = type;
107 :
108 2 : now = vlib_time_now (vm);
109 :
110 : /* RFC 5798 specifies that timers are in centiseconds, so x / 100.0 */
111 2 : switch (type)
112 : {
113 2 : case VRRP_VR_TIMER_ADV:
114 2 : timer->expire_time = now + (vr->config.adv_interval / 100.0);
115 2 : break;
116 0 : case VRRP_VR_TIMER_MASTER_DOWN:
117 0 : timer->expire_time = now + (vr->runtime.master_down_int / 100.0);
118 0 : break;
119 0 : default:
120 : /* should never reach here */
121 0 : clib_warning ("Unrecognized VRRP timer type (%d)", type);
122 0 : return;
123 : }
124 :
125 2 : vec_add1 (vmp->pending_timers, vr->runtime.timer_index);
126 :
127 2 : vec_sort_with_function (vmp->pending_timers, vrrp_vr_timer_compare);
128 :
129 2 : vlib_process_signal_event (vmp->vlib_main, vrrp_periodic_node.index,
130 : VRRP_EVENT_VR_TIMER_UPDATE, 0);
131 : }
132 :
133 : void
134 0 : vrrp_vr_timer_timeout (u32 timer_index)
135 : {
136 0 : vrrp_main_t *vmp = &vrrp_main;
137 : vrrp_vr_timer_t *timer;
138 : vrrp_vr_t *vr;
139 :
140 0 : if (pool_is_free_index (vmp->vr_timers, timer_index))
141 : {
142 0 : clib_warning ("Timeout on free timer index %u", timer_index);
143 0 : return;
144 : }
145 :
146 0 : timer = pool_elt_at_index (vmp->vr_timers, timer_index);
147 0 : vr = pool_elt_at_index (vmp->vrs, timer->vr_index);
148 :
149 0 : switch (timer->type)
150 : {
151 0 : case VRRP_VR_TIMER_ADV:
152 0 : vrrp_adv_send (vr, 0);
153 0 : vrrp_vr_timer_set (vr, VRRP_VR_TIMER_ADV);
154 0 : break;
155 0 : case VRRP_VR_TIMER_MASTER_DOWN:
156 0 : vrrp_vr_transition (vr, VRRP_VR_STATE_MASTER, NULL);
157 0 : break;
158 0 : default:
159 0 : clib_warning ("Unrecognized timer type %d", timer->type);
160 0 : return;
161 : }
162 :
163 : }
164 :
165 : static uword
166 575 : vrrp_periodic_process (vlib_main_t * vm,
167 : vlib_node_runtime_t * rt, vlib_frame_t * f)
168 : {
169 575 : vrrp_main_t *pm = &vrrp_main;
170 : f64 now;
171 575 : f64 timeout = 10.0;
172 575 : uword *event_data = 0;
173 : uword event_type;
174 575 : u32 next_timer = ~0;
175 : vrrp_vr_timer_t *timer;
176 :
177 : while (1)
178 : {
179 579 : now = vlib_time_now (vm);
180 :
181 579 : if (next_timer == ~0)
182 : {
183 577 : vlib_process_wait_for_event (vm);
184 : }
185 : else
186 : {
187 2 : timer = pool_elt_at_index (pm->vr_timers, next_timer);
188 2 : timeout = timer->expire_time - now;
189 :
190 2 : vlib_process_wait_for_event_or_clock (vm, timeout);
191 : }
192 :
193 4 : event_type = vlib_process_get_events (vm, (uword **) & event_data);
194 :
195 4 : switch (event_type)
196 : {
197 : /* Handle VRRP_EVENT_VR_TIMER_UPDATE */
198 4 : case VRRP_EVENT_VR_TIMER_UPDATE:
199 4 : next_timer = vrrp_vr_timer_get_next ();
200 4 : break;
201 :
202 : /* Handle periodic timeouts */
203 0 : case ~0:
204 0 : vrrp_vr_timer_timeout (next_timer);
205 0 : next_timer = vrrp_vr_timer_get_next ();
206 0 : break;
207 : }
208 4 : vec_reset_length (event_data);
209 : }
210 : return 0;
211 : }
212 :
213 : /* *INDENT-OFF* */
214 13247 : VLIB_REGISTER_NODE (vrrp_periodic_node) = {
215 : .function = vrrp_periodic_process,
216 : .type = VLIB_NODE_TYPE_PROCESS,
217 : .name = "vrrp-periodic-process",
218 : .process_log2_n_stack_bytes = 17,
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 : */
|