GRE tests and fixes
[vpp.git] / vnet / vnet / interface_output.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  * interface_output.c: interface output node
17  *
18  * Copyright (c) 2008 Eliot Dresselhaus
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining
21  * a copy of this software and associated documentation files (the
22  * "Software"), to deal in the Software without restriction, including
23  * without limitation the rights to use, copy, modify, merge, publish,
24  * distribute, sublicense, and/or sell copies of the Software, and to
25  * permit persons to whom the Software is furnished to do so, subject to
26  * the following conditions:
27  *
28  * The above copyright notice and this permission notice shall be
29  * included in all copies or substantial portions of the Software.
30  *
31  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38  */
39
40 #include <vnet/vnet.h>
41
42 typedef struct
43 {
44   u32 sw_if_index;
45   u8 data[128 - sizeof (u32)];
46 }
47 interface_output_trace_t;
48
49 u8 *
50 format_vnet_interface_output_trace (u8 * s, va_list * va)
51 {
52   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
53   vlib_node_t *node = va_arg (*va, vlib_node_t *);
54   interface_output_trace_t *t = va_arg (*va, interface_output_trace_t *);
55   vnet_main_t *vnm = vnet_get_main ();
56   vnet_sw_interface_t *si;
57   uword indent;
58
59   if (t->sw_if_index != (u32) ~ 0)
60     {
61       indent = format_get_indent (s);
62
63       if (pool_is_free_index
64           (vnm->interface_main.sw_interfaces, t->sw_if_index))
65         {
66           /* the interface may have been deleted by the time the trace is printed */
67           s = format (s, "sw_if_index: %d\n%U%U",
68                       t->sw_if_index,
69                       format_white_space, indent,
70                       node->format_buffer ? node->
71                       format_buffer : format_hex_bytes, t->data,
72                       sizeof (t->data));
73         }
74       else
75         {
76           si = vnet_get_sw_interface (vnm, t->sw_if_index);
77
78           s = format (s, "%U\n%U%U",
79                       format_vnet_sw_interface_name, vnm, si,
80                       format_white_space, indent,
81                       node->format_buffer ? node->
82                       format_buffer : format_hex_bytes, t->data,
83                       sizeof (t->data));
84         }
85     }
86   return s;
87 }
88
89 static void
90 vnet_interface_output_trace (vlib_main_t * vm,
91                              vlib_node_runtime_t * node,
92                              vlib_frame_t * frame, uword n_buffers)
93 {
94   u32 n_left, *from;
95
96   n_left = n_buffers;
97   from = vlib_frame_args (frame);
98
99   while (n_left >= 4)
100     {
101       u32 bi0, bi1;
102       vlib_buffer_t *b0, *b1;
103       interface_output_trace_t *t0, *t1;
104
105       /* Prefetch next iteration. */
106       vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
107       vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
108
109       bi0 = from[0];
110       bi1 = from[1];
111
112       b0 = vlib_get_buffer (vm, bi0);
113       b1 = vlib_get_buffer (vm, bi1);
114
115       if (b0->flags & VLIB_BUFFER_IS_TRACED)
116         {
117           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
118           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
119           clib_memcpy (t0->data, vlib_buffer_get_current (b0),
120                        sizeof (t0->data));
121         }
122       if (b1->flags & VLIB_BUFFER_IS_TRACED)
123         {
124           t1 = vlib_add_trace (vm, node, b1, sizeof (t1[0]));
125           t1->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_TX];
126           clib_memcpy (t1->data, vlib_buffer_get_current (b1),
127                        sizeof (t1->data));
128         }
129       from += 2;
130       n_left -= 2;
131     }
132
133   while (n_left >= 1)
134     {
135       u32 bi0;
136       vlib_buffer_t *b0;
137       interface_output_trace_t *t0;
138
139       bi0 = from[0];
140
141       b0 = vlib_get_buffer (vm, bi0);
142
143       if (b0->flags & VLIB_BUFFER_IS_TRACED)
144         {
145           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
146           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
147           clib_memcpy (t0->data, vlib_buffer_get_current (b0),
148                        sizeof (t0->data));
149         }
150       from += 1;
151       n_left -= 1;
152     }
153 }
154
155 static never_inline u32
156 slow_path (vlib_main_t * vm,
157            u32 bi,
158            vlib_buffer_t * b,
159            u32 n_left_to_tx, u32 * to_tx, u32 * n_slow_bytes_result)
160 {
161   /* We've already enqueued a single buffer. */
162   u32 n_buffers = 0;
163   u32 n_slow_bytes = 0;
164
165   while (n_left_to_tx > 0)
166     {
167       to_tx[0] = bi;
168       to_tx += 1;
169       n_left_to_tx -= 1;
170       n_buffers += 1;
171       n_slow_bytes += vlib_buffer_length_in_chain (vm, b);
172
173       /* Be grumpy about zero length buffers for benefit of
174          driver tx function. */
175       ASSERT (b->current_length > 0);
176
177       if (!(b->flags & VLIB_BUFFER_NEXT_PRESENT))
178         break;
179
180       bi = b->next_buffer;
181       b = vlib_get_buffer (vm, bi);
182     }
183
184   /* Ran out of space in next frame trying to enqueue buffers? */
185   if (b->flags & VLIB_BUFFER_NEXT_PRESENT)
186     return 0;
187
188   *n_slow_bytes_result = n_slow_bytes;
189   return n_buffers;
190 }
191
192 /*
193  * Increment TX stats. Roll up consecutive increments to the same sw_if_index
194  * into one increment.
195  */
196 static_always_inline void
197 incr_output_stats (vnet_main_t * vnm,
198                    u32 cpu_index,
199                    u32 length,
200                    u32 sw_if_index,
201                    u32 * last_sw_if_index, u32 * n_packets, u32 * n_bytes)
202 {
203   vnet_interface_main_t *im;
204
205   if (PREDICT_TRUE (sw_if_index == *last_sw_if_index))
206     {
207       *n_packets += 1;
208       *n_bytes += length;
209     }
210   else
211     {
212       if (PREDICT_TRUE (*last_sw_if_index != ~0))
213         {
214           im = &vnm->interface_main;
215
216           vlib_increment_combined_counter (im->combined_sw_if_counters
217                                            + VNET_INTERFACE_COUNTER_TX,
218                                            cpu_index,
219                                            *last_sw_if_index,
220                                            *n_packets, *n_bytes);
221         }
222       *last_sw_if_index = sw_if_index;
223       *n_packets = 1;
224       *n_bytes = length;
225     }
226 }
227
228
229 /* Interface output functions. */
230 uword
231 vnet_interface_output_node (vlib_main_t * vm,
232                             vlib_node_runtime_t * node, vlib_frame_t * frame)
233 {
234   vnet_main_t *vnm = vnet_get_main ();
235   vnet_interface_output_runtime_t *rt = (void *) node->runtime_data;
236   vnet_sw_interface_t *si;
237   vnet_hw_interface_t *hi;
238   u32 n_left_to_tx, *from, *from_end, *to_tx;
239   u32 n_bytes, n_buffers, n_packets;
240   u32 last_sw_if_index;
241   u32 cpu_index = vm->cpu_index;
242
243   n_buffers = frame->n_vectors;
244
245   if (node->flags & VLIB_NODE_FLAG_TRACE)
246     vnet_interface_output_trace (vm, node, frame, n_buffers);
247
248   from = vlib_frame_args (frame);
249
250   if (rt->is_deleted)
251     return vlib_error_drop_buffers (vm, node, from,
252                                     /* buffer stride */ 1,
253                                     n_buffers,
254                                     VNET_INTERFACE_OUTPUT_NEXT_DROP,
255                                     node->node_index,
256                                     VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DELETED);
257
258   si = vnet_get_sw_interface (vnm, rt->sw_if_index);
259   hi = vnet_get_sup_hw_interface (vnm, rt->sw_if_index);
260   if (!(si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ||
261       !(hi->flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
262     {
263       vlib_simple_counter_main_t *cm;
264
265       cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
266                              VNET_INTERFACE_COUNTER_TX_ERROR);
267       vlib_increment_simple_counter (cm, cpu_index,
268                                      rt->sw_if_index, n_buffers);
269       return vlib_error_drop_buffers (vm, node, from,
270                                       /* buffer stride */ 1,
271                                       n_buffers,
272                                       VNET_INTERFACE_OUTPUT_NEXT_DROP,
273                                       node->node_index,
274                                       VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DOWN);
275     }
276
277   from_end = from + n_buffers;
278
279   /* Total byte count of all buffers. */
280   n_bytes = 0;
281   n_packets = 0;
282   last_sw_if_index = ~0;
283
284   while (from < from_end)
285     {
286       /* Get new next frame since previous incomplete frame may have less
287          than VNET_FRAME_SIZE vectors in it. */
288       vlib_get_new_next_frame (vm, node, VNET_INTERFACE_OUTPUT_NEXT_TX,
289                                to_tx, n_left_to_tx);
290
291       while (from + 4 <= from_end && n_left_to_tx >= 2)
292         {
293           u32 bi0, bi1;
294           vlib_buffer_t *b0, *b1;
295
296           /* Prefetch next iteration. */
297           vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
298           vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
299
300           bi0 = from[0];
301           bi1 = from[1];
302           to_tx[0] = bi0;
303           to_tx[1] = bi1;
304           from += 2;
305           to_tx += 2;
306           n_left_to_tx -= 2;
307
308           b0 = vlib_get_buffer (vm, bi0);
309           b1 = vlib_get_buffer (vm, bi1);
310
311           /* Be grumpy about zero length buffers for benefit of
312              driver tx function. */
313           ASSERT (b0->current_length > 0);
314           ASSERT (b1->current_length > 0);
315
316           if (PREDICT_FALSE
317               ((b0->flags | b1->flags) & VLIB_BUFFER_NEXT_PRESENT))
318             {
319               u32 n_buffers, n_slow_bytes, i;
320
321               /* Undo. */
322               from -= 2;
323               to_tx -= 2;
324               n_left_to_tx += 2;
325
326               /* Do slow path two times. */
327               for (i = 0; i < 2; i++)
328                 {
329                   u32 bi = i ? bi1 : bi0;
330                   vlib_buffer_t *b = i ? b1 : b0;
331
332                   n_buffers = slow_path (vm, bi, b,
333                                          n_left_to_tx, to_tx, &n_slow_bytes);
334
335                   /* Not enough room for single packet? */
336                   if (n_buffers == 0)
337                     goto put;
338
339                   from += 1;
340                   to_tx += n_buffers;
341                   n_left_to_tx -= n_buffers;
342                   incr_output_stats (vnm, cpu_index, n_slow_bytes,
343                                      vnet_buffer (b)->sw_if_index[VLIB_TX],
344                                      &last_sw_if_index, &n_packets, &n_bytes);
345                 }
346             }
347           else
348             {
349               incr_output_stats (vnm, cpu_index,
350                                  vlib_buffer_length_in_chain (vm, b0),
351                                  vnet_buffer (b0)->sw_if_index[VLIB_TX],
352                                  &last_sw_if_index, &n_packets, &n_bytes);
353               incr_output_stats (vnm, cpu_index,
354                                  vlib_buffer_length_in_chain (vm, b0),
355                                  vnet_buffer (b1)->sw_if_index[VLIB_TX],
356                                  &last_sw_if_index, &n_packets, &n_bytes);
357             }
358         }
359
360       while (from + 1 <= from_end && n_left_to_tx >= 1)
361         {
362           u32 bi0;
363           vlib_buffer_t *b0;
364
365           bi0 = from[0];
366           to_tx[0] = bi0;
367           from += 1;
368           to_tx += 1;
369           n_left_to_tx -= 1;
370
371           b0 = vlib_get_buffer (vm, bi0);
372
373           /* Be grumpy about zero length buffers for benefit of
374              driver tx function. */
375           ASSERT (b0->current_length > 0);
376
377           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_NEXT_PRESENT))
378             {
379               u32 n_buffers, n_slow_bytes;
380
381               /* Undo. */
382               from -= 1;
383               to_tx -= 1;
384               n_left_to_tx += 1;
385
386               n_buffers = slow_path (vm, bi0, b0,
387                                      n_left_to_tx, to_tx, &n_slow_bytes);
388
389               /* Not enough room for single packet? */
390               if (n_buffers == 0)
391                 goto put;
392
393               from += 1;
394               to_tx += n_buffers;
395               n_left_to_tx -= n_buffers;
396             }
397           incr_output_stats (vnm, cpu_index,
398                              vlib_buffer_length_in_chain (vm, b0),
399                              vnet_buffer (b0)->sw_if_index[VLIB_TX],
400                              &last_sw_if_index, &n_packets, &n_bytes);
401         }
402
403     put:
404       vlib_put_next_frame (vm, node, VNET_INTERFACE_OUTPUT_NEXT_TX,
405                            n_left_to_tx);
406     }
407
408   /* Final update of interface stats. */
409   incr_output_stats (vnm, cpu_index, 0, ~0,     /* ~0 will flush stats */
410                      &last_sw_if_index, &n_packets, &n_bytes);
411
412   return n_buffers;
413 }
414
415 VLIB_NODE_FUNCTION_MULTIARCH_CLONE (vnet_interface_output_node);
416 CLIB_MULTIARCH_SELECT_FN (vnet_interface_output_node);
417
418 uword
419 vnet_interface_output_node_no_flatten (vlib_main_t * vm,
420                                        vlib_node_runtime_t * node,
421                                        vlib_frame_t * frame)
422 {
423   vnet_main_t *vnm = vnet_get_main ();
424   vnet_interface_output_runtime_t *rt = (void *) node->runtime_data;
425   vnet_sw_interface_t *si;
426   vnet_hw_interface_t *hi;
427   u32 n_left_to_tx, *from, *from_end, *to_tx;
428   u32 n_bytes, n_buffers, n_packets;
429   u32 n_bytes_b0, n_bytes_b1;
430   u32 cpu_index = vm->cpu_index;
431   vnet_interface_main_t *im = &vnm->interface_main;
432   u32 next_index = VNET_INTERFACE_OUTPUT_NEXT_TX;
433
434   n_buffers = frame->n_vectors;
435
436   if (node->flags & VLIB_NODE_FLAG_TRACE)
437     vnet_interface_output_trace (vm, node, frame, n_buffers);
438
439   from = vlib_frame_args (frame);
440
441   if (rt->is_deleted)
442     return vlib_error_drop_buffers (vm, node, from,
443                                     /* buffer stride */ 1,
444                                     n_buffers,
445                                     VNET_INTERFACE_OUTPUT_NEXT_DROP,
446                                     node->node_index,
447                                     VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DELETED);
448
449   si = vnet_get_sw_interface (vnm, rt->sw_if_index);
450   hi = vnet_get_sup_hw_interface (vnm, rt->sw_if_index);
451   if (!(si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ||
452       !(hi->flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
453     {
454       vlib_simple_counter_main_t *cm;
455
456       cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
457                              VNET_INTERFACE_COUNTER_TX_ERROR);
458       vlib_increment_simple_counter (cm, cpu_index,
459                                      rt->sw_if_index, n_buffers);
460
461       return vlib_error_drop_buffers (vm, node, from,
462                                       /* buffer stride */ 1,
463                                       n_buffers,
464                                       VNET_INTERFACE_OUTPUT_NEXT_DROP,
465                                       node->node_index,
466                                       VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DOWN);
467     }
468
469   from_end = from + n_buffers;
470
471   /* Total byte count of all buffers. */
472   n_bytes = 0;
473   n_packets = 0;
474
475   while (from < from_end)
476     {
477       /* Get new next frame since previous incomplete frame may have less
478          than VNET_FRAME_SIZE vectors in it. */
479       vlib_get_new_next_frame (vm, node, next_index, to_tx, n_left_to_tx);
480
481       while (from + 4 <= from_end && n_left_to_tx >= 2)
482         {
483           u32 bi0, bi1;
484           vlib_buffer_t *b0, *b1;
485           u32 tx_swif0, tx_swif1;
486
487           /* Prefetch next iteration. */
488           vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
489           vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
490
491           bi0 = from[0];
492           bi1 = from[1];
493           to_tx[0] = bi0;
494           to_tx[1] = bi1;
495           from += 2;
496           to_tx += 2;
497           n_left_to_tx -= 2;
498
499           b0 = vlib_get_buffer (vm, bi0);
500           b1 = vlib_get_buffer (vm, bi1);
501
502           /* Be grumpy about zero length buffers for benefit of
503              driver tx function. */
504           ASSERT (b0->current_length > 0);
505           ASSERT (b1->current_length > 0);
506
507           n_bytes_b0 = vlib_buffer_length_in_chain (vm, b0);
508           n_bytes_b1 = vlib_buffer_length_in_chain (vm, b1);
509           tx_swif0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
510           tx_swif1 = vnet_buffer (b1)->sw_if_index[VLIB_TX];
511
512           n_bytes += n_bytes_b0 + n_bytes_b1;
513           n_packets += 2;
514
515           if (PREDICT_FALSE (tx_swif0 != rt->sw_if_index))
516             {
517               /* update vlan subif tx counts, if required */
518               vlib_increment_combined_counter (im->combined_sw_if_counters +
519                                                VNET_INTERFACE_COUNTER_TX,
520                                                cpu_index, tx_swif0, 1,
521                                                n_bytes_b0);
522             }
523
524           /* update vlan subif tx counts, if required */
525           if (PREDICT_FALSE (tx_swif1 != rt->sw_if_index))
526             {
527
528               vlib_increment_combined_counter (im->combined_sw_if_counters +
529                                                VNET_INTERFACE_COUNTER_TX,
530                                                cpu_index, tx_swif1, 1,
531                                                n_bytes_b1);
532             }
533         }
534
535       while (from + 1 <= from_end && n_left_to_tx >= 1)
536         {
537           u32 bi0;
538           vlib_buffer_t *b0;
539           u32 tx_swif0;
540
541           bi0 = from[0];
542           to_tx[0] = bi0;
543           from += 1;
544           to_tx += 1;
545           n_left_to_tx -= 1;
546
547           b0 = vlib_get_buffer (vm, bi0);
548
549           /* Be grumpy about zero length buffers for benefit of
550              driver tx function. */
551           ASSERT (b0->current_length > 0);
552
553           n_bytes_b0 = vlib_buffer_length_in_chain (vm, b0);
554           tx_swif0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
555           n_bytes += n_bytes_b0;
556           n_packets += 1;
557
558           if (PREDICT_FALSE (tx_swif0 != rt->sw_if_index))
559             {
560
561               vlib_increment_combined_counter (im->combined_sw_if_counters +
562                                                VNET_INTERFACE_COUNTER_TX,
563                                                cpu_index, tx_swif0, 1,
564                                                n_bytes_b0);
565             }
566         }
567
568       vlib_put_next_frame (vm, node, next_index, n_left_to_tx);
569     }
570
571   /* Update main interface stats. */
572   vlib_increment_combined_counter (im->combined_sw_if_counters
573                                    + VNET_INTERFACE_COUNTER_TX,
574                                    cpu_index,
575                                    rt->sw_if_index, n_packets, n_bytes);
576   return n_buffers;
577 }
578
579 VLIB_NODE_FUNCTION_MULTIARCH_CLONE (vnet_interface_output_node_no_flatten);
580 CLIB_MULTIARCH_SELECT_FN (vnet_interface_output_node_no_flatten);
581
582 /* Use buffer's sw_if_index[VNET_TX] to choose output interface. */
583 static uword
584 vnet_per_buffer_interface_output (vlib_main_t * vm,
585                                   vlib_node_runtime_t * node,
586                                   vlib_frame_t * frame)
587 {
588   vnet_main_t *vnm = vnet_get_main ();
589   u32 n_left_to_next, *from, *to_next;
590   u32 n_left_from, next_index;
591
592   n_left_from = frame->n_vectors;
593
594   from = vlib_frame_args (frame);
595   next_index = node->cached_next_index;
596
597   while (n_left_from > 0)
598     {
599       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
600
601       while (n_left_from >= 4 && n_left_to_next >= 2)
602         {
603           u32 bi0, bi1, next0, next1;
604           vlib_buffer_t *b0, *b1;
605           vnet_hw_interface_t *hi0, *hi1;
606
607           /* Prefetch next iteration. */
608           vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
609           vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
610
611           bi0 = from[0];
612           bi1 = from[1];
613           to_next[0] = bi0;
614           to_next[1] = bi1;
615           from += 2;
616           to_next += 2;
617           n_left_to_next -= 2;
618           n_left_from -= 2;
619
620           b0 = vlib_get_buffer (vm, bi0);
621           b1 = vlib_get_buffer (vm, bi1);
622
623           hi0 =
624             vnet_get_sup_hw_interface (vnm,
625                                        vnet_buffer (b0)->sw_if_index
626                                        [VLIB_TX]);
627           hi1 =
628             vnet_get_sup_hw_interface (vnm,
629                                        vnet_buffer (b1)->sw_if_index
630                                        [VLIB_TX]);
631
632           next0 = hi0->hw_if_index;
633           next1 = hi1->hw_if_index;
634
635           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
636                                            n_left_to_next, bi0, bi1, next0,
637                                            next1);
638         }
639
640       while (n_left_from > 0 && n_left_to_next > 0)
641         {
642           u32 bi0, next0;
643           vlib_buffer_t *b0;
644           vnet_hw_interface_t *hi0;
645
646           bi0 = from[0];
647           to_next[0] = bi0;
648           from += 1;
649           to_next += 1;
650           n_left_to_next -= 1;
651           n_left_from -= 1;
652
653           b0 = vlib_get_buffer (vm, bi0);
654
655           hi0 =
656             vnet_get_sup_hw_interface (vnm,
657                                        vnet_buffer (b0)->sw_if_index
658                                        [VLIB_TX]);
659
660           next0 = hi0->hw_if_index;
661
662           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
663                                            n_left_to_next, bi0, next0);
664         }
665
666       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
667     }
668
669   return frame->n_vectors;
670 }
671
672 always_inline u32
673 counter_index (vlib_main_t * vm, vlib_error_t e)
674 {
675   vlib_node_t *n;
676   u32 ci, ni;
677
678   ni = vlib_error_get_node (e);
679   n = vlib_get_node (vm, ni);
680
681   ci = vlib_error_get_code (e);
682   ASSERT (ci < n->n_errors);
683
684   ci += n->error_heap_index;
685
686   return ci;
687 }
688
689 static u8 *
690 format_vnet_error_trace (u8 * s, va_list * va)
691 {
692   vlib_main_t *vm = va_arg (*va, vlib_main_t *);
693   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
694   vlib_error_t *e = va_arg (*va, vlib_error_t *);
695   vlib_node_t *error_node;
696   vlib_error_main_t *em = &vm->error_main;
697   u32 i;
698
699   error_node = vlib_get_node (vm, vlib_error_get_node (e[0]));
700   i = counter_index (vm, e[0]);
701   s = format (s, "%v: %s", error_node->name, em->error_strings_heap[i]);
702
703   return s;
704 }
705
706 static void
707 trace_errors_with_buffers (vlib_main_t * vm,
708                            vlib_node_runtime_t * node, vlib_frame_t * frame)
709 {
710   u32 n_left, *buffers;
711
712   buffers = vlib_frame_vector_args (frame);
713   n_left = frame->n_vectors;
714
715   while (n_left >= 4)
716     {
717       u32 bi0, bi1;
718       vlib_buffer_t *b0, *b1;
719       vlib_error_t *t0, *t1;
720
721       /* Prefetch next iteration. */
722       vlib_prefetch_buffer_with_index (vm, buffers[2], LOAD);
723       vlib_prefetch_buffer_with_index (vm, buffers[3], LOAD);
724
725       bi0 = buffers[0];
726       bi1 = buffers[1];
727
728       b0 = vlib_get_buffer (vm, bi0);
729       b1 = vlib_get_buffer (vm, bi1);
730
731       if (b0->flags & VLIB_BUFFER_IS_TRACED)
732         {
733           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
734           t0[0] = b0->error;
735         }
736       if (b1->flags & VLIB_BUFFER_IS_TRACED)
737         {
738           t1 = vlib_add_trace (vm, node, b1, sizeof (t1[0]));
739           t1[0] = b1->error;
740         }
741       buffers += 2;
742       n_left -= 2;
743     }
744
745   while (n_left >= 1)
746     {
747       u32 bi0;
748       vlib_buffer_t *b0;
749       vlib_error_t *t0;
750
751       bi0 = buffers[0];
752
753       b0 = vlib_get_buffer (vm, bi0);
754
755       if (b0->flags & VLIB_BUFFER_IS_TRACED)
756         {
757           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
758           t0[0] = b0->error;
759         }
760       buffers += 1;
761       n_left -= 1;
762     }
763 }
764
765 static u8 *
766 validate_error (vlib_main_t * vm, vlib_error_t * e, u32 index)
767 {
768   uword node_index = vlib_error_get_node (e[0]);
769   uword code = vlib_error_get_code (e[0]);
770   vlib_node_t *n;
771
772   if (node_index >= vec_len (vm->node_main.nodes))
773     return format (0, "[%d], node index out of range 0x%x, error 0x%x",
774                    index, node_index, e[0]);
775
776   n = vlib_get_node (vm, node_index);
777   if (code >= n->n_errors)
778     return format (0, "[%d], code %d out of range for node %v",
779                    index, code, n->name);
780
781   return 0;
782 }
783
784 static u8 *
785 validate_error_frame (vlib_main_t * vm,
786                       vlib_node_runtime_t * node, vlib_frame_t * f)
787 {
788   u32 *buffers = vlib_frame_args (f);
789   vlib_buffer_t *b;
790   u8 *msg = 0;
791   uword i;
792
793   for (i = 0; i < f->n_vectors; i++)
794     {
795       b = vlib_get_buffer (vm, buffers[i]);
796       msg = validate_error (vm, &b->error, i);
797       if (msg)
798         return msg;
799     }
800
801   return msg;
802 }
803
804 typedef enum
805 {
806   VNET_ERROR_DISPOSITION_DROP,
807   VNET_ERROR_DISPOSITION_PUNT,
808   VNET_ERROR_N_DISPOSITION,
809 } vnet_error_disposition_t;
810
811 always_inline void
812 do_packet (vlib_main_t * vm, vlib_error_t a)
813 {
814   vlib_error_main_t *em = &vm->error_main;
815   u32 i = counter_index (vm, a);
816   em->counters[i] += 1;
817   vlib_error_elog_count (vm, i, 1);
818 }
819
820 static_always_inline uword
821 process_drop_punt (vlib_main_t * vm,
822                    vlib_node_runtime_t * node,
823                    vlib_frame_t * frame, vnet_error_disposition_t disposition)
824 {
825   vnet_main_t *vnm = vnet_get_main ();
826   vlib_error_main_t *em = &vm->error_main;
827   u32 *buffers, *first_buffer;
828   vlib_error_t current_error;
829   u32 current_counter_index, n_errors_left;
830   u32 current_sw_if_index, n_errors_current_sw_if_index;
831   u64 current_counter;
832   vlib_simple_counter_main_t *cm;
833   u32 cpu_index = vm->cpu_index;
834
835   static vlib_error_t memory[VNET_ERROR_N_DISPOSITION];
836   static char memory_init[VNET_ERROR_N_DISPOSITION];
837
838   buffers = vlib_frame_args (frame);
839   first_buffer = buffers;
840
841   {
842     vlib_buffer_t *b = vlib_get_buffer (vm, first_buffer[0]);
843
844     if (!memory_init[disposition])
845       {
846         memory_init[disposition] = 1;
847         memory[disposition] = b->error;
848       }
849
850     current_sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
851     n_errors_current_sw_if_index = 0;
852   }
853
854   current_error = memory[disposition];
855   current_counter_index = counter_index (vm, memory[disposition]);
856   current_counter = em->counters[current_counter_index];
857
858   if (node->flags & VLIB_NODE_FLAG_TRACE)
859     trace_errors_with_buffers (vm, node, frame);
860
861   n_errors_left = frame->n_vectors;
862   cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
863                          (disposition == VNET_ERROR_DISPOSITION_PUNT
864                           ? VNET_INTERFACE_COUNTER_PUNT
865                           : VNET_INTERFACE_COUNTER_DROP));
866
867   while (n_errors_left >= 2)
868     {
869       vlib_buffer_t *b0, *b1;
870       vnet_sw_interface_t *sw_if0, *sw_if1;
871       vlib_error_t e0, e1;
872       u32 bi0, bi1;
873       u32 sw_if_index0, sw_if_index1;
874
875       bi0 = buffers[0];
876       bi1 = buffers[1];
877
878       buffers += 2;
879       n_errors_left -= 2;
880
881       b0 = vlib_get_buffer (vm, bi0);
882       b1 = vlib_get_buffer (vm, bi1);
883
884       e0 = b0->error;
885       e1 = b1->error;
886
887       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
888       sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
889
890       /* Speculate that sw_if_index == sw_if_index[01]. */
891       n_errors_current_sw_if_index += 2;
892
893       /* Speculatively assume all 2 (node, code) pairs are equal
894          to current (node, code). */
895       current_counter += 2;
896
897       if (PREDICT_FALSE (e0 != current_error
898                          || e1 != current_error
899                          || sw_if_index0 != current_sw_if_index
900                          || sw_if_index1 != current_sw_if_index))
901         {
902           current_counter -= 2;
903           n_errors_current_sw_if_index -= 2;
904
905           vlib_increment_simple_counter (cm, cpu_index, sw_if_index0, 1);
906           vlib_increment_simple_counter (cm, cpu_index, sw_if_index1, 1);
907
908           /* Increment super-interface drop/punt counters for
909              sub-interfaces. */
910           sw_if0 = vnet_get_sw_interface (vnm, sw_if_index0);
911           vlib_increment_simple_counter
912             (cm, cpu_index, sw_if0->sup_sw_if_index,
913              sw_if0->sup_sw_if_index != sw_if_index0);
914
915           sw_if1 = vnet_get_sw_interface (vnm, sw_if_index1);
916           vlib_increment_simple_counter
917             (cm, cpu_index, sw_if1->sup_sw_if_index,
918              sw_if1->sup_sw_if_index != sw_if_index1);
919
920           em->counters[current_counter_index] = current_counter;
921           do_packet (vm, e0);
922           do_packet (vm, e1);
923
924           /* For 2 repeated errors, change current error. */
925           if (e0 == e1 && e1 != current_error)
926             {
927               current_error = e0;
928               current_counter_index = counter_index (vm, e0);
929             }
930           current_counter = em->counters[current_counter_index];
931         }
932     }
933
934   while (n_errors_left >= 1)
935     {
936       vlib_buffer_t *b0;
937       vnet_sw_interface_t *sw_if0;
938       vlib_error_t e0;
939       u32 bi0, sw_if_index0;
940
941       bi0 = buffers[0];
942
943       buffers += 1;
944       n_errors_left -= 1;
945       current_counter += 1;
946
947       b0 = vlib_get_buffer (vm, bi0);
948       e0 = b0->error;
949
950       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
951
952       /* Increment drop/punt counters. */
953       vlib_increment_simple_counter (cm, cpu_index, sw_if_index0, 1);
954
955       /* Increment super-interface drop/punt counters for sub-interfaces. */
956       sw_if0 = vnet_get_sw_interface (vnm, sw_if_index0);
957       vlib_increment_simple_counter (cm, cpu_index, sw_if0->sup_sw_if_index,
958                                      sw_if0->sup_sw_if_index != sw_if_index0);
959
960       if (PREDICT_FALSE (e0 != current_error))
961         {
962           current_counter -= 1;
963
964           vlib_error_elog_count (vm, current_counter_index,
965                                  (current_counter
966                                   - em->counters[current_counter_index]));
967
968           em->counters[current_counter_index] = current_counter;
969
970           do_packet (vm, e0);
971           current_error = e0;
972           current_counter_index = counter_index (vm, e0);
973           current_counter = em->counters[current_counter_index];
974         }
975     }
976
977   if (n_errors_current_sw_if_index > 0)
978     {
979       vnet_sw_interface_t *si;
980
981       vlib_increment_simple_counter (cm, cpu_index, current_sw_if_index,
982                                      n_errors_current_sw_if_index);
983
984       si = vnet_get_sw_interface (vnm, current_sw_if_index);
985       if (si->sup_sw_if_index != current_sw_if_index)
986         vlib_increment_simple_counter (cm, cpu_index, si->sup_sw_if_index,
987                                        n_errors_current_sw_if_index);
988     }
989
990   vlib_error_elog_count (vm, current_counter_index,
991                          (current_counter
992                           - em->counters[current_counter_index]));
993
994   /* Return cached counter. */
995   em->counters[current_counter_index] = current_counter;
996
997   /* Save memory for next iteration. */
998   memory[disposition] = current_error;
999
1000   if (disposition == VNET_ERROR_DISPOSITION_DROP || !vm->os_punt_frame)
1001     {
1002       vlib_buffer_free (vm, first_buffer, frame->n_vectors);
1003
1004       /* If there is no punt function, free the frame as well. */
1005       if (disposition == VNET_ERROR_DISPOSITION_PUNT && !vm->os_punt_frame)
1006         vlib_frame_free (vm, node, frame);
1007     }
1008   else
1009     vm->os_punt_frame (vm, node, frame);
1010
1011   return frame->n_vectors;
1012 }
1013
1014 static inline void
1015 pcap_drop_trace (vlib_main_t * vm,
1016                  vnet_interface_main_t * im, vlib_frame_t * f)
1017 {
1018   u32 *from;
1019   u32 n_left = f->n_vectors;
1020   vlib_buffer_t *b0, *p1;
1021   u32 bi0;
1022   i16 save_current_data;
1023   u16 save_current_length;
1024
1025   from = vlib_frame_vector_args (f);
1026
1027   while (n_left > 0)
1028     {
1029       if (PREDICT_TRUE (n_left > 1))
1030         {
1031           p1 = vlib_get_buffer (vm, from[1]);
1032           vlib_prefetch_buffer_header (p1, LOAD);
1033         }
1034
1035       bi0 = from[0];
1036       b0 = vlib_get_buffer (vm, bi0);
1037       from++;
1038       n_left--;
1039
1040       /* See if we're pointedly ignoring this specific error */
1041       if (im->pcap_drop_filter_hash
1042           && hash_get (im->pcap_drop_filter_hash, b0->error))
1043         continue;
1044
1045       /* Trace all drops, or drops received on a specific interface */
1046       if (im->pcap_sw_if_index == 0 ||
1047           im->pcap_sw_if_index == vnet_buffer (b0)->sw_if_index[VLIB_RX])
1048         {
1049           save_current_data = b0->current_data;
1050           save_current_length = b0->current_length;
1051
1052           /*
1053            * Typically, we'll need to rewind the buffer
1054            */
1055           if (b0->current_data > 0)
1056             vlib_buffer_advance (b0, (word) - b0->current_data);
1057
1058           pcap_add_buffer (&im->pcap_main, vm, bi0, 512);
1059
1060           b0->current_data = save_current_data;
1061           b0->current_length = save_current_length;
1062         }
1063     }
1064 }
1065
1066 void
1067 vnet_pcap_drop_trace_filter_add_del (u32 error_index, int is_add)
1068 {
1069   vnet_interface_main_t *im = &vnet_get_main ()->interface_main;
1070
1071   if (im->pcap_drop_filter_hash == 0)
1072     im->pcap_drop_filter_hash = hash_create (0, sizeof (uword));
1073
1074   if (is_add)
1075     hash_set (im->pcap_drop_filter_hash, error_index, 1);
1076   else
1077     hash_unset (im->pcap_drop_filter_hash, error_index);
1078 }
1079
1080 static uword
1081 process_drop (vlib_main_t * vm,
1082               vlib_node_runtime_t * node, vlib_frame_t * frame)
1083 {
1084   vnet_interface_main_t *im = &vnet_get_main ()->interface_main;
1085
1086   if (PREDICT_FALSE (im->drop_pcap_enable))
1087     pcap_drop_trace (vm, im, frame);
1088
1089   return process_drop_punt (vm, node, frame, VNET_ERROR_DISPOSITION_DROP);
1090 }
1091
1092 static uword
1093 process_punt (vlib_main_t * vm,
1094               vlib_node_runtime_t * node, vlib_frame_t * frame)
1095 {
1096   return process_drop_punt (vm, node, frame, VNET_ERROR_DISPOSITION_PUNT);
1097 }
1098
1099 /* *INDENT-OFF* */
1100 VLIB_REGISTER_NODE (drop_buffers,static) = {
1101   .function = process_drop,
1102   .name = "error-drop",
1103   .flags = VLIB_NODE_FLAG_IS_DROP,
1104   .vector_size = sizeof (u32),
1105   .format_trace = format_vnet_error_trace,
1106   .validate_frame = validate_error_frame,
1107 };
1108 /* *INDENT-ON* */
1109
1110 VLIB_NODE_FUNCTION_MULTIARCH (drop_buffers, process_drop);
1111
1112 /* *INDENT-OFF* */
1113 VLIB_REGISTER_NODE (punt_buffers,static) = {
1114   .function = process_punt,
1115   .flags = (VLIB_NODE_FLAG_FRAME_NO_FREE_AFTER_DISPATCH
1116             | VLIB_NODE_FLAG_IS_PUNT),
1117   .name = "error-punt",
1118   .vector_size = sizeof (u32),
1119   .format_trace = format_vnet_error_trace,
1120   .validate_frame = validate_error_frame,
1121 };
1122 /* *INDENT-ON* */
1123
1124 VLIB_NODE_FUNCTION_MULTIARCH (punt_buffers, process_punt);
1125
1126 /* *INDENT-OFF* */
1127 VLIB_REGISTER_NODE (vnet_per_buffer_interface_output_node,static) = {
1128   .function = vnet_per_buffer_interface_output,
1129   .name = "interface-output",
1130   .vector_size = sizeof (u32),
1131 };
1132 /* *INDENT-ON* */
1133
1134 VLIB_NODE_FUNCTION_MULTIARCH (vnet_per_buffer_interface_output_node,
1135                               vnet_per_buffer_interface_output);
1136
1137 clib_error_t *
1138 vnet_per_buffer_interface_output_hw_interface_add_del (vnet_main_t * vnm,
1139                                                        u32 hw_if_index,
1140                                                        u32 is_create)
1141 {
1142   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
1143   u32 next_index;
1144
1145   next_index = vlib_node_add_next_with_slot
1146     (vnm->vlib_main, vnet_per_buffer_interface_output_node.index,
1147      hi->output_node_index,
1148      /* next_index */ hw_if_index);
1149
1150   ASSERT (next_index == hw_if_index);
1151
1152   return 0;
1153 }
1154
1155 VNET_HW_INTERFACE_ADD_DEL_FUNCTION
1156   (vnet_per_buffer_interface_output_hw_interface_add_del);
1157
1158 static clib_error_t *
1159 pcap_drop_trace_command_fn (vlib_main_t * vm,
1160                             unformat_input_t * input,
1161                             vlib_cli_command_t * cmd)
1162 {
1163   vnet_main_t *vnm = vnet_get_main ();
1164   vnet_interface_main_t *im = &vnm->interface_main;
1165   u8 *filename;
1166   u32 max;
1167   int matched = 0;
1168   clib_error_t *error = 0;
1169
1170   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1171     {
1172       if (unformat (input, "on"))
1173         {
1174           if (im->drop_pcap_enable == 0)
1175             {
1176               if (im->pcap_filename == 0)
1177                 im->pcap_filename = format (0, "/tmp/drop.pcap%c", 0);
1178
1179               memset (&im->pcap_main, 0, sizeof (im->pcap_main));
1180               im->pcap_main.file_name = (char *) im->pcap_filename;
1181               im->pcap_main.n_packets_to_capture = 100;
1182               if (im->pcap_pkts_to_capture)
1183                 im->pcap_main.n_packets_to_capture = im->pcap_pkts_to_capture;
1184
1185               im->pcap_main.packet_type = PCAP_PACKET_TYPE_ethernet;
1186               im->drop_pcap_enable = 1;
1187               matched = 1;
1188               vlib_cli_output (vm, "pcap drop capture on...");
1189             }
1190           else
1191             {
1192               vlib_cli_output (vm, "pcap drop capture already on...");
1193             }
1194           matched = 1;
1195         }
1196       else if (unformat (input, "off"))
1197         {
1198           matched = 1;
1199
1200           if (im->drop_pcap_enable)
1201             {
1202               vlib_cli_output (vm, "captured %d pkts...",
1203                                im->pcap_main.n_packets_captured);
1204               if (im->pcap_main.n_packets_captured)
1205                 {
1206                   im->pcap_main.n_packets_to_capture =
1207                     im->pcap_main.n_packets_captured;
1208                   error = pcap_write (&im->pcap_main);
1209                   if (error)
1210                     clib_error_report (error);
1211                   else
1212                     vlib_cli_output (vm, "saved to %s...", im->pcap_filename);
1213                 }
1214             }
1215           else
1216             {
1217               vlib_cli_output (vm, "pcap drop capture already off...");
1218             }
1219
1220           im->drop_pcap_enable = 0;
1221         }
1222       else if (unformat (input, "max %d", &max))
1223         {
1224           im->pcap_pkts_to_capture = max;
1225           matched = 1;
1226         }
1227
1228       else if (unformat (input, "intfc %U",
1229                          unformat_vnet_sw_interface, vnm,
1230                          &im->pcap_sw_if_index))
1231         matched = 1;
1232       else if (unformat (input, "intfc any"))
1233         {
1234           im->pcap_sw_if_index = 0;
1235           matched = 1;
1236         }
1237       else if (unformat (input, "file %s", &filename))
1238         {
1239           u8 *chroot_filename;
1240           /* Brain-police user path input */
1241           if (strstr ((char *) filename, "..")
1242               || index ((char *) filename, '/'))
1243             {
1244               vlib_cli_output (vm, "illegal characters in filename '%s'",
1245                                filename);
1246               continue;
1247             }
1248
1249           chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1250           vec_free (filename);
1251
1252           if (im->pcap_filename)
1253             vec_free (im->pcap_filename);
1254           vec_add1 (filename, 0);
1255           im->pcap_filename = chroot_filename;
1256           matched = 1;
1257         }
1258       else if (unformat (input, "status"))
1259         {
1260           if (im->drop_pcap_enable == 0)
1261             {
1262               vlib_cli_output (vm, "pcap drop capture is off...");
1263               continue;
1264             }
1265
1266           vlib_cli_output (vm, "pcap drop capture: %d of %d pkts...",
1267                            im->pcap_main.n_packets_captured,
1268                            im->pcap_main.n_packets_to_capture);
1269           matched = 1;
1270         }
1271
1272       else
1273         break;
1274     }
1275
1276   if (matched == 0)
1277     return clib_error_return (0, "unknown input `%U'",
1278                               format_unformat_error, input);
1279
1280   return 0;
1281 }
1282
1283 /* *INDENT-OFF* */
1284 VLIB_CLI_COMMAND (pcap_trace_command, static) = {
1285   .path = "pcap drop trace",
1286   .short_help =
1287   "pcap drop trace on off max <nn> intfc <intfc> file <name> status",
1288   .function = pcap_drop_trace_command_fn,
1289 };
1290 /* *INDENT-ON* */
1291
1292 /*
1293  * fd.io coding-style-patch-verification: ON
1294  *
1295  * Local Variables:
1296  * eval: (c-set-style "gnu")
1297  * End:
1298  */