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