Line data Source code
1 : /*
2 : * Copyright (c) 2016-2018 Cisco and/or its affiliates.
3 : * Licensed under the Apache License, Version 2.0 (the "License");
4 : * you may not use this file except in compliance with the License.
5 : * You may obtain a copy of the License at:
6 : *
7 : * http://www.apache.org/licenses/LICENSE-2.0
8 : *
9 : * Unless required by applicable law or agreed to in writing, software
10 : * distributed under the License is distributed on an "AS IS" BASIS,
11 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : * See the License for the specific language governing permissions and
13 : * limitations under the License.
14 : */
15 : #include <stddef.h>
16 : #include <netinet/in.h>
17 :
18 : #include <vlib/vlib.h>
19 : #include <vnet/vnet.h>
20 : #include <vppinfra/error.h>
21 :
22 :
23 : #include <acl/acl.h>
24 : #include <vnet/ip/icmp46_packet.h>
25 :
26 : #include <plugins/acl/fa_node.h>
27 : #include <plugins/acl/acl.h>
28 : #include <plugins/acl/lookup_context.h>
29 : #include <plugins/acl/public_inlines.h>
30 : #include <plugins/acl/session_inlines.h>
31 :
32 :
33 :
34 : static_always_inline u8 *
35 160 : format_ip46_session_bihash_kv (u8 * s, va_list * args, int is_ip6)
36 : {
37 : fa_5tuple_t a5t;
38 : void *paddr0;
39 : void *paddr1;
40 : void *format_addr_func;
41 :
42 160 : if (is_ip6)
43 : {
44 80 : clib_bihash_kv_40_8_t *kv_40_8 =
45 : va_arg (*args, clib_bihash_kv_40_8_t *);
46 80 : a5t.kv_40_8 = *kv_40_8;
47 80 : paddr0 = &a5t.ip6_addr[0];
48 80 : paddr1 = &a5t.ip6_addr[1];
49 80 : format_addr_func = format_ip6_address;
50 : }
51 : else
52 : {
53 80 : clib_bihash_kv_16_8_t *kv_16_8 =
54 : va_arg (*args, clib_bihash_kv_16_8_t *);
55 80 : a5t.kv_16_8 = *kv_16_8;
56 80 : paddr0 = &a5t.ip4_addr[0];
57 80 : paddr1 = &a5t.ip4_addr[1];
58 80 : format_addr_func = format_ip4_address;
59 : }
60 :
61 160 : fa_full_session_id_t *sess = (fa_full_session_id_t *) & a5t.pkt;
62 :
63 320 : return (format (s, "l3 %U -> %U %U | sess id %d thread id %d epoch %04x",
64 : format_addr_func, paddr0,
65 : format_addr_func, paddr1,
66 : format_fa_session_l4_key, &a5t.l4,
67 160 : sess->session_index, sess->thread_index,
68 160 : sess->intf_policy_epoch));
69 : }
70 :
71 : static u8 *
72 80 : format_ip6_session_bihash_kv (u8 * s, va_list * args)
73 : {
74 80 : return format_ip46_session_bihash_kv (s, args, 1);
75 : }
76 :
77 : static u8 *
78 80 : format_ip4_session_bihash_kv (u8 * s, va_list * args)
79 : {
80 80 : return format_ip46_session_bihash_kv (s, args, 0);
81 : }
82 :
83 :
84 : static void
85 323 : acl_fa_verify_init_sessions (acl_main_t * am)
86 : {
87 323 : if (!am->fa_sessions_hash_is_initialized)
88 : {
89 : u16 wk;
90 : /* Allocate the per-worker sessions pools */
91 26 : for (wk = 0; wk < vec_len (am->per_worker_data); wk++)
92 : {
93 15 : acl_fa_per_worker_data_t *pw = &am->per_worker_data[wk];
94 :
95 : /*
96 : * // In lieu of trying to preallocate the pool and its free bitmap, rather use pool_init_fixed
97 : * pool_alloc_aligned(pw->fa_sessions_pool, am->fa_conn_table_max_entries, CLIB_CACHE_LINE_BYTES);
98 : * clib_bitmap_validate(pool_header(pw->fa_sessions_pool)->free_bitmap, am->fa_conn_table_max_entries);
99 : */
100 15 : pool_init_fixed (pw->fa_sessions_pool,
101 : am->fa_conn_table_max_entries);
102 : }
103 :
104 : /* ... and the interface session hash table */
105 11 : clib_bihash_init_40_8 (&am->fa_ip6_sessions_hash,
106 : "ACL plugin FA IPv6 session bihash",
107 : am->fa_conn_table_hash_num_buckets,
108 : am->fa_conn_table_hash_memory_size);
109 11 : clib_bihash_set_kvp_format_fn_40_8 (&am->fa_ip6_sessions_hash,
110 : format_ip6_session_bihash_kv);
111 :
112 11 : clib_bihash_init_16_8 (&am->fa_ip4_sessions_hash,
113 : "ACL plugin FA IPv4 session bihash",
114 : am->fa_conn_table_hash_num_buckets,
115 : am->fa_conn_table_hash_memory_size);
116 11 : clib_bihash_set_kvp_format_fn_16_8 (&am->fa_ip4_sessions_hash,
117 : format_ip4_session_bihash_kv);
118 :
119 11 : am->fa_sessions_hash_is_initialized = 1;
120 : }
121 323 : }
122 :
123 :
124 : /*
125 : * Get the timeout of the session in a list since its enqueue time.
126 : */
127 :
128 : static u64
129 328 : fa_session_get_list_timeout (acl_main_t * am, fa_session_t * sess)
130 : {
131 328 : u64 timeout = am->vlib_main->clib_time.clocks_per_second / 1000;
132 328 : timeout = fa_session_get_timeout (am, sess);
133 : /* for all user lists, check them twice per timeout */
134 328 : timeout >>= (sess->link_list_id != ACL_TIMEOUT_PURGATORY);
135 328 : return timeout;
136 : }
137 :
138 : static u64
139 30675 : acl_fa_get_list_head_expiry_time (acl_main_t * am,
140 : acl_fa_per_worker_data_t * pw, u64 now,
141 : u16 thread_index, int timeout_type)
142 : {
143 30675 : return pw->fa_conn_list_head_expiry_time[timeout_type];
144 : }
145 :
146 : static int
147 50849 : acl_fa_conn_time_to_check (acl_main_t * am, acl_fa_per_worker_data_t * pw,
148 : u64 now, u16 thread_index, u32 session_index)
149 : {
150 50849 : if (session_index == FA_SESSION_BOGUS_INDEX)
151 50524 : return 0;
152 325 : fa_session_t *sess = get_session_ptr (am, thread_index, session_index);
153 328 : u64 timeout_time =
154 328 : sess->link_enqueue_time + fa_session_get_list_timeout (am, sess);
155 : return (timeout_time < now)
156 328 : || (sess->link_enqueue_time <= pw->swipe_end_time);
157 : }
158 :
159 : /*
160 : * see if there are sessions ready to be checked,
161 : * do the maintenance (requeue or delete), and
162 : * return the total number of sessions reclaimed.
163 : */
164 : static int
165 10130 : acl_fa_check_idle_sessions (acl_main_t * am, u16 thread_index, u64 now)
166 : {
167 10130 : acl_fa_per_worker_data_t *pw = &am->per_worker_data[thread_index];
168 : fa_full_session_id_t fsid;
169 10130 : fsid.thread_index = thread_index;
170 10130 : int total_expired = 0;
171 :
172 : /* let the other threads enqueue more requests while we process, if they like */
173 10130 : aclp_swap_wip_and_pending_session_change_requests (am, thread_index);
174 10171 : u64 *psr = NULL;
175 :
176 10171 : vec_foreach (psr, pw->wip_session_change_requests)
177 : {
178 0 : acl_fa_sess_req_t op = *psr >> 32;
179 0 : fsid.session_index = *psr & 0xffffffff;
180 0 : switch (op)
181 : {
182 0 : case ACL_FA_REQ_SESS_RESCHEDULE:
183 0 : acl_fa_restart_timer_for_session (am, now, fsid);
184 0 : break;
185 0 : default:
186 : /* do nothing */
187 0 : break;
188 : }
189 : }
190 10171 : if (pw->wip_session_change_requests)
191 0 : vec_set_len (pw->wip_session_change_requests, 0);
192 :
193 : {
194 10160 : u8 tt = 0;
195 10160 : int n_pending_swipes = 0;
196 60797 : for (tt = 0; tt < ACL_N_TIMEOUTS; tt++)
197 : {
198 50644 : int n_expired = 0;
199 50844 : while (n_expired < am->fa_max_deleted_sessions_per_interval)
200 : {
201 50846 : fsid.session_index = pw->fa_conn_list_head[tt];
202 50849 : if (!acl_fa_conn_time_to_check
203 50846 : (am, pw, now, thread_index, pw->fa_conn_list_head[tt]))
204 : {
205 50639 : break;
206 : }
207 210 : if (am->trace_sessions > 3)
208 : {
209 0 : elog_acl_maybe_trace_X3 (am,
210 : "acl_fa_check_idle_sessions: expire session %d in list %d on thread %d",
211 : "i4i4i4", (u32) fsid.session_index,
212 : (u32) tt, (u32) thread_index);
213 : }
214 210 : vec_add1 (pw->expired, fsid.session_index);
215 208 : n_expired++;
216 208 : acl_fa_conn_list_delete_session (am, fsid, now);
217 : }
218 : }
219 10153 : for (tt = 0; tt < ACL_N_TIMEOUTS; tt++)
220 : {
221 10121 : u32 session_index = pw->fa_conn_list_head[tt];
222 10121 : if (session_index == FA_SESSION_BOGUS_INDEX)
223 10121 : break;
224 : fa_session_t *sess =
225 0 : get_session_ptr (am, thread_index, session_index);
226 0 : n_pending_swipes += sess->link_enqueue_time <= pw->swipe_end_time;
227 : }
228 10153 : if (n_pending_swipes == 0)
229 : {
230 10121 : pw->swipe_end_time = 0;
231 : }
232 : }
233 :
234 10153 : u32 *psid = NULL;
235 10361 : vec_foreach (psid, pw->expired)
236 : {
237 208 : fsid.session_index = *psid;
238 208 : if (!pool_is_free_index (pw->fa_sessions_pool, fsid.session_index))
239 : {
240 : fa_session_t *sess =
241 208 : get_session_ptr (am, thread_index, fsid.session_index);
242 208 : u32 sw_if_index = sess->sw_if_index;
243 208 : u64 sess_timeout_time =
244 208 : sess->last_active_time + fa_session_get_timeout (am, sess);
245 208 : int timeout_passed = (now >= sess_timeout_time);
246 208 : int clearing_interface =
247 208 : clib_bitmap_get (pw->pending_clear_sw_if_index_bitmap, sw_if_index);
248 208 : if (am->trace_sessions > 3)
249 : {
250 0 : elog_acl_maybe_trace_X2 (am,
251 : "acl_fa_check_idle_sessions: now %lu sess_timeout_time %lu",
252 : "i8i8", now, sess_timeout_time);
253 0 : elog_acl_maybe_trace_X4 (am,
254 : "acl_fa_check_idle_sessions: session %d sw_if_index %d timeout_passed %d clearing_interface %d",
255 : "i4i4i4i4", (u32) fsid.session_index,
256 : (u32) sess->sw_if_index,
257 : (u32) timeout_passed,
258 : (u32) clearing_interface);
259 : }
260 208 : if (timeout_passed || clearing_interface)
261 : {
262 168 : if (acl_fa_two_stage_delete_session (am, sw_if_index, fsid, now))
263 : {
264 84 : if (am->trace_sessions > 3)
265 : {
266 0 : elog_acl_maybe_trace_X2 (am,
267 : "acl_fa_check_idle_sessions: deleted session %d sw_if_index %d",
268 : "i4i4", (u32) fsid.session_index,
269 : (u32) sess->sw_if_index);
270 : }
271 : /* the session has been put */
272 84 : pw->cnt_deleted_sessions++;
273 : }
274 : else
275 : {
276 : /* the connection marked as deleted and put to purgatory */
277 84 : if (am->trace_sessions > 3)
278 : {
279 0 : elog_acl_maybe_trace_X2 (am,
280 : "acl_fa_check_idle_sessions: session %d sw_if_index %d marked as deleted, put to purgatory",
281 : "i4i4", (u32) fsid.session_index,
282 : (u32) sess->sw_if_index);
283 : }
284 : }
285 : }
286 : else
287 :
288 : {
289 40 : if (am->trace_sessions > 3)
290 : {
291 0 : elog_acl_maybe_trace_X2 (am,
292 : "acl_fa_check_idle_sessions: restart timer for session %d sw_if_index %d",
293 : "i4i4", (u32) fsid.session_index,
294 : (u32) sess->sw_if_index);
295 : }
296 : /* There was activity on the session, so the idle timeout
297 : has not passed. Enqueue for another time period. */
298 :
299 40 : acl_fa_conn_list_add_session (am, fsid, now);
300 40 : pw->cnt_session_timer_restarted++;
301 : }
302 : }
303 : else
304 : {
305 0 : pw->cnt_already_deleted_sessions++;
306 : }
307 : }
308 10161 : total_expired = vec_len (pw->expired);
309 : /* zero out the vector which we have acted on */
310 10158 : if (pw->expired)
311 10131 : vec_set_len (pw->expired, 0);
312 : /* if we were advancing and reached the end
313 : * (no more sessions to recycle), reset the fast-forward timestamp */
314 :
315 10119 : if (pw->swipe_end_time && 0 == total_expired)
316 0 : pw->swipe_end_time = 0;
317 :
318 10119 : elog_acl_maybe_trace_X1 (am,
319 : "acl_fa_check_idle_sessions: done, total sessions expired: %d",
320 : "i4", (u32) total_expired);
321 10119 : return (total_expired);
322 : }
323 :
324 : /*
325 : * This process ensures the connection cleanup happens every so often
326 : * even in absence of traffic, as well as provides general orchestration
327 : * for requests like connection deletion on a given sw_if_index.
328 : */
329 :
330 :
331 : /* *INDENT-OFF* */
332 : #define foreach_acl_fa_cleaner_error \
333 : _(UNKNOWN_EVENT, "unknown event received") \
334 : /* end of errors */
335 :
336 : typedef enum
337 : {
338 : #define _(sym,str) ACL_FA_CLEANER_ERROR_##sym,
339 : foreach_acl_fa_cleaner_error
340 : #undef _
341 : ACL_FA_CLEANER_N_ERROR,
342 : } acl_fa_cleaner_error_t;
343 :
344 : static char *acl_fa_cleaner_error_strings[] = {
345 : #define _(sym,string) string,
346 : foreach_acl_fa_cleaner_error
347 : #undef _
348 : };
349 :
350 : /* *INDENT-ON* */
351 :
352 : static vlib_node_registration_t acl_fa_session_cleaner_process_node;
353 : static vlib_node_registration_t acl_fa_worker_session_cleaner_process_node;
354 :
355 : static void
356 10521 : send_one_worker_interrupt (vlib_main_t * vm, acl_main_t * am,
357 : int thread_index)
358 : {
359 10521 : acl_fa_per_worker_data_t *pw = &am->per_worker_data[thread_index];
360 10521 : if (!pw->interrupt_is_pending)
361 : {
362 10204 : pw->interrupt_is_pending = 1;
363 10204 : vlib_node_set_interrupt_pending (
364 : vlib_get_main_by_index (thread_index),
365 : acl_fa_worker_session_cleaner_process_node.index);
366 10204 : elog_acl_maybe_trace_X1 (am,
367 : "send_one_worker_interrupt: send interrupt to worker %u",
368 : "i4", ((u32) thread_index));
369 : /* if the interrupt was requested, mark that done. */
370 : /* pw->interrupt_is_needed = 0; */
371 10204 : CLIB_MEMORY_BARRIER ();
372 : }
373 10521 : }
374 :
375 : void
376 0 : aclp_post_session_change_request (acl_main_t * am, u32 target_thread,
377 : u32 target_session, u32 request_type)
378 : {
379 0 : acl_fa_per_worker_data_t *pw_me =
380 0 : &am->per_worker_data[os_get_thread_index ()];
381 0 : acl_fa_per_worker_data_t *pw = &am->per_worker_data[target_thread];
382 0 : clib_spinlock_lock_if_init (&pw->pending_session_change_request_lock);
383 : /* vec_add1 might cause a reallocation */
384 0 : vec_add1 (pw->pending_session_change_requests,
385 : (((u64) request_type) << 32) | target_session);
386 0 : pw->rcvd_session_change_requests++;
387 0 : pw_me->sent_session_change_requests++;
388 0 : if (vec_len (pw->pending_session_change_requests) == 1)
389 : {
390 : /* ensure the requests get processed */
391 0 : send_one_worker_interrupt (am->vlib_main, am, target_thread);
392 : }
393 0 : clib_spinlock_unlock_if_init (&pw->pending_session_change_request_lock);
394 0 : }
395 :
396 : void
397 10128 : aclp_swap_wip_and_pending_session_change_requests (acl_main_t * am,
398 : u32 target_thread)
399 : {
400 10128 : acl_fa_per_worker_data_t *pw = &am->per_worker_data[target_thread];
401 : u64 *tmp;
402 10128 : clib_spinlock_lock_if_init (&pw->pending_session_change_request_lock);
403 10180 : tmp = pw->pending_session_change_requests;
404 10180 : pw->pending_session_change_requests = pw->wip_session_change_requests;
405 10180 : pw->wip_session_change_requests = tmp;
406 10180 : clib_spinlock_unlock_if_init (&pw->pending_session_change_request_lock);
407 10169 : }
408 :
409 :
410 : static int
411 10118 : purgatory_has_connections (vlib_main_t * vm, acl_main_t * am,
412 : int thread_index)
413 : {
414 10118 : acl_fa_per_worker_data_t *pw = &am->per_worker_data[thread_index];
415 :
416 20236 : return (FA_SESSION_BOGUS_INDEX !=
417 10118 : pw->fa_conn_list_head[ACL_TIMEOUT_PURGATORY]);
418 :
419 : }
420 :
421 :
422 : /*
423 : * Per-worker thread interrupt-driven cleaner thread
424 : * to clean idle connections if there are no packets
425 : */
426 : static uword
427 10144 : acl_fa_worker_conn_cleaner_process (vlib_main_t * vm,
428 : vlib_node_runtime_t * rt,
429 : vlib_frame_t * f)
430 : {
431 10144 : acl_main_t *am = &acl_main;
432 10144 : u64 now = clib_cpu_time_now ();
433 10141 : u16 thread_index = os_get_thread_index ();
434 10145 : acl_fa_per_worker_data_t *pw = &am->per_worker_data[thread_index];
435 : int num_expired;
436 10145 : elog_acl_maybe_trace_X1 (am,
437 : "acl_fa_worker_conn_cleaner interrupt: now %lu",
438 : "i8", now);
439 : /* allow another interrupt to be queued */
440 10145 : pw->interrupt_is_pending = 0;
441 10145 : if (pw->clear_in_process)
442 : {
443 4944 : if (0 == pw->swipe_end_time)
444 : {
445 : /*
446 : * Someone has just set the flag to start clearing.
447 : * we do this by combing through the connections up to a "time T"
448 : * which is now, and requeueing everything except the expired
449 : * connections and those matching the interface(s) being cleared.
450 : */
451 :
452 : /*
453 : * first filter the sw_if_index bitmap that they want from us, by
454 : * a bitmap of sw_if_index for which we actually have connections.
455 : */
456 4944 : if ((pw->pending_clear_sw_if_index_bitmap == 0)
457 596 : || (pw->serviced_sw_if_index_bitmap == 0))
458 : {
459 4630 : elog_acl_maybe_trace_X1 (am,
460 : "acl_fa_worker_conn_cleaner: now %lu, someone tried to call clear but one of the bitmaps are empty",
461 : "i8", now);
462 4630 : clib_bitmap_zero (pw->pending_clear_sw_if_index_bitmap);
463 : }
464 : else
465 : {
466 : #ifdef FA_NODE_VERBOSE_DEBUG
467 : clib_warning
468 : ("WORKER-CLEAR: (before and) swiping sw-if-index bitmap: %U, my serviced bitmap %U",
469 : format_bitmap_hex, pw->pending_clear_sw_if_index_bitmap,
470 : format_bitmap_hex, pw->serviced_sw_if_index_bitmap);
471 : #endif
472 314 : pw->pending_clear_sw_if_index_bitmap =
473 314 : clib_bitmap_and (pw->pending_clear_sw_if_index_bitmap,
474 : pw->serviced_sw_if_index_bitmap);
475 : }
476 :
477 4939 : if (clib_bitmap_is_zero (pw->pending_clear_sw_if_index_bitmap))
478 : {
479 : /* if the cross-section is a zero vector, no need to do anything. */
480 4713 : elog_acl_maybe_trace_X1 (am,
481 : "acl_fa_worker_conn_cleaner: now %lu, clearing done, nothing to do",
482 : "i8", now);
483 4711 : pw->clear_in_process = 0;
484 4711 : pw->swipe_end_time = 0;
485 : }
486 : else
487 : {
488 : #ifdef FA_NODE_VERBOSE_DEBUG
489 : clib_warning
490 : ("WORKER-CLEAR: swiping sw-if-index bitmap: %U, my serviced bitmap %U",
491 : format_bitmap_hex, pw->pending_clear_sw_if_index_bitmap,
492 : format_bitmap_hex, pw->serviced_sw_if_index_bitmap);
493 : #endif
494 219 : elog_acl_maybe_trace_X1 (am,
495 : "acl_fa_worker_conn_cleaner: swiping until %lu",
496 : "i8", now);
497 : /* swipe through the connection lists until enqueue timestamps become above "now" */
498 219 : pw->swipe_end_time = now;
499 : }
500 : }
501 : }
502 10131 : num_expired = acl_fa_check_idle_sessions (am, thread_index, now);
503 : // clib_warning("WORKER-CLEAR: checked %d sessions (clear_in_progress: %d)", num_expired, pw->clear_in_process);
504 10118 : elog_acl_maybe_trace_X2 (am,
505 : "acl_fa_worker_conn_cleaner: checked %d sessions (clear_in_process: %d)",
506 : "i4i4", (u32) num_expired,
507 : (u32) pw->clear_in_process);
508 10118 : if (pw->clear_in_process)
509 : {
510 219 : if (pw->swipe_end_time == 0)
511 : {
512 : /* we were clearing but we could not process any more connections. time to stop. */
513 219 : clib_bitmap_zero (pw->pending_clear_sw_if_index_bitmap);
514 219 : pw->clear_in_process = 0;
515 219 : elog_acl_maybe_trace_X1 (am,
516 : "acl_fa_worker_conn_cleaner: now %lu, clearing done - all done",
517 : "i8", now);
518 : }
519 : else
520 : {
521 0 : elog_acl_maybe_trace_X1 (am,
522 : "acl_fa_worker_conn_cleaner: now %lu, more work to do - requesting interrupt",
523 : "i8", now);
524 : /* should continue clearing.. So could they please sent an interrupt again? */
525 0 : send_one_worker_interrupt (vm, am, thread_index);
526 : // pw->interrupt_is_needed = 1;
527 : }
528 : }
529 : else
530 : {
531 9899 : if (num_expired > 0)
532 : {
533 : /* there was too much work, we should get an interrupt ASAP */
534 : // pw->interrupt_is_needed = 1;
535 25 : send_one_worker_interrupt (vm, am, thread_index);
536 25 : pw->interrupt_is_unwanted = 0;
537 : }
538 : else
539 : {
540 : /* the current rate of interrupts is ok */
541 9874 : pw->interrupt_is_needed = 0;
542 9874 : pw->interrupt_is_unwanted = 0;
543 : }
544 9899 : elog_acl_maybe_trace_X3 (am,
545 : "acl_fa_worker_conn_cleaner: now %lu, interrupt needed: %u, interrupt unwanted: %u",
546 : "i8i4i4", now, ((u32) pw->interrupt_is_needed),
547 : ((u32) pw->interrupt_is_unwanted));
548 : }
549 : /* be persistent about quickly deleting the connections from the purgatory */
550 10118 : if (purgatory_has_connections (vm, am, thread_index))
551 : {
552 25 : send_one_worker_interrupt (vm, am, thread_index);
553 : }
554 10119 : pw->interrupt_generation = am->fa_interrupt_generation;
555 10119 : return 0;
556 : }
557 :
558 : static void
559 10155 : send_interrupts_to_workers (vlib_main_t * vm, acl_main_t * am)
560 : {
561 : int i;
562 : /* Can't use vec_len(am->per_worker_data) since the threads might not have come up yet; */
563 10155 : int n_threads = vlib_get_n_threads ();
564 20626 : for (i = 0; i < n_threads; i++)
565 : {
566 10471 : send_one_worker_interrupt (vm, am, i);
567 : }
568 10155 : }
569 :
570 : /* centralized process to drive per-worker cleaners */
571 : static uword
572 575 : acl_fa_session_cleaner_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
573 : vlib_frame_t * f)
574 : {
575 575 : acl_main_t *am = &acl_main;
576 : u64 now;
577 575 : f64 cpu_cps = vm->clib_time.clocks_per_second;
578 : u64 next_expire;
579 : /* We should check if there are connections to clean up - at least twice a second */
580 575 : u64 max_timer_wait_interval = cpu_cps / 2;
581 575 : uword event_type, *event_data = 0;
582 : acl_fa_per_worker_data_t *pw0;
583 :
584 575 : am->fa_current_cleaner_timer_wait_interval = max_timer_wait_interval;
585 575 : am->fa_cleaner_node_index = acl_fa_session_cleaner_process_node.index;
586 575 : am->fa_interrupt_generation = 1;
587 : while (1)
588 5321 : {
589 5896 : now = clib_cpu_time_now ();
590 5896 : next_expire = now + am->fa_current_cleaner_timer_wait_interval;
591 5896 : int has_pending_conns = 0;
592 : u16 ti;
593 : u8 tt;
594 :
595 : /*
596 : * walk over all per-thread list heads of different timeouts,
597 : * and see if there are any connections pending.
598 : * If there aren't - we do not need to wake up until the
599 : * worker code signals that it has added a connection.
600 : *
601 : * Also, while we are at it, calculate the earliest we need to wake up.
602 : */
603 12031 : for (ti = 0; ti < vlib_get_n_threads (); ti++)
604 : {
605 6135 : if (ti >= vec_len (am->per_worker_data))
606 : {
607 0 : continue;
608 : }
609 6135 : acl_fa_per_worker_data_t *pw = &am->per_worker_data[ti];
610 36810 : for (tt = 0; tt < vec_len (pw->fa_conn_list_head); tt++)
611 : {
612 : u64 head_expiry =
613 30675 : acl_fa_get_list_head_expiry_time (am, pw, now, ti, tt);
614 30675 : if ((head_expiry < next_expire) && !pw->interrupt_is_pending)
615 : {
616 0 : elog_acl_maybe_trace_X3 (am,
617 : "acl_fa_session_cleaner_process: now %lu, worker: %u tt: %u",
618 : "i8i2i2", now, ti, tt);
619 0 : elog_acl_maybe_trace_X2 (am,
620 : "acl_fa_session_cleaner_process: head expiry: %lu, is earlier than curr next expire: %lu",
621 : "i8i8", head_expiry, next_expire);
622 0 : next_expire = head_expiry;
623 : }
624 30675 : if (FA_SESSION_BOGUS_INDEX != pw->fa_conn_list_head[tt])
625 : {
626 119 : has_pending_conns = 1;
627 : }
628 : }
629 : }
630 :
631 : /* If no pending connections and no ACL applied then no point in timing out */
632 5896 : if (!has_pending_conns && (0 == am->fa_total_enabled_count))
633 : {
634 4905 : am->fa_cleaner_cnt_wait_without_timeout++;
635 4905 : elog_acl_maybe_trace_X1 (am,
636 : "acl_conn_cleaner: now %lu entering wait without timeout",
637 : "i8", now);
638 4905 : (void) vlib_process_wait_for_event (vm);
639 4331 : event_type = vlib_process_get_events (vm, &event_data);
640 : }
641 : else
642 : {
643 991 : f64 timeout = ((i64) next_expire - (i64) now) / cpu_cps;
644 991 : if (timeout <= 0)
645 : {
646 : /* skip waiting altogether */
647 0 : event_type = ~0;
648 : }
649 : else
650 : {
651 991 : am->fa_cleaner_cnt_wait_with_timeout++;
652 991 : elog_acl_maybe_trace_X2 (am,
653 : "acl_conn_cleaner: now %lu entering wait with timeout %.6f sec",
654 : "i8f8", now, timeout);
655 991 : (void) vlib_process_wait_for_event_or_clock (vm, timeout);
656 990 : event_type = vlib_process_get_events (vm, &event_data);
657 : }
658 : }
659 :
660 5321 : switch (event_type)
661 : {
662 236 : case ~0:
663 : /* nothing to do */
664 236 : break;
665 251 : case ACL_FA_CLEANER_RESCHEDULE:
666 : /* Nothing to do. */
667 251 : break;
668 4834 : case ACL_FA_CLEANER_DELETE_BY_SW_IF_INDEX:
669 : {
670 4834 : uword *clear_sw_if_index_bitmap = 0;
671 : uword *sw_if_index0;
672 4834 : int clear_all = 0;
673 4834 : now = clib_cpu_time_now ();
674 4834 : elog_acl_maybe_trace_X1 (am,
675 : "acl_fa_session_cleaner_process: now %lu, received ACL_FA_CLEANER_DELETE_BY_SW_IF_INDEX",
676 : "i8", now);
677 9876 : vec_foreach (sw_if_index0, event_data)
678 : {
679 5042 : am->fa_cleaner_cnt_delete_by_sw_index++;
680 5042 : elog_acl_maybe_trace_X1 (am,
681 : "acl_fa_session_cleaner_process: ACL_FA_CLEANER_DELETE_BY_SW_IF_INDEX %u",
682 : "i4", *sw_if_index0);
683 5042 : if (*sw_if_index0 == ~0)
684 : {
685 0 : clear_all = 1;
686 : }
687 : else
688 : {
689 5042 : if (!pool_is_free_index
690 5042 : (am->vnet_main->interface_main.sw_interfaces,
691 : *sw_if_index0))
692 : {
693 790 : clear_sw_if_index_bitmap =
694 790 : clib_bitmap_set (clear_sw_if_index_bitmap,
695 : *sw_if_index0, 1);
696 : }
697 : }
698 : }
699 4834 : acl_log_info
700 : ("ACL_FA_CLEANER_DELETE_BY_SW_IF_INDEX bitmap: %U, clear_all: %u",
701 : format_bitmap_hex, clear_sw_if_index_bitmap, clear_all);
702 9800 : vec_foreach (pw0, am->per_worker_data)
703 : {
704 4966 : CLIB_MEMORY_BARRIER ();
705 4966 : while (pw0->clear_in_process)
706 : {
707 0 : CLIB_MEMORY_BARRIER ();
708 0 : elog_acl_maybe_trace_X1 (am,
709 : "ACL_FA_NODE_CLEAN: waiting previous cleaning cycle to finish on %u",
710 : "i4",
711 : (u32) (pw0 - am->per_worker_data));
712 0 : vlib_process_suspend (vm, 0.0001);
713 0 : if (pw0->interrupt_is_needed)
714 : {
715 0 : send_one_worker_interrupt (vm, am,
716 0 : (pw0 - am->per_worker_data));
717 : }
718 : }
719 4966 : if (pw0->clear_in_process)
720 : {
721 0 : acl_log_err
722 : ("ERROR-BUG! Could not initiate cleaning on worker because another cleanup in progress");
723 : }
724 : else
725 : {
726 4966 : clib_bitmap_free (pw0->pending_clear_sw_if_index_bitmap);
727 4966 : if (clear_all)
728 : {
729 : /* if we need to clear all, then just clear the interfaces that we are servicing */
730 0 : pw0->pending_clear_sw_if_index_bitmap =
731 0 : clib_bitmap_dup (pw0->serviced_sw_if_index_bitmap);
732 : }
733 : else
734 : {
735 4966 : pw0->pending_clear_sw_if_index_bitmap =
736 4966 : clib_bitmap_dup (clear_sw_if_index_bitmap);
737 : }
738 4966 : acl_log_info
739 : ("ACL_FA_CLEANER: thread %u, pending clear bitmap: %U",
740 : (am->per_worker_data - pw0), format_bitmap_hex,
741 : pw0->pending_clear_sw_if_index_bitmap);
742 4966 : pw0->clear_in_process = 1;
743 : }
744 : }
745 : /* send some interrupts so they can start working */
746 4834 : send_interrupts_to_workers (vm, am);
747 :
748 : /* now wait till they all complete */
749 4834 : acl_log_info ("CLEANER mains len: %u per-worker len: %d",
750 : vlib_get_n_threads (),
751 : vec_len (am->per_worker_data));
752 9800 : vec_foreach (pw0, am->per_worker_data)
753 : {
754 4966 : CLIB_MEMORY_BARRIER ();
755 9800 : while (pw0->clear_in_process)
756 : {
757 4834 : CLIB_MEMORY_BARRIER ();
758 4834 : elog_acl_maybe_trace_X1 (am,
759 : "ACL_FA_NODE_CLEAN: waiting for my cleaning cycle to finish on %u",
760 : "i4",
761 : (u32) (pw0 - am->per_worker_data));
762 4834 : vlib_process_suspend (vm, 0.0001);
763 4834 : if (pw0->interrupt_is_needed)
764 : {
765 0 : send_one_worker_interrupt (vm, am,
766 0 : (pw0 - am->per_worker_data));
767 : }
768 : }
769 : }
770 4834 : acl_log_info ("ACL_FA_NODE_CLEAN: cleaning done");
771 4834 : clib_bitmap_free (clear_sw_if_index_bitmap);
772 : }
773 4834 : am->fa_cleaner_cnt_delete_by_sw_index_ok++;
774 4834 : break;
775 0 : default:
776 : #ifdef FA_NODE_VERBOSE_DEBUG
777 : clib_warning ("ACL plugin connection cleaner: unknown event %u",
778 : event_type);
779 : #endif
780 0 : vlib_node_increment_counter (vm,
781 : acl_fa_session_cleaner_process_node.
782 : index,
783 : ACL_FA_CLEANER_ERROR_UNKNOWN_EVENT, 1);
784 0 : am->fa_cleaner_cnt_unknown_event++;
785 0 : break;
786 : }
787 :
788 5321 : send_interrupts_to_workers (vm, am);
789 :
790 5321 : if (event_data)
791 5321 : vec_set_len (event_data, 0);
792 :
793 : /*
794 : * If the interrupts were not processed yet, ensure we wait a bit,
795 : * but up to a point.
796 : */
797 5321 : int need_more_wait = 0;
798 5321 : int max_wait_cycles = 100;
799 : do
800 : {
801 5737 : need_more_wait = 0;
802 11742 : vec_foreach (pw0, am->per_worker_data)
803 : {
804 6005 : if (pw0->interrupt_generation != am->fa_interrupt_generation)
805 : {
806 479 : need_more_wait = 1;
807 : }
808 : }
809 5737 : if (need_more_wait)
810 : {
811 416 : vlib_process_suspend (vm, 0.0001);
812 : }
813 : }
814 5737 : while (need_more_wait && (--max_wait_cycles > 0));
815 :
816 5321 : int interrupts_needed = 0;
817 5321 : int interrupts_unwanted = 0;
818 :
819 10826 : vec_foreach (pw0, am->per_worker_data)
820 : {
821 5505 : if (pw0->interrupt_is_needed)
822 : {
823 0 : interrupts_needed++;
824 : /* the per-worker value is reset when sending the interrupt */
825 : }
826 5505 : if (pw0->interrupt_is_unwanted)
827 : {
828 0 : interrupts_unwanted++;
829 0 : pw0->interrupt_is_unwanted = 0;
830 : }
831 : }
832 5321 : if (interrupts_needed)
833 : {
834 : /* they need more interrupts, do less waiting around next time */
835 0 : am->fa_current_cleaner_timer_wait_interval /= 2;
836 : /* never go into zero-wait either though - we need to give the space to others */
837 0 : am->fa_current_cleaner_timer_wait_interval += 1;
838 : }
839 5321 : else if (interrupts_unwanted)
840 : {
841 : /* slowly increase the amount of sleep up to a limit */
842 0 : if (am->fa_current_cleaner_timer_wait_interval <
843 : max_timer_wait_interval)
844 0 : am->fa_current_cleaner_timer_wait_interval +=
845 0 : cpu_cps * am->fa_cleaner_wait_time_increment;
846 : }
847 5321 : am->fa_cleaner_cnt_event_cycles++;
848 5321 : am->fa_interrupt_generation++;
849 : }
850 : /* NOT REACHED */
851 : return 0;
852 : }
853 :
854 :
855 : void
856 646 : acl_fa_enable_disable (u32 sw_if_index, int is_input, int enable_disable)
857 : {
858 646 : acl_main_t *am = &acl_main;
859 646 : if (enable_disable)
860 : {
861 323 : acl_fa_verify_init_sessions (am);
862 323 : am->fa_total_enabled_count++;
863 323 : vlib_process_signal_event (am->vlib_main, am->fa_cleaner_node_index,
864 : ACL_FA_CLEANER_RESCHEDULE, 0);
865 : }
866 : else
867 : {
868 323 : am->fa_total_enabled_count--;
869 : }
870 :
871 646 : if (is_input)
872 : {
873 404 : ASSERT (clib_bitmap_get (am->fa_in_acl_on_sw_if_index, sw_if_index) !=
874 : enable_disable);
875 404 : vnet_feature_enable_disable ("ip4-unicast", "acl-plugin-in-ip4-fa",
876 : sw_if_index, enable_disable, 0, 0);
877 404 : vnet_feature_enable_disable ("ip6-unicast", "acl-plugin-in-ip6-fa",
878 : sw_if_index, enable_disable, 0, 0);
879 404 : am->fa_in_acl_on_sw_if_index =
880 404 : clib_bitmap_set (am->fa_in_acl_on_sw_if_index, sw_if_index,
881 : enable_disable);
882 : }
883 : else
884 : {
885 242 : ASSERT (clib_bitmap_get (am->fa_out_acl_on_sw_if_index, sw_if_index) !=
886 : enable_disable);
887 242 : vnet_feature_enable_disable ("ip4-output", "acl-plugin-out-ip4-fa",
888 : sw_if_index, enable_disable, 0, 0);
889 242 : vnet_feature_enable_disable ("ip6-output", "acl-plugin-out-ip6-fa",
890 : sw_if_index, enable_disable, 0, 0);
891 242 : am->fa_out_acl_on_sw_if_index =
892 242 : clib_bitmap_set (am->fa_out_acl_on_sw_if_index, sw_if_index,
893 : enable_disable);
894 : }
895 646 : if ((!enable_disable) && (!acl_fa_ifc_has_in_acl (am, sw_if_index))
896 234 : && (!acl_fa_ifc_has_out_acl (am, sw_if_index)))
897 : {
898 : #ifdef FA_NODE_VERBOSE_DEBUG
899 : clib_warning ("ENABLE-DISABLE: clean the connections on interface %d",
900 : sw_if_index);
901 : #endif
902 218 : vlib_process_signal_event (am->vlib_main, am->fa_cleaner_node_index,
903 : ACL_FA_CLEANER_DELETE_BY_SW_IF_INDEX,
904 : sw_if_index);
905 : }
906 646 : }
907 :
908 : void
909 41 : show_fa_sessions_hash (vlib_main_t * vm, u32 verbose)
910 : {
911 41 : acl_main_t *am = &acl_main;
912 41 : if (am->fa_sessions_hash_is_initialized)
913 : {
914 40 : vlib_cli_output (vm, "\nIPv6 Session lookup hash table:\n%U\n\n",
915 : format_bihash_40_8, &am->fa_ip6_sessions_hash,
916 : verbose);
917 :
918 40 : vlib_cli_output (vm, "\nIPv4 Session lookup hash table:\n%U\n\n",
919 : format_bihash_16_8, &am->fa_ip4_sessions_hash,
920 : verbose);
921 : }
922 : else
923 : {
924 1 : vlib_cli_output (vm,
925 : "\nSession lookup hash table is not allocated.\n\n");
926 : }
927 41 : }
928 :
929 :
930 : /* *INDENT-OFF* */
931 :
932 175724 : VLIB_REGISTER_NODE (acl_fa_worker_session_cleaner_process_node, static) = {
933 : .function = acl_fa_worker_conn_cleaner_process,
934 : .name = "acl-plugin-fa-worker-cleaner-process",
935 : .type = VLIB_NODE_TYPE_INPUT,
936 : .state = VLIB_NODE_STATE_INTERRUPT,
937 : };
938 :
939 175724 : VLIB_REGISTER_NODE (acl_fa_session_cleaner_process_node, static) = {
940 : .function = acl_fa_session_cleaner_process,
941 : .type = VLIB_NODE_TYPE_PROCESS,
942 : .name = "acl-plugin-fa-cleaner-process",
943 : .n_errors = ARRAY_LEN (acl_fa_cleaner_error_strings),
944 : .error_strings = acl_fa_cleaner_error_strings,
945 : .n_next_nodes = 0,
946 : .next_nodes = {},
947 : };
948 :
949 :
950 : /* *INDENT-ON* */
951 :
952 : /*
953 : * fd.io coding-style-patch-verification: ON
954 : *
955 : * Local Variables:
956 : * eval: (c-set-style "gnu")
957 : * End:
958 : */
|