VPP-521: Classify API enhancement to redirect traffic to pre-defined VRF
[vpp.git] / vnet / vnet / ip / ip_input_acl.c
1 /*
2  * Copyright (c) 2015 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 <vnet/ip/ip.h>
16 #include <vnet/classify/vnet_classify.h>
17 #include <vnet/classify/input_acl.h>
18
19 typedef struct {
20   u32 sw_if_index;
21   u32 next_index;
22   u32 table_index;
23   u32 offset;
24 } ip_inacl_trace_t;
25
26 /* packet trace format function */
27 static u8 * format_ip_inacl_trace (u8 * s, va_list * args)
28 {
29   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
30   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
31   ip_inacl_trace_t * t = va_arg (*args, ip_inacl_trace_t *);
32
33   s = format (s, "INACL: sw_if_index %d, next_index %d, table %d, offset %d",
34               t->sw_if_index, t->next_index, t->table_index, t->offset);
35   return s;
36 }
37
38 vlib_node_registration_t ip4_inacl_node;
39 vlib_node_registration_t ip6_inacl_node;
40
41 #define foreach_ip_inacl_error                  \
42 _(MISS, "input ACL misses")                     \
43 _(HIT, "input ACL hits")                        \
44 _(CHAIN_HIT, "input ACL hits after chain walk")
45
46 typedef enum {
47 #define _(sym,str) IP_INACL_ERROR_##sym,
48   foreach_ip_inacl_error
49 #undef _
50   IP_INACL_N_ERROR,
51 } ip_inacl_error_t;
52
53 static char * ip_inacl_error_strings[] = {
54 #define _(sym,string) string,
55   foreach_ip_inacl_error
56 #undef _
57 };
58
59 static inline uword
60 ip_inacl_inline (vlib_main_t * vm,
61                vlib_node_runtime_t * node,
62                vlib_frame_t * frame, int is_ip4)
63 {
64   u32 n_left_from, * from, * to_next;
65   acl_next_index_t next_index;
66   input_acl_main_t * am = &input_acl_main;
67   vnet_classify_main_t * vcm = am->vnet_classify_main;
68   f64 now = vlib_time_now (vm);
69   u32 hits = 0;
70   u32 misses = 0;
71   u32 chain_hits = 0;
72   input_acl_table_id_t tid;
73   vlib_node_runtime_t * error_node;
74   u32 n_next_nodes;
75
76   n_next_nodes = node->n_next_nodes;
77
78   if (is_ip4)
79     {
80       tid = INPUT_ACL_TABLE_IP4;
81       error_node = vlib_node_get_runtime (vm, ip4_input_node.index);
82     }
83   else
84     {
85       tid = INPUT_ACL_TABLE_IP6;
86       error_node = vlib_node_get_runtime (vm, ip6_input_node.index);
87     }
88
89   from = vlib_frame_vector_args (frame);
90   n_left_from = frame->n_vectors;
91
92   /* First pass: compute hashes */
93
94   while (n_left_from > 2)
95     {
96       vlib_buffer_t * b0, * b1;
97       u32 bi0, bi1;
98       u8 * h0, * h1;
99       u32 sw_if_index0, sw_if_index1;
100       u32 table_index0, table_index1;
101       vnet_classify_table_t * t0, * t1;
102
103       /* prefetch next iteration */
104       {
105         vlib_buffer_t * p1, * p2;
106
107         p1 = vlib_get_buffer (vm, from[1]);
108         p2 = vlib_get_buffer (vm, from[2]);
109
110         vlib_prefetch_buffer_header (p1, STORE);
111         CLIB_PREFETCH (p1->data, CLIB_CACHE_LINE_BYTES, STORE);
112         vlib_prefetch_buffer_header (p2, STORE);
113         CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
114       }
115
116       bi0 = from[0];
117       b0 = vlib_get_buffer (vm, bi0);
118
119       bi1 = from[1];
120       b1 = vlib_get_buffer (vm, bi1);
121
122       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
123       table_index0 = am->classify_table_index_by_sw_if_index[tid][sw_if_index0];
124
125       sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
126       table_index1 = am->classify_table_index_by_sw_if_index[tid][sw_if_index1];
127
128       t0 = pool_elt_at_index (vcm->tables, table_index0);
129
130       t1 = pool_elt_at_index (vcm->tables, table_index1);
131
132       if (t0->current_data_flag == CLASSIFY_FLAG_USE_CURR_DATA)
133         h0 = (void *)vlib_buffer_get_current (b0) + t0->current_data_offset;
134       else
135         h0 = b0->data;
136
137       vnet_buffer(b0)->l2_classify.hash =
138         vnet_classify_hash_packet (t0, (u8 *) h0);
139
140       vnet_classify_prefetch_bucket (t0, vnet_buffer(b0)->l2_classify.hash);
141
142       if (t1->current_data_flag == CLASSIFY_FLAG_USE_CURR_DATA)
143         h1 = (void *)vlib_buffer_get_current (b1) + t1->current_data_offset;
144       else
145         h1 = b1->data;
146
147       vnet_buffer(b1)->l2_classify.hash =
148         vnet_classify_hash_packet (t1, (u8 *) h1);
149
150       vnet_classify_prefetch_bucket (t1, vnet_buffer(b1)->l2_classify.hash);
151
152       vnet_buffer(b0)->l2_classify.table_index = table_index0;
153
154       vnet_buffer(b1)->l2_classify.table_index = table_index1;
155
156       from += 2;
157       n_left_from -= 2;
158     }
159
160   while (n_left_from > 0)
161     {
162       vlib_buffer_t * b0;
163       u32 bi0;
164       u8 * h0;
165       u32 sw_if_index0;
166       u32 table_index0;
167       vnet_classify_table_t * t0;
168
169       bi0 = from[0];
170       b0 = vlib_get_buffer (vm, bi0);
171
172       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
173       table_index0 = am->classify_table_index_by_sw_if_index[tid][sw_if_index0];
174
175       t0 = pool_elt_at_index (vcm->tables, table_index0);
176
177       if (t0->current_data_flag == CLASSIFY_FLAG_USE_CURR_DATA)
178         h0 = (void *)vlib_buffer_get_current (b0) + t0->current_data_offset;
179       else
180         h0 = b0->data;
181
182       vnet_buffer(b0)->l2_classify.hash =
183         vnet_classify_hash_packet (t0, (u8 *) h0);
184
185       vnet_buffer(b0)->l2_classify.table_index = table_index0;
186       vnet_classify_prefetch_bucket (t0, vnet_buffer(b0)->l2_classify.hash);
187
188       from++;
189       n_left_from--;
190     }
191
192   next_index = node->cached_next_index;
193   from = vlib_frame_vector_args (frame);
194   n_left_from = frame->n_vectors;
195
196   while (n_left_from > 0)
197     {
198       u32 n_left_to_next;
199
200       vlib_get_next_frame (vm, node, next_index,
201                            to_next, n_left_to_next);
202
203       /* Not enough load/store slots to dual loop... */
204       while (n_left_from > 0 && n_left_to_next > 0)
205         {
206           u32 bi0;
207           vlib_buffer_t * b0;
208           u32 next0 = ACL_NEXT_INDEX_DENY;
209           u32 table_index0;
210           vnet_classify_table_t * t0;
211           vnet_classify_entry_t * e0;
212           u64 hash0;
213           u8 * h0;
214           u8 error0;
215
216           /* Stride 3 seems to work best */
217           if (PREDICT_TRUE (n_left_from > 3))
218             {
219               vlib_buffer_t * p1 = vlib_get_buffer(vm, from[3]);
220               vnet_classify_table_t * tp1;
221               u32 table_index1;
222               u64 phash1;
223
224               table_index1 = vnet_buffer(p1)->l2_classify.table_index;
225
226               if (PREDICT_TRUE (table_index1 != ~0))
227                 {
228                   tp1 = pool_elt_at_index (vcm->tables, table_index1);
229                   phash1 = vnet_buffer(p1)->l2_classify.hash;
230                   vnet_classify_prefetch_entry (tp1, phash1);
231                 }
232             }
233
234           /* speculatively enqueue b0 to the current next frame */
235           bi0 = from[0];
236           to_next[0] = bi0;
237           from += 1;
238           to_next += 1;
239           n_left_from -= 1;
240           n_left_to_next -= 1;
241
242           b0 = vlib_get_buffer (vm, bi0);
243           table_index0 = vnet_buffer(b0)->l2_classify.table_index;
244           e0 = 0;
245           t0 = 0;
246           vnet_get_config_data (am->vnet_config_main[tid],
247                                 &b0->current_config_index,
248                                 &next0,
249                                 /* # bytes of config data */ 0);
250
251           vnet_buffer(b0)->l2_classify.opaque_index = ~0;
252
253           if (PREDICT_TRUE(table_index0 != ~0))
254             {
255               hash0 = vnet_buffer(b0)->l2_classify.hash;
256               t0 = pool_elt_at_index (vcm->tables, table_index0);
257
258               if (t0->current_data_flag == CLASSIFY_FLAG_USE_CURR_DATA)
259                 h0 = (void *)vlib_buffer_get_current (b0) + t0->current_data_offset;
260               else
261                 h0 = b0->data;
262
263               e0 = vnet_classify_find_entry (t0, (u8 *) h0, hash0,
264                                              now);
265               if (e0)
266                 {
267                   vnet_buffer(b0)->l2_classify.opaque_index
268                     = e0->opaque_index;
269                   vlib_buffer_advance (b0, e0->advance);
270
271                   next0 = (e0->next_index < n_next_nodes)?
272                            e0->next_index:next0;
273
274                   hits++;
275
276                   if (is_ip4)
277                     error0 = (next0 == ACL_NEXT_INDEX_DENY)?
278                       IP4_ERROR_INACL_SESSION_DENY:IP4_ERROR_NONE;
279                   else
280                     error0 = (next0 == ACL_NEXT_INDEX_DENY)?
281                       IP6_ERROR_INACL_SESSION_DENY:IP6_ERROR_NONE;
282                   b0->error = error_node->errors[error0];
283
284                   if (e0->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX ||
285                       e0->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
286                     vnet_buffer (b0)->sw_if_index[VLIB_TX] = e0->metadata;
287                 }
288               else
289                 {
290                   while (1)
291                     {
292                       if (PREDICT_TRUE(t0->next_table_index != ~0))
293                         t0 = pool_elt_at_index (vcm->tables,
294                                                 t0->next_table_index);
295                       else
296                         {
297                           next0 = (t0->miss_next_index < n_next_nodes)?
298                                    t0->miss_next_index:next0;
299
300                           misses++;
301
302                           if (is_ip4)
303                             error0 = (next0 == ACL_NEXT_INDEX_DENY)?
304                               IP4_ERROR_INACL_TABLE_MISS:IP4_ERROR_NONE;
305                           else
306                             error0 = (next0 == ACL_NEXT_INDEX_DENY)?
307                               IP6_ERROR_INACL_TABLE_MISS:IP6_ERROR_NONE;
308                           b0->error = error_node->errors[error0];
309                           break;
310                         }
311
312                       if (t0->current_data_flag == CLASSIFY_FLAG_USE_CURR_DATA)
313                         h0 = (void *)vlib_buffer_get_current (b0) + t0->current_data_offset;
314                       else
315                         h0 = b0->data;
316
317                       hash0 = vnet_classify_hash_packet (t0, (u8 *) h0);
318                       e0 = vnet_classify_find_entry
319                         (t0, (u8 *) h0, hash0, now);
320                       if (e0)
321                         {
322                           vnet_buffer(b0)->l2_classify.opaque_index
323                             = e0->opaque_index;
324                           vlib_buffer_advance (b0, e0->advance);
325                           next0 = (e0->next_index < n_next_nodes)?
326                                    e0->next_index:next0;
327                           hits++;
328                           chain_hits++;
329
330                           if (is_ip4)
331                             error0 = (next0 == ACL_NEXT_INDEX_DENY)?
332                               IP4_ERROR_INACL_SESSION_DENY:IP4_ERROR_NONE;
333                           else
334                             error0 = (next0 == ACL_NEXT_INDEX_DENY)?
335                               IP6_ERROR_INACL_SESSION_DENY:IP6_ERROR_NONE;
336                           b0->error = error_node->errors[error0];
337
338                           if (e0->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX ||
339                               e0->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
340                             vnet_buffer (b0)->sw_if_index[VLIB_TX] = e0->metadata;
341                           break;
342                         }
343                     }
344                 }
345             }
346
347           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
348                             && (b0->flags & VLIB_BUFFER_IS_TRACED)))
349             {
350               ip_inacl_trace_t *t =
351                 vlib_add_trace (vm, node, b0, sizeof (*t));
352               t->sw_if_index = vnet_buffer(b0)->sw_if_index[VLIB_RX];
353               t->next_index = next0;
354               t->table_index = t0 ? t0 - vcm->tables : ~0;
355               t->offset = (e0 && t0) ? vnet_classify_get_offset (t0, e0): ~0;
356             }
357
358           /* verify speculative enqueue, maybe switch current next frame */
359           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
360                                            to_next, n_left_to_next,
361                                            bi0, next0);
362         }
363
364       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
365     }
366
367   vlib_node_increment_counter (vm, node->node_index,
368                                IP_INACL_ERROR_MISS,
369                                misses);
370   vlib_node_increment_counter (vm, node->node_index,
371                                IP_INACL_ERROR_HIT,
372                                hits);
373   vlib_node_increment_counter (vm, node->node_index,
374                                IP_INACL_ERROR_CHAIN_HIT,
375                                chain_hits);
376   return frame->n_vectors;
377 }
378
379 static uword
380 ip4_inacl (vlib_main_t * vm,
381          vlib_node_runtime_t * node,
382          vlib_frame_t * frame)
383 {
384   return ip_inacl_inline (vm, node, frame, 1 /* is_ip4 */);
385 }
386
387
388 VLIB_REGISTER_NODE (ip4_inacl_node) = {
389   .function = ip4_inacl,
390   .name = "ip4-inacl",
391   .vector_size = sizeof (u32),
392   .format_trace = format_ip_inacl_trace,
393   .n_errors = ARRAY_LEN(ip_inacl_error_strings),
394   .error_strings = ip_inacl_error_strings,
395
396   .n_next_nodes = ACL_NEXT_INDEX_N_NEXT,
397   .next_nodes = {
398     [ACL_NEXT_INDEX_DENY] = "error-drop",
399   },
400 };
401
402 VLIB_NODE_FUNCTION_MULTIARCH (ip4_inacl_node, ip4_inacl)
403
404 static uword
405 ip6_inacl (vlib_main_t * vm,
406               vlib_node_runtime_t * node,
407               vlib_frame_t * frame)
408 {
409   return ip_inacl_inline (vm, node, frame, 0 /* is_ip4 */);
410 }
411
412
413 VLIB_REGISTER_NODE (ip6_inacl_node) = {
414   .function = ip6_inacl,
415   .name = "ip6-inacl",
416   .vector_size = sizeof (u32),
417   .format_trace = format_ip_inacl_trace,
418   .n_errors = ARRAY_LEN(ip_inacl_error_strings),
419   .error_strings = ip_inacl_error_strings,
420
421   .n_next_nodes = ACL_NEXT_INDEX_N_NEXT,
422   .next_nodes = {
423     [ACL_NEXT_INDEX_DENY] = "error-drop",
424   },
425 };
426
427 VLIB_NODE_FUNCTION_MULTIARCH (ip6_inacl_node, ip6_inacl)
428
429 static clib_error_t *
430 ip_inacl_init (vlib_main_t * vm)
431 {
432   return 0;
433 }
434
435 VLIB_INIT_FUNCTION (ip_inacl_init);
436