6e7ac5b3c4c9c069142143b2369c646661d8a66f
[vpp.git] / src / vnet / ip / ip_punt_drop.h
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 #ifndef __IP_PUNT_DROP_H__
17 #define __IP_PUNT_DROP_H__
18
19 #include <vnet/ip/ip.h>
20 #include <vnet/policer/policer.h>
21 #include <vnet/policer/police_inlines.h>
22
23 /**
24  * IP4 punt policer configuration
25  *   we police the punt rate to prevent overloading the host
26  */
27 typedef struct ip_punt_policer_t_
28 {
29   u32 policer_index;
30   u32 fq_index;
31 } ip_punt_policer_t;
32
33 typedef enum ip_punt_policer_next_t_
34 {
35   IP_PUNT_POLICER_NEXT_DROP,
36   IP_PUNT_POLICER_NEXT_HANDOFF,
37   IP_PUNT_POLICER_N_NEXT,
38 } ip_punt_policer_next_t;
39
40 typedef struct ip_punt_policer_trace_t_
41 {
42   u32 policer_index;
43   u32 next;
44 } ip_punt_policer_trace_t;
45
46 #define foreach_ip_punt_policer_error          \
47 _(DROP, "ip punt policer drop")
48
49 typedef enum
50 {
51 #define _(sym,str) IP_PUNT_POLICER_ERROR_##sym,
52   foreach_ip_punt_policer_error
53 #undef _
54     IP4_PUNT_POLICER_N_ERROR,
55 } ip_punt_policer_error_t;
56
57 extern u8 *format_ip_punt_policer_trace (u8 * s, va_list * args);
58 extern vlib_node_registration_t ip4_punt_policer_node;
59 extern ip_punt_policer_t ip4_punt_policer_cfg;
60 extern vlib_node_registration_t ip6_punt_policer_node;
61 extern ip_punt_policer_t ip6_punt_policer_cfg;
62
63 /**
64  * IP punt policing node function
65  */
66 always_inline uword
67 ip_punt_policer (vlib_main_t * vm,
68                  vlib_node_runtime_t * node,
69                  vlib_frame_t * frame, u8 arc_index, u32 policer_index)
70 {
71   u32 *from, *to_next, n_left_from, n_left_to_next, next_index;
72   u64 time_in_policer_periods;
73   vnet_feature_main_t *fm = &feature_main;
74   vnet_feature_config_main_t *cm = &fm->feature_config_mains[arc_index];
75   vnet_policer_main_t *pm = &vnet_policer_main;
76   policer_read_response_type_st *pol = &pm->policers[policer_index];
77   u32 pol_thread_index = pol->thread_index;
78   u32 this_thread_index = vm->thread_index;
79
80   time_in_policer_periods =
81     clib_cpu_time_now () >> POLICER_TICKS_PER_PERIOD_SHIFT;
82
83   from = vlib_frame_vector_args (frame);
84   n_left_from = frame->n_vectors;
85   next_index = node->cached_next_index;
86
87   if (PREDICT_FALSE (pol_thread_index == ~0))
88     {
89       /*
90        * This is the first packet to use this policer. Set the
91        * thread index in the policer to this thread and any
92        * packets seen by this node on other threads will
93        * be handed off to this one.
94        *
95        * This could happen simultaneously on another thread.
96        */
97       clib_atomic_cmp_and_swap (&pol->thread_index, ~0, this_thread_index);
98       pol_thread_index = this_thread_index;
99     }
100
101   while (n_left_from > 0)
102     {
103       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
104
105       while (n_left_from >= 4 && n_left_to_next >= 2)
106         {
107           vlib_buffer_t *b0, *b1;
108           u32 next0, next1;
109           u8 act0, act1;
110           u32 bi0, bi1;
111
112           next0 = next1 = 0;
113           bi0 = to_next[0] = from[0];
114           bi1 = to_next[1] = from[1];
115
116           from += 2;
117           n_left_from -= 2;
118           to_next += 2;
119           n_left_to_next -= 2;
120
121           b0 = vlib_get_buffer (vm, bi0);
122           b1 = vlib_get_buffer (vm, bi1);
123
124           if (PREDICT_FALSE (this_thread_index != pol_thread_index))
125             {
126               next0 = next1 = IP_PUNT_POLICER_NEXT_HANDOFF;
127             }
128           else
129             {
130
131               vnet_get_config_data (&cm->config_main,
132                                     &b0->current_config_index, &next0, 0);
133               vnet_get_config_data (&cm->config_main,
134                                     &b1->current_config_index, &next1, 0);
135
136               act0 =
137                 vnet_policer_police (vm, b0, policer_index,
138                                      time_in_policer_periods, POLICE_CONFORM);
139               act1 =
140                 vnet_policer_police (vm, b1, policer_index,
141                                      time_in_policer_periods, POLICE_CONFORM);
142
143               if (PREDICT_FALSE (act0 == QOS_ACTION_DROP))
144                 {
145                   next0 = IP_PUNT_POLICER_NEXT_DROP;
146                   b0->error = node->errors[IP_PUNT_POLICER_ERROR_DROP];
147                 }
148               if (PREDICT_FALSE (act1 == QOS_ACTION_DROP))
149                 {
150                   next1 = IP_PUNT_POLICER_NEXT_DROP;
151                   b1->error = node->errors[IP_PUNT_POLICER_ERROR_DROP];
152                 }
153
154               if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
155                 {
156                   ip_punt_policer_trace_t *t =
157                     vlib_add_trace (vm, node, b0, sizeof (*t));
158                   t->next = next0;
159                   t->policer_index = policer_index;
160                 }
161               if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
162                 {
163                   ip_punt_policer_trace_t *t =
164                     vlib_add_trace (vm, node, b1, sizeof (*t));
165                   t->next = next1;
166                   t->policer_index = policer_index;
167                 }
168             }
169
170           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
171                                            n_left_to_next,
172                                            bi0, bi1, next0, next1);
173         }
174       while (n_left_from > 0 && n_left_to_next > 0)
175         {
176           vlib_buffer_t *b0;
177           u32 next0;
178           u32 bi0;
179           u8 act0;
180
181           next0 = 0;
182           bi0 = to_next[0] = from[0];
183
184           from += 1;
185           n_left_from -= 1;
186           to_next += 1;
187           n_left_to_next -= 1;
188
189           b0 = vlib_get_buffer (vm, bi0);
190
191           if (PREDICT_FALSE (this_thread_index != pol_thread_index))
192             {
193               next0 = IP_PUNT_POLICER_NEXT_HANDOFF;
194             }
195           else
196             {
197               vnet_get_config_data (&cm->config_main,
198                                     &b0->current_config_index, &next0, 0);
199
200               act0 =
201                 vnet_policer_police (vm, b0, policer_index,
202                                      time_in_policer_periods, POLICE_CONFORM);
203               if (PREDICT_FALSE (act0 == QOS_ACTION_DROP))
204                 {
205                   next0 = IP_PUNT_POLICER_NEXT_DROP;
206                   b0->error = node->errors[IP_PUNT_POLICER_ERROR_DROP];
207                 }
208
209               if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
210                 {
211                   ip_punt_policer_trace_t *t =
212                     vlib_add_trace (vm, node, b0, sizeof (*t));
213                   t->next = next0;
214                   t->policer_index = policer_index;
215                 }
216             }
217           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
218                                            n_left_to_next, bi0, next0);
219         }
220       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
221     }
222
223   return frame->n_vectors;
224 }
225
226 /**
227  * IP4 punt redirect per-rx interface configuration
228  *   redirect punted traffic to another location.
229  */
230 typedef struct ip_punt_redirect_rx_t_
231 {
232   /**
233    * Node linkage into the FIB graph
234    */
235   fib_node_t node;
236
237   fib_protocol_t fproto;
238   fib_forward_chain_type_t payload_type;
239   fib_node_index_t pl;
240   u32 sibling;
241
242   /**
243    * redirect forwarding
244    */
245   dpo_id_t dpo;
246 } ip_punt_redirect_rx_t;
247
248 /**
249  * IP punt redirect configuration
250  */
251 typedef struct ip_punt_redirect_t_
252 {
253   ip_punt_redirect_rx_t *pool;
254
255   /**
256    * per-RX interface configuration.
257    *  sw_if_index = 0 (from which packets are never received) is used to
258    *  indicate 'from-any'
259    */
260   index_t *redirect_by_rx_sw_if_index[FIB_PROTOCOL_IP_MAX];
261 } ip_punt_redirect_cfg_t;
262
263 extern ip_punt_redirect_cfg_t ip_punt_redirect_cfg;
264
265 /**
266  * IP punt redirect next nodes
267  */
268 typedef enum ip_punt_redirect_next_t_
269 {
270   IP_PUNT_REDIRECT_NEXT_DROP,
271   IP_PUNT_REDIRECT_NEXT_TX,
272   IP_PUNT_REDIRECT_NEXT_ARP,
273   IP_PUNT_REDIRECT_N_NEXT,
274 } ip_punt_redirect_next_t;
275
276 /**
277  * IP Punt redirect trace
278  */
279 typedef struct ip4_punt_redirect_trace_t_
280 {
281   index_t rrxi;
282   u32 next;
283 } ip_punt_redirect_trace_t;
284
285 /**
286  * Add a punt redirect entry
287  */
288 extern void ip_punt_redirect_add (fib_protocol_t fproto,
289                                   u32 rx_sw_if_index,
290                                   fib_forward_chain_type_t ct,
291                                   fib_route_path_t * rpaths);
292
293 extern void ip_punt_redirect_del (fib_protocol_t fproto, u32 rx_sw_if_index);
294 extern index_t ip_punt_redirect_find (fib_protocol_t fproto,
295                                       u32 rx_sw_if_index);
296 extern u8 *format_ip_punt_redirect (u8 * s, va_list * args);
297
298 extern u8 *format_ip_punt_redirect_trace (u8 * s, va_list * args);
299
300 typedef walk_rc_t (*ip_punt_redirect_walk_cb_t) (u32 rx_sw_if_index,
301                                                  const ip_punt_redirect_rx_t *
302                                                  redirect, void *arg);
303 extern void ip_punt_redirect_walk (fib_protocol_t fproto,
304                                    ip_punt_redirect_walk_cb_t cb, void *ctx);
305
306 static_always_inline ip_punt_redirect_rx_t *
307 ip_punt_redirect_get (index_t rrxi)
308 {
309   return (pool_elt_at_index (ip_punt_redirect_cfg.pool, rrxi));
310 }
311
312 always_inline uword
313 ip_punt_redirect (vlib_main_t * vm,
314                   vlib_node_runtime_t * node,
315                   vlib_frame_t * frame, u8 arc_index, fib_protocol_t fproto)
316 {
317   u32 *from, *to_next, n_left_from, n_left_to_next, next_index;
318   vnet_feature_main_t *fm = &feature_main;
319   vnet_feature_config_main_t *cm = &fm->feature_config_mains[arc_index];
320   index_t *redirects;
321
322   from = vlib_frame_vector_args (frame);
323   n_left_from = frame->n_vectors;
324   next_index = node->cached_next_index;
325   redirects = ip_punt_redirect_cfg.redirect_by_rx_sw_if_index[fproto];
326
327   while (n_left_from > 0)
328     {
329       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
330
331       while (n_left_from > 0 && n_left_to_next > 0)
332         {
333           u32 rx_sw_if_index0, rrxi0;
334           ip_punt_redirect_rx_t *rrx0;
335           vlib_buffer_t *b0;
336           u32 next0;
337           u32 bi0;
338
339           rrxi0 = INDEX_INVALID;
340           next0 = 0;
341           bi0 = to_next[0] = from[0];
342
343           from += 1;
344           n_left_from -= 1;
345           to_next += 1;
346           n_left_to_next -= 1;
347
348           b0 = vlib_get_buffer (vm, bi0);
349
350           vnet_get_config_data (&cm->config_main,
351                                 &b0->current_config_index, &next0, 0);
352
353           rx_sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
354
355           /*
356            * If config exists for this particular RX interface use it,
357            * else use the default (at RX = 0)
358            */
359           if (vec_len (redirects) > rx_sw_if_index0)
360             {
361               rrxi0 = redirects[rx_sw_if_index0];
362               if (INDEX_INVALID == rrxi0)
363                 rrxi0 = redirects[0];
364             }
365           else if (vec_len (redirects) >= 1)
366             rrxi0 = redirects[0];
367
368           if (PREDICT_TRUE (INDEX_INVALID != rrxi0))
369             {
370               rrx0 = ip_punt_redirect_get (rrxi0);
371               vnet_buffer (b0)->ip.adj_index[VLIB_TX] = rrx0->dpo.dpoi_index;
372               next0 = rrx0->dpo.dpoi_next_node;
373             }
374
375           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
376             {
377               ip_punt_redirect_trace_t *t =
378                 vlib_add_trace (vm, node, b0, sizeof (*t));
379               t->next = next0;
380               t->rrxi = rrxi0;
381             }
382
383           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
384                                            n_left_to_next, bi0, next0);
385         }
386
387       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
388     }
389
390   return frame->n_vectors;
391 }
392
393 always_inline uword
394 ip_drop_or_punt (vlib_main_t * vm,
395                  vlib_node_runtime_t * node,
396                  vlib_frame_t * frame, u8 arc_index)
397 {
398   u32 *from, *to_next, n_left_from, n_left_to_next, next_index;
399
400   from = vlib_frame_vector_args (frame);
401   n_left_from = frame->n_vectors;
402   next_index = node->cached_next_index;
403
404   while (n_left_from > 0)
405     {
406       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
407
408       while (n_left_from >= 8 && n_left_to_next >= 4)
409         {
410           vlib_buffer_t *b0, *b1, *b2, *b3;
411           u32 next0, next1, next2, next3;
412           u32 bi0, bi1, bi2, bi3;
413
414           next0 = next1 = next2 = next3 = 0;
415
416           /* Prefetch next iteration. */
417           {
418             vlib_buffer_t *p4, *p5, *p6, *p7;
419
420             p4 = vlib_get_buffer (vm, from[4]);
421             p5 = vlib_get_buffer (vm, from[5]);
422             p6 = vlib_get_buffer (vm, from[6]);
423             p7 = vlib_get_buffer (vm, from[7]);
424
425             vlib_prefetch_buffer_header (p4, LOAD);
426             vlib_prefetch_buffer_header (p5, LOAD);
427             vlib_prefetch_buffer_header (p6, LOAD);
428             vlib_prefetch_buffer_header (p7, LOAD);
429           }
430
431           bi0 = to_next[0] = from[0];
432           bi1 = to_next[1] = from[1];
433           bi2 = to_next[2] = from[2];
434           bi3 = to_next[3] = from[3];
435
436           from += 4;
437           n_left_from -= 4;
438           to_next += 4;
439           n_left_to_next -= 4;
440
441           b0 = vlib_get_buffer (vm, bi0);
442           b1 = vlib_get_buffer (vm, bi1);
443           b2 = vlib_get_buffer (vm, bi2);
444           b3 = vlib_get_buffer (vm, bi3);
445
446           /* punt and drop features are not associated with a given interface
447            * so the special index 0 is used */
448           vnet_feature_arc_start (arc_index, 0, &next0, b0);
449           vnet_feature_arc_start (arc_index, 0, &next1, b1);
450           vnet_feature_arc_start (arc_index, 0, &next2, b2);
451           vnet_feature_arc_start (arc_index, 0, &next3, b3);
452
453           vlib_validate_buffer_enqueue_x4 (vm, node, next_index,
454                                            to_next, n_left_to_next,
455                                            bi0, bi1, bi2, bi3,
456                                            next0, next1, next2, next3);
457         }
458
459       while (n_left_from > 0 && n_left_to_next > 0)
460         {
461           vlib_buffer_t *b0;
462           u32 next0;
463           u32 bi0;
464
465           next0 = 0;
466           bi0 = to_next[0] = from[0];
467
468           from += 1;
469           n_left_from -= 1;
470           to_next += 1;
471           n_left_to_next -= 1;
472
473           b0 = vlib_get_buffer (vm, bi0);
474
475           vnet_feature_arc_start (arc_index, 0, &next0, b0);
476
477           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
478                                            n_left_to_next, bi0, next0);
479         }
480       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
481     }
482
483   return frame->n_vectors;
484 }
485
486 #endif
487
488 /*
489  * fd.io coding-style-patch-verification: ON
490  *
491  * Local Variables:
492  * eval: (c-set-style "gnu")
493  * End:
494  */