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