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