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