2 * Copyright (c) 2016 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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 /* ICMPv4 invert type for stateful ACL */
18 static const u8 icmp4_invmap[] = {
19 [ICMP4_echo_reply] = ICMP4_echo_request + 1,
20 [ICMP4_timestamp_reply] = ICMP4_timestamp_request + 1,
21 [ICMP4_information_reply] = ICMP4_information_request + 1,
22 [ICMP4_address_mask_reply] = ICMP4_address_mask_request + 1
25 /* Supported ICMPv4 messages for session creation */
26 static const u8 icmp4_valid_new[] = {
27 [ICMP4_echo_request] = 1,
28 [ICMP4_timestamp_request] = 1,
29 [ICMP4_information_request] = 1,
30 [ICMP4_address_mask_request] = 1
33 /* ICMPv6 invert type for stateful ACL */
34 static const u8 icmp6_invmap[] = {
35 [ICMP6_echo_reply - 128] = ICMP6_echo_request + 1,
36 [ICMP6_node_information_response - 128] = ICMP6_node_information_request + 1
39 /* Supported ICMPv6 messages for session creation */
40 static const u8 icmp6_valid_new[] = {
41 [ICMP6_echo_request - 128] = 1,
42 [ICMP6_node_information_request - 128] = 1
45 /* IP4 and IP6 protocol numbers of ICMP */
46 static u8 icmp_protos[] = { IP_PROTOCOL_ICMP, IP_PROTOCOL_ICMP6 };
51 acl_fa_ifc_has_sessions (acl_main_t * am, int sw_if_index0)
53 return am->fa_sessions_hash_is_initialized;
57 acl_fa_ifc_has_in_acl (acl_main_t * am, int sw_if_index0)
59 int it_has = clib_bitmap_get (am->fa_in_acl_on_sw_if_index, sw_if_index0);
64 acl_fa_ifc_has_out_acl (acl_main_t * am, int sw_if_index0)
66 int it_has = clib_bitmap_get (am->fa_out_acl_on_sw_if_index, sw_if_index0);
70 /* Session keys match the packets received, and mirror the packets sent */
72 acl_make_5tuple_session_key (acl_main_t * am, int is_input, int is_ip6,
73 u32 sw_if_index, fa_5tuple_t * p5tuple_pkt,
74 fa_5tuple_t * p5tuple_sess)
76 int src_index = is_input ? 0 : 1;
77 int dst_index = is_input ? 1 : 0;
78 u32 valid_new_sess = 1;
79 p5tuple_sess->addr[src_index] = p5tuple_pkt->addr[0];
80 p5tuple_sess->addr[dst_index] = p5tuple_pkt->addr[1];
81 p5tuple_sess->l4.as_u64 = p5tuple_pkt->l4.as_u64;
83 if (PREDICT_TRUE (p5tuple_pkt->l4.proto != icmp_protos[is_ip6]))
85 p5tuple_sess->l4.port[src_index] = p5tuple_pkt->l4.port[0];
86 p5tuple_sess->l4.port[dst_index] = p5tuple_pkt->l4.port[1];
90 static const u8 *icmp_invmap[] = { icmp4_invmap, icmp6_invmap };
91 static const u8 *icmp_valid_new[] =
92 { icmp4_valid_new, icmp6_valid_new };
93 static const u8 icmp_invmap_size[] = { sizeof (icmp4_invmap),
96 static const u8 icmp_valid_new_size[] = { sizeof (icmp4_valid_new),
97 sizeof (icmp6_valid_new)
100 is_ip6 ? p5tuple_pkt->l4.port[0] - 128 : p5tuple_pkt->l4.port[0];
102 p5tuple_sess->l4.port[0] = p5tuple_pkt->l4.port[0];
103 p5tuple_sess->l4.port[1] = p5tuple_pkt->l4.port[1];
106 * Invert ICMP type for valid icmp_invmap messages:
107 * 1) input node with outbound ACL interface
108 * 2) output node with inbound ACL interface
111 if ((is_input && acl_fa_ifc_has_out_acl (am, sw_if_index)) ||
112 (!is_input && acl_fa_ifc_has_in_acl (am, sw_if_index)))
115 type <= icmp_invmap_size[is_ip6] && icmp_invmap[is_ip6][type])
117 p5tuple_sess->l4.port[0] = icmp_invmap[is_ip6][type] - 1;
122 * ONLY ICMP messages defined in icmp4_valid_new/icmp6_valid_new table
123 * are allowed to create stateful ACL.
124 * The other messages will be forwarded without creating a reflexive ACL.
127 type > icmp_valid_new_size[is_ip6] || !icmp_valid_new[is_ip6][type])
133 return valid_new_sess;
137 fa_session_get_timeout_type (acl_main_t * am, fa_session_t * sess)
139 /* seen both SYNs and ACKs but not FINs means we are in establshed state */
141 sess->tcp_flags_seen.as_u16 & ((TCP_FLAGS_RSTFINACKSYN << 8) +
142 TCP_FLAGS_RSTFINACKSYN);
143 switch (sess->info.l4.proto)
146 if (((TCP_FLAGS_ACKSYN << 8) + TCP_FLAGS_ACKSYN) == masked_flags)
148 return ACL_TIMEOUT_TCP_IDLE;
152 return ACL_TIMEOUT_TCP_TRANSIENT;
156 return ACL_TIMEOUT_UDP_IDLE;
159 return ACL_TIMEOUT_UDP_IDLE;
164 * Get the idle timeout of a session.
168 fa_session_get_timeout (acl_main_t * am, fa_session_t * sess)
170 u64 timeout = am->vlib_main->clib_time.clocks_per_second;
171 int timeout_type = fa_session_get_timeout_type (am, sess);
172 timeout *= am->session_timeout_sec[timeout_type];
178 always_inline fa_session_t *
179 get_session_ptr (acl_main_t * am, u16 thread_index, u32 session_index)
181 acl_fa_per_worker_data_t *pw = &am->per_worker_data[thread_index];
182 fa_session_t *sess = pool_is_free_index (pw->fa_sessions_pool,
184 pool_elt_at_index (pw->fa_sessions_pool,
190 is_valid_session_ptr (acl_main_t * am, u16 thread_index, fa_session_t * sess)
192 acl_fa_per_worker_data_t *pw = &am->per_worker_data[thread_index];
194 && ((sess - pw->fa_sessions_pool) <
195 pool_len (pw->fa_sessions_pool)));
199 acl_fa_conn_list_add_session (acl_main_t * am, fa_full_session_id_t sess_id,
203 get_session_ptr (am, sess_id.thread_index, sess_id.session_index);
204 u8 list_id = fa_session_get_timeout_type (am, sess);
205 uword thread_index = os_get_thread_index ();
206 acl_fa_per_worker_data_t *pw = &am->per_worker_data[thread_index];
207 /* the retrieved session thread index must be necessarily the same as the one in the key */
208 ASSERT (sess->thread_index == sess_id.thread_index);
209 /* the retrieved session thread index must be the same as current thread */
210 ASSERT (sess->thread_index == thread_index);
211 sess->link_enqueue_time = now;
212 sess->link_list_id = list_id;
213 sess->link_next_idx = ~0;
214 sess->link_prev_idx = pw->fa_conn_list_tail[list_id];
215 if (~0 != pw->fa_conn_list_tail[list_id])
217 fa_session_t *prev_sess =
218 get_session_ptr (am, thread_index, pw->fa_conn_list_tail[list_id]);
219 prev_sess->link_next_idx = sess_id.session_index;
220 /* We should never try to link with a session on another thread */
221 ASSERT (prev_sess->thread_index == sess->thread_index);
223 pw->fa_conn_list_tail[list_id] = sess_id.session_index;
225 #ifdef FA_NODE_VERBOSE_DEBUG
227 ("FA-SESSION-DEBUG: add session id %d on thread %d sw_if_index %d",
228 sess_id.session_index, thread_index, sess->sw_if_index);
230 pw->serviced_sw_if_index_bitmap =
231 clib_bitmap_set (pw->serviced_sw_if_index_bitmap, sess->sw_if_index, 1);
233 if (~0 == pw->fa_conn_list_head[list_id])
235 pw->fa_conn_list_head[list_id] = sess_id.session_index;
240 acl_fa_conn_list_delete_session (acl_main_t * am,
241 fa_full_session_id_t sess_id)
243 uword thread_index = os_get_thread_index ();
244 acl_fa_per_worker_data_t *pw = &am->per_worker_data[thread_index];
245 if (thread_index != sess_id.thread_index)
247 /* If another thread attempts to delete the session, fail it. */
248 #ifdef FA_NODE_VERBOSE_DEBUG
249 clib_warning ("thread id in key %d != curr thread index, not deleting");
254 get_session_ptr (am, sess_id.thread_index, sess_id.session_index);
255 /* we should never try to delete the session with another thread index */
256 ASSERT (sess->thread_index == thread_index);
257 if (~0 != sess->link_prev_idx)
259 fa_session_t *prev_sess =
260 get_session_ptr (am, thread_index, sess->link_prev_idx);
261 /* the previous session must be in the same list as this one */
262 ASSERT (prev_sess->link_list_id == sess->link_list_id);
263 prev_sess->link_next_idx = sess->link_next_idx;
265 if (~0 != sess->link_next_idx)
267 fa_session_t *next_sess =
268 get_session_ptr (am, thread_index, sess->link_next_idx);
269 /* The next session must be in the same list as the one we are deleting */
270 ASSERT (next_sess->link_list_id == sess->link_list_id);
271 next_sess->link_prev_idx = sess->link_prev_idx;
273 if (pw->fa_conn_list_head[sess->link_list_id] == sess_id.session_index)
275 pw->fa_conn_list_head[sess->link_list_id] = sess->link_next_idx;
277 if (pw->fa_conn_list_tail[sess->link_list_id] == sess_id.session_index)
279 pw->fa_conn_list_tail[sess->link_list_id] = sess->link_prev_idx;
285 acl_fa_restart_timer_for_session (acl_main_t * am, u64 now,
286 fa_full_session_id_t sess_id)
288 if (acl_fa_conn_list_delete_session (am, sess_id))
290 acl_fa_conn_list_add_session (am, sess_id, now);
296 * Our thread does not own this connection, so we can not delete
297 * The session. To avoid the complicated signaling, we simply
298 * pick the list waiting time to be the shortest of the timeouts.
299 * This way we do not have to do anything special, and let
300 * the regular requeue check take care of everything.
308 acl_fa_track_session (acl_main_t * am, int is_input, u32 sw_if_index, u64 now,
309 fa_session_t * sess, fa_5tuple_t * pkt_5tuple)
311 sess->last_active_time = now;
312 if (pkt_5tuple->pkt.tcp_flags_valid)
314 sess->tcp_flags_seen.as_u8[is_input] |= pkt_5tuple->pkt.tcp_flags;
321 acl_fa_delete_session (acl_main_t * am, u32 sw_if_index,
322 fa_full_session_id_t sess_id)
324 void *oldheap = clib_mem_set_heap (am->acl_mheap);
326 get_session_ptr (am, sess_id.thread_index, sess_id.session_index);
327 ASSERT (sess->thread_index == os_get_thread_index ());
328 clib_bihash_add_del_40_8 (&am->fa_sessions_hash, &sess->info.kv, 0);
329 acl_fa_per_worker_data_t *pw = &am->per_worker_data[sess_id.thread_index];
330 pool_put_index (pw->fa_sessions_pool, sess_id.session_index);
331 /* Deleting from timer structures not needed,
332 as the caller must have dealt with the timers. */
333 vec_validate (pw->fa_session_dels_by_sw_if_index, sw_if_index);
334 clib_mem_set_heap (oldheap);
335 pw->fa_session_dels_by_sw_if_index[sw_if_index]++;
336 clib_smp_atomic_add (&am->fa_session_total_dels, 1);
340 acl_fa_can_add_session (acl_main_t * am, int is_input, u32 sw_if_index)
343 curr_sess_count = am->fa_session_total_adds - am->fa_session_total_dels;
344 return (curr_sess_count < am->fa_conn_table_max_entries);
349 acl_fa_try_recycle_session (acl_main_t * am, int is_input, u16 thread_index,
352 /* try to recycle a TCP transient session */
353 acl_fa_per_worker_data_t *pw = &am->per_worker_data[thread_index];
354 u8 timeout_type = ACL_TIMEOUT_TCP_TRANSIENT;
355 fa_full_session_id_t sess_id;
356 sess_id.session_index = pw->fa_conn_list_head[timeout_type];
357 if (~0 != sess_id.session_index)
359 sess_id.thread_index = thread_index;
360 acl_fa_conn_list_delete_session (am, sess_id);
361 acl_fa_delete_session (am, sw_if_index, sess_id);
365 always_inline fa_session_t *
366 acl_fa_add_session (acl_main_t * am, int is_input, u32 sw_if_index, u64 now,
367 fa_5tuple_t * p5tuple, u16 current_policy_epoch)
369 clib_bihash_kv_40_8_t *pkv = &p5tuple->kv;
370 clib_bihash_kv_40_8_t kv;
371 fa_full_session_id_t f_sess_id;
372 uword thread_index = os_get_thread_index ();
373 void *oldheap = clib_mem_set_heap (am->acl_mheap);
374 acl_fa_per_worker_data_t *pw = &am->per_worker_data[thread_index];
376 f_sess_id.thread_index = thread_index;
379 pool_get_aligned (pw->fa_sessions_pool, sess, CLIB_CACHE_LINE_BYTES);
380 f_sess_id.session_index = sess - pw->fa_sessions_pool;
381 f_sess_id.intf_policy_epoch = current_policy_epoch;
383 kv.key[0] = pkv->key[0];
384 kv.key[1] = pkv->key[1];
385 kv.key[2] = pkv->key[2];
386 kv.key[3] = pkv->key[3];
387 kv.key[4] = pkv->key[4];
388 kv.value = f_sess_id.as_u64;
390 memcpy (sess, pkv, sizeof (pkv->key));
391 sess->last_active_time = now;
392 sess->sw_if_index = sw_if_index;
393 sess->tcp_flags_seen.as_u16 = 0;
394 sess->thread_index = thread_index;
395 sess->link_list_id = ~0;
396 sess->link_prev_idx = ~0;
397 sess->link_next_idx = ~0;
401 ASSERT (am->fa_sessions_hash_is_initialized == 1);
402 clib_bihash_add_del_40_8 (&am->fa_sessions_hash, &kv, 1);
403 acl_fa_conn_list_add_session (am, f_sess_id, now);
405 vec_validate (pw->fa_session_adds_by_sw_if_index, sw_if_index);
406 clib_mem_set_heap (oldheap);
407 pw->fa_session_adds_by_sw_if_index[sw_if_index]++;
408 clib_smp_atomic_add (&am->fa_session_total_adds, 1);
413 acl_fa_find_session (acl_main_t * am, u32 sw_if_index0, fa_5tuple_t * p5tuple,
414 clib_bihash_kv_40_8_t * pvalue_sess)
416 return (clib_bihash_search_40_8
417 (&am->fa_sessions_hash, &p5tuple->kv, pvalue_sess) == 0);
421 * fd.io coding-style-patch-verification: ON
424 * eval: (c-set-style "gnu")