misc: Purge unused pg includes
[vpp.git] / src / plugins / acl / dataplane_node.c
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 #include <vppinfra/bihash_40_8.h>
33 #include <vppinfra/bihash_template.h>
34
35 typedef struct
36 {
37   u32 next_index;
38   u32 sw_if_index;
39   u32 lc_index;
40   u32 match_acl_in_index;
41   u32 match_rule_index;
42   u64 packet_info[6];
43   u32 trace_bitmap;
44   u8 action;
45 } acl_fa_trace_t;
46
47 /* *INDENT-OFF* */
48 #define foreach_acl_fa_error \
49 _(ACL_DROP, "ACL deny packets")  \
50 _(ACL_PERMIT, "ACL permit packets")  \
51 _(ACL_NEW_SESSION, "new sessions added") \
52 _(ACL_EXIST_SESSION, "existing session packets") \
53 _(ACL_CHECK, "checked packets") \
54 _(ACL_RESTART_SESSION_TIMER, "restart session timer") \
55 _(ACL_TOO_MANY_SESSIONS, "too many sessions to add new") \
56 /* end  of errors */
57
58 typedef enum
59 {
60 #define _(sym,str) ACL_FA_ERROR_##sym,
61   foreach_acl_fa_error
62 #undef _
63     ACL_FA_N_ERROR,
64 } acl_fa_error_t;
65
66 /* *INDENT-ON* */
67
68 typedef struct
69 {
70   u32 next_index;
71   u32 sw_if_index;
72   u16 ethertype;
73 } nonip_in_out_trace_t;
74
75 /* packet trace format function */
76 static u8 *
77 format_nonip_in_out_trace (u8 * s, u32 is_output, va_list * args)
78 {
79   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
80   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
81   nonip_in_out_trace_t *t = va_arg (*args, nonip_in_out_trace_t *);
82
83   s = format (s, "%s: sw_if_index %d next_index %x ethertype %x",
84               is_output ? "OUT-ETHER-WHITELIST" : "IN-ETHER-WHITELIST",
85               t->sw_if_index, t->next_index, t->ethertype);
86   return s;
87 }
88
89 static u8 *
90 format_l2_nonip_in_trace (u8 * s, va_list * args)
91 {
92   return format_nonip_in_out_trace (s, 0, args);
93 }
94
95 static u8 *
96 format_l2_nonip_out_trace (u8 * s, va_list * args)
97 {
98   return format_nonip_in_out_trace (s, 1, args);
99 }
100
101 #define foreach_nonip_in_error                    \
102 _(DROP, "dropped inbound non-whitelisted non-ip packets") \
103 _(PERMIT, "permitted inbound whitelisted non-ip packets") \
104
105
106 #define foreach_nonip_out_error                    \
107 _(DROP, "dropped outbound non-whitelisted non-ip packets") \
108 _(PERMIT, "permitted outbound whitelisted non-ip packets") \
109
110
111 /* *INDENT-OFF* */
112
113 typedef enum
114 {
115 #define _(sym,str) FA_IN_NONIP_ERROR_##sym,
116   foreach_nonip_in_error
117 #undef _
118     FA_IN_NONIP_N_ERROR,
119 } l2_in_feat_arc_error_t;
120
121 static char *fa_in_nonip_error_strings[] = {
122 #define _(sym,string) string,
123   foreach_nonip_in_error
124 #undef _
125 };
126
127 typedef enum
128 {
129 #define _(sym,str) FA_OUT_NONIP_ERROR_##sym,
130   foreach_nonip_out_error
131 #undef _
132     FA_OUT_NONIP_N_ERROR,
133 } l2_out_feat_arc_error_t;
134
135 static char *fa_out_nonip_error_strings[] = {
136 #define _(sym,string) string,
137   foreach_nonip_out_error
138 #undef _
139 };
140 /* *INDENT-ON* */
141
142
143 always_inline int
144 is_permitted_ethertype (acl_main_t * am, int sw_if_index0, int is_output,
145                         u16 ethertype)
146 {
147   u16 **v = is_output
148     ? am->output_etype_whitelist_by_sw_if_index
149     : am->input_etype_whitelist_by_sw_if_index;
150   u16 *whitelist = vec_elt (v, sw_if_index0);
151   int i;
152
153   if (vec_len (whitelist) == 0)
154     return 1;
155
156   for (i = 0; i < vec_len (whitelist); i++)
157     if (whitelist[i] == ethertype)
158       return 1;
159   return 0;
160 }
161
162 #define get_u16(addr) ( *((u16 *)(addr)) )
163
164 always_inline uword
165 nonip_in_out_node_fn (vlib_main_t * vm,
166                       vlib_node_runtime_t * node, vlib_frame_t * frame,
167                       int is_output)
168 {
169   acl_main_t *am = &acl_main;
170   u32 n_left, *from;
171   u16 nexts[VLIB_FRAME_SIZE], *next;
172   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
173   vlib_node_runtime_t *error_node;
174
175   from = vlib_frame_vector_args (frame);
176   error_node = vlib_node_get_runtime (vm, node->node_index);
177   vlib_get_buffers (vm, from, bufs, frame->n_vectors);
178   /* set the initial values for the current buffer the next pointers */
179   b = bufs;
180   next = nexts;
181
182   n_left = frame->n_vectors;
183   while (n_left > 0)
184     {
185       u32 next_index = 0;
186       u32 sw_if_index0 =
187         vnet_buffer (b[0])->sw_if_index[is_output ? VLIB_TX : VLIB_RX];
188       u16 ethertype = 0;
189
190       int error0 = 0;
191
192       ethernet_header_t *h0 = vlib_buffer_get_current (b[0]);
193       u8 *l3h0 = (u8 *) h0 + vnet_buffer (b[0])->l2.l2_len;
194       ethertype = clib_net_to_host_u16 (get_u16 (l3h0 - 2));
195
196       if (is_permitted_ethertype (am, sw_if_index0, is_output, ethertype))
197         vnet_feature_next (&next_index, b[0]);
198
199       next[0] = next_index;
200
201       if (0 == next[0])
202         b[0]->error = error_node->errors[error0];
203
204       if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
205                          && (b[0]->flags & VLIB_BUFFER_IS_TRACED)))
206         {
207           nonip_in_out_trace_t *t =
208             vlib_add_trace (vm, node, b[0], sizeof (*t));
209           t->sw_if_index = sw_if_index0;
210           t->ethertype = ethertype;
211           t->next_index = next[0];
212         }
213       next[0] = next[0] < node->n_next_nodes ? next[0] : 0;
214
215       next++;
216       b++;
217       n_left--;
218     }
219   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
220
221   return frame->n_vectors;
222 }
223
224 VLIB_NODE_FN (acl_in_nonip_node) (vlib_main_t * vm,
225                                   vlib_node_runtime_t * node,
226                                   vlib_frame_t * frame)
227 {
228   return nonip_in_out_node_fn (vm, node, frame, 0);
229 }
230
231 VLIB_NODE_FN (acl_out_nonip_node) (vlib_main_t * vm,
232                                    vlib_node_runtime_t * node,
233                                    vlib_frame_t * frame)
234 {
235   return nonip_in_out_node_fn (vm, node, frame, 1);
236 }
237
238
239 /* *INDENT-OFF* */
240
241 VLIB_REGISTER_NODE (acl_in_nonip_node) =
242 {
243   .name = "acl-plugin-in-nonip-l2",
244   .vector_size = sizeof (u32),
245   .format_trace = format_l2_nonip_in_trace,
246   .type = VLIB_NODE_TYPE_INTERNAL,
247   .n_errors = ARRAY_LEN (fa_in_nonip_error_strings),
248   .error_strings = fa_in_nonip_error_strings,
249   .n_next_nodes = ACL_FA_N_NEXT,
250   .next_nodes =
251   {
252     [ACL_FA_ERROR_DROP] = "error-drop",
253   }
254 };
255
256 VNET_FEATURE_INIT (acl_in_l2_nonip_fa_feature, static) =
257 {
258   .arc_name = "l2-input-nonip",
259   .node_name = "acl-plugin-in-nonip-l2",
260   .runs_before = VNET_FEATURES ("l2-input-feat-arc-end"),
261 };
262
263 VLIB_REGISTER_NODE (acl_out_nonip_node) =
264 {
265   .name = "acl-plugin-out-nonip-l2",
266   .vector_size = sizeof (u32),
267   .format_trace = format_l2_nonip_out_trace,
268   .type = VLIB_NODE_TYPE_INTERNAL,
269   .n_errors = ARRAY_LEN (fa_out_nonip_error_strings),
270   .error_strings = fa_out_nonip_error_strings,
271   .n_next_nodes = ACL_FA_N_NEXT,
272   .next_nodes =
273   {
274     [ACL_FA_ERROR_DROP] = "error-drop",
275   }
276 };
277
278 VNET_FEATURE_INIT (acl_out_l2_nonip_fa_feature, static) =
279 {
280   .arc_name = "l2-output-nonip",
281   .node_name = "acl-plugin-out-nonip-l2",
282   .runs_before = VNET_FEATURES ("l2-output-feat-arc-end"),
283 };
284
285 /* *INDENT-ON* */
286
287
288
289 always_inline u16
290 get_current_policy_epoch (acl_main_t * am, int is_input, u32 sw_if_index0)
291 {
292   u32 **p_epoch_vec =
293     is_input ? &am->input_policy_epoch_by_sw_if_index :
294     &am->output_policy_epoch_by_sw_if_index;
295   u16 current_policy_epoch =
296     sw_if_index0 < vec_len (*p_epoch_vec) ? vec_elt (*p_epoch_vec,
297                                                      sw_if_index0)
298     : (is_input * FA_POLICY_EPOCH_IS_INPUT);
299   return current_policy_epoch;
300 }
301
302 always_inline void
303 maybe_trace_buffer (vlib_main_t * vm, vlib_node_runtime_t * node,
304                     vlib_buffer_t * b, u32 sw_if_index0, u32 lc_index0,
305                     u16 next0, int match_acl_in_index, int match_rule_index,
306                     fa_5tuple_t * fa_5tuple, u8 action, u32 trace_bitmap)
307 {
308   if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
309     {
310       acl_fa_trace_t *t = vlib_add_trace (vm, node, b, sizeof (*t));
311       t->sw_if_index = sw_if_index0;
312       t->lc_index = lc_index0;
313       t->next_index = next0;
314       t->match_acl_in_index = match_acl_in_index;
315       t->match_rule_index = match_rule_index;
316       t->packet_info[0] = fa_5tuple->kv_40_8.key[0];
317       t->packet_info[1] = fa_5tuple->kv_40_8.key[1];
318       t->packet_info[2] = fa_5tuple->kv_40_8.key[2];
319       t->packet_info[3] = fa_5tuple->kv_40_8.key[3];
320       t->packet_info[4] = fa_5tuple->kv_40_8.key[4];
321       t->packet_info[5] = fa_5tuple->kv_40_8.value;
322       t->action = action;
323       t->trace_bitmap = trace_bitmap;
324     }
325 }
326
327
328 always_inline int
329 stale_session_deleted (acl_main_t * am, int is_input,
330                        acl_fa_per_worker_data_t * pw, u64 now,
331                        u32 sw_if_index0, fa_full_session_id_t f_sess_id)
332 {
333   u16 current_policy_epoch =
334     get_current_policy_epoch (am, is_input, sw_if_index0);
335
336   /* if the MSB of policy epoch matches but not the LSB means it is a stale session */
337   if ((0 ==
338        ((current_policy_epoch ^
339          f_sess_id.intf_policy_epoch) &
340         FA_POLICY_EPOCH_IS_INPUT))
341       && (current_policy_epoch != f_sess_id.intf_policy_epoch))
342     {
343       /* delete session and increment the counter */
344       vec_validate (pw->fa_session_epoch_change_by_sw_if_index, sw_if_index0);
345       vec_elt (pw->fa_session_epoch_change_by_sw_if_index, sw_if_index0)++;
346       if (acl_fa_conn_list_delete_session (am, f_sess_id, now))
347         {
348           /* delete the session only if we were able to unlink it */
349           acl_fa_two_stage_delete_session (am, sw_if_index0, f_sess_id, now);
350         }
351       return 1;
352     }
353   else
354     return 0;
355 }
356
357
358
359
360
361 always_inline void
362 get_sw_if_index_xN (int vector_sz, int is_input, vlib_buffer_t ** b,
363                     u32 * out_sw_if_index)
364 {
365   int ii;
366   for (ii = 0; ii < vector_sz; ii++)
367     if (is_input)
368       out_sw_if_index[ii] = vnet_buffer (b[ii])->sw_if_index[VLIB_RX];
369     else
370       out_sw_if_index[ii] = vnet_buffer (b[ii])->sw_if_index[VLIB_TX];
371 }
372
373 always_inline void
374 fill_5tuple_xN (int vector_sz, acl_main_t * am, int is_ip6, int is_input,
375                 int is_l2_path, vlib_buffer_t ** b, u32 * sw_if_index,
376                 fa_5tuple_t * out_fa_5tuple)
377 {
378   int ii;
379   for (ii = 0; ii < vector_sz; ii++)
380     acl_fill_5tuple (am, sw_if_index[ii], b[ii], is_ip6,
381                      is_input, is_l2_path, &out_fa_5tuple[ii]);
382 }
383
384 always_inline void
385 make_session_hash_xN (int vector_sz, acl_main_t * am, int is_ip6,
386                       u32 * sw_if_index, fa_5tuple_t * fa_5tuple,
387                       u64 * out_hash)
388 {
389   int ii;
390   for (ii = 0; ii < vector_sz; ii++)
391     out_hash[ii] =
392       acl_fa_make_session_hash (am, is_ip6, sw_if_index[ii], &fa_5tuple[ii]);
393 }
394
395 always_inline void
396 prefetch_session_entry (acl_main_t * am, fa_full_session_id_t f_sess_id)
397 {
398   fa_session_t *sess = get_session_ptr_no_check (am, f_sess_id.thread_index,
399                                                  f_sess_id.session_index);
400   CLIB_PREFETCH (sess, 2 * CLIB_CACHE_LINE_BYTES, STORE);
401 }
402
403 always_inline u8
404 process_established_session (vlib_main_t * vm, acl_main_t * am,
405                              u32 counter_node_index, int is_input, u64 now,
406                              fa_full_session_id_t f_sess_id,
407                              u32 * sw_if_index, fa_5tuple_t * fa_5tuple,
408                              u32 pkt_len, int node_trace_on,
409                              u32 * trace_bitmap)
410 {
411   u8 action = 0;
412   fa_session_t *sess = get_session_ptr_no_check (am, f_sess_id.thread_index,
413                                                  f_sess_id.session_index);
414
415   int old_timeout_type = fa_session_get_timeout_type (am, sess);
416   action =
417     acl_fa_track_session (am, is_input, sw_if_index[0], now,
418                           sess, &fa_5tuple[0], pkt_len);
419   int new_timeout_type = fa_session_get_timeout_type (am, sess);
420   /* Tracking might have changed the session timeout type, e.g. from transient to established */
421   if (PREDICT_FALSE (old_timeout_type != new_timeout_type))
422     {
423       acl_fa_restart_timer_for_session (am, now, f_sess_id);
424       vlib_node_increment_counter (vm, counter_node_index,
425                                    ACL_FA_ERROR_ACL_RESTART_SESSION_TIMER, 1);
426       if (node_trace_on)
427         *trace_bitmap |=
428           0x00010000 + ((0xff & old_timeout_type) << 8) +
429           (0xff & new_timeout_type);
430     }
431   /*
432    * I estimate the likelihood to be very low - the VPP needs
433    * to have >64K interfaces to start with and then on
434    * exactly 64K indices apart needs to be exactly the same
435    * 5-tuple... Anyway, since this probability is nonzero -
436    * print an error and drop the unlucky packet.
437    * If this shows up in real world, we would need to bump
438    * the hash key length.
439    */
440   if (PREDICT_FALSE (sess->sw_if_index != sw_if_index[0]))
441     {
442       clib_warning
443         ("BUG: session LSB16(sw_if_index)=%d and 5-tuple=%d collision!",
444          sess->sw_if_index, sw_if_index[0]);
445       action = 0;
446     }
447   return action;
448
449 }
450
451 #define ACL_PLUGIN_VECTOR_SIZE 4
452 #define ACL_PLUGIN_PREFETCH_GAP 3
453
454 always_inline void
455 acl_fa_node_common_prepare_fn (vlib_main_t * vm,
456                                vlib_node_runtime_t * node,
457                                vlib_frame_t * frame, int is_ip6, int is_input,
458                                int is_l2_path, int with_stateful_datapath)
459         /* , int node_trace_on,
460            int reclassify_sessions) */
461 {
462   u32 n_left, *from;
463   acl_main_t *am = &acl_main;
464   uword thread_index = os_get_thread_index ();
465   acl_fa_per_worker_data_t *pw = &am->per_worker_data[thread_index];
466
467   vlib_buffer_t **b;
468   u32 *sw_if_index;
469   fa_5tuple_t *fa_5tuple;
470   u64 *hash;
471
472
473
474   from = vlib_frame_vector_args (frame);
475   vlib_get_buffers (vm, from, pw->bufs, frame->n_vectors);
476
477   /* set the initial values for the current buffer the next pointers */
478   b = pw->bufs;
479   sw_if_index = pw->sw_if_indices;
480   fa_5tuple = pw->fa_5tuples;
481   hash = pw->hashes;
482
483
484   /*
485    * fill the sw_if_index, 5tuple and session hash,
486    * First in strides of size ACL_PLUGIN_VECTOR_SIZE,
487    * with buffer prefetch being
488    * ACL_PLUGIN_PREFETCH_GAP * ACL_PLUGIN_VECTOR_SIZE entries
489    * in front. Then with a simple single loop.
490    */
491
492   n_left = frame->n_vectors;
493   while (n_left >= (ACL_PLUGIN_PREFETCH_GAP + 1) * ACL_PLUGIN_VECTOR_SIZE)
494     {
495       const int vec_sz = ACL_PLUGIN_VECTOR_SIZE;
496       {
497         int ii;
498         for (ii = ACL_PLUGIN_PREFETCH_GAP * vec_sz;
499              ii < (ACL_PLUGIN_PREFETCH_GAP + 1) * vec_sz; ii++)
500           {
501             CLIB_PREFETCH (b[ii], CLIB_CACHE_LINE_BYTES, LOAD);
502             CLIB_PREFETCH (b[ii]->data, 2 * CLIB_CACHE_LINE_BYTES, LOAD);
503           }
504       }
505
506
507       get_sw_if_index_xN (vec_sz, is_input, b, sw_if_index);
508       fill_5tuple_xN (vec_sz, am, is_ip6, is_input, is_l2_path, &b[0],
509                       &sw_if_index[0], &fa_5tuple[0]);
510       if (with_stateful_datapath)
511         make_session_hash_xN (vec_sz, am, is_ip6, &sw_if_index[0],
512                               &fa_5tuple[0], &hash[0]);
513
514       n_left -= vec_sz;
515
516       fa_5tuple += vec_sz;
517       b += vec_sz;
518       sw_if_index += vec_sz;
519       hash += vec_sz;
520     }
521
522   while (n_left > 0)
523     {
524       const int vec_sz = 1;
525
526       get_sw_if_index_xN (vec_sz, is_input, b, sw_if_index);
527       fill_5tuple_xN (vec_sz, am, is_ip6, is_input, is_l2_path, &b[0],
528                       &sw_if_index[0], &fa_5tuple[0]);
529       if (with_stateful_datapath)
530         make_session_hash_xN (vec_sz, am, is_ip6, &sw_if_index[0],
531                               &fa_5tuple[0], &hash[0]);
532
533       n_left -= vec_sz;
534
535       fa_5tuple += vec_sz;
536       b += vec_sz;
537       sw_if_index += vec_sz;
538       hash += vec_sz;
539     }
540 }
541
542
543 always_inline uword
544 acl_fa_inner_node_fn (vlib_main_t * vm,
545                       vlib_node_runtime_t * node, vlib_frame_t * frame,
546                       int is_ip6, int is_input, int is_l2_path,
547                       int with_stateful_datapath, int node_trace_on,
548                       int reclassify_sessions)
549 {
550   u32 n_left, *from;
551   u32 pkts_exist_session = 0;
552   u32 pkts_new_session = 0;
553   u32 pkts_acl_permit = 0;
554   u32 trace_bitmap = 0;
555   acl_main_t *am = &acl_main;
556   vlib_node_runtime_t *error_node;
557   vlib_error_t no_error_existing_session;
558   u64 now = clib_cpu_time_now ();
559   uword thread_index = os_get_thread_index ();
560   acl_fa_per_worker_data_t *pw = &am->per_worker_data[thread_index];
561
562   u16 *next;
563   vlib_buffer_t **b;
564   u32 *sw_if_index;
565   fa_5tuple_t *fa_5tuple;
566   u64 *hash;
567   /* for the delayed counters */
568   u32 saved_matched_acl_index = 0;
569   u32 saved_matched_ace_index = 0;
570   u32 saved_packet_count = 0;
571   u32 saved_byte_count = 0;
572
573   from = vlib_frame_vector_args (frame);
574   error_node = vlib_node_get_runtime (vm, node->node_index);
575   no_error_existing_session =
576     error_node->errors[ACL_FA_ERROR_ACL_EXIST_SESSION];
577
578   b = pw->bufs;
579   next = pw->nexts;
580   sw_if_index = pw->sw_if_indices;
581   fa_5tuple = pw->fa_5tuples;
582   hash = pw->hashes;
583
584   /*
585    * Now the "hard" work of session lookups and ACL lookups for new sessions.
586    * Due to the complexity, do it for the time being in single loop with
587    * the pipeline of three prefetches:
588    *    1) bucket for the session bihash
589    *    2) data for the session bihash
590    *    3) worker session record
591    */
592
593   fa_full_session_id_t f_sess_id_next = {.as_u64 = ~0ULL };
594
595   /* find the "next" session so we can kickstart the pipeline */
596   if (with_stateful_datapath)
597     acl_fa_find_session_with_hash (am, is_ip6, sw_if_index[0], hash[0],
598                                    &fa_5tuple[0], &f_sess_id_next.as_u64);
599
600   n_left = frame->n_vectors;
601   while (n_left > 0)
602     {
603       u8 action = 0;
604       u32 lc_index0 = ~0;
605       int acl_check_needed = 1;
606       u32 match_acl_in_index = ~0;
607       u32 match_acl_pos = ~0;
608       u32 match_rule_index = ~0;
609
610       next[0] = 0;              /* drop by default */
611
612       /* Try to match an existing session first */
613
614       if (with_stateful_datapath)
615         {
616           fa_full_session_id_t f_sess_id = f_sess_id_next;
617           switch (n_left)
618             {
619             default:
620               acl_fa_prefetch_session_bucket_for_hash (am, is_ip6, hash[5]);
621               /* fallthrough */
622             case 5:
623             case 4:
624               acl_fa_prefetch_session_data_for_hash (am, is_ip6, hash[3]);
625               /* fallthrough */
626             case 3:
627             case 2:
628               acl_fa_find_session_with_hash (am, is_ip6, sw_if_index[1],
629                                              hash[1], &fa_5tuple[1],
630                                              &f_sess_id_next.as_u64);
631               if (f_sess_id_next.as_u64 != ~0ULL)
632                 {
633                   prefetch_session_entry (am, f_sess_id_next);
634                 }
635               /* fallthrough */
636             case 1:
637               if (f_sess_id.as_u64 != ~0ULL)
638                 {
639                   if (node_trace_on)
640                     {
641                       trace_bitmap |= 0x80000000;
642                     }
643                   ASSERT (f_sess_id.thread_index < vec_len (vlib_mains));
644                   b[0]->error = no_error_existing_session;
645                   acl_check_needed = 0;
646                   pkts_exist_session += 1;
647                   action =
648                     process_established_session (vm, am, node->node_index,
649                                                  is_input, now, f_sess_id,
650                                                  &sw_if_index[0],
651                                                  &fa_5tuple[0],
652                                                  b[0]->current_length,
653                                                  node_trace_on,
654                                                  &trace_bitmap);
655
656                   /* expose the session id to the tracer */
657                   if (node_trace_on)
658                     {
659                       match_rule_index = f_sess_id.session_index;
660                     }
661
662                   if (reclassify_sessions)
663                     {
664                       if (PREDICT_FALSE
665                           (stale_session_deleted
666                            (am, is_input, pw, now, sw_if_index[0],
667                             f_sess_id)))
668                         {
669                           acl_check_needed = 1;
670                           if (node_trace_on)
671                             {
672                               trace_bitmap |= 0x40000000;
673                             }
674                           /*
675                            * If we have just deleted the session, and the next
676                            * buffer is the same 5-tuple, that session prediction
677                            * is wrong, correct it.
678                            */
679                           if ((f_sess_id_next.as_u64 != ~0ULL)
680                               && 0 == memcmp (&fa_5tuple[1], &fa_5tuple[0],
681                                               sizeof (fa_5tuple[1])))
682                             f_sess_id_next.as_u64 = ~0ULL;
683                         }
684                     }
685                 }
686             }
687
688           if (acl_check_needed)
689             {
690               if (is_input)
691                 lc_index0 = am->input_lc_index_by_sw_if_index[sw_if_index[0]];
692               else
693                 lc_index0 =
694                   am->output_lc_index_by_sw_if_index[sw_if_index[0]];
695
696               action = 0;       /* deny by default */
697               int is_match = acl_plugin_match_5tuple_inline (am, lc_index0,
698                                                              (fa_5tuple_opaque_t *) & fa_5tuple[0], is_ip6,
699                                                              &action,
700                                                              &match_acl_pos,
701                                                              &match_acl_in_index,
702                                                              &match_rule_index,
703                                                              &trace_bitmap);
704               if (PREDICT_FALSE
705                   (is_match && am->interface_acl_counters_enabled))
706                 {
707                   u32 buf_len = vlib_buffer_length_in_chain (vm, b[0]);
708                   vlib_increment_combined_counter (am->combined_acl_counters +
709                                                    saved_matched_acl_index,
710                                                    thread_index,
711                                                    saved_matched_ace_index,
712                                                    saved_packet_count,
713                                                    saved_byte_count);
714                   saved_matched_acl_index = match_acl_in_index;
715                   saved_matched_ace_index = match_rule_index;
716                   saved_packet_count = 1;
717                   saved_byte_count = buf_len;
718                   /* prefetch the counter that we are going to increment */
719                   vlib_prefetch_combined_counter (am->combined_acl_counters +
720                                                   saved_matched_acl_index,
721                                                   thread_index,
722                                                   saved_matched_ace_index);
723                 }
724
725               b[0]->error = error_node->errors[action];
726
727               if (1 == action)
728                 pkts_acl_permit++;
729
730               if (2 == action)
731                 {
732                   if (!acl_fa_can_add_session (am, is_input, sw_if_index[0]))
733                     acl_fa_try_recycle_session (am, is_input,
734                                                 thread_index,
735                                                 sw_if_index[0], now);
736
737                   if (acl_fa_can_add_session (am, is_input, sw_if_index[0]))
738                     {
739                       u16 current_policy_epoch =
740                         get_current_policy_epoch (am, is_input,
741                                                   sw_if_index[0]);
742                       fa_full_session_id_t f_sess_id =
743                         acl_fa_add_session (am, is_input, is_ip6,
744                                             sw_if_index[0],
745                                             now, &fa_5tuple[0],
746                                             current_policy_epoch);
747
748                       /* perform the accounting for the newly added session */
749                       process_established_session (vm, am,
750                                                    node->node_index,
751                                                    is_input, now,
752                                                    f_sess_id,
753                                                    &sw_if_index[0],
754                                                    &fa_5tuple[0],
755                                                    b[0]->current_length,
756                                                    node_trace_on,
757                                                    &trace_bitmap);
758                       pkts_new_session++;
759                       /*
760                        * If the next 5tuple is the same and we just added the session,
761                        * the f_sess_id_next can not be ~0. Correct it.
762                        */
763                       if ((f_sess_id_next.as_u64 == ~0ULL)
764                           && 0 == memcmp (&fa_5tuple[1], &fa_5tuple[0],
765                                           sizeof (fa_5tuple[1])))
766                         f_sess_id_next = f_sess_id;
767                     }
768                   else
769                     {
770                       action = 0;
771                       b[0]->error =
772                         error_node->errors
773                         [ACL_FA_ERROR_ACL_TOO_MANY_SESSIONS];
774                     }
775                 }
776
777             }
778
779           {
780             /* speculatively get the next0 */
781             vnet_feature_next_u16 (&next[0], b[0]);
782             /* if the action is not deny - then use that next */
783             next[0] = action ? next[0] : 0;
784           }
785
786           if (node_trace_on)    // PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
787             {
788               maybe_trace_buffer (vm, node, b[0], sw_if_index[0], lc_index0,
789                                   next[0], match_acl_in_index,
790                                   match_rule_index, &fa_5tuple[0], action,
791                                   trace_bitmap);
792             }
793
794           next++;
795           b++;
796           fa_5tuple++;
797           sw_if_index++;
798           hash++;
799           n_left -= 1;
800         }
801     }
802
803   vlib_buffer_enqueue_to_next (vm, node, from, pw->nexts, frame->n_vectors);
804
805   /*
806    * if we were had an acl match then we have a counter to increment.
807    * else it is all zeroes, so this will be harmless.
808    */
809   vlib_increment_combined_counter (am->combined_acl_counters +
810                                    saved_matched_acl_index,
811                                    thread_index,
812                                    saved_matched_ace_index,
813                                    saved_packet_count, saved_byte_count);
814
815   vlib_node_increment_counter (vm, node->node_index,
816                                ACL_FA_ERROR_ACL_CHECK, frame->n_vectors);
817   vlib_node_increment_counter (vm, node->node_index,
818                                ACL_FA_ERROR_ACL_EXIST_SESSION,
819                                pkts_exist_session);
820   vlib_node_increment_counter (vm, node->node_index,
821                                ACL_FA_ERROR_ACL_NEW_SESSION,
822                                pkts_new_session);
823   vlib_node_increment_counter (vm, node->node_index,
824                                ACL_FA_ERROR_ACL_PERMIT, pkts_acl_permit);
825   return frame->n_vectors;
826 }
827
828 always_inline uword
829 acl_fa_outer_node_fn (vlib_main_t * vm,
830                       vlib_node_runtime_t * node, vlib_frame_t * frame,
831                       int is_ip6, int is_input, int is_l2_path,
832                       int do_stateful_datapath)
833 {
834   acl_main_t *am = &acl_main;
835
836   acl_fa_node_common_prepare_fn (vm, node, frame, is_ip6, is_input,
837                                  is_l2_path, do_stateful_datapath);
838
839   if (am->reclassify_sessions)
840     {
841       if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
842         return acl_fa_inner_node_fn (vm, node, frame, is_ip6, is_input,
843                                      is_l2_path, do_stateful_datapath,
844                                      1 /* trace */ ,
845                                      1 /* reclassify */ );
846       else
847         return acl_fa_inner_node_fn (vm, node, frame, is_ip6, is_input,
848                                      is_l2_path, do_stateful_datapath, 0,
849                                      1 /* reclassify */ );
850     }
851   else
852     {
853       if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE))
854         return acl_fa_inner_node_fn (vm, node, frame, is_ip6, is_input,
855                                      is_l2_path, do_stateful_datapath,
856                                      1 /* trace */ ,
857                                      0);
858       else
859         return acl_fa_inner_node_fn (vm, node, frame, is_ip6, is_input,
860                                      is_l2_path, do_stateful_datapath, 0, 0);
861     }
862 }
863
864 always_inline uword
865 acl_fa_node_fn (vlib_main_t * vm,
866                 vlib_node_runtime_t * node, vlib_frame_t * frame, int is_ip6,
867                 int is_input, int is_l2_path)
868 {
869   /* select the reclassify/no-reclassify version of the datapath */
870   acl_main_t *am = &acl_main;
871
872   if (am->fa_sessions_hash_is_initialized)
873     return acl_fa_outer_node_fn (vm, node, frame, is_ip6, is_input,
874                                  is_l2_path, 1);
875   else
876     return acl_fa_outer_node_fn (vm, node, frame, is_ip6, is_input,
877                                  is_l2_path, 0);
878 }
879
880
881 static u8 *
882 format_fa_5tuple (u8 * s, va_list * args)
883 {
884   fa_5tuple_t *p5t = va_arg (*args, fa_5tuple_t *);
885   void *paddr0;
886   void *paddr1;
887   void *format_address_func;
888   void *ip_af;
889   void *ip_frag_txt =
890     p5t->pkt.is_nonfirst_fragment ? " non-initial fragment" : "";
891
892   if (p5t->pkt.is_ip6)
893     {
894       ip_af = "ip6";
895       format_address_func = format_ip6_address;
896       paddr0 = &p5t->ip6_addr[0];
897       paddr1 = &p5t->ip6_addr[1];
898     }
899   else
900     {
901       ip_af = "ip4";
902       format_address_func = format_ip4_address;
903       paddr0 = &p5t->ip4_addr[0];
904       paddr1 = &p5t->ip4_addr[1];
905     }
906
907   s =
908     format (s, "lc_index %d l3 %s%s ", p5t->pkt.lc_index, ip_af, ip_frag_txt);
909   s =
910     format (s, "%U -> %U ", format_address_func, paddr0, format_address_func,
911             paddr1);
912   s = format (s, "%U ", format_fa_session_l4_key, &p5t->l4);
913   s = format (s, "tcp flags (%s) %02x rsvd %x",
914               p5t->pkt.tcp_flags_valid ? "valid" : "invalid",
915               p5t->pkt.tcp_flags, p5t->pkt.flags_reserved);
916   return s;
917 }
918
919 #ifndef CLIB_MARCH_VARIANT
920 u8 *
921 format_acl_plugin_5tuple (u8 * s, va_list * args)
922 {
923   return format_fa_5tuple (s, args);
924 }
925 #endif
926
927 /* packet trace format function */
928 static u8 *
929 format_acl_plugin_trace (u8 * s, va_list * args)
930 {
931   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
932   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
933   acl_fa_trace_t *t = va_arg (*args, acl_fa_trace_t *);
934
935   s =
936     format (s,
937             "acl-plugin: lc_index: %d, sw_if_index %d, next index %d, action: %d, match: acl %d rule %d trace_bits %08x\n"
938             "  pkt info %016llx %016llx %016llx %016llx %016llx %016llx",
939             t->lc_index, t->sw_if_index, t->next_index, t->action,
940             t->match_acl_in_index, t->match_rule_index, t->trace_bitmap,
941             t->packet_info[0], t->packet_info[1], t->packet_info[2],
942             t->packet_info[3], t->packet_info[4], t->packet_info[5]);
943
944   /* Now also print out the packet_info in a form usable by humans */
945   s = format (s, "\n   %U", format_fa_5tuple, t->packet_info);
946   return s;
947 }
948
949 /* *INDENT-OFF* */
950
951 static char *acl_fa_error_strings[] = {
952 #define _(sym,string) string,
953   foreach_acl_fa_error
954 #undef _
955 };
956
957 VLIB_NODE_FN (acl_in_l2_ip6_node) (vlib_main_t * vm,
958                                    vlib_node_runtime_t * node,
959                                    vlib_frame_t * frame)
960 {
961   return acl_fa_node_fn (vm, node, frame, 1, 1, 1);
962 }
963
964 VLIB_NODE_FN (acl_in_l2_ip4_node) (vlib_main_t * vm,
965                                    vlib_node_runtime_t * node,
966                                    vlib_frame_t * frame)
967 {
968   return acl_fa_node_fn (vm, node, frame, 0, 1, 1);
969 }
970
971 VLIB_NODE_FN (acl_out_l2_ip6_node) (vlib_main_t * vm,
972                                     vlib_node_runtime_t * node,
973                                     vlib_frame_t * frame)
974 {
975   return acl_fa_node_fn (vm, node, frame, 1, 0, 1);
976 }
977
978 VLIB_NODE_FN (acl_out_l2_ip4_node) (vlib_main_t * vm,
979                                     vlib_node_runtime_t * node,
980                                     vlib_frame_t * frame)
981 {
982   return acl_fa_node_fn (vm, node, frame, 0, 0, 1);
983 }
984
985 /**** L3 processing path nodes ****/
986
987 VLIB_NODE_FN (acl_in_fa_ip6_node) (vlib_main_t * vm,
988                                    vlib_node_runtime_t * node,
989                                    vlib_frame_t * frame)
990 {
991   return acl_fa_node_fn (vm, node, frame, 1, 1, 0);
992 }
993
994 VLIB_NODE_FN (acl_in_fa_ip4_node) (vlib_main_t * vm,
995                                    vlib_node_runtime_t * node,
996                                    vlib_frame_t * frame)
997 {
998   return acl_fa_node_fn (vm, node, frame, 0, 1, 0);
999 }
1000
1001 VLIB_NODE_FN (acl_out_fa_ip6_node) (vlib_main_t * vm,
1002                                     vlib_node_runtime_t * node,
1003                                     vlib_frame_t * frame)
1004 {
1005   return acl_fa_node_fn (vm, node, frame, 1, 0, 0);
1006 }
1007
1008 VLIB_NODE_FN (acl_out_fa_ip4_node) (vlib_main_t * vm,
1009                                     vlib_node_runtime_t * node,
1010                                     vlib_frame_t * frame)
1011 {
1012   return acl_fa_node_fn (vm, node, frame, 0, 0, 0);
1013 }
1014
1015 VLIB_REGISTER_NODE (acl_in_l2_ip6_node) =
1016 {
1017   .name = "acl-plugin-in-ip6-l2",
1018   .vector_size = sizeof (u32),
1019   .format_trace = format_acl_plugin_trace,
1020   .type = VLIB_NODE_TYPE_INTERNAL,
1021   .n_errors = ARRAY_LEN (acl_fa_error_strings),
1022   .error_strings = acl_fa_error_strings,
1023   .n_next_nodes = ACL_FA_N_NEXT,
1024   .next_nodes =
1025   {
1026     [ACL_FA_ERROR_DROP] = "error-drop",
1027   }
1028 };
1029
1030 VNET_FEATURE_INIT (acl_in_l2_ip6_fa_feature, static) =
1031 {
1032   .arc_name = "l2-input-ip6",
1033   .node_name = "acl-plugin-in-ip6-l2",
1034   .runs_before = VNET_FEATURES ("l2-input-feat-arc-end"),
1035 };
1036
1037 VLIB_REGISTER_NODE (acl_in_l2_ip4_node) =
1038 {
1039   .name = "acl-plugin-in-ip4-l2",
1040   .vector_size = sizeof (u32),
1041   .format_trace = format_acl_plugin_trace,
1042   .type = VLIB_NODE_TYPE_INTERNAL,
1043   .n_errors = ARRAY_LEN (acl_fa_error_strings),
1044   .error_strings = acl_fa_error_strings,
1045   .n_next_nodes = ACL_FA_N_NEXT,
1046   .next_nodes =
1047   {
1048     [ACL_FA_ERROR_DROP] = "error-drop",
1049   }
1050 };
1051
1052 VNET_FEATURE_INIT (acl_in_l2_ip4_fa_feature, static) =
1053 {
1054   .arc_name = "l2-input-ip4",
1055   .node_name = "acl-plugin-in-ip4-l2",
1056   .runs_before = VNET_FEATURES ("l2-input-feat-arc-end"),
1057 };
1058
1059
1060 VLIB_REGISTER_NODE (acl_out_l2_ip6_node) =
1061 {
1062   .name = "acl-plugin-out-ip6-l2",
1063   .vector_size = sizeof (u32),
1064   .format_trace = format_acl_plugin_trace,
1065   .type = VLIB_NODE_TYPE_INTERNAL,
1066   .n_errors = ARRAY_LEN (acl_fa_error_strings),
1067   .error_strings = acl_fa_error_strings,
1068   .n_next_nodes = ACL_FA_N_NEXT,
1069   .next_nodes =
1070   {
1071     [ACL_FA_ERROR_DROP] = "error-drop",
1072   }
1073 };
1074
1075 VNET_FEATURE_INIT (acl_out_l2_ip6_fa_feature, static) =
1076 {
1077   .arc_name = "l2-output-ip6",
1078   .node_name = "acl-plugin-out-ip6-l2",
1079   .runs_before = VNET_FEATURES ("l2-output-feat-arc-end"),
1080 };
1081
1082
1083 VLIB_REGISTER_NODE (acl_out_l2_ip4_node) =
1084 {
1085   .name = "acl-plugin-out-ip4-l2",
1086   .vector_size = sizeof (u32),
1087   .format_trace = format_acl_plugin_trace,
1088   .type = VLIB_NODE_TYPE_INTERNAL,
1089   .n_errors = ARRAY_LEN (acl_fa_error_strings),
1090   .error_strings = acl_fa_error_strings,
1091   .n_next_nodes = ACL_FA_N_NEXT,
1092   .next_nodes =
1093   {
1094     [ACL_FA_ERROR_DROP] = "error-drop",
1095   }
1096 };
1097
1098 VNET_FEATURE_INIT (acl_out_l2_ip4_fa_feature, static) =
1099 {
1100   .arc_name = "l2-output-ip4",
1101   .node_name = "acl-plugin-out-ip4-l2",
1102   .runs_before = VNET_FEATURES ("l2-output-feat-arc-end"),
1103 };
1104
1105
1106 VLIB_REGISTER_NODE (acl_in_fa_ip6_node) =
1107 {
1108   .name = "acl-plugin-in-ip6-fa",
1109   .vector_size = sizeof (u32),
1110   .format_trace = format_acl_plugin_trace,
1111   .type = VLIB_NODE_TYPE_INTERNAL,
1112   .n_errors = ARRAY_LEN (acl_fa_error_strings),
1113   .error_strings = acl_fa_error_strings,
1114   .n_next_nodes = ACL_FA_N_NEXT,
1115   .next_nodes =
1116   {
1117     [ACL_FA_ERROR_DROP] = "error-drop",
1118   }
1119 };
1120
1121 VNET_FEATURE_INIT (acl_in_ip6_fa_feature, static) =
1122 {
1123   .arc_name = "ip6-unicast",
1124   .node_name = "acl-plugin-in-ip6-fa",
1125   .runs_before = VNET_FEATURES ("ip6-flow-classify"),
1126 };
1127
1128 VLIB_REGISTER_NODE (acl_in_fa_ip4_node) =
1129 {
1130   .name = "acl-plugin-in-ip4-fa",
1131   .vector_size = sizeof (u32),
1132   .format_trace = format_acl_plugin_trace,
1133   .type = VLIB_NODE_TYPE_INTERNAL,
1134   .n_errors = ARRAY_LEN (acl_fa_error_strings),
1135   .error_strings = acl_fa_error_strings,
1136   .n_next_nodes = ACL_FA_N_NEXT,
1137   .next_nodes =
1138   {
1139     [ACL_FA_ERROR_DROP] = "error-drop",
1140   }
1141 };
1142
1143 VNET_FEATURE_INIT (acl_in_ip4_fa_feature, static) =
1144 {
1145   .arc_name = "ip4-unicast",
1146   .node_name = "acl-plugin-in-ip4-fa",
1147   .runs_before = VNET_FEATURES ("ip4-flow-classify"),
1148 };
1149
1150
1151 VLIB_REGISTER_NODE (acl_out_fa_ip6_node) =
1152 {
1153   .name = "acl-plugin-out-ip6-fa",
1154   .vector_size = sizeof (u32),
1155   .format_trace = format_acl_plugin_trace,
1156   .type = VLIB_NODE_TYPE_INTERNAL,
1157   .n_errors = ARRAY_LEN (acl_fa_error_strings),
1158   .error_strings = acl_fa_error_strings,
1159   .n_next_nodes = ACL_FA_N_NEXT,
1160   .next_nodes =
1161   {
1162     [ACL_FA_ERROR_DROP] = "error-drop",
1163   }
1164 };
1165
1166 VNET_FEATURE_INIT (acl_out_ip6_fa_feature, static) =
1167 {
1168   .arc_name = "ip6-output",
1169   .node_name = "acl-plugin-out-ip6-fa",
1170   .runs_before = VNET_FEATURES ("interface-output"),
1171 };
1172
1173 VLIB_REGISTER_NODE (acl_out_fa_ip4_node) =
1174 {
1175   .name = "acl-plugin-out-ip4-fa",
1176   .vector_size = sizeof (u32),
1177   .format_trace = format_acl_plugin_trace,
1178   .type = VLIB_NODE_TYPE_INTERNAL,
1179   .n_errors = ARRAY_LEN (acl_fa_error_strings),
1180   .error_strings = acl_fa_error_strings,
1181   .n_next_nodes = ACL_FA_N_NEXT,
1182     /* edit / add dispositions here */
1183   .next_nodes =
1184   {
1185     [ACL_FA_ERROR_DROP] = "error-drop",
1186   }
1187 };
1188
1189 VNET_FEATURE_INIT (acl_out_ip4_fa_feature, static) =
1190 {
1191   .arc_name = "ip4-output",
1192   .node_name = "acl-plugin-out-ip4-fa",
1193   .runs_before = VNET_FEATURES ("interface-output"),
1194 };
1195
1196 /* *INDENT-ON* */
1197
1198 /*
1199  * fd.io coding-style-patch-verification: ON
1200  *
1201  * Local Variables:
1202  * eval: (c-set-style "gnu")
1203  * End:
1204  */