c4a4b0f228d576d1f0093bcf81f1eea4b50898a9
[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 not all three flags IP4_,TCP_,UDP_CKSUM set, do compute them
479    * here before sending to the interface */
480   if ((hi->caps & VNET_HW_INTERFACE_CAP_SUPPORTS_TX_CKSUM) !=
481       VNET_HW_INTERFACE_CAP_SUPPORTS_TX_CKSUM)
482     do_tx_offloads = 1;
483
484   if (do_tx_offloads == 0 && arc_or_subif == 0)
485     n_bytes = vnet_interface_output_node_inline (
486       vm, sw_if_index, ccm, bufs, config_index, arc, n_buffers, 0, 0);
487   else if (do_tx_offloads == 0 && arc_or_subif == 1)
488     n_bytes = vnet_interface_output_node_inline (
489       vm, sw_if_index, ccm, bufs, config_index, arc, n_buffers, 0, 1);
490   else if (do_tx_offloads == 1 && arc_or_subif == 0)
491     n_bytes = vnet_interface_output_node_inline (
492       vm, sw_if_index, ccm, bufs, config_index, arc, n_buffers, 1, 0);
493   else
494     n_bytes = vnet_interface_output_node_inline (
495       vm, sw_if_index, ccm, bufs, config_index, arc, n_buffers, 1, 1);
496
497   from = vlib_frame_vector_args (frame);
498   if (PREDICT_TRUE (next_index == VNET_INTERFACE_OUTPUT_NEXT_TX))
499     {
500       enqueu_to_tx_node (vm, node, hi, from, frame->n_vectors);
501     }
502   else
503     {
504       vlib_buffer_enqueue_to_single_next (vm, node, from, next_index,
505                                           frame->n_vectors);
506     }
507
508   /* Update main interface stats. */
509   vlib_increment_combined_counter (ccm, ti, sw_if_index, n_buffers, n_bytes);
510   return n_buffers;
511 }
512
513 VLIB_REGISTER_NODE (vnet_interface_output_node) = {
514   .name = "interface-output-template",
515   .vector_size = sizeof (u32),
516 };
517
518 /* Use buffer's sw_if_index[VNET_TX] to choose output interface. */
519 VLIB_NODE_FN (vnet_per_buffer_interface_output_node) (vlib_main_t * vm,
520                                                       vlib_node_runtime_t *
521                                                       node,
522                                                       vlib_frame_t * frame)
523 {
524   vnet_main_t *vnm = vnet_get_main ();
525   u32 n_left_to_next, *from, *to_next;
526   u32 n_left_from, next_index;
527
528   vnet_interface_pcap_tx_trace (vm, node, frame, 1 /* in_interface_ouput */);
529
530   n_left_from = frame->n_vectors;
531
532   from = vlib_frame_vector_args (frame);
533   next_index = node->cached_next_index;
534
535   while (n_left_from > 0)
536     {
537       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
538
539       while (n_left_from >= 4 && n_left_to_next >= 2)
540         {
541           u32 bi0, bi1, next0, next1;
542           vlib_buffer_t *b0, *b1;
543           vnet_hw_interface_t *hi0, *hi1;
544
545           /* Prefetch next iteration. */
546           vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
547           vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
548
549           bi0 = from[0];
550           bi1 = from[1];
551           to_next[0] = bi0;
552           to_next[1] = bi1;
553           from += 2;
554           to_next += 2;
555           n_left_to_next -= 2;
556           n_left_from -= 2;
557
558           b0 = vlib_get_buffer (vm, bi0);
559           b1 = vlib_get_buffer (vm, bi1);
560
561           hi0 =
562             vnet_get_sup_hw_interface (vnm,
563                                        vnet_buffer (b0)->sw_if_index
564                                        [VLIB_TX]);
565           hi1 =
566             vnet_get_sup_hw_interface (vnm,
567                                        vnet_buffer (b1)->sw_if_index
568                                        [VLIB_TX]);
569
570           next0 = hi0->output_node_next_index;
571           next1 = hi1->output_node_next_index;
572
573           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
574                                            n_left_to_next, bi0, bi1, next0,
575                                            next1);
576         }
577
578       while (n_left_from > 0 && n_left_to_next > 0)
579         {
580           u32 bi0, next0;
581           vlib_buffer_t *b0;
582           vnet_hw_interface_t *hi0;
583
584           bi0 = from[0];
585           to_next[0] = bi0;
586           from += 1;
587           to_next += 1;
588           n_left_to_next -= 1;
589           n_left_from -= 1;
590
591           b0 = vlib_get_buffer (vm, bi0);
592
593           hi0 =
594             vnet_get_sup_hw_interface (vnm,
595                                        vnet_buffer (b0)->sw_if_index
596                                        [VLIB_TX]);
597
598           next0 = hi0->output_node_next_index;
599
600           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
601                                            n_left_to_next, bi0, next0);
602         }
603
604       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
605     }
606
607   return frame->n_vectors;
608 }
609
610 typedef struct vnet_error_trace_t_
611 {
612   u32 sw_if_index;
613   i8 details_valid;
614   u8 is_ip6;
615   u8 pad[2];
616   u16 mactype;
617   ip46_address_t src, dst;
618 } vnet_error_trace_t;
619
620 static u8 *
621 format_vnet_error_trace (u8 * s, va_list * va)
622 {
623   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
624   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
625   vnet_error_trace_t *t = va_arg (*va, vnet_error_trace_t *);
626
627   /* Normal, non-catchup trace */
628   if (t->details_valid == 0)
629     {
630       s = format (s, "rx:%U", format_vnet_sw_if_index_name,
631                   vnet_get_main (), t->sw_if_index);
632     }
633   else if (t->details_valid == 1)
634     {
635       /* The trace capture code didn't understant the mactype */
636       s = format (s, "mactype 0x%4x (not decoded)", t->mactype);
637     }
638   else if (t->details_valid == 2)
639     {
640       /* Dump the src/dst addresses */
641       if (t->is_ip6 == 0)
642         s = format (s, "IP4: %U -> %U",
643                     format_ip4_address, &t->src.ip4,
644                     format_ip4_address, &t->dst.ip4);
645       else
646         s = format (s, "IP6: %U -> %U",
647                     format_ip6_address, &t->src.ip6,
648                     format_ip6_address, &t->dst.ip6);
649     }
650   return s;
651 }
652
653 static void
654 interface_trace_buffers (vlib_main_t * vm,
655                          vlib_node_runtime_t * node, vlib_frame_t * frame)
656 {
657   u32 n_left, *buffers;
658
659   buffers = vlib_frame_vector_args (frame);
660   n_left = frame->n_vectors;
661
662   while (n_left >= 4)
663     {
664       u32 bi0, bi1;
665       vlib_buffer_t *b0, *b1;
666       vnet_error_trace_t *t0, *t1;
667
668       /* Prefetch next iteration. */
669       vlib_prefetch_buffer_with_index (vm, buffers[2], LOAD);
670       vlib_prefetch_buffer_with_index (vm, buffers[3], LOAD);
671
672       bi0 = buffers[0];
673       bi1 = buffers[1];
674
675       b0 = vlib_get_buffer (vm, bi0);
676       b1 = vlib_get_buffer (vm, bi1);
677
678       if (b0->flags & VLIB_BUFFER_IS_TRACED)
679         {
680           t0 = vlib_add_trace (vm, node, b0,
681                                STRUCT_OFFSET_OF (vnet_error_trace_t, pad));
682           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
683           t0->details_valid = 0;
684         }
685       if (b1->flags & VLIB_BUFFER_IS_TRACED)
686         {
687           t1 = vlib_add_trace (vm, node, b1,
688                                STRUCT_OFFSET_OF (vnet_error_trace_t, pad));
689           t1->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX];
690           t1->details_valid = 0;
691         }
692       buffers += 2;
693       n_left -= 2;
694     }
695
696   while (n_left >= 1)
697     {
698       u32 bi0;
699       vlib_buffer_t *b0;
700       vnet_error_trace_t *t0;
701
702       bi0 = buffers[0];
703
704       b0 = vlib_get_buffer (vm, bi0);
705
706       if (b0->flags & VLIB_BUFFER_IS_TRACED)
707         {
708           t0 = vlib_add_trace (vm, node, b0,
709                                STRUCT_OFFSET_OF (vnet_error_trace_t, pad));
710           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
711           t0->details_valid = 0;
712         }
713       buffers += 1;
714       n_left -= 1;
715     }
716 }
717
718 typedef enum
719 {
720   VNET_ERROR_DISPOSITION_DROP,
721   VNET_ERROR_DISPOSITION_PUNT,
722   VNET_ERROR_N_DISPOSITION,
723 } vnet_error_disposition_t;
724
725 static void
726 drop_catchup_trace (vlib_main_t * vm,
727                     vlib_node_runtime_t * node, vlib_buffer_t * b)
728 {
729   /* Can we safely rewind the buffer? If not, fagedaboudit */
730   if (b->flags & VNET_BUFFER_F_L2_HDR_OFFSET_VALID)
731     {
732       vnet_error_trace_t *t;
733       ip4_header_t *ip4;
734       ip6_header_t *ip6;
735       ethernet_header_t *eh;
736       i16 delta;
737
738       t = vlib_add_trace (vm, node, b, sizeof (*t));
739       delta = vnet_buffer (b)->l2_hdr_offset - b->current_data;
740       vlib_buffer_advance (b, delta);
741
742       eh = vlib_buffer_get_current (b);
743       /* Save mactype */
744       t->mactype = clib_net_to_host_u16 (eh->type);
745       t->details_valid = 1;
746       switch (t->mactype)
747         {
748         case ETHERNET_TYPE_IP4:
749           ip4 = (void *) (eh + 1);
750           t->details_valid = 2;
751           t->is_ip6 = 0;
752           t->src.ip4.as_u32 = ip4->src_address.as_u32;
753           t->dst.ip4.as_u32 = ip4->dst_address.as_u32;
754           break;
755
756         case ETHERNET_TYPE_IP6:
757           ip6 = (void *) (eh + 1);
758           t->details_valid = 2;
759           t->is_ip6 = 1;
760           clib_memcpy_fast (t->src.as_u8, ip6->src_address.as_u8,
761                             sizeof (ip6_address_t));
762           clib_memcpy_fast (t->dst.as_u8, ip6->dst_address.as_u8,
763                             sizeof (ip6_address_t));
764           break;
765
766         default:
767           /* Dunno, do nothing, leave details_valid alone */
768           break;
769         }
770       /* Restore current data (probably unnecessary) */
771       vlib_buffer_advance (b, -delta);
772     }
773 }
774
775 static_always_inline uword
776 interface_drop_punt (vlib_main_t * vm,
777                      vlib_node_runtime_t * node,
778                      vlib_frame_t * frame,
779                      vnet_error_disposition_t disposition)
780 {
781   u32 *from, n_left, thread_index, *sw_if_index;
782   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
783   u32 sw_if_indices[VLIB_FRAME_SIZE];
784   vlib_simple_counter_main_t *cm;
785   u16 nexts[VLIB_FRAME_SIZE];
786   u32 n_trace;
787   vnet_main_t *vnm;
788
789   vnm = vnet_get_main ();
790   thread_index = vm->thread_index;
791   from = vlib_frame_vector_args (frame);
792   n_left = frame->n_vectors;
793   b = bufs;
794   sw_if_index = sw_if_indices;
795
796   vlib_get_buffers (vm, from, bufs, n_left);
797
798   /* "trace add error-drop NNN?" */
799   if (PREDICT_FALSE ((n_trace = vlib_get_trace_count (vm, node))))
800     {
801       /* If pkts aren't otherwise traced... */
802       if ((node->flags & VLIB_NODE_FLAG_TRACE) == 0)
803         {
804           /* Trace them from here */
805           node->flags |= VLIB_NODE_FLAG_TRACE;
806           while (n_trace && n_left)
807             {
808               if (PREDICT_TRUE
809                   (vlib_trace_buffer (vm, node, 0 /* next_index */ , b[0],
810                                       0 /* follow chain */ )))
811                 {
812                   /*
813                    * Here we have a wireshark dissector problem.
814                    * Packets may be well-formed, or not. We
815                    * must not blow chunks in any case.
816                    *
817                    * Try to produce trace records which will help
818                    * folks understand what's going on.
819                    */
820                   drop_catchup_trace (vm, node, b[0]);
821                   n_trace--;
822                 }
823               n_left--;
824               b++;
825             }
826         }
827
828       vlib_set_trace_count (vm, node, n_trace);
829       b = bufs;
830       n_left = frame->n_vectors;
831     }
832
833   if (node->flags & VLIB_NODE_FLAG_TRACE)
834     interface_trace_buffers (vm, node, frame);
835
836   /* All going to drop regardless, this is just a counting exercise */
837   clib_memset (nexts, 0, sizeof (nexts));
838
839   cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
840                          (disposition == VNET_ERROR_DISPOSITION_PUNT
841                           ? VNET_INTERFACE_COUNTER_PUNT
842                           : VNET_INTERFACE_COUNTER_DROP));
843
844   /* collect the array of interfaces first ... */
845   while (n_left >= 4)
846     {
847       if (n_left >= 12)
848         {
849           /* Prefetch 8 ahead - there's not much going on in each iteration */
850           vlib_prefetch_buffer_header (b[4], LOAD);
851           vlib_prefetch_buffer_header (b[5], LOAD);
852           vlib_prefetch_buffer_header (b[6], LOAD);
853           vlib_prefetch_buffer_header (b[7], LOAD);
854         }
855       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
856       sw_if_index[1] = vnet_buffer (b[1])->sw_if_index[VLIB_RX];
857       sw_if_index[2] = vnet_buffer (b[2])->sw_if_index[VLIB_RX];
858       sw_if_index[3] = vnet_buffer (b[3])->sw_if_index[VLIB_RX];
859
860       sw_if_index += 4;
861       n_left -= 4;
862       b += 4;
863     }
864   while (n_left)
865     {
866       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
867
868       sw_if_index += 1;
869       n_left -= 1;
870       b += 1;
871     }
872
873   /* ... then count against them in blocks */
874   n_left = frame->n_vectors;
875
876   while (n_left)
877     {
878       vnet_sw_interface_t *sw_if0;
879       u16 off, count;
880
881       off = frame->n_vectors - n_left;
882
883       sw_if_index = sw_if_indices + off;
884
885       count = clib_count_equal_u32 (sw_if_index, n_left);
886       n_left -= count;
887
888       vlib_increment_simple_counter (cm, thread_index, sw_if_index[0], count);
889
890       /* Increment super-interface drop/punt counters for
891          sub-interfaces. */
892       sw_if0 = vnet_get_sw_interface (vnm, sw_if_index[0]);
893       if (sw_if0->sup_sw_if_index != sw_if_index[0])
894         vlib_increment_simple_counter
895           (cm, thread_index, sw_if0->sup_sw_if_index, count);
896     }
897
898   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
899
900   return frame->n_vectors;
901 }
902
903 static inline void
904 pcap_drop_trace (vlib_main_t * vm,
905                  vnet_interface_main_t * im,
906                  vnet_pcap_t * pp, vlib_frame_t * f)
907 {
908   u32 *from;
909   u32 n_left = f->n_vectors;
910   vlib_buffer_t *b0, *p1;
911   u32 bi0;
912   i16 save_current_data;
913   u16 save_current_length;
914   vlib_error_main_t *em = &vm->error_main;
915
916   from = vlib_frame_vector_args (f);
917
918   while (n_left > 0)
919     {
920       if (PREDICT_TRUE (n_left > 1))
921         {
922           p1 = vlib_get_buffer (vm, from[1]);
923           vlib_prefetch_buffer_header (p1, LOAD);
924         }
925
926       bi0 = from[0];
927       b0 = vlib_get_buffer (vm, bi0);
928       from++;
929       n_left--;
930
931       /* See if we're pointedly ignoring this specific error */
932       if (im->pcap_drop_filter_hash
933           && hash_get (im->pcap_drop_filter_hash, b0->error))
934         continue;
935
936       if (!vnet_is_packet_pcaped (pp, b0, ~0))
937         continue; /* not matching, skip */
938
939       /* Trace all drops, or drops received on a specific interface */
940       save_current_data = b0->current_data;
941       save_current_length = b0->current_length;
942
943       /*
944        * Typically, we'll need to rewind the buffer
945        * if l2_hdr_offset is valid, make sure to rewind to the start of
946        * the L2 header. This may not be the buffer start in case we pop-ed
947        * vlan tags.
948        * Otherwise, rewind to buffer start and hope for the best.
949        */
950       if (b0->flags & VNET_BUFFER_F_L2_HDR_OFFSET_VALID)
951         {
952           if (b0->current_data > vnet_buffer (b0)->l2_hdr_offset)
953             vlib_buffer_advance (b0, vnet_buffer (b0)->l2_hdr_offset -
954                                        b0->current_data);
955         }
956       else if (b0->current_data > 0)
957         {
958           vlib_buffer_advance (b0, (word) -b0->current_data);
959         }
960
961       {
962         vlib_buffer_t *last = b0;
963         u32 error_node_index;
964         int drop_string_len;
965         vlib_node_t *n;
966         /* Length of the error string */
967         int error_string_len =
968           clib_strnlen (em->counters_heap[b0->error].name, 128);
969
970         /* Dig up the drop node */
971         error_node_index = vm->node_main.node_by_error[b0->error];
972         n = vlib_get_node (vm, error_node_index);
973
974         /* Length of full drop string, w/ "nodename: " prepended */
975         drop_string_len = error_string_len + vec_len (n->name) + 2;
976
977         /* Find the last buffer in the chain */
978         while (last->flags & VLIB_BUFFER_NEXT_PRESENT)
979           last = vlib_get_buffer (vm, last->next_buffer);
980
981         /*
982          * Append <nodename>: <error-string> to the capture,
983          * only if we can do that without allocating a new buffer.
984          */
985         if (PREDICT_TRUE ((last->current_data + last->current_length) <
986                           (VLIB_BUFFER_DEFAULT_DATA_SIZE - drop_string_len)))
987           {
988             clib_memcpy_fast (last->data + last->current_data +
989                                 last->current_length,
990                               n->name, vec_len (n->name));
991             clib_memcpy_fast (last->data + last->current_data +
992                                 last->current_length + vec_len (n->name),
993                               ": ", 2);
994             clib_memcpy_fast (last->data + last->current_data +
995                                 last->current_length + vec_len (n->name) + 2,
996                               em->counters_heap[b0->error].name,
997                               error_string_len);
998             last->current_length += drop_string_len;
999             b0->flags &= ~(VLIB_BUFFER_TOTAL_LENGTH_VALID);
1000             pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt);
1001             last->current_length -= drop_string_len;
1002             b0->current_data = save_current_data;
1003             b0->current_length = save_current_length;
1004             continue;
1005           }
1006       }
1007
1008       /*
1009        * Didn't have space in the last buffer, here's the dropped
1010        * packet as-is
1011        */
1012       pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt);
1013
1014       b0->current_data = save_current_data;
1015       b0->current_length = save_current_length;
1016     }
1017 }
1018
1019 #ifndef CLIB_MARCH_VARIANT
1020 void
1021 vnet_pcap_drop_trace_filter_add_del (u32 error_index, int is_add)
1022 {
1023   vnet_interface_main_t *im = &vnet_get_main ()->interface_main;
1024
1025   if (im->pcap_drop_filter_hash == 0)
1026     im->pcap_drop_filter_hash = hash_create (0, sizeof (uword));
1027
1028   if (is_add)
1029     hash_set (im->pcap_drop_filter_hash, error_index, 1);
1030   else
1031     hash_unset (im->pcap_drop_filter_hash, error_index);
1032 }
1033 #endif /* CLIB_MARCH_VARIANT */
1034
1035 VLIB_NODE_FN (interface_drop) (vlib_main_t * vm,
1036                                vlib_node_runtime_t * node,
1037                                vlib_frame_t * frame)
1038 {
1039   vnet_main_t *vnm = vnet_get_main ();
1040   vnet_interface_main_t *im = &vnet_get_main ()->interface_main;
1041   vnet_pcap_t *pp = &vnm->pcap;
1042
1043   if (PREDICT_FALSE (pp->pcap_drop_enable))
1044     pcap_drop_trace (vm, im, pp, frame);
1045
1046   return interface_drop_punt (vm, node, frame, VNET_ERROR_DISPOSITION_DROP);
1047 }
1048
1049 VLIB_NODE_FN (interface_punt) (vlib_main_t * vm,
1050                                vlib_node_runtime_t * node,
1051                                vlib_frame_t * frame)
1052 {
1053   return interface_drop_punt (vm, node, frame, VNET_ERROR_DISPOSITION_PUNT);
1054 }
1055
1056 /* *INDENT-OFF* */
1057 VLIB_REGISTER_NODE (interface_drop) = {
1058   .name = "error-drop",
1059   .vector_size = sizeof (u32),
1060   .format_trace = format_vnet_error_trace,
1061   .flags = VLIB_NODE_FLAG_TRACE_SUPPORTED,
1062   .n_next_nodes = 1,
1063   .next_nodes = {
1064     [0] = "drop",
1065   },
1066 };
1067 /* *INDENT-ON* */
1068
1069 /* *INDENT-OFF* */
1070 VLIB_REGISTER_NODE (interface_punt) = {
1071   .name = "error-punt",
1072   .vector_size = sizeof (u32),
1073   .format_trace = format_vnet_error_trace,
1074   .flags = VLIB_NODE_FLAG_TRACE_SUPPORTED,
1075   .n_next_nodes = 1,
1076   .next_nodes = {
1077     [0] = "punt",
1078   },
1079 };
1080 /* *INDENT-ON* */
1081
1082 VLIB_REGISTER_NODE (vnet_per_buffer_interface_output_node) = {
1083   .name = "interface-output",
1084   .vector_size = sizeof (u32),
1085 };
1086
1087 VLIB_NODE_FN (vnet_interface_output_arc_end_node)
1088 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
1089 {
1090   vnet_main_t *vnm = vnet_get_main ();
1091   vnet_interface_main_t *im = &vnm->interface_main;
1092   vnet_hw_if_output_node_runtime_t *r = 0;
1093   vnet_hw_interface_t *hi;
1094   vnet_hw_if_tx_frame_t *tf;
1095   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
1096   u32 sw_if_indices[VLIB_FRAME_SIZE], *sw_if_index = sw_if_indices;
1097   u64 used_elts[VLIB_FRAME_SIZE / 64] = {};
1098   u64 mask[VLIB_FRAME_SIZE / 64] = {};
1099   u32 *tmp, *from, n_left, n_free, n_comp, *to, swif, off;
1100   u16 next_index;
1101   vlib_frame_t *f;
1102
1103   from = vlib_frame_vector_args (frame);
1104   n_left = frame->n_vectors;
1105   vlib_get_buffers (vm, from, bufs, n_left);
1106
1107   while (n_left >= 8)
1108     {
1109       vlib_prefetch_buffer_header (b[4], LOAD);
1110       vlib_prefetch_buffer_header (b[5], LOAD);
1111       vlib_prefetch_buffer_header (b[6], LOAD);
1112       vlib_prefetch_buffer_header (b[7], LOAD);
1113       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
1114       sw_if_index[1] = vnet_buffer (b[1])->sw_if_index[VLIB_TX];
1115       sw_if_index[2] = vnet_buffer (b[2])->sw_if_index[VLIB_TX];
1116       sw_if_index[3] = vnet_buffer (b[3])->sw_if_index[VLIB_TX];
1117
1118       b += 4;
1119       sw_if_index += 4;
1120       n_left -= 4;
1121     }
1122
1123   while (n_left)
1124     {
1125       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
1126       b++;
1127       sw_if_index++;
1128       n_left--;
1129     }
1130
1131   n_left = frame->n_vectors;
1132   swif = sw_if_indices[0];
1133   off = 0;
1134
1135   /* a bit ugly but it allows us to reuse stack space for temporary store
1136    * which may also improve memory latency */
1137   tmp = (u32 *) bufs;
1138
1139 more:
1140   next_index = vec_elt (im->if_out_arc_end_next_index_by_sw_if_index, swif);
1141   hi = vnet_get_sup_hw_interface (vnm, swif);
1142   if (hi->output_node_thread_runtimes)
1143     r = vec_elt_at_index (hi->output_node_thread_runtimes, vm->thread_index);
1144   f = vlib_get_next_frame_internal (vm, node, next_index, 0);
1145   tf = vlib_frame_scalar_args (f);
1146
1147   if (f->n_vectors > 0 && (r == 0 || r->frame.queue_id == tf->queue_id))
1148     {
1149       /* append frame */
1150       n_free = VLIB_FRAME_SIZE - f->n_vectors;
1151       if (n_free >= f->n_vectors)
1152         to = (u32 *) vlib_frame_vector_args (f) + f->n_vectors;
1153       else
1154         to = tmp;
1155     }
1156   else
1157     {
1158       if (f->n_vectors > 0)
1159         {
1160           /* current frame doesn't fit - grab empty one */
1161           f = vlib_get_next_frame_internal (vm, node, next_index, 1);
1162           tf = vlib_frame_scalar_args (f);
1163         }
1164
1165       /* empty frame - store scalar data */
1166       store_tx_frame_scalar_data (r, tf);
1167       n_free = VLIB_FRAME_SIZE;
1168       to = vlib_frame_vector_args (f);
1169     }
1170
1171   /* compare and compress based on comparison mask */
1172   clib_mask_compare_u32 (swif, sw_if_indices, mask, frame->n_vectors);
1173   n_comp = clib_compress_u32 (to, from, mask, frame->n_vectors);
1174
1175   if (tmp != to)
1176     {
1177       /* indices already written to frame, just close it */
1178       vlib_put_next_frame (vm, node, next_index, n_free - n_comp);
1179     }
1180   else if (n_free >= n_comp)
1181     {
1182       /* enough space in the existing frame */
1183       to = (u32 *) vlib_frame_vector_args (f) + f->n_vectors;
1184       vlib_buffer_copy_indices (to, tmp, n_comp);
1185       vlib_put_next_frame (vm, node, next_index, n_free - n_comp);
1186     }
1187   else
1188     {
1189       /* full frame */
1190       to = (u32 *) vlib_frame_vector_args (f) + f->n_vectors;
1191       vlib_buffer_copy_indices (to, tmp, n_free);
1192       vlib_put_next_frame (vm, node, next_index, 0);
1193
1194       /* second frame */
1195       u32 n_frame2 = n_comp - n_free;
1196       f = vlib_get_next_frame_internal (vm, node, next_index, 1);
1197       to = vlib_frame_vector_args (f);
1198       vlib_buffer_copy_indices (to, tmp + n_free, n_frame2);
1199       tf = vlib_frame_scalar_args (f);
1200       store_tx_frame_scalar_data (r, tf);
1201       vlib_put_next_frame (vm, node, next_index, VLIB_FRAME_SIZE - n_frame2);
1202     }
1203
1204   n_left -= n_comp;
1205   if (n_left)
1206     {
1207       /* store comparison mask so we can find next unused element */
1208       for (int i = 0; i < ARRAY_LEN (used_elts); i++)
1209         used_elts[i] |= mask[i];
1210
1211       /* fine first unused sw_if_index by scanning trough used_elts bitmap */
1212       while (PREDICT_FALSE (used_elts[off] == ~0))
1213         off++;
1214
1215       swif =
1216         sw_if_indices[(off << 6) + count_trailing_zeros (~used_elts[off])];
1217       goto more;
1218     }
1219
1220   return frame->n_vectors;
1221 }
1222
1223 VLIB_REGISTER_NODE (vnet_interface_output_arc_end_node) = {
1224   .name = "interface-output-arc-end",
1225   .vector_size = sizeof (u32),
1226   .n_next_nodes = 1,
1227   .next_nodes = {
1228     [0] = "error-drop",
1229   },
1230 };
1231
1232 VNET_FEATURE_ARC_INIT (interface_output, static) = {
1233   .arc_name = "interface-output",
1234   .start_nodes = VNET_FEATURES (0),
1235   .last_in_arc = "interface-output-arc-end",
1236   .arc_index_ptr = &vnet_main.interface_main.output_feature_arc_index,
1237 };
1238
1239 VNET_FEATURE_INIT (span_tx, static) = {
1240   .arc_name = "interface-output",
1241   .node_name = "span-output",
1242   .runs_before = VNET_FEATURES ("interface-output-arc-end"),
1243 };
1244
1245 VNET_FEATURE_INIT (ipsec_if_tx, static) = {
1246   .arc_name = "interface-output",
1247   .node_name = "ipsec-if-output",
1248   .runs_before = VNET_FEATURES ("interface-output-arc-end"),
1249 };
1250
1251 VNET_FEATURE_INIT (interface_output_arc_end, static) = {
1252   .arc_name = "interface-output",
1253   .node_name = "interface-output-arc-end",
1254   .runs_before = 0,
1255 };
1256
1257 #ifndef CLIB_MARCH_VARIANT
1258 clib_error_t *
1259 vnet_per_buffer_interface_output_hw_interface_add_del (vnet_main_t * vnm,
1260                                                        u32 hw_if_index,
1261                                                        u32 is_create)
1262 {
1263   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
1264   u32 next_index;
1265
1266   if (hi->output_node_index == 0)
1267     return 0;
1268
1269   next_index = vlib_node_add_next
1270     (vnm->vlib_main, vnet_per_buffer_interface_output_node.index,
1271      hi->output_node_index);
1272   hi->output_node_next_index = next_index;
1273
1274   return 0;
1275 }
1276
1277 VNET_HW_INTERFACE_ADD_DEL_FUNCTION
1278   (vnet_per_buffer_interface_output_hw_interface_add_del);
1279
1280 void
1281 vnet_set_interface_output_node (vnet_main_t * vnm,
1282                                 u32 hw_if_index, u32 node_index)
1283 {
1284   ASSERT (node_index);
1285   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
1286   u32 next_index = vlib_node_add_next
1287     (vnm->vlib_main, vnet_per_buffer_interface_output_node.index, node_index);
1288   hi->output_node_next_index = next_index;
1289   hi->output_node_index = node_index;
1290 }
1291 #endif /* CLIB_MARCH_VARIANT */
1292
1293 /*
1294  * fd.io coding-style-patch-verification: ON
1295  *
1296  * Local Variables:
1297  * eval: (c-set-style "gnu")
1298  * End:
1299  */