Initial commit of vpp code.
[vpp.git] / vnet / vnet / policer / node_funcs.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 <vlib/vlib.h>
16 #include <vnet/vnet.h>
17 #include <vnet/policer/policer.h>
18
19 /* Dispatch functions meant to be instantiated elsewhere */
20
21 typedef struct {
22   u32 next_index;
23   u32 sw_if_index;
24   u32 policer_index;
25 } vnet_policer_trace_t;
26
27 /* packet trace format function */
28 static u8 * format_policer_trace (u8 * s, va_list * args)
29 {
30   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
31   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
32   vnet_policer_trace_t * t = va_arg (*args, vnet_policer_trace_t *);
33   
34   s = format (s, "VNET_POLICER: sw_if_index %d policer_index %d next %d",
35               t->sw_if_index, t->policer_index, t->next_index);
36   return s;
37 }
38
39 #define foreach_vnet_policer_error              \
40 _(TRANSMIT, "Packets Transmitted")              \
41 _(DROP, "Packets Dropped")
42
43 typedef enum {
44 #define _(sym,str) VNET_POLICER_ERROR_##sym,
45   foreach_vnet_policer_error
46 #undef _
47   VNET_POLICER_N_ERROR,
48 } vnet_policer_error_t;
49
50 static char * vnet_policer_error_strings[] = {
51 #define _(sym,string) string,
52   foreach_vnet_policer_error
53 #undef _
54 };
55
56 static inline
57 uword vnet_policer_inline (vlib_main_t * vm,
58                            vlib_node_runtime_t * node,
59                            vlib_frame_t * frame,
60                            vnet_policer_index_t which)
61 {
62   u32 n_left_from, * from, * to_next;
63   vnet_policer_next_t next_index;
64   vnet_policer_main_t * pm = &vnet_policer_main;
65   u64 time_in_policer_periods;
66   u32 transmitted = 0;
67
68   time_in_policer_periods = 
69     clib_cpu_time_now() >> POLICER_TICKS_PER_PERIOD_SHIFT;
70
71   from = vlib_frame_vector_args (frame);
72   n_left_from = frame->n_vectors;
73   next_index = node->cached_next_index;
74
75   while (n_left_from > 0)
76     {
77       u32 n_left_to_next;
78
79       vlib_get_next_frame (vm, node, next_index,
80                            to_next, n_left_to_next);
81
82       while (n_left_from >= 4 && n_left_to_next >= 2)
83         {
84           u32 bi0, bi1;
85           vlib_buffer_t * b0, * b1;
86           u32 next0, next1;
87           u32 sw_if_index0, sw_if_index1;
88           u32 pi0, pi1;
89           u32 len0, len1;
90           u32 col0, col1;
91           policer_read_response_type_st * pol0, * pol1;
92           
93           /* Prefetch next iteration. */
94           {
95             vlib_buffer_t * b2, * b3;
96             
97             b2 = vlib_get_buffer (vm, from[2]);
98             b3 = vlib_get_buffer (vm, from[3]);
99             
100             vlib_prefetch_buffer_header (b2, LOAD);
101             vlib_prefetch_buffer_header (b3, LOAD);
102           }
103
104           /* speculatively enqueue b0 and b1 to the current next frame */
105           to_next[0] = bi0 = from[0];
106           to_next[1] = bi1 = from[1];
107           from += 2;
108           to_next += 2;
109           n_left_from -= 2;
110           n_left_to_next -= 2;
111
112           b0 = vlib_get_buffer (vm, bi0);
113           b1 = vlib_get_buffer (vm, bi1);
114
115           sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
116           next0 = VNET_POLICER_NEXT_TRANSMIT;
117
118           sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
119           next1 = VNET_POLICER_NEXT_TRANSMIT;
120
121
122           if (which == VNET_POLICER_INDEX_BY_SW_IF_INDEX)
123             {
124               pi0 = pm->policer_index_by_sw_if_index[sw_if_index0];
125               pi1 = pm->policer_index_by_sw_if_index[sw_if_index1];
126             }
127
128           if (which == VNET_POLICER_INDEX_BY_OPAQUE)
129             {
130               pi0 = vnet_buffer(b0)->policer.index;
131               pi1 = vnet_buffer(b1)->policer.index;
132             }
133
134           if (which == VNET_POLICER_INDEX_BY_EITHER)
135             {
136               pi0 = vnet_buffer(b0)->policer.index;
137               pi0 = (pi0 != ~0) ? pi0 : 
138                 pm->policer_index_by_sw_if_index [sw_if_index0];
139               pi1 = vnet_buffer(b1)->policer.index;
140               pi1 = (pi1 != ~0) ? pi1 : 
141                 pm->policer_index_by_sw_if_index [sw_if_index1];
142             }
143
144           len0 = vlib_buffer_length_in_chain (vm, b0);
145           pol0 = &pm->policers [pi0];
146           col0 = vnet_police_packet (pol0, len0, 
147                                      POLICE_CONFORM /* no chaining */,
148                                      time_in_policer_periods);
149
150           len1 = vlib_buffer_length_in_chain (vm, b1);
151           pol1 = &pm->policers [pi1];
152           col1 = vnet_police_packet (pol1, len1, 
153                                      POLICE_CONFORM /* no chaining */,
154                                      time_in_policer_periods);
155           
156           if (PREDICT_FALSE(col0 > 0))
157             {
158               next0 = VNET_POLICER_NEXT_DROP;
159               b0->error = node->errors[VNET_POLICER_ERROR_DROP];
160             }
161           else
162             transmitted++;
163           
164           if (PREDICT_FALSE(col1 > 0))
165             {
166               next1 = VNET_POLICER_NEXT_DROP;
167               b1->error = node->errors[VNET_POLICER_ERROR_DROP];
168             }
169           else
170             transmitted++;
171
172
173           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)))
174             {
175               if (b0->flags & VLIB_BUFFER_IS_TRACED) 
176                 {
177                   vnet_policer_trace_t *t = 
178                     vlib_add_trace (vm, node, b0, sizeof (*t));
179                   t->sw_if_index = sw_if_index0;
180                   t->next_index = next0;
181                 }
182               if (b1->flags & VLIB_BUFFER_IS_TRACED) 
183                 {
184                   vnet_policer_trace_t *t = 
185                     vlib_add_trace (vm, node, b1, sizeof (*t));
186                   t->sw_if_index = sw_if_index1;
187                   t->next_index = next1;
188                 }
189             }
190             
191           /* verify speculative enqueues, maybe switch current next frame */
192           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
193                                            to_next, n_left_to_next,
194                                            bi0, bi1, next0, next1);
195         }
196       
197       while (n_left_from > 0 && n_left_to_next > 0)
198         {
199           u32 bi0;
200           vlib_buffer_t * b0;
201           u32 next0;
202           u32 sw_if_index0;
203           u32 pi0;
204           u32 len0;
205           u32 col0;
206           policer_read_response_type_st * pol0;
207
208           bi0 = from[0];
209           to_next[0] = bi0;
210           from += 1;
211           to_next += 1;
212           n_left_from -= 1;
213           n_left_to_next -= 1;
214
215           b0 = vlib_get_buffer (vm, bi0);
216
217           sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
218           next0 = VNET_POLICER_NEXT_TRANSMIT;
219
220           if (which == VNET_POLICER_INDEX_BY_SW_IF_INDEX)
221             pi0 = pm->policer_index_by_sw_if_index[sw_if_index0];
222
223           if (which == VNET_POLICER_INDEX_BY_OPAQUE)
224             pi0 = vnet_buffer(b0)->policer.index;
225
226           if (which == VNET_POLICER_INDEX_BY_EITHER)
227             {
228               pi0 = vnet_buffer(b0)->policer.index;
229               pi0 = (pi0 != ~0) ? pi0 : 
230                 pm->policer_index_by_sw_if_index [sw_if_index0];
231             }
232
233           len0 = vlib_buffer_length_in_chain (vm, b0);
234           pol0 = &pm->policers [pi0];
235           col0 = vnet_police_packet (pol0, len0, 
236                                      POLICE_CONFORM /* no chaining */,
237                                      time_in_policer_periods);
238           
239           if (PREDICT_FALSE(col0 > 0))
240             {
241               next0 = VNET_POLICER_NEXT_DROP;
242               b0->error = node->errors[VNET_POLICER_ERROR_DROP];
243             }
244           else
245             {
246               transmitted++;
247             }
248           
249           if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) 
250                             && (b0->flags & VLIB_BUFFER_IS_TRACED))) 
251             {
252               vnet_policer_trace_t *t = 
253                 vlib_add_trace (vm, node, b0, sizeof (*t));
254               t->sw_if_index = sw_if_index0;
255               t->next_index = next0;
256               t->policer_index = pi0;
257             }
258             
259           /* verify speculative enqueue, maybe switch current next frame */
260           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
261                                            to_next, n_left_to_next,
262                                            bi0, next0);
263         }
264
265       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
266     }
267
268   vlib_node_increment_counter (vm, node->node_index, 
269                                VNET_POLICER_ERROR_TRANSMIT, 
270                                transmitted);
271   return frame->n_vectors;
272 }
273
274 uword vnet_policer_by_sw_if_index (vlib_main_t * vm,
275                                    vlib_node_runtime_t * node,
276                                    vlib_frame_t * frame)
277 {
278   return vnet_policer_inline (vm, node, frame, 
279                               VNET_POLICER_INDEX_BY_SW_IF_INDEX);
280 }
281
282 uword vnet_policer_by_opaque (vlib_main_t * vm,
283                               vlib_node_runtime_t * node,
284                               vlib_frame_t * frame)
285 {
286   return vnet_policer_inline (vm, node, frame, 
287                               VNET_POLICER_INDEX_BY_OPAQUE);
288 }
289
290 uword vnet_policer_by_either (vlib_main_t * vm,
291                               vlib_node_runtime_t * node,
292                               vlib_frame_t * frame)
293 {
294   return vnet_policer_inline (vm, node, frame, 
295                               VNET_POLICER_INDEX_BY_EITHER);
296 }
297
298 void vnet_policer_node_funcs_reference (void) { }
299
300
301 #define TEST_CODE 1
302
303 #ifdef TEST_CODE
304
305 VLIB_REGISTER_NODE (policer_by_sw_if_index_node, static) = {
306   .function = vnet_policer_by_sw_if_index,
307   .name = "policer-by-sw-if-index",
308   .vector_size = sizeof (u32),
309   .format_trace = format_policer_trace,
310   .type = VLIB_NODE_TYPE_INTERNAL,
311   
312   .n_errors = ARRAY_LEN(vnet_policer_error_strings),
313   .error_strings = vnet_policer_error_strings,
314   
315   .n_next_nodes = VNET_POLICER_N_NEXT,
316
317   /* edit / add dispositions here */
318   .next_nodes = {
319     [VNET_POLICER_NEXT_TRANSMIT] = "ethernet-input",
320     [VNET_POLICER_NEXT_DROP] = "error-drop",
321   },
322 };
323
324
325 int test_policer_add_del (u32 rx_sw_if_index, u8 *config_name,
326                           int is_add)
327 {
328   vnet_policer_main_t * pm = &vnet_policer_main;  
329   policer_read_response_type_st * template;
330   policer_read_response_type_st * policer;
331   vnet_hw_interface_t * rxhi;
332   uword * p;
333
334   rxhi = vnet_get_sup_hw_interface (pm->vnet_main, rx_sw_if_index);
335
336   /* Make sure caller didn't pass a vlan subif, etc. */
337   if (rxhi->sw_if_index != rx_sw_if_index)
338     return VNET_API_ERROR_INVALID_SW_IF_INDEX;
339
340   if (is_add)
341     {
342       
343       p = hash_get_mem (pm->policer_config_by_name, config_name);
344
345       if (p == 0)
346         return -2;
347
348       template = pool_elt_at_index (pm->policer_templates, p[0]);
349
350       vnet_hw_interface_rx_redirect_to_node 
351         (pm->vnet_main, 
352          rxhi->hw_if_index,
353          policer_by_sw_if_index_node.index);
354
355       pool_get_aligned (pm->policers, policer, CLIB_CACHE_LINE_BYTES);
356
357       policer[0] = template[0];
358
359       vec_validate (pm->policer_index_by_sw_if_index, rx_sw_if_index);
360       pm->policer_index_by_sw_if_index[rx_sw_if_index] 
361           = policer - pm->policers;
362     }
363   else
364     {
365       u32 pi;
366       vnet_hw_interface_rx_redirect_to_node (pm->vnet_main, 
367                                              rxhi->hw_if_index,
368                                              ~0 /* disable */);
369
370       pi = pm->policer_index_by_sw_if_index[rx_sw_if_index];
371       pm->policer_index_by_sw_if_index[rx_sw_if_index] = ~0;
372       pool_put_index (pm->policers, pi);
373     }
374   
375   return 0;
376 }
377
378 static clib_error_t *
379 test_policer_command_fn (vlib_main_t * vm,
380                          unformat_input_t * input,
381                          vlib_cli_command_t * cmd)
382 {
383   vnet_policer_main_t * pm = &vnet_policer_main;  
384   unformat_input_t _line_input, * line_input = &_line_input;
385   u32 rx_sw_if_index;
386   int rv;
387   u8 * config_name = 0;
388   int rx_set = 0;
389   int is_add = 1;
390   int is_show = 0;
391
392   /* Get a line of input. */
393   if (! unformat_user (input, unformat_line_input, line_input))
394     return 0;
395
396   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
397     {
398       if (unformat (line_input, "intfc %U", unformat_vnet_sw_interface,
399                     pm->vnet_main, &rx_sw_if_index))
400         rx_set = 1;
401       else if (unformat (line_input, "show"))
402         is_show=1;
403       else if (unformat (line_input, "policer %s", &config_name))
404         ;
405       else if (unformat (line_input, "del"))
406         is_add = 0;
407       else break;
408     }
409
410   if (rx_set == 0)
411     return clib_error_return (0, "interface not set");
412
413   if (is_show)
414     {
415       u32 pi = pm->policer_index_by_sw_if_index[rx_sw_if_index];
416       policer_read_response_type_st * policer;
417       policer = pool_elt_at_index (pm->policers, pi);
418       
419       vlib_cli_output (vm, "%U", format_policer_instance, policer);
420       return 0;
421     }
422
423   if (is_add && config_name == 0)
424     {
425       return clib_error_return (0, "policer config name required");
426     }
427
428   rv = test_policer_add_del (rx_sw_if_index, config_name, is_add);
429
430   switch (rv)
431     {
432     case 0:
433       break;
434
435     default:
436       return clib_error_return 
437         (0, "WARNING: vnet_vnet_policer_add_del returned %d", rv);
438     }
439   
440   return 0;
441 }
442
443 VLIB_CLI_COMMAND (test_patch_command, static) = {
444     .path = "test policer",
445     .short_help = 
446     "intfc <intfc> policer <policer-config-name> [del]",
447     .function = test_policer_command_fn,
448 };
449
450
451 #endif /* TEST_CODE */