session: optimize ct fifo segment allocations
[vpp.git] / src / 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 #include <vnet/ip/icmp46_packet.h>
42 #include <vnet/ethernet/packet.h>
43 #include <vnet/ip/format.h>
44 #include <vnet/ip/ip4.h>
45 #include <vnet/ip/ip6.h>
46 #include <vnet/udp/udp_packet.h>
47 #include <vnet/feature/feature.h>
48 #include <vnet/classify/pcap_classify.h>
49 #include <vnet/interface_output.h>
50 #include <vppinfra/vector_funcs.h>
51
52 typedef struct
53 {
54   u32 sw_if_index;
55   u32 flags;
56   u8 data[128 - 2 * sizeof (u32)];
57 }
58 interface_output_trace_t;
59
60 #ifndef CLIB_MARCH_VARIANT
61 u8 *
62 format_vnet_interface_output_trace (u8 * s, va_list * va)
63 {
64   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
65   vlib_node_t *node = va_arg (*va, vlib_node_t *);
66   interface_output_trace_t *t = va_arg (*va, interface_output_trace_t *);
67   vnet_main_t *vnm = vnet_get_main ();
68   vnet_sw_interface_t *si;
69   u32 indent;
70
71   if (t->sw_if_index != (u32) ~ 0)
72     {
73       indent = format_get_indent (s);
74
75       if (pool_is_free_index
76           (vnm->interface_main.sw_interfaces, t->sw_if_index))
77         {
78           /* the interface may have been deleted by the time the trace is printed */
79           s = format (s, "sw_if_index: %d ", t->sw_if_index);
80         }
81       else
82         {
83           si = vnet_get_sw_interface (vnm, t->sw_if_index);
84           s =
85             format (s, "%U ", format_vnet_sw_interface_name, vnm, si,
86                     t->flags);
87         }
88       s =
89         format (s, "\n%U%U", format_white_space, indent,
90                 node->format_buffer ? node->format_buffer : format_hex_bytes,
91                 t->data, sizeof (t->data));
92     }
93   return s;
94 }
95 #endif /* CLIB_MARCH_VARIANT */
96
97 static void
98 vnet_interface_output_trace (vlib_main_t * vm,
99                              vlib_node_runtime_t * node,
100                              vlib_frame_t * frame, uword n_buffers)
101 {
102   u32 n_left, *from;
103
104   n_left = n_buffers;
105   from = vlib_frame_vector_args (frame);
106
107   while (n_left >= 4)
108     {
109       u32 bi0, bi1;
110       vlib_buffer_t *b0, *b1;
111       interface_output_trace_t *t0, *t1;
112
113       /* Prefetch next iteration. */
114       vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
115       vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
116
117       bi0 = from[0];
118       bi1 = from[1];
119
120       b0 = vlib_get_buffer (vm, bi0);
121       b1 = vlib_get_buffer (vm, bi1);
122
123       if (b0->flags & VLIB_BUFFER_IS_TRACED)
124         {
125           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
126           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
127           t0->flags = b0->flags;
128           clib_memcpy_fast (t0->data, vlib_buffer_get_current (b0),
129                             sizeof (t0->data));
130         }
131       if (b1->flags & VLIB_BUFFER_IS_TRACED)
132         {
133           t1 = vlib_add_trace (vm, node, b1, sizeof (t1[0]));
134           t1->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_TX];
135           t1->flags = b1->flags;
136           clib_memcpy_fast (t1->data, vlib_buffer_get_current (b1),
137                             sizeof (t1->data));
138         }
139       from += 2;
140       n_left -= 2;
141     }
142
143   while (n_left >= 1)
144     {
145       u32 bi0;
146       vlib_buffer_t *b0;
147       interface_output_trace_t *t0;
148
149       bi0 = from[0];
150
151       b0 = vlib_get_buffer (vm, bi0);
152
153       if (b0->flags & VLIB_BUFFER_IS_TRACED)
154         {
155           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
156           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
157           t0->flags = b0->flags;
158           clib_memcpy_fast (t0->data, vlib_buffer_get_current (b0),
159                             sizeof (t0->data));
160         }
161       from += 1;
162       n_left -= 1;
163     }
164 }
165
166 static_always_inline void
167 vnet_interface_output_handle_offload (vlib_main_t *vm, vlib_buffer_t *b)
168 {
169   vnet_calc_checksums_inline (vm, b, b->flags & VNET_BUFFER_F_IS_IP4,
170                               b->flags & VNET_BUFFER_F_IS_IP6);
171 }
172
173 static_always_inline uword
174 vnet_interface_output_node_inline (vlib_main_t *vm, u32 sw_if_index,
175                                    vlib_combined_counter_main_t *ccm,
176                                    vlib_buffer_t **b, u32 config_index, u8 arc,
177                                    u32 n_left, int do_tx_offloads,
178                                    int arc_or_subif)
179 {
180   u32 n_bytes = 0;
181   u32 n_bytes0, n_bytes1, n_bytes2, n_bytes3;
182   u32 ti = vm->thread_index;
183
184   while (n_left >= 8)
185     {
186       u32 or_flags;
187
188       /* Prefetch next iteration. */
189       vlib_prefetch_buffer_header (b[4], LOAD);
190       vlib_prefetch_buffer_header (b[5], LOAD);
191       vlib_prefetch_buffer_header (b[6], LOAD);
192       vlib_prefetch_buffer_header (b[7], LOAD);
193
194       if (do_tx_offloads)
195         or_flags = b[0]->flags | b[1]->flags | b[2]->flags | b[3]->flags;
196
197       /* Be grumpy about zero length buffers for benefit of
198          driver tx function. */
199       ASSERT (b[0]->current_length > 0);
200       ASSERT (b[1]->current_length > 0);
201       ASSERT (b[2]->current_length > 0);
202       ASSERT (b[3]->current_length > 0);
203
204       n_bytes += n_bytes0 = vlib_buffer_length_in_chain (vm, b[0]);
205       n_bytes += n_bytes1 = vlib_buffer_length_in_chain (vm, b[1]);
206       n_bytes += n_bytes2 = vlib_buffer_length_in_chain (vm, b[2]);
207       n_bytes += n_bytes3 = vlib_buffer_length_in_chain (vm, b[3]);
208
209       if (arc_or_subif)
210         {
211           u32 tx_swif0, tx_swif1, tx_swif2, tx_swif3;
212           tx_swif0 = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
213           tx_swif1 = vnet_buffer (b[1])->sw_if_index[VLIB_TX];
214           tx_swif2 = vnet_buffer (b[2])->sw_if_index[VLIB_TX];
215           tx_swif3 = vnet_buffer (b[3])->sw_if_index[VLIB_TX];
216
217           /* update vlan subif tx counts, if required */
218           if (PREDICT_FALSE (tx_swif0 != sw_if_index))
219             vlib_increment_combined_counter (ccm, ti, tx_swif0, 1, n_bytes0);
220
221           if (PREDICT_FALSE (tx_swif1 != sw_if_index))
222             vlib_increment_combined_counter (ccm, ti, tx_swif1, 1, n_bytes1);
223
224           if (PREDICT_FALSE (tx_swif2 != sw_if_index))
225             vlib_increment_combined_counter (ccm, ti, tx_swif2, 1, n_bytes2);
226
227           if (PREDICT_FALSE (tx_swif3 != sw_if_index))
228             vlib_increment_combined_counter (ccm, ti, tx_swif3, 1, n_bytes3);
229
230           if (PREDICT_FALSE (config_index != ~0))
231             {
232               vnet_buffer (b[0])->feature_arc_index = arc;
233               b[0]->current_config_index = config_index;
234               vnet_buffer (b[1])->feature_arc_index = arc;
235               b[1]->current_config_index = config_index;
236               vnet_buffer (b[2])->feature_arc_index = arc;
237               b[2]->current_config_index = config_index;
238               vnet_buffer (b[3])->feature_arc_index = arc;
239               b[3]->current_config_index = config_index;
240             }
241         }
242
243       if (do_tx_offloads && (or_flags & VNET_BUFFER_F_OFFLOAD))
244         {
245           vnet_interface_output_handle_offload (vm, b[0]);
246           vnet_interface_output_handle_offload (vm, b[1]);
247           vnet_interface_output_handle_offload (vm, b[2]);
248           vnet_interface_output_handle_offload (vm, b[3]);
249         }
250
251       n_left -= 4;
252       b += 4;
253     }
254
255   while (n_left)
256     {
257       /* Be grumpy about zero length buffers for benefit of
258          driver tx function. */
259       ASSERT (b[0]->current_length > 0);
260
261       n_bytes += n_bytes0 = vlib_buffer_length_in_chain (vm, b[0]);
262
263       if (arc_or_subif)
264         {
265           u32 tx_swif0 = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
266
267           if (PREDICT_FALSE (config_index != ~0))
268             {
269               vnet_buffer (b[0])->feature_arc_index = arc;
270               b[0]->current_config_index = config_index;
271             }
272
273           if (PREDICT_FALSE (tx_swif0 != sw_if_index))
274             vlib_increment_combined_counter (ccm, ti, tx_swif0, 1, n_bytes0);
275         }
276
277       if (do_tx_offloads)
278         vnet_interface_output_handle_offload (vm, b[0]);
279
280       n_left -= 1;
281       b += 1;
282     }
283
284   return n_bytes;
285 }
286
287 static_always_inline void vnet_interface_pcap_tx_trace
288   (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame,
289    int sw_if_index_from_buffer)
290 {
291   vnet_main_t *vnm = vnet_get_main ();
292   u32 n_left_from, *from;
293   u32 sw_if_index;
294   vnet_pcap_t *pp = &vnm->pcap;
295
296   if (PREDICT_TRUE (pp->pcap_tx_enable == 0))
297     return;
298
299   if (sw_if_index_from_buffer == 0)
300     {
301       vnet_interface_output_runtime_t *rt = (void *) node->runtime_data;
302       sw_if_index = rt->sw_if_index;
303     }
304   else
305     sw_if_index = ~0;
306
307   n_left_from = frame->n_vectors;
308   from = vlib_frame_vector_args (frame);
309
310   while (n_left_from > 0)
311     {
312       u32 bi0 = from[0];
313       vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0);
314       from++;
315       n_left_from--;
316
317       if (sw_if_index_from_buffer)
318         sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
319
320       if (vnet_is_packet_pcaped (pp, b0, sw_if_index))
321         pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt);
322     }
323 }
324
325 static_always_inline void
326 store_tx_frame_scalar_data (vnet_hw_if_output_node_runtime_t *r,
327                             vnet_hw_if_tx_frame_t *tf)
328 {
329   if (r)
330     clib_memcpy_fast (tf, &r->frame, sizeof (vnet_hw_if_tx_frame_t));
331 }
332
333 static_always_inline void
334 enqueu_to_tx_node (vlib_main_t *vm, vlib_node_runtime_t *node,
335                    vnet_hw_interface_t *hi, u32 *from, u32 n_vectors)
336 {
337   u32 next_index = VNET_INTERFACE_OUTPUT_NEXT_TX;
338   vnet_hw_if_output_node_runtime_t *r = 0;
339   u32 n_free, n_copy, *to;
340   vnet_hw_if_tx_frame_t *tf;
341   vlib_frame_t *f;
342
343   ASSERT (n_vectors <= VLIB_FRAME_SIZE);
344
345   if (hi->output_node_thread_runtimes)
346     r = vec_elt_at_index (hi->output_node_thread_runtimes, vm->thread_index);
347
348   f = vlib_get_next_frame_internal (vm, node, next_index, 0);
349   tf = vlib_frame_scalar_args (f);
350
351   if (f->n_vectors > 0 && (r == 0 || tf->queue_id == r->frame.queue_id))
352     {
353       /* append current next frame */
354       n_free = VLIB_FRAME_SIZE - f->n_vectors;
355       n_copy = clib_min (n_vectors, n_free);
356       n_vectors -= n_copy;
357       to = vlib_frame_vector_args (f);
358       to += f->n_vectors;
359     }
360   else
361     {
362       if (f->n_vectors > 0)
363         {
364           /* current frame doesn't fit - grab empty one */
365           f = vlib_get_next_frame_internal (vm, node, next_index, 1);
366           tf = vlib_frame_scalar_args (f);
367         }
368
369       /* empty frame - store scalar data */
370       store_tx_frame_scalar_data (r, tf);
371       to = vlib_frame_vector_args (f);
372       n_free = VLIB_FRAME_SIZE;
373       n_copy = n_vectors;
374       n_vectors = 0;
375     }
376
377   vlib_buffer_copy_indices (to, from, n_copy);
378   vlib_put_next_frame (vm, node, next_index, n_free - n_copy);
379
380   if (n_vectors == 0)
381     return;
382
383   /* we have more indices to store, take empty frame */
384   from += n_copy;
385   f = vlib_get_next_frame_internal (vm, node, next_index, 1);
386   store_tx_frame_scalar_data (r, vlib_frame_scalar_args (f));
387   vlib_buffer_copy_indices (vlib_frame_vector_args (f), from, n_vectors);
388   vlib_put_next_frame (vm, node, next_index, VLIB_FRAME_SIZE - n_vectors);
389 }
390
391 VLIB_NODE_FN (vnet_interface_output_node)
392 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
393 {
394   vnet_main_t *vnm = vnet_get_main ();
395   vnet_interface_main_t *im = &vnm->interface_main;
396   vlib_combined_counter_main_t *ccm;
397   vnet_hw_interface_t *hi;
398   vnet_sw_interface_t *si;
399   vnet_interface_output_runtime_t *rt = (void *) node->runtime_data;
400   vlib_buffer_t *bufs[VLIB_FRAME_SIZE];
401   u32 n_bytes, n_buffers = frame->n_vectors;
402   u32 config_index = ~0;
403   u32 sw_if_index = rt->sw_if_index;
404   u32 next_index = VNET_INTERFACE_OUTPUT_NEXT_TX;
405   u32 ti = vm->thread_index;
406   u8 arc = im->output_feature_arc_index;
407   int arc_or_subif = 0;
408   int do_tx_offloads = 0;
409   u32 *from;
410
411   if (node->flags & VLIB_NODE_FLAG_TRACE)
412     vnet_interface_output_trace (vm, node, frame, n_buffers);
413
414   from = vlib_frame_vector_args (frame);
415
416   if (rt->is_deleted)
417     return vlib_error_drop_buffers (
418       vm, node, from,
419       /* buffer stride */ 1, n_buffers, VNET_INTERFACE_OUTPUT_NEXT_DROP,
420       node->node_index, VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DELETED);
421
422   vnet_interface_pcap_tx_trace (vm, node, frame,
423                                 0 /* sw_if_index_from_buffer */ );
424
425   vlib_get_buffers (vm, from, bufs, n_buffers);
426
427   si = vnet_get_sw_interface (vnm, sw_if_index);
428   hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
429
430   if (!(si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ||
431       !(hi->flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
432     {
433       vlib_simple_counter_main_t *cm;
434
435       cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
436                              VNET_INTERFACE_COUNTER_TX_ERROR);
437       vlib_increment_simple_counter (cm, ti, sw_if_index, n_buffers);
438
439       return vlib_error_drop_buffers (
440         vm, node, from,
441         /* buffer stride */ 1, n_buffers, VNET_INTERFACE_OUTPUT_NEXT_DROP,
442         node->node_index, VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DOWN);
443     }
444
445   /* interface-output feature arc handling */
446   if (PREDICT_FALSE (vnet_have_features (arc, sw_if_index)))
447     {
448       vnet_feature_config_main_t *fcm;
449       fcm = vnet_feature_get_config_main (arc);
450       config_index = vnet_get_feature_config_index (arc, sw_if_index);
451       vnet_get_config_data (&fcm->config_main, &config_index, &next_index, 0);
452       arc_or_subif = 1;
453     }
454   else if (hash_elts (hi->sub_interface_sw_if_index_by_id))
455     arc_or_subif = 1;
456
457   ccm = im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX;
458
459   if ((hi->caps & VNET_HW_INTERFACE_CAP_SUPPORTS_TX_CKSUM) == 0)
460     do_tx_offloads = 1;
461
462   if (do_tx_offloads == 0 && arc_or_subif == 0)
463     n_bytes = vnet_interface_output_node_inline (
464       vm, sw_if_index, ccm, bufs, config_index, arc, n_buffers, 0, 0);
465   else if (do_tx_offloads == 0 && arc_or_subif == 1)
466     n_bytes = vnet_interface_output_node_inline (
467       vm, sw_if_index, ccm, bufs, config_index, arc, n_buffers, 0, 1);
468   else if (do_tx_offloads == 1 && arc_or_subif == 0)
469     n_bytes = vnet_interface_output_node_inline (
470       vm, sw_if_index, ccm, bufs, config_index, arc, n_buffers, 1, 0);
471   else
472     n_bytes = vnet_interface_output_node_inline (
473       vm, sw_if_index, ccm, bufs, config_index, arc, n_buffers, 1, 1);
474
475   from = vlib_frame_vector_args (frame);
476   if (PREDICT_TRUE (next_index == VNET_INTERFACE_OUTPUT_NEXT_TX))
477     {
478       enqueu_to_tx_node (vm, node, hi, from, frame->n_vectors);
479     }
480   else
481     {
482       vlib_buffer_enqueue_to_single_next (vm, node, from, next_index,
483                                           frame->n_vectors);
484     }
485
486   /* Update main interface stats. */
487   vlib_increment_combined_counter (ccm, ti, sw_if_index, n_buffers, n_bytes);
488   return n_buffers;
489 }
490
491 VLIB_REGISTER_NODE (vnet_interface_output_node) = {
492   .name = "interface-output-template",
493   .vector_size = sizeof (u32),
494 };
495
496 /* Use buffer's sw_if_index[VNET_TX] to choose output interface. */
497 VLIB_NODE_FN (vnet_per_buffer_interface_output_node) (vlib_main_t * vm,
498                                                       vlib_node_runtime_t *
499                                                       node,
500                                                       vlib_frame_t * frame)
501 {
502   vnet_main_t *vnm = vnet_get_main ();
503   u32 n_left_to_next, *from, *to_next;
504   u32 n_left_from, next_index;
505
506   vnet_interface_pcap_tx_trace (vm, node, frame,
507                                 1 /* sw_if_index_from_buffer */ );
508
509   n_left_from = frame->n_vectors;
510
511   from = vlib_frame_vector_args (frame);
512   next_index = node->cached_next_index;
513
514   while (n_left_from > 0)
515     {
516       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
517
518       while (n_left_from >= 4 && n_left_to_next >= 2)
519         {
520           u32 bi0, bi1, next0, next1;
521           vlib_buffer_t *b0, *b1;
522           vnet_hw_interface_t *hi0, *hi1;
523
524           /* Prefetch next iteration. */
525           vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
526           vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
527
528           bi0 = from[0];
529           bi1 = from[1];
530           to_next[0] = bi0;
531           to_next[1] = bi1;
532           from += 2;
533           to_next += 2;
534           n_left_to_next -= 2;
535           n_left_from -= 2;
536
537           b0 = vlib_get_buffer (vm, bi0);
538           b1 = vlib_get_buffer (vm, bi1);
539
540           hi0 =
541             vnet_get_sup_hw_interface (vnm,
542                                        vnet_buffer (b0)->sw_if_index
543                                        [VLIB_TX]);
544           hi1 =
545             vnet_get_sup_hw_interface (vnm,
546                                        vnet_buffer (b1)->sw_if_index
547                                        [VLIB_TX]);
548
549           next0 = hi0->output_node_next_index;
550           next1 = hi1->output_node_next_index;
551
552           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
553                                            n_left_to_next, bi0, bi1, next0,
554                                            next1);
555         }
556
557       while (n_left_from > 0 && n_left_to_next > 0)
558         {
559           u32 bi0, next0;
560           vlib_buffer_t *b0;
561           vnet_hw_interface_t *hi0;
562
563           bi0 = from[0];
564           to_next[0] = bi0;
565           from += 1;
566           to_next += 1;
567           n_left_to_next -= 1;
568           n_left_from -= 1;
569
570           b0 = vlib_get_buffer (vm, bi0);
571
572           hi0 =
573             vnet_get_sup_hw_interface (vnm,
574                                        vnet_buffer (b0)->sw_if_index
575                                        [VLIB_TX]);
576
577           next0 = hi0->output_node_next_index;
578
579           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
580                                            n_left_to_next, bi0, next0);
581         }
582
583       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
584     }
585
586   return frame->n_vectors;
587 }
588
589 typedef struct vnet_error_trace_t_
590 {
591   u32 sw_if_index;
592   i8 details_valid;
593   u8 is_ip6;
594   u8 pad[2];
595   u16 mactype;
596   ip46_address_t src, dst;
597 } vnet_error_trace_t;
598
599 static u8 *
600 format_vnet_error_trace (u8 * s, va_list * va)
601 {
602   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
603   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
604   vnet_error_trace_t *t = va_arg (*va, vnet_error_trace_t *);
605
606   /* Normal, non-catchup trace */
607   if (t->details_valid == 0)
608     {
609       s = format (s, "rx:%U", format_vnet_sw_if_index_name,
610                   vnet_get_main (), t->sw_if_index);
611     }
612   else if (t->details_valid == 1)
613     {
614       /* The trace capture code didn't understant the mactype */
615       s = format (s, "mactype 0x%4x (not decoded)", t->mactype);
616     }
617   else if (t->details_valid == 2)
618     {
619       /* Dump the src/dst addresses */
620       if (t->is_ip6 == 0)
621         s = format (s, "IP4: %U -> %U",
622                     format_ip4_address, &t->src.ip4,
623                     format_ip4_address, &t->dst.ip4);
624       else
625         s = format (s, "IP6: %U -> %U",
626                     format_ip6_address, &t->src.ip6,
627                     format_ip6_address, &t->dst.ip6);
628     }
629   return s;
630 }
631
632 static void
633 interface_trace_buffers (vlib_main_t * vm,
634                          vlib_node_runtime_t * node, vlib_frame_t * frame)
635 {
636   u32 n_left, *buffers;
637
638   buffers = vlib_frame_vector_args (frame);
639   n_left = frame->n_vectors;
640
641   while (n_left >= 4)
642     {
643       u32 bi0, bi1;
644       vlib_buffer_t *b0, *b1;
645       vnet_error_trace_t *t0, *t1;
646
647       /* Prefetch next iteration. */
648       vlib_prefetch_buffer_with_index (vm, buffers[2], LOAD);
649       vlib_prefetch_buffer_with_index (vm, buffers[3], LOAD);
650
651       bi0 = buffers[0];
652       bi1 = buffers[1];
653
654       b0 = vlib_get_buffer (vm, bi0);
655       b1 = vlib_get_buffer (vm, bi1);
656
657       if (b0->flags & VLIB_BUFFER_IS_TRACED)
658         {
659           t0 = vlib_add_trace (vm, node, b0,
660                                STRUCT_OFFSET_OF (vnet_error_trace_t, pad));
661           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
662           t0->details_valid = 0;
663         }
664       if (b1->flags & VLIB_BUFFER_IS_TRACED)
665         {
666           t1 = vlib_add_trace (vm, node, b1,
667                                STRUCT_OFFSET_OF (vnet_error_trace_t, pad));
668           t1->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX];
669           t1->details_valid = 0;
670         }
671       buffers += 2;
672       n_left -= 2;
673     }
674
675   while (n_left >= 1)
676     {
677       u32 bi0;
678       vlib_buffer_t *b0;
679       vnet_error_trace_t *t0;
680
681       bi0 = buffers[0];
682
683       b0 = vlib_get_buffer (vm, bi0);
684
685       if (b0->flags & VLIB_BUFFER_IS_TRACED)
686         {
687           t0 = vlib_add_trace (vm, node, b0,
688                                STRUCT_OFFSET_OF (vnet_error_trace_t, pad));
689           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
690           t0->details_valid = 0;
691         }
692       buffers += 1;
693       n_left -= 1;
694     }
695 }
696
697 typedef enum
698 {
699   VNET_ERROR_DISPOSITION_DROP,
700   VNET_ERROR_DISPOSITION_PUNT,
701   VNET_ERROR_N_DISPOSITION,
702 } vnet_error_disposition_t;
703
704 static void
705 drop_catchup_trace (vlib_main_t * vm,
706                     vlib_node_runtime_t * node, vlib_buffer_t * b)
707 {
708   /* Can we safely rewind the buffer? If not, fagedaboudit */
709   if (b->flags & VNET_BUFFER_F_L2_HDR_OFFSET_VALID)
710     {
711       vnet_error_trace_t *t;
712       ip4_header_t *ip4;
713       ip6_header_t *ip6;
714       ethernet_header_t *eh;
715       i16 delta;
716
717       t = vlib_add_trace (vm, node, b, sizeof (*t));
718       delta = vnet_buffer (b)->l2_hdr_offset - b->current_data;
719       vlib_buffer_advance (b, delta);
720
721       eh = vlib_buffer_get_current (b);
722       /* Save mactype */
723       t->mactype = clib_net_to_host_u16 (eh->type);
724       t->details_valid = 1;
725       switch (t->mactype)
726         {
727         case ETHERNET_TYPE_IP4:
728           ip4 = (void *) (eh + 1);
729           t->details_valid = 2;
730           t->is_ip6 = 0;
731           t->src.ip4.as_u32 = ip4->src_address.as_u32;
732           t->dst.ip4.as_u32 = ip4->dst_address.as_u32;
733           break;
734
735         case ETHERNET_TYPE_IP6:
736           ip6 = (void *) (eh + 1);
737           t->details_valid = 2;
738           t->is_ip6 = 1;
739           clib_memcpy_fast (t->src.as_u8, ip6->src_address.as_u8,
740                             sizeof (ip6_address_t));
741           clib_memcpy_fast (t->dst.as_u8, ip6->dst_address.as_u8,
742                             sizeof (ip6_address_t));
743           break;
744
745         default:
746           /* Dunno, do nothing, leave details_valid alone */
747           break;
748         }
749       /* Restore current data (probably unnecessary) */
750       vlib_buffer_advance (b, -delta);
751     }
752 }
753
754 static_always_inline uword
755 interface_drop_punt (vlib_main_t * vm,
756                      vlib_node_runtime_t * node,
757                      vlib_frame_t * frame,
758                      vnet_error_disposition_t disposition)
759 {
760   u32 *from, n_left, thread_index, *sw_if_index;
761   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
762   u32 sw_if_indices[VLIB_FRAME_SIZE];
763   vlib_simple_counter_main_t *cm;
764   u16 nexts[VLIB_FRAME_SIZE];
765   u32 n_trace;
766   vnet_main_t *vnm;
767
768   vnm = vnet_get_main ();
769   thread_index = vm->thread_index;
770   from = vlib_frame_vector_args (frame);
771   n_left = frame->n_vectors;
772   b = bufs;
773   sw_if_index = sw_if_indices;
774
775   vlib_get_buffers (vm, from, bufs, n_left);
776
777   /* "trace add error-drop NNN?" */
778   if (PREDICT_FALSE ((n_trace = vlib_get_trace_count (vm, node))))
779     {
780       /* If pkts aren't otherwise traced... */
781       if ((node->flags & VLIB_NODE_FLAG_TRACE) == 0)
782         {
783           /* Trace them from here */
784           node->flags |= VLIB_NODE_FLAG_TRACE;
785           while (n_trace && n_left)
786             {
787               if (PREDICT_TRUE
788                   (vlib_trace_buffer (vm, node, 0 /* next_index */ , b[0],
789                                       0 /* follow chain */ )))
790                 {
791                   /*
792                    * Here we have a wireshark dissector problem.
793                    * Packets may be well-formed, or not. We
794                    * must not blow chunks in any case.
795                    *
796                    * Try to produce trace records which will help
797                    * folks understand what's going on.
798                    */
799                   drop_catchup_trace (vm, node, b[0]);
800                   n_trace--;
801                 }
802               n_left--;
803               b++;
804             }
805         }
806
807       vlib_set_trace_count (vm, node, n_trace);
808       b = bufs;
809       n_left = frame->n_vectors;
810     }
811
812   if (node->flags & VLIB_NODE_FLAG_TRACE)
813     interface_trace_buffers (vm, node, frame);
814
815   /* All going to drop regardless, this is just a counting exercise */
816   clib_memset (nexts, 0, sizeof (nexts));
817
818   cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
819                          (disposition == VNET_ERROR_DISPOSITION_PUNT
820                           ? VNET_INTERFACE_COUNTER_PUNT
821                           : VNET_INTERFACE_COUNTER_DROP));
822
823   /* collect the array of interfaces first ... */
824   while (n_left >= 4)
825     {
826       if (n_left >= 12)
827         {
828           /* Prefetch 8 ahead - there's not much going on in each iteration */
829           vlib_prefetch_buffer_header (b[4], LOAD);
830           vlib_prefetch_buffer_header (b[5], LOAD);
831           vlib_prefetch_buffer_header (b[6], LOAD);
832           vlib_prefetch_buffer_header (b[7], LOAD);
833         }
834       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
835       sw_if_index[1] = vnet_buffer (b[1])->sw_if_index[VLIB_RX];
836       sw_if_index[2] = vnet_buffer (b[2])->sw_if_index[VLIB_RX];
837       sw_if_index[3] = vnet_buffer (b[3])->sw_if_index[VLIB_RX];
838
839       sw_if_index += 4;
840       n_left -= 4;
841       b += 4;
842     }
843   while (n_left)
844     {
845       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
846
847       sw_if_index += 1;
848       n_left -= 1;
849       b += 1;
850     }
851
852   /* ... then count against them in blocks */
853   n_left = frame->n_vectors;
854
855   while (n_left)
856     {
857       vnet_sw_interface_t *sw_if0;
858       u16 off, count;
859
860       off = frame->n_vectors - n_left;
861
862       sw_if_index = sw_if_indices + off;
863
864       count = clib_count_equal_u32 (sw_if_index, n_left);
865       n_left -= count;
866
867       vlib_increment_simple_counter (cm, thread_index, sw_if_index[0], count);
868
869       /* Increment super-interface drop/punt counters for
870          sub-interfaces. */
871       sw_if0 = vnet_get_sw_interface (vnm, sw_if_index[0]);
872       if (sw_if0->sup_sw_if_index != sw_if_index[0])
873         vlib_increment_simple_counter
874           (cm, thread_index, sw_if0->sup_sw_if_index, count);
875     }
876
877   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
878
879   return frame->n_vectors;
880 }
881
882 static inline void
883 pcap_drop_trace (vlib_main_t * vm,
884                  vnet_interface_main_t * im,
885                  vnet_pcap_t * pp, vlib_frame_t * f)
886 {
887   u32 *from;
888   u32 n_left = f->n_vectors;
889   vlib_buffer_t *b0, *p1;
890   u32 bi0;
891   i16 save_current_data;
892   u16 save_current_length;
893   vlib_error_main_t *em = &vm->error_main;
894
895   from = vlib_frame_vector_args (f);
896
897   while (n_left > 0)
898     {
899       if (PREDICT_TRUE (n_left > 1))
900         {
901           p1 = vlib_get_buffer (vm, from[1]);
902           vlib_prefetch_buffer_header (p1, LOAD);
903         }
904
905       bi0 = from[0];
906       b0 = vlib_get_buffer (vm, bi0);
907       from++;
908       n_left--;
909
910       /* See if we're pointedly ignoring this specific error */
911       if (im->pcap_drop_filter_hash
912           && hash_get (im->pcap_drop_filter_hash, b0->error))
913         continue;
914
915       if (!vnet_is_packet_pcaped (pp, b0, ~0))
916         continue; /* not matching, skip */
917
918       /* Trace all drops, or drops received on a specific interface */
919       save_current_data = b0->current_data;
920       save_current_length = b0->current_length;
921
922       /*
923        * Typically, we'll need to rewind the buffer
924        * if l2_hdr_offset is valid, make sure to rewind to the start of
925        * the L2 header. This may not be the buffer start in case we pop-ed
926        * vlan tags.
927        * Otherwise, rewind to buffer start and hope for the best.
928        */
929       if (b0->flags & VNET_BUFFER_F_L2_HDR_OFFSET_VALID)
930         {
931           if (b0->current_data > vnet_buffer (b0)->l2_hdr_offset)
932             vlib_buffer_advance (b0, vnet_buffer (b0)->l2_hdr_offset -
933                                        b0->current_data);
934         }
935       else if (b0->current_data > 0)
936         {
937           vlib_buffer_advance (b0, (word) -b0->current_data);
938         }
939
940       {
941         vlib_buffer_t *last = b0;
942         u32 error_node_index;
943         int drop_string_len;
944         vlib_node_t *n;
945         /* Length of the error string */
946         int error_string_len =
947           clib_strnlen (em->counters_heap[b0->error].name, 128);
948
949         /* Dig up the drop node */
950         error_node_index = vm->node_main.node_by_error[b0->error];
951         n = vlib_get_node (vm, error_node_index);
952
953         /* Length of full drop string, w/ "nodename: " prepended */
954         drop_string_len = error_string_len + vec_len (n->name) + 2;
955
956         /* Find the last buffer in the chain */
957         while (last->flags & VLIB_BUFFER_NEXT_PRESENT)
958           last = vlib_get_buffer (vm, last->next_buffer);
959
960         /*
961          * Append <nodename>: <error-string> to the capture,
962          * only if we can do that without allocating a new buffer.
963          */
964         if (PREDICT_TRUE ((last->current_data + last->current_length) <
965                           (VLIB_BUFFER_DEFAULT_DATA_SIZE - drop_string_len)))
966           {
967             clib_memcpy_fast (last->data + last->current_data +
968                                 last->current_length,
969                               n->name, vec_len (n->name));
970             clib_memcpy_fast (last->data + last->current_data +
971                                 last->current_length + vec_len (n->name),
972                               ": ", 2);
973             clib_memcpy_fast (last->data + last->current_data +
974                                 last->current_length + vec_len (n->name) + 2,
975                               em->counters_heap[b0->error].name,
976                               error_string_len);
977             last->current_length += drop_string_len;
978             b0->flags &= ~(VLIB_BUFFER_TOTAL_LENGTH_VALID);
979             pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt);
980             last->current_length -= drop_string_len;
981             b0->current_data = save_current_data;
982             b0->current_length = save_current_length;
983             continue;
984           }
985       }
986
987       /*
988        * Didn't have space in the last buffer, here's the dropped
989        * packet as-is
990        */
991       pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt);
992
993       b0->current_data = save_current_data;
994       b0->current_length = save_current_length;
995     }
996 }
997
998 #ifndef CLIB_MARCH_VARIANT
999 void
1000 vnet_pcap_drop_trace_filter_add_del (u32 error_index, int is_add)
1001 {
1002   vnet_interface_main_t *im = &vnet_get_main ()->interface_main;
1003
1004   if (im->pcap_drop_filter_hash == 0)
1005     im->pcap_drop_filter_hash = hash_create (0, sizeof (uword));
1006
1007   if (is_add)
1008     hash_set (im->pcap_drop_filter_hash, error_index, 1);
1009   else
1010     hash_unset (im->pcap_drop_filter_hash, error_index);
1011 }
1012 #endif /* CLIB_MARCH_VARIANT */
1013
1014 VLIB_NODE_FN (interface_drop) (vlib_main_t * vm,
1015                                vlib_node_runtime_t * node,
1016                                vlib_frame_t * frame)
1017 {
1018   vnet_main_t *vnm = vnet_get_main ();
1019   vnet_interface_main_t *im = &vnet_get_main ()->interface_main;
1020   vnet_pcap_t *pp = &vnm->pcap;
1021
1022   if (PREDICT_FALSE (pp->pcap_drop_enable))
1023     pcap_drop_trace (vm, im, pp, frame);
1024
1025   return interface_drop_punt (vm, node, frame, VNET_ERROR_DISPOSITION_DROP);
1026 }
1027
1028 VLIB_NODE_FN (interface_punt) (vlib_main_t * vm,
1029                                vlib_node_runtime_t * node,
1030                                vlib_frame_t * frame)
1031 {
1032   return interface_drop_punt (vm, node, frame, VNET_ERROR_DISPOSITION_PUNT);
1033 }
1034
1035 /* *INDENT-OFF* */
1036 VLIB_REGISTER_NODE (interface_drop) = {
1037   .name = "error-drop",
1038   .vector_size = sizeof (u32),
1039   .format_trace = format_vnet_error_trace,
1040   .flags = VLIB_NODE_FLAG_TRACE_SUPPORTED,
1041   .n_next_nodes = 1,
1042   .next_nodes = {
1043     [0] = "drop",
1044   },
1045 };
1046 /* *INDENT-ON* */
1047
1048 /* *INDENT-OFF* */
1049 VLIB_REGISTER_NODE (interface_punt) = {
1050   .name = "error-punt",
1051   .vector_size = sizeof (u32),
1052   .format_trace = format_vnet_error_trace,
1053   .flags = VLIB_NODE_FLAG_TRACE_SUPPORTED,
1054   .n_next_nodes = 1,
1055   .next_nodes = {
1056     [0] = "punt",
1057   },
1058 };
1059 /* *INDENT-ON* */
1060
1061 VLIB_REGISTER_NODE (vnet_per_buffer_interface_output_node) = {
1062   .name = "interface-output",
1063   .vector_size = sizeof (u32),
1064 };
1065
1066 VLIB_NODE_FN (vnet_interface_output_arc_end_node)
1067 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
1068 {
1069   vnet_main_t *vnm = vnet_get_main ();
1070   vnet_interface_main_t *im = &vnm->interface_main;
1071   vnet_hw_if_output_node_runtime_t *r = 0;
1072   vnet_hw_interface_t *hi;
1073   vnet_hw_if_tx_frame_t *tf;
1074   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
1075   u32 sw_if_indices[VLIB_FRAME_SIZE], *sw_if_index = sw_if_indices;
1076   u64 used_elts[VLIB_FRAME_SIZE / 64] = {};
1077   u64 mask[VLIB_FRAME_SIZE / 64] = {};
1078   u32 *tmp, *from, n_left, n_free, n_comp, *to, swif, off;
1079   u16 next_index;
1080   vlib_frame_t *f;
1081
1082   from = vlib_frame_vector_args (frame);
1083   n_left = frame->n_vectors;
1084   vlib_get_buffers (vm, from, bufs, n_left);
1085
1086   while (n_left >= 8)
1087     {
1088       vlib_prefetch_buffer_header (b[4], LOAD);
1089       vlib_prefetch_buffer_header (b[5], LOAD);
1090       vlib_prefetch_buffer_header (b[6], LOAD);
1091       vlib_prefetch_buffer_header (b[7], LOAD);
1092       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
1093       sw_if_index[1] = vnet_buffer (b[1])->sw_if_index[VLIB_TX];
1094       sw_if_index[2] = vnet_buffer (b[2])->sw_if_index[VLIB_TX];
1095       sw_if_index[3] = vnet_buffer (b[3])->sw_if_index[VLIB_TX];
1096
1097       b += 4;
1098       sw_if_index += 4;
1099       n_left -= 4;
1100     }
1101
1102   while (n_left)
1103     {
1104       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
1105       b++;
1106       sw_if_index++;
1107       n_left--;
1108     }
1109
1110   n_left = frame->n_vectors;
1111   swif = sw_if_indices[0];
1112   off = 0;
1113
1114   /* a bit ugly but it allows us to reuse stack space for temporary store
1115    * which may also improve memory latency */
1116   tmp = (u32 *) bufs;
1117
1118 more:
1119   next_index = vec_elt (im->if_out_arc_end_next_index_by_sw_if_index, swif);
1120   hi = vnet_get_sup_hw_interface (vnm, swif);
1121   if (hi->output_node_thread_runtimes)
1122     r = vec_elt_at_index (hi->output_node_thread_runtimes, vm->thread_index);
1123   f = vlib_get_next_frame_internal (vm, node, next_index, 0);
1124   tf = vlib_frame_scalar_args (f);
1125
1126   if (f->n_vectors > 0 && (r == 0 || r->frame.queue_id == tf->queue_id))
1127     {
1128       /* append frame */
1129       n_free = VLIB_FRAME_SIZE - f->n_vectors;
1130       if (n_free >= f->n_vectors)
1131         to = (u32 *) vlib_frame_vector_args (f) + f->n_vectors;
1132       else
1133         to = tmp;
1134     }
1135   else
1136     {
1137       if (f->n_vectors > 0)
1138         {
1139           /* current frame doesn't fit - grab empty one */
1140           f = vlib_get_next_frame_internal (vm, node, next_index, 1);
1141           tf = vlib_frame_scalar_args (f);
1142         }
1143
1144       /* empty frame - store scalar data */
1145       store_tx_frame_scalar_data (r, tf);
1146       n_free = VLIB_FRAME_SIZE;
1147       to = vlib_frame_vector_args (f);
1148     }
1149
1150   /* compare and compress based on comparison mask */
1151   clib_mask_compare_u32 (swif, sw_if_indices, mask, frame->n_vectors);
1152   n_comp = clib_compress_u32 (to, from, mask, frame->n_vectors);
1153
1154   if (tmp != to)
1155     {
1156       /* indices already written to frame, just close it */
1157       vlib_put_next_frame (vm, node, next_index, n_free - n_comp);
1158     }
1159   else if (n_free >= n_comp)
1160     {
1161       /* enough space in the existing frame */
1162       to = (u32 *) vlib_frame_vector_args (f) + f->n_vectors;
1163       vlib_buffer_copy_indices (to, tmp, n_comp);
1164       vlib_put_next_frame (vm, node, next_index, n_free - n_comp);
1165     }
1166   else
1167     {
1168       /* full frame */
1169       to = (u32 *) vlib_frame_vector_args (f) + f->n_vectors;
1170       vlib_buffer_copy_indices (to, tmp, n_free);
1171       vlib_put_next_frame (vm, node, next_index, 0);
1172
1173       /* second frame */
1174       u32 n_frame2 = n_comp - n_free;
1175       f = vlib_get_next_frame_internal (vm, node, next_index, 1);
1176       to = vlib_frame_vector_args (f);
1177       vlib_buffer_copy_indices (to, tmp + n_free, n_frame2);
1178       tf = vlib_frame_scalar_args (f);
1179       store_tx_frame_scalar_data (r, tf);
1180       vlib_put_next_frame (vm, node, next_index, VLIB_FRAME_SIZE - n_frame2);
1181     }
1182
1183   n_left -= n_comp;
1184   if (n_left)
1185     {
1186       /* store comparison mask so we can find next unused element */
1187       for (int i = 0; i < ARRAY_LEN (used_elts); i++)
1188         used_elts[i] |= mask[i];
1189
1190       /* fine first unused sw_if_index by scanning trough used_elts bitmap */
1191       while (PREDICT_FALSE (used_elts[off] == ~0))
1192         off++;
1193
1194       swif =
1195         sw_if_indices[(off << 6) + count_trailing_zeros (~used_elts[off])];
1196       goto more;
1197     }
1198
1199   return frame->n_vectors;
1200 }
1201
1202 VLIB_REGISTER_NODE (vnet_interface_output_arc_end_node) = {
1203   .name = "interface-output-arc-end",
1204   .vector_size = sizeof (u32),
1205   .n_next_nodes = 1,
1206   .next_nodes = {
1207     [0] = "error-drop",
1208   },
1209 };
1210
1211 VNET_FEATURE_ARC_INIT (interface_output, static) = {
1212   .arc_name = "interface-output",
1213   .start_nodes = VNET_FEATURES (0),
1214   .last_in_arc = "interface-output-arc-end",
1215   .arc_index_ptr = &vnet_main.interface_main.output_feature_arc_index,
1216 };
1217
1218 VNET_FEATURE_INIT (span_tx, static) = {
1219   .arc_name = "interface-output",
1220   .node_name = "span-output",
1221   .runs_before = VNET_FEATURES ("interface-output-arc-end"),
1222 };
1223
1224 VNET_FEATURE_INIT (ipsec_if_tx, static) = {
1225   .arc_name = "interface-output",
1226   .node_name = "ipsec-if-output",
1227   .runs_before = VNET_FEATURES ("interface-output-arc-end"),
1228 };
1229
1230 VNET_FEATURE_INIT (interface_output_arc_end, static) = {
1231   .arc_name = "interface-output",
1232   .node_name = "interface-output-arc-end",
1233   .runs_before = 0,
1234 };
1235
1236 #ifndef CLIB_MARCH_VARIANT
1237 clib_error_t *
1238 vnet_per_buffer_interface_output_hw_interface_add_del (vnet_main_t * vnm,
1239                                                        u32 hw_if_index,
1240                                                        u32 is_create)
1241 {
1242   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
1243   u32 next_index;
1244
1245   if (hi->output_node_index == 0)
1246     return 0;
1247
1248   next_index = vlib_node_add_next
1249     (vnm->vlib_main, vnet_per_buffer_interface_output_node.index,
1250      hi->output_node_index);
1251   hi->output_node_next_index = next_index;
1252
1253   return 0;
1254 }
1255
1256 VNET_HW_INTERFACE_ADD_DEL_FUNCTION
1257   (vnet_per_buffer_interface_output_hw_interface_add_del);
1258
1259 void
1260 vnet_set_interface_output_node (vnet_main_t * vnm,
1261                                 u32 hw_if_index, u32 node_index)
1262 {
1263   ASSERT (node_index);
1264   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
1265   u32 next_index = vlib_node_add_next
1266     (vnm->vlib_main, vnet_per_buffer_interface_output_node.index, node_index);
1267   hi->output_node_next_index = next_index;
1268   hi->output_node_index = node_index;
1269 }
1270 #endif /* CLIB_MARCH_VARIANT */
1271
1272 /*
1273  * fd.io coding-style-patch-verification: ON
1274  *
1275  * Local Variables:
1276  * eval: (c-set-style "gnu")
1277  * End:
1278  */