devices interface tests: vhosst GSO support
[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/ip/ip4.h>
43 #include <vnet/ip/ip6.h>
44 #include <vnet/udp/udp_packet.h>
45 #include <vnet/feature/feature.h>
46
47 typedef struct
48 {
49   u32 sw_if_index;
50   u32 flags;
51   u16 gso_size;
52   u8 gso_l4_hdr_sz;
53   u8 data[128 - 3 * sizeof (u32)];
54 }
55 interface_output_trace_t;
56
57 #ifndef CLIB_MARCH_VARIANT
58 u8 *
59 format_vnet_interface_output_trace (u8 * s, va_list * va)
60 {
61   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
62   vlib_node_t *node = va_arg (*va, vlib_node_t *);
63   interface_output_trace_t *t = va_arg (*va, interface_output_trace_t *);
64   vnet_main_t *vnm = vnet_get_main ();
65   vnet_sw_interface_t *si;
66   u32 indent;
67
68   if (t->sw_if_index != (u32) ~ 0)
69     {
70       indent = format_get_indent (s);
71
72       if (pool_is_free_index
73           (vnm->interface_main.sw_interfaces, t->sw_if_index))
74         {
75           /* the interface may have been deleted by the time the trace is printed */
76           s = format (s, "sw_if_index: %d ", t->sw_if_index);
77         }
78       else
79         {
80           si = vnet_get_sw_interface (vnm, t->sw_if_index);
81           s =
82             format (s, "%U ", format_vnet_sw_interface_name, vnm, si,
83                     t->flags);
84         }
85 #define _(bit, name, v, x) \
86           if (v && (t->flags & VNET_BUFFER_F_##name)) \
87             s = format (s, "%s ", v);
88       foreach_vnet_buffer_flag
89 #undef _
90         if (t->flags & VNET_BUFFER_F_GSO)
91         {
92           s = format (s, "\n%Ugso_sz %d gso_l4_hdr_sz %d",
93                       format_white_space, indent + 2, t->gso_size,
94                       t->gso_l4_hdr_sz);
95         }
96       s =
97         format (s, "\n%U%U", format_white_space, indent,
98                 node->format_buffer ? node->format_buffer : format_hex_bytes,
99                 t->data, sizeof (t->data));
100     }
101   return s;
102 }
103
104 static void
105 vnet_interface_output_trace (vlib_main_t * vm,
106                              vlib_node_runtime_t * node,
107                              vlib_frame_t * frame, uword n_buffers)
108 {
109   u32 n_left, *from;
110
111   n_left = n_buffers;
112   from = vlib_frame_vector_args (frame);
113
114   while (n_left >= 4)
115     {
116       u32 bi0, bi1;
117       vlib_buffer_t *b0, *b1;
118       interface_output_trace_t *t0, *t1;
119
120       /* Prefetch next iteration. */
121       vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
122       vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
123
124       bi0 = from[0];
125       bi1 = from[1];
126
127       b0 = vlib_get_buffer (vm, bi0);
128       b1 = vlib_get_buffer (vm, bi1);
129
130       if (b0->flags & VLIB_BUFFER_IS_TRACED)
131         {
132           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
133           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
134           t0->flags = b0->flags;
135           t0->gso_size = vnet_buffer2 (b0)->gso_size;
136           t0->gso_l4_hdr_sz = vnet_buffer2 (b0)->gso_l4_hdr_sz;
137           clib_memcpy_fast (t0->data, vlib_buffer_get_current (b0),
138                             sizeof (t0->data));
139         }
140       if (b1->flags & VLIB_BUFFER_IS_TRACED)
141         {
142           t1 = vlib_add_trace (vm, node, b1, sizeof (t1[0]));
143           t1->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_TX];
144           t1->flags = b1->flags;
145           t1->gso_size = vnet_buffer2 (b1)->gso_size;
146           t1->gso_l4_hdr_sz = vnet_buffer2 (b1)->gso_l4_hdr_sz;
147           clib_memcpy_fast (t1->data, vlib_buffer_get_current (b1),
148                             sizeof (t1->data));
149         }
150       from += 2;
151       n_left -= 2;
152     }
153
154   while (n_left >= 1)
155     {
156       u32 bi0;
157       vlib_buffer_t *b0;
158       interface_output_trace_t *t0;
159
160       bi0 = from[0];
161
162       b0 = vlib_get_buffer (vm, bi0);
163
164       if (b0->flags & VLIB_BUFFER_IS_TRACED)
165         {
166           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
167           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
168           t0->flags = b0->flags;
169           t0->gso_size = vnet_buffer2 (b0)->gso_size;
170           t0->gso_l4_hdr_sz = vnet_buffer2 (b0)->gso_l4_hdr_sz;
171           clib_memcpy_fast (t0->data, vlib_buffer_get_current (b0),
172                             sizeof (t0->data));
173         }
174       from += 1;
175       n_left -= 1;
176     }
177 }
178
179 static_always_inline void
180 calc_checksums (vlib_main_t * vm, vlib_buffer_t * b)
181 {
182   tcp_header_t *th;
183   udp_header_t *uh;
184
185   int is_ip4 = (b->flags & VNET_BUFFER_F_IS_IP4) != 0;
186   int is_ip6 = (b->flags & VNET_BUFFER_F_IS_IP6) != 0;
187
188   ASSERT (!(is_ip4 && is_ip6));
189
190   th = (tcp_header_t *) (b->data + vnet_buffer (b)->l4_hdr_offset);
191   uh = (udp_header_t *) (b->data + vnet_buffer (b)->l4_hdr_offset);
192
193   if (is_ip4)
194     {
195       ip4_header_t *ip4;
196
197       ip4 = (ip4_header_t *) (b->data + vnet_buffer (b)->l3_hdr_offset);
198       if (b->flags & VNET_BUFFER_F_OFFLOAD_IP_CKSUM)
199         ip4->checksum = ip4_header_checksum (ip4);
200       if (b->flags & VNET_BUFFER_F_OFFLOAD_TCP_CKSUM)
201         {
202           th->checksum = 0;
203           th->checksum = ip4_tcp_udp_compute_checksum (vm, b, ip4);
204         }
205       else if (b->flags & VNET_BUFFER_F_OFFLOAD_UDP_CKSUM)
206         uh->checksum = ip4_tcp_udp_compute_checksum (vm, b, ip4);
207     }
208   else if (is_ip6)
209     {
210       int bogus;
211       ip6_header_t *ip6;
212
213       ip6 = (ip6_header_t *) (b->data + vnet_buffer (b)->l3_hdr_offset);
214       if (b->flags & VNET_BUFFER_F_OFFLOAD_TCP_CKSUM)
215         {
216           th->checksum = 0;
217           th->checksum =
218             ip6_tcp_udp_icmp_compute_checksum (vm, b, ip6, &bogus);
219         }
220       else if (b->flags & VNET_BUFFER_F_OFFLOAD_UDP_CKSUM)
221         {
222           uh->checksum = 0;
223           uh->checksum =
224             ip6_tcp_udp_icmp_compute_checksum (vm, b, ip6, &bogus);
225         }
226     }
227 }
228
229 static_always_inline u16
230 tso_alloc_tx_bufs (vlib_main_t * vm,
231                    vnet_interface_per_thread_data_t * ptd,
232                    vlib_buffer_t * b0, u16 l4_hdr_sz)
233 {
234   u32 n_bytes_b0 = vlib_buffer_length_in_chain (vm, b0);
235   u16 gso_size = vnet_buffer2 (b0)->gso_size;
236   u16 l234_sz = vnet_buffer (b0)->l4_hdr_offset + l4_hdr_sz;
237   /* rounded-up division */
238   u16 n_bufs = (n_bytes_b0 - l234_sz + (gso_size - 1)) / gso_size;
239   u16 n_alloc;
240
241   ASSERT (n_bufs > 0);
242   vec_validate (ptd->split_buffers, n_bufs - 1);
243
244   n_alloc = vlib_buffer_alloc (vm, ptd->split_buffers, n_bufs);
245   if (n_alloc < n_bufs)
246     {
247       vlib_buffer_free (vm, ptd->split_buffers, n_alloc);
248       return 0;
249     }
250   return 1;
251 }
252
253 static_always_inline void
254 tso_init_buf_from_template_base (vlib_buffer_t * nb0, vlib_buffer_t * b0,
255                                  u32 flags, u16 length)
256 {
257   nb0->current_data = 0;
258   nb0->total_length_not_including_first_buffer = 0;
259   nb0->flags = VLIB_BUFFER_TOTAL_LENGTH_VALID | flags;
260   clib_memcpy_fast (&nb0->opaque, &b0->opaque, sizeof (nb0->opaque));
261   clib_memcpy_fast (nb0->data, b0->data, length);
262   nb0->current_length = length;
263 }
264
265 static_always_inline void
266 tso_init_buf_from_template (vlib_main_t * vm, vlib_buffer_t * nb0,
267                             vlib_buffer_t * b0, u16 template_data_sz,
268                             u16 gso_size, u8 ** p_dst_ptr, u16 * p_dst_left,
269                             u32 next_tcp_seq, u32 flags)
270 {
271   tso_init_buf_from_template_base (nb0, b0, flags, template_data_sz);
272
273   *p_dst_left =
274     clib_min (gso_size,
275               vlib_buffer_get_default_data_size (vm) - template_data_sz);
276   *p_dst_ptr = nb0->data + template_data_sz;
277
278   tcp_header_t *tcp =
279     (tcp_header_t *) (nb0->data + vnet_buffer (nb0)->l4_hdr_offset);
280   tcp->seq_number = clib_host_to_net_u32 (next_tcp_seq);
281 }
282
283 static_always_inline void
284 tso_fixup_segmented_buf (vlib_buffer_t * b0, u8 tcp_flags, int is_ip6)
285 {
286   u16 l3_hdr_offset = vnet_buffer (b0)->l3_hdr_offset;
287   u16 l4_hdr_offset = vnet_buffer (b0)->l4_hdr_offset;
288   ip4_header_t *ip4 = (ip4_header_t *) (b0->data + l3_hdr_offset);
289   ip6_header_t *ip6 = (ip6_header_t *) (b0->data + l3_hdr_offset);
290   tcp_header_t *tcp = (tcp_header_t *) (b0->data + l4_hdr_offset);
291
292   tcp->flags = tcp_flags;
293
294   if (is_ip6)
295     ip6->payload_length =
296       clib_host_to_net_u16 (b0->current_length -
297                             vnet_buffer (b0)->l4_hdr_offset);
298   else
299     ip4->length =
300       clib_host_to_net_u16 (b0->current_length -
301                             vnet_buffer (b0)->l3_hdr_offset);
302 }
303
304 /**
305  * Allocate the necessary number of ptd->split_buffers,
306  * and segment the possibly chained buffer(s) from b0 into
307  * there.
308  *
309  * Return the cumulative number of bytes sent or zero
310  * if allocation failed.
311  */
312
313 static_always_inline u32
314 tso_segment_buffer (vlib_main_t * vm, vnet_interface_per_thread_data_t * ptd,
315                     int do_tx_offloads, u32 sbi0, vlib_buffer_t * sb0,
316                     u32 n_bytes_b0)
317 {
318   u32 n_tx_bytes = 0;
319   int is_ip4 = sb0->flags & VNET_BUFFER_F_IS_IP4;
320   int is_ip6 = sb0->flags & VNET_BUFFER_F_IS_IP6;
321   ASSERT (is_ip4 || is_ip6);
322   ASSERT (sb0->flags & VNET_BUFFER_F_L2_HDR_OFFSET_VALID);
323   ASSERT (sb0->flags & VNET_BUFFER_F_L3_HDR_OFFSET_VALID);
324   ASSERT (sb0->flags & VNET_BUFFER_F_L4_HDR_OFFSET_VALID);
325   u16 gso_size = vnet_buffer2 (sb0)->gso_size;
326
327   int l4_hdr_sz = vnet_buffer2 (sb0)->gso_l4_hdr_sz;
328   u8 save_tcp_flags = 0;
329   u8 tcp_flags_no_fin_psh = 0;
330   u32 next_tcp_seq = 0;
331
332   tcp_header_t *tcp =
333     (tcp_header_t *) (sb0->data + vnet_buffer (sb0)->l4_hdr_offset);
334   next_tcp_seq = clib_net_to_host_u32 (tcp->seq_number);
335   /* store original flags for last packet and reset FIN and PSH */
336   save_tcp_flags = tcp->flags;
337   tcp_flags_no_fin_psh = tcp->flags & ~(TCP_FLAG_FIN | TCP_FLAG_PSH);
338   tcp->checksum = 0;
339
340   u32 default_bflags =
341     sb0->flags & ~(VNET_BUFFER_F_GSO | VLIB_BUFFER_NEXT_PRESENT);
342   u16 l234_sz = vnet_buffer (sb0)->l4_hdr_offset + l4_hdr_sz;
343   int first_data_size = clib_min (gso_size, sb0->current_length - l234_sz);
344   next_tcp_seq += first_data_size;
345
346   if (PREDICT_FALSE (!tso_alloc_tx_bufs (vm, ptd, sb0, l4_hdr_sz)))
347     return 0;
348
349   vlib_buffer_t *b0 = vlib_get_buffer (vm, ptd->split_buffers[0]);
350   tso_init_buf_from_template_base (b0, sb0, default_bflags,
351                                    l234_sz + first_data_size);
352
353   u32 total_src_left = n_bytes_b0 - l234_sz - first_data_size;
354   if (total_src_left)
355     {
356       /* Need to copy more segments */
357       u8 *src_ptr, *dst_ptr;
358       u16 src_left, dst_left;
359       /* current source buffer */
360       vlib_buffer_t *csb0 = sb0;
361       u32 csbi0 = sbi0;
362       /* current dest buffer */
363       vlib_buffer_t *cdb0;
364       u16 dbi = 1;              /* the buffer [0] is b0 */
365
366       src_ptr = sb0->data + l234_sz + first_data_size;
367       src_left = sb0->current_length - l234_sz - first_data_size;
368       b0->current_length = l234_sz + first_data_size;
369
370       tso_fixup_segmented_buf (b0, tcp_flags_no_fin_psh, is_ip6);
371       if (do_tx_offloads)
372         calc_checksums (vm, b0);
373
374       /* grab a second buffer and prepare the loop */
375       ASSERT (dbi < vec_len (ptd->split_buffers));
376       cdb0 = vlib_get_buffer (vm, ptd->split_buffers[dbi++]);
377       tso_init_buf_from_template (vm, cdb0, b0, l234_sz, gso_size, &dst_ptr,
378                                   &dst_left, next_tcp_seq, default_bflags);
379
380       /* an arbitrary large number to catch the runaway loops */
381       int nloops = 2000;
382       while (total_src_left)
383         {
384           if (nloops-- <= 0)
385             clib_panic ("infinite loop detected");
386           u16 bytes_to_copy = clib_min (src_left, dst_left);
387
388           clib_memcpy_fast (dst_ptr, src_ptr, bytes_to_copy);
389
390           src_left -= bytes_to_copy;
391           src_ptr += bytes_to_copy;
392           total_src_left -= bytes_to_copy;
393           dst_left -= bytes_to_copy;
394           dst_ptr += bytes_to_copy;
395           next_tcp_seq += bytes_to_copy;
396           cdb0->current_length += bytes_to_copy;
397
398           if (0 == src_left)
399             {
400               int has_next = (csb0->flags & VLIB_BUFFER_NEXT_PRESENT);
401               u32 next_bi = csb0->next_buffer;
402
403               /* init src to the next buffer in chain */
404               if (has_next)
405                 {
406                   csbi0 = next_bi;
407                   csb0 = vlib_get_buffer (vm, csbi0);
408                   src_left = csb0->current_length;
409                   src_ptr = vlib_buffer_get_current (csb0);
410                 }
411               else
412                 {
413                   ASSERT (total_src_left == 0);
414                   break;
415                 }
416             }
417           if (0 == dst_left && total_src_left)
418             {
419               if (do_tx_offloads)
420                 calc_checksums (vm, cdb0);
421               n_tx_bytes += cdb0->current_length;
422               ASSERT (dbi < vec_len (ptd->split_buffers));
423               cdb0 = vlib_get_buffer (vm, ptd->split_buffers[dbi++]);
424               tso_init_buf_from_template (vm, cdb0, b0, l234_sz,
425                                           gso_size, &dst_ptr, &dst_left,
426                                           next_tcp_seq, default_bflags);
427             }
428         }
429
430       tso_fixup_segmented_buf (cdb0, save_tcp_flags, is_ip6);
431       if (do_tx_offloads)
432         calc_checksums (vm, cdb0);
433
434       n_tx_bytes += cdb0->current_length;
435     }
436   n_tx_bytes += b0->current_length;
437   return n_tx_bytes;
438 }
439
440 static_always_inline void
441 drop_one_buffer_and_count (vlib_main_t * vm, vnet_main_t * vnm,
442                            vlib_node_runtime_t * node, u32 * pbi0,
443                            u32 drop_error_code)
444 {
445   u32 thread_index = vm->thread_index;
446   vnet_interface_output_runtime_t *rt = (void *) node->runtime_data;
447
448   vlib_simple_counter_main_t *cm;
449   cm =
450     vec_elt_at_index (vnm->interface_main.sw_if_counters,
451                       VNET_INTERFACE_COUNTER_TX_ERROR);
452   vlib_increment_simple_counter (cm, thread_index, rt->sw_if_index, 1);
453
454   vlib_error_drop_buffers (vm, node, pbi0,
455                            /* buffer stride */ 1,
456                            /* n_buffers */ 1,
457                            VNET_INTERFACE_OUTPUT_NEXT_DROP,
458                            node->node_index, drop_error_code);
459 }
460
461 static_always_inline uword
462 vnet_interface_output_node_inline_gso (vlib_main_t * vm,
463                                        vlib_node_runtime_t * node,
464                                        vlib_frame_t * frame,
465                                        vnet_main_t * vnm,
466                                        vnet_hw_interface_t * hi,
467                                        int do_tx_offloads,
468                                        int do_segmentation)
469 {
470   vnet_interface_output_runtime_t *rt = (void *) node->runtime_data;
471   vnet_sw_interface_t *si;
472   u32 n_left_to_tx, *from, *from_end, *to_tx;
473   u32 n_bytes, n_buffers, n_packets;
474   u32 n_bytes_b0, n_bytes_b1, n_bytes_b2, n_bytes_b3;
475   u32 thread_index = vm->thread_index;
476   vnet_interface_main_t *im = &vnm->interface_main;
477   u32 next_index = VNET_INTERFACE_OUTPUT_NEXT_TX;
478   u32 current_config_index = ~0;
479   u8 arc = im->output_feature_arc_index;
480   vnet_interface_per_thread_data_t *ptd =
481     vec_elt_at_index (im->per_thread_data, thread_index);
482   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
483
484   n_buffers = frame->n_vectors;
485
486   if (node->flags & VLIB_NODE_FLAG_TRACE)
487     vnet_interface_output_trace (vm, node, frame, n_buffers);
488
489   from = vlib_frame_vector_args (frame);
490   vlib_get_buffers (vm, from, b, n_buffers);
491
492   if (rt->is_deleted)
493     return vlib_error_drop_buffers (vm, node, from,
494                                     /* buffer stride */ 1,
495                                     n_buffers,
496                                     VNET_INTERFACE_OUTPUT_NEXT_DROP,
497                                     node->node_index,
498                                     VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DELETED);
499
500   si = vnet_get_sw_interface (vnm, rt->sw_if_index);
501   hi = vnet_get_sup_hw_interface (vnm, rt->sw_if_index);
502   if (!(si->flags & (VNET_SW_INTERFACE_FLAG_ADMIN_UP |
503                      VNET_SW_INTERFACE_FLAG_BOND_SLAVE)) ||
504       !(hi->flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
505     {
506       vlib_simple_counter_main_t *cm;
507
508       cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
509                              VNET_INTERFACE_COUNTER_TX_ERROR);
510       vlib_increment_simple_counter (cm, thread_index,
511                                      rt->sw_if_index, n_buffers);
512
513       return vlib_error_drop_buffers (vm, node, from,
514                                       /* buffer stride */ 1,
515                                       n_buffers,
516                                       VNET_INTERFACE_OUTPUT_NEXT_DROP,
517                                       node->node_index,
518                                       VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DOWN);
519     }
520
521   from_end = from + n_buffers;
522
523   /* Total byte count of all buffers. */
524   n_bytes = 0;
525   n_packets = 0;
526
527   /* interface-output feature arc handling */
528   if (PREDICT_FALSE (vnet_have_features (arc, rt->sw_if_index)))
529     {
530       vnet_feature_config_main_t *fcm;
531       fcm = vnet_feature_get_config_main (arc);
532       current_config_index = vnet_get_feature_config_index (arc,
533                                                             rt->sw_if_index);
534       vnet_get_config_data (&fcm->config_main, &current_config_index,
535                             &next_index, 0);
536     }
537
538   while (from < from_end)
539     {
540       /* Get new next frame since previous incomplete frame may have less
541          than VNET_FRAME_SIZE vectors in it. */
542       vlib_get_new_next_frame (vm, node, next_index, to_tx, n_left_to_tx);
543
544       while (from + 8 <= from_end && n_left_to_tx >= 4)
545         {
546           u32 bi0, bi1, bi2, bi3;
547           u32 tx_swif0, tx_swif1, tx_swif2, tx_swif3;
548           u32 or_flags;
549
550           /* Prefetch next iteration. */
551           vlib_prefetch_buffer_header (b[4], LOAD);
552           vlib_prefetch_buffer_header (b[5], LOAD);
553           vlib_prefetch_buffer_header (b[6], LOAD);
554           vlib_prefetch_buffer_header (b[7], LOAD);
555
556           bi0 = from[0];
557           bi1 = from[1];
558           bi2 = from[2];
559           bi3 = from[3];
560           to_tx[0] = bi0;
561           to_tx[1] = bi1;
562           to_tx[2] = bi2;
563           to_tx[3] = bi3;
564
565           or_flags = b[0]->flags | b[1]->flags | b[2]->flags | b[3]->flags;
566
567           if (do_segmentation)
568             {
569               /* go to single loop if we need TSO segmentation */
570               if (PREDICT_FALSE (or_flags & VNET_BUFFER_F_GSO))
571                 break;
572             }
573           from += 4;
574           to_tx += 4;
575           n_left_to_tx -= 4;
576
577           /* Be grumpy about zero length buffers for benefit of
578              driver tx function. */
579           ASSERT (b[0]->current_length > 0);
580           ASSERT (b[1]->current_length > 0);
581           ASSERT (b[2]->current_length > 0);
582           ASSERT (b[3]->current_length > 0);
583
584           n_bytes_b0 = vlib_buffer_length_in_chain (vm, b[0]);
585           n_bytes_b1 = vlib_buffer_length_in_chain (vm, b[1]);
586           n_bytes_b2 = vlib_buffer_length_in_chain (vm, b[2]);
587           n_bytes_b3 = vlib_buffer_length_in_chain (vm, b[3]);
588           tx_swif0 = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
589           tx_swif1 = vnet_buffer (b[1])->sw_if_index[VLIB_TX];
590           tx_swif2 = vnet_buffer (b[2])->sw_if_index[VLIB_TX];
591           tx_swif3 = vnet_buffer (b[3])->sw_if_index[VLIB_TX];
592
593           n_bytes += n_bytes_b0 + n_bytes_b1;
594           n_bytes += n_bytes_b2 + n_bytes_b3;
595           n_packets += 4;
596
597           if (PREDICT_FALSE (current_config_index != ~0))
598             {
599               vnet_buffer (b[0])->feature_arc_index = arc;
600               vnet_buffer (b[1])->feature_arc_index = arc;
601               vnet_buffer (b[2])->feature_arc_index = arc;
602               vnet_buffer (b[3])->feature_arc_index = arc;
603               b[0]->current_config_index = current_config_index;
604               b[1]->current_config_index = current_config_index;
605               b[2]->current_config_index = current_config_index;
606               b[3]->current_config_index = current_config_index;
607             }
608
609           /* update vlan subif tx counts, if required */
610           if (PREDICT_FALSE (tx_swif0 != rt->sw_if_index))
611             {
612               vlib_increment_combined_counter (im->combined_sw_if_counters +
613                                                VNET_INTERFACE_COUNTER_TX,
614                                                thread_index, tx_swif0, 1,
615                                                n_bytes_b0);
616             }
617
618           if (PREDICT_FALSE (tx_swif1 != rt->sw_if_index))
619             {
620
621               vlib_increment_combined_counter (im->combined_sw_if_counters +
622                                                VNET_INTERFACE_COUNTER_TX,
623                                                thread_index, tx_swif1, 1,
624                                                n_bytes_b1);
625             }
626
627           if (PREDICT_FALSE (tx_swif2 != rt->sw_if_index))
628             {
629
630               vlib_increment_combined_counter (im->combined_sw_if_counters +
631                                                VNET_INTERFACE_COUNTER_TX,
632                                                thread_index, tx_swif2, 1,
633                                                n_bytes_b2);
634             }
635           if (PREDICT_FALSE (tx_swif3 != rt->sw_if_index))
636             {
637
638               vlib_increment_combined_counter (im->combined_sw_if_counters +
639                                                VNET_INTERFACE_COUNTER_TX,
640                                                thread_index, tx_swif3, 1,
641                                                n_bytes_b3);
642             }
643
644           if (do_tx_offloads)
645             {
646               if (or_flags &
647                   (VNET_BUFFER_F_OFFLOAD_TCP_CKSUM |
648                    VNET_BUFFER_F_OFFLOAD_UDP_CKSUM |
649                    VNET_BUFFER_F_OFFLOAD_IP_CKSUM))
650                 {
651                   calc_checksums (vm, b[0]);
652                   calc_checksums (vm, b[1]);
653                   calc_checksums (vm, b[2]);
654                   calc_checksums (vm, b[3]);
655                 }
656             }
657           b += 4;
658
659         }
660
661       while (from + 1 <= from_end && n_left_to_tx >= 1)
662         {
663           u32 bi0;
664           u32 tx_swif0;
665
666           bi0 = from[0];
667           to_tx[0] = bi0;
668           from += 1;
669           to_tx += 1;
670           n_left_to_tx -= 1;
671
672           /* Be grumpy about zero length buffers for benefit of
673              driver tx function. */
674           ASSERT (b[0]->current_length > 0);
675
676           n_bytes_b0 = vlib_buffer_length_in_chain (vm, b[0]);
677           tx_swif0 = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
678           n_bytes += n_bytes_b0;
679           n_packets += 1;
680
681           if (PREDICT_FALSE (current_config_index != ~0))
682             {
683               vnet_buffer (b[0])->feature_arc_index = arc;
684               b[0]->current_config_index = current_config_index;
685             }
686
687           if (do_segmentation)
688             {
689               if (PREDICT_FALSE (b[0]->flags & VNET_BUFFER_F_GSO))
690                 {
691                   /*
692                    * Undo the enqueue of the b0 - it is not going anywhere,
693                    * and will be freed either after it's segmented or
694                    * when dropped, if there is no buffers to segment into.
695                    */
696                   to_tx -= 1;
697                   n_left_to_tx += 1;
698                   /* undo the counting. */
699                   n_bytes -= n_bytes_b0;
700                   n_packets -= 1;
701
702                   u32 n_tx_bytes = 0;
703
704                   n_tx_bytes =
705                     tso_segment_buffer (vm, ptd, do_tx_offloads, bi0, b[0],
706                                         n_bytes_b0);
707
708                   if (PREDICT_FALSE (n_tx_bytes == 0))
709                     {
710                       drop_one_buffer_and_count (vm, vnm, node, from - 1,
711                                                  VNET_INTERFACE_OUTPUT_ERROR_NO_BUFFERS_FOR_GSO);
712                       b += 1;
713                       continue;
714                     }
715
716                   u16 n_tx_bufs = vec_len (ptd->split_buffers);
717                   u32 *from_tx_seg = ptd->split_buffers;
718
719                   while (n_tx_bufs > 0)
720                     {
721                       if (n_tx_bufs >= n_left_to_tx)
722                         {
723                           while (n_left_to_tx > 0)
724                             {
725                               to_tx[0] = from_tx_seg[0];
726                               to_tx += 1;
727                               from_tx_seg += 1;
728                               n_left_to_tx -= 1;
729                               n_tx_bufs -= 1;
730                               n_packets += 1;
731                             }
732                           vlib_put_next_frame (vm, node, next_index,
733                                                n_left_to_tx);
734                           vlib_get_new_next_frame (vm, node, next_index,
735                                                    to_tx, n_left_to_tx);
736                         }
737                       while (n_tx_bufs > 0)
738                         {
739                           to_tx[0] = from_tx_seg[0];
740                           to_tx += 1;
741                           from_tx_seg += 1;
742                           n_left_to_tx -= 1;
743                           n_tx_bufs -= 1;
744                           n_packets += 1;
745                         }
746                     }
747                   n_bytes += n_tx_bytes;
748                   if (PREDICT_FALSE (tx_swif0 != rt->sw_if_index))
749                     {
750
751                       vlib_increment_combined_counter
752                         (im->combined_sw_if_counters +
753                          VNET_INTERFACE_COUNTER_TX, thread_index, tx_swif0,
754                          _vec_len (ptd->split_buffers), n_tx_bytes);
755                     }
756                   /* The buffers were enqueued. Reset the length */
757                   _vec_len (ptd->split_buffers) = 0;
758                   /* Free the now segmented buffer */
759                   vlib_buffer_free_one (vm, bi0);
760                   b += 1;
761                   continue;
762                 }
763             }
764
765           if (PREDICT_FALSE (tx_swif0 != rt->sw_if_index))
766             {
767
768               vlib_increment_combined_counter (im->combined_sw_if_counters +
769                                                VNET_INTERFACE_COUNTER_TX,
770                                                thread_index, tx_swif0, 1,
771                                                n_bytes_b0);
772             }
773
774           if (do_tx_offloads)
775             calc_checksums (vm, b[0]);
776
777           b += 1;
778         }
779
780       vlib_put_next_frame (vm, node, next_index, n_left_to_tx);
781     }
782
783   /* Update main interface stats. */
784   vlib_increment_combined_counter (im->combined_sw_if_counters
785                                    + VNET_INTERFACE_COUNTER_TX,
786                                    thread_index,
787                                    rt->sw_if_index, n_packets, n_bytes);
788   return n_buffers;
789 }
790 #endif /* CLIB_MARCH_VARIANT */
791
792 static_always_inline void vnet_interface_pcap_tx_trace
793   (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame,
794    int sw_if_index_from_buffer)
795 {
796   u32 n_left_from, *from;
797   u32 sw_if_index;
798
799   if (PREDICT_TRUE (vlib_global_main.pcap[VLIB_TX].pcap_enable == 0))
800     return;
801
802   if (sw_if_index_from_buffer == 0)
803     {
804       vnet_interface_output_runtime_t *rt = (void *) node->runtime_data;
805       sw_if_index = rt->sw_if_index;
806     }
807   else
808     sw_if_index = ~0;
809
810   n_left_from = frame->n_vectors;
811   from = vlib_frame_vector_args (frame);
812
813   while (n_left_from > 0)
814     {
815       u32 bi0 = from[0];
816       vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0);
817
818       if (sw_if_index_from_buffer)
819         sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
820
821       if (vlib_global_main.pcap[VLIB_TX].pcap_sw_if_index == 0 ||
822           vlib_global_main.pcap[VLIB_TX].pcap_sw_if_index == sw_if_index)
823         pcap_add_buffer (&vlib_global_main.pcap[VLIB_TX].pcap_main, vm, bi0,
824                          512);
825       from++;
826       n_left_from--;
827     }
828 }
829
830 #ifndef CLIB_MARCH_VARIANT
831 static_always_inline uword
832 vnet_interface_output_node_inline (vlib_main_t * vm,
833                                    vlib_node_runtime_t * node,
834                                    vlib_frame_t * frame, vnet_main_t * vnm,
835                                    vnet_hw_interface_t * hi,
836                                    int do_tx_offloads)
837 {
838   /*
839    * The 3-headed "if" is here because we want to err on the side
840    * of not impacting the non-GSO performance - so for the more
841    * common case of no GSO interfaces we want to prevent the
842    * segmentation codepath from being there altogether.
843    */
844   if (PREDICT_TRUE (vnm->interface_main.gso_interface_count == 0))
845     return vnet_interface_output_node_inline_gso (vm, node, frame, vnm, hi,
846                                                   do_tx_offloads,
847                                                   /* do_segmentation */ 0);
848   else if (hi->flags & VNET_HW_INTERFACE_FLAG_SUPPORTS_GSO)
849     return vnet_interface_output_node_inline_gso (vm, node, frame, vnm, hi,
850                                                   do_tx_offloads,
851                                                   /* do_segmentation */ 0);
852   else
853     return vnet_interface_output_node_inline_gso (vm, node, frame, vnm, hi,
854                                                   do_tx_offloads,
855                                                   /* do_segmentation */ 1);
856 }
857
858 uword
859 vnet_interface_output_node (vlib_main_t * vm, vlib_node_runtime_t * node,
860                             vlib_frame_t * frame)
861 {
862   vnet_main_t *vnm = vnet_get_main ();
863   vnet_hw_interface_t *hi;
864   vnet_interface_output_runtime_t *rt = (void *) node->runtime_data;
865   hi = vnet_get_sup_hw_interface (vnm, rt->sw_if_index);
866
867   vnet_interface_pcap_tx_trace (vm, node, frame,
868                                 0 /* sw_if_index_from_buffer */ );
869
870   if (hi->flags & VNET_HW_INTERFACE_FLAG_SUPPORTS_TX_L4_CKSUM_OFFLOAD)
871     return vnet_interface_output_node_inline (vm, node, frame, vnm, hi,
872                                               /* do_tx_offloads */ 0);
873   else
874     return vnet_interface_output_node_inline (vm, node, frame, vnm, hi,
875                                               /* do_tx_offloads */ 1);
876 }
877 #endif /* CLIB_MARCH_VARIANT */
878
879 /* Use buffer's sw_if_index[VNET_TX] to choose output interface. */
880 VLIB_NODE_FN (vnet_per_buffer_interface_output_node) (vlib_main_t * vm,
881                                                       vlib_node_runtime_t *
882                                                       node,
883                                                       vlib_frame_t * frame)
884 {
885   vnet_main_t *vnm = vnet_get_main ();
886   u32 n_left_to_next, *from, *to_next;
887   u32 n_left_from, next_index;
888
889   vnet_interface_pcap_tx_trace (vm, node, frame,
890                                 1 /* sw_if_index_from_buffer */ );
891
892   n_left_from = frame->n_vectors;
893
894   from = vlib_frame_vector_args (frame);
895   next_index = node->cached_next_index;
896
897   while (n_left_from > 0)
898     {
899       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
900
901       while (n_left_from >= 4 && n_left_to_next >= 2)
902         {
903           u32 bi0, bi1, next0, next1;
904           vlib_buffer_t *b0, *b1;
905           vnet_hw_interface_t *hi0, *hi1;
906
907           /* Prefetch next iteration. */
908           vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
909           vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
910
911           bi0 = from[0];
912           bi1 = from[1];
913           to_next[0] = bi0;
914           to_next[1] = bi1;
915           from += 2;
916           to_next += 2;
917           n_left_to_next -= 2;
918           n_left_from -= 2;
919
920           b0 = vlib_get_buffer (vm, bi0);
921           b1 = vlib_get_buffer (vm, bi1);
922
923           hi0 =
924             vnet_get_sup_hw_interface (vnm,
925                                        vnet_buffer (b0)->sw_if_index
926                                        [VLIB_TX]);
927           hi1 =
928             vnet_get_sup_hw_interface (vnm,
929                                        vnet_buffer (b1)->sw_if_index
930                                        [VLIB_TX]);
931
932           next0 = hi0->output_node_next_index;
933           next1 = hi1->output_node_next_index;
934
935           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
936                                            n_left_to_next, bi0, bi1, next0,
937                                            next1);
938         }
939
940       while (n_left_from > 0 && n_left_to_next > 0)
941         {
942           u32 bi0, next0;
943           vlib_buffer_t *b0;
944           vnet_hw_interface_t *hi0;
945
946           bi0 = from[0];
947           to_next[0] = bi0;
948           from += 1;
949           to_next += 1;
950           n_left_to_next -= 1;
951           n_left_from -= 1;
952
953           b0 = vlib_get_buffer (vm, bi0);
954
955           hi0 =
956             vnet_get_sup_hw_interface (vnm,
957                                        vnet_buffer (b0)->sw_if_index
958                                        [VLIB_TX]);
959
960           next0 = hi0->output_node_next_index;
961
962           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
963                                            n_left_to_next, bi0, next0);
964         }
965
966       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
967     }
968
969   return frame->n_vectors;
970 }
971
972 typedef struct vnet_error_trace_t_
973 {
974   u32 sw_if_index;
975 } vnet_error_trace_t;
976
977
978 static u8 *
979 format_vnet_error_trace (u8 * s, va_list * va)
980 {
981   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
982   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
983   vnet_error_trace_t *t = va_arg (*va, vnet_error_trace_t *);
984
985   s = format (s, "rx:%U", format_vnet_sw_if_index_name,
986               vnet_get_main (), t->sw_if_index);
987
988   return s;
989 }
990
991 static void
992 interface_trace_buffers (vlib_main_t * vm,
993                          vlib_node_runtime_t * node, vlib_frame_t * frame)
994 {
995   u32 n_left, *buffers;
996
997   buffers = vlib_frame_vector_args (frame);
998   n_left = frame->n_vectors;
999
1000   while (n_left >= 4)
1001     {
1002       u32 bi0, bi1;
1003       vlib_buffer_t *b0, *b1;
1004       vnet_error_trace_t *t0, *t1;
1005
1006       /* Prefetch next iteration. */
1007       vlib_prefetch_buffer_with_index (vm, buffers[2], LOAD);
1008       vlib_prefetch_buffer_with_index (vm, buffers[3], LOAD);
1009
1010       bi0 = buffers[0];
1011       bi1 = buffers[1];
1012
1013       b0 = vlib_get_buffer (vm, bi0);
1014       b1 = vlib_get_buffer (vm, bi1);
1015
1016       if (b0->flags & VLIB_BUFFER_IS_TRACED)
1017         {
1018           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
1019           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
1020         }
1021       if (b1->flags & VLIB_BUFFER_IS_TRACED)
1022         {
1023           t1 = vlib_add_trace (vm, node, b1, sizeof (t1[0]));
1024           t1->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX];
1025         }
1026       buffers += 2;
1027       n_left -= 2;
1028     }
1029
1030   while (n_left >= 1)
1031     {
1032       u32 bi0;
1033       vlib_buffer_t *b0;
1034       vnet_error_trace_t *t0;
1035
1036       bi0 = buffers[0];
1037
1038       b0 = vlib_get_buffer (vm, bi0);
1039
1040       if (b0->flags & VLIB_BUFFER_IS_TRACED)
1041         {
1042           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
1043           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
1044         }
1045       buffers += 1;
1046       n_left -= 1;
1047     }
1048 }
1049
1050 typedef enum
1051 {
1052   VNET_ERROR_DISPOSITION_DROP,
1053   VNET_ERROR_DISPOSITION_PUNT,
1054   VNET_ERROR_N_DISPOSITION,
1055 } vnet_error_disposition_t;
1056
1057 static_always_inline uword
1058 interface_drop_punt (vlib_main_t * vm,
1059                      vlib_node_runtime_t * node,
1060                      vlib_frame_t * frame,
1061                      vnet_error_disposition_t disposition)
1062 {
1063   u32 *from, n_left, thread_index, *sw_if_index;
1064   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
1065   u32 sw_if_indices[VLIB_FRAME_SIZE];
1066   vlib_simple_counter_main_t *cm;
1067   u16 nexts[VLIB_FRAME_SIZE];
1068   vnet_main_t *vnm;
1069
1070   vnm = vnet_get_main ();
1071   thread_index = vm->thread_index;
1072   from = vlib_frame_vector_args (frame);
1073   n_left = frame->n_vectors;
1074   b = bufs;
1075   sw_if_index = sw_if_indices;
1076
1077   vlib_get_buffers (vm, from, bufs, n_left);
1078
1079   if (node->flags & VLIB_NODE_FLAG_TRACE)
1080     interface_trace_buffers (vm, node, frame);
1081
1082   /* All going to drop regardless, this is just a counting exercise */
1083   clib_memset (nexts, 0, sizeof (nexts));
1084
1085   cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
1086                          (disposition == VNET_ERROR_DISPOSITION_PUNT
1087                           ? VNET_INTERFACE_COUNTER_PUNT
1088                           : VNET_INTERFACE_COUNTER_DROP));
1089
1090   /* collect the array of interfaces first ... */
1091   while (n_left >= 4)
1092     {
1093       if (n_left >= 12)
1094         {
1095           /* Prefetch 8 ahead - there's not much going on in each iteration */
1096           vlib_prefetch_buffer_header (b[4], LOAD);
1097           vlib_prefetch_buffer_header (b[5], LOAD);
1098           vlib_prefetch_buffer_header (b[6], LOAD);
1099           vlib_prefetch_buffer_header (b[7], LOAD);
1100         }
1101       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
1102       sw_if_index[1] = vnet_buffer (b[1])->sw_if_index[VLIB_RX];
1103       sw_if_index[2] = vnet_buffer (b[2])->sw_if_index[VLIB_RX];
1104       sw_if_index[3] = vnet_buffer (b[3])->sw_if_index[VLIB_RX];
1105
1106       sw_if_index += 4;
1107       n_left -= 4;
1108       b += 4;
1109     }
1110   while (n_left)
1111     {
1112       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
1113
1114       sw_if_index += 1;
1115       n_left -= 1;
1116       b += 1;
1117     }
1118
1119   /* ... then count against them in blocks */
1120   n_left = frame->n_vectors;
1121
1122   while (n_left)
1123     {
1124       vnet_sw_interface_t *sw_if0;
1125       u16 off, count;
1126
1127       off = frame->n_vectors - n_left;
1128
1129       sw_if_index = sw_if_indices + off;
1130
1131       count = clib_count_equal_u32 (sw_if_index, n_left);
1132       n_left -= count;
1133
1134       vlib_increment_simple_counter (cm, thread_index, sw_if_index[0], count);
1135
1136       /* Increment super-interface drop/punt counters for
1137          sub-interfaces. */
1138       sw_if0 = vnet_get_sw_interface (vnm, sw_if_index[0]);
1139       if (sw_if0->sup_sw_if_index != sw_if_index[0])
1140         vlib_increment_simple_counter
1141           (cm, thread_index, sw_if0->sup_sw_if_index, count);
1142     }
1143
1144   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
1145
1146   return frame->n_vectors;
1147 }
1148
1149 static inline void
1150 pcap_drop_trace (vlib_main_t * vm,
1151                  vnet_interface_main_t * im, vlib_frame_t * f)
1152 {
1153   u32 *from;
1154   u32 n_left = f->n_vectors;
1155   vlib_buffer_t *b0, *p1;
1156   u32 bi0;
1157   i16 save_current_data;
1158   u16 save_current_length;
1159
1160   from = vlib_frame_vector_args (f);
1161
1162   while (n_left > 0)
1163     {
1164       if (PREDICT_TRUE (n_left > 1))
1165         {
1166           p1 = vlib_get_buffer (vm, from[1]);
1167           vlib_prefetch_buffer_header (p1, LOAD);
1168         }
1169
1170       bi0 = from[0];
1171       b0 = vlib_get_buffer (vm, bi0);
1172       from++;
1173       n_left--;
1174
1175       /* See if we're pointedly ignoring this specific error */
1176       if (im->pcap_drop_filter_hash
1177           && hash_get (im->pcap_drop_filter_hash, b0->error))
1178         continue;
1179
1180       /* Trace all drops, or drops received on a specific interface */
1181       if (im->pcap_sw_if_index == 0 ||
1182           im->pcap_sw_if_index == vnet_buffer (b0)->sw_if_index[VLIB_RX])
1183         {
1184           save_current_data = b0->current_data;
1185           save_current_length = b0->current_length;
1186
1187           /*
1188            * Typically, we'll need to rewind the buffer
1189            */
1190           if (b0->current_data > 0)
1191             vlib_buffer_advance (b0, (word) - b0->current_data);
1192
1193           pcap_add_buffer (&im->pcap_main, vm, bi0, 512);
1194
1195           b0->current_data = save_current_data;
1196           b0->current_length = save_current_length;
1197         }
1198     }
1199 }
1200
1201 #ifndef CLIB_MARCH_VARIANT
1202 void
1203 vnet_pcap_drop_trace_filter_add_del (u32 error_index, int is_add)
1204 {
1205   vnet_interface_main_t *im = &vnet_get_main ()->interface_main;
1206
1207   if (im->pcap_drop_filter_hash == 0)
1208     im->pcap_drop_filter_hash = hash_create (0, sizeof (uword));
1209
1210   if (is_add)
1211     hash_set (im->pcap_drop_filter_hash, error_index, 1);
1212   else
1213     hash_unset (im->pcap_drop_filter_hash, error_index);
1214 }
1215 #endif /* CLIB_MARCH_VARIANT */
1216
1217 VLIB_NODE_FN (interface_drop) (vlib_main_t * vm,
1218                                vlib_node_runtime_t * node,
1219                                vlib_frame_t * frame)
1220 {
1221   vnet_interface_main_t *im = &vnet_get_main ()->interface_main;
1222
1223   if (PREDICT_FALSE (im->drop_pcap_enable))
1224     pcap_drop_trace (vm, im, frame);
1225
1226   return interface_drop_punt (vm, node, frame, VNET_ERROR_DISPOSITION_DROP);
1227 }
1228
1229 VLIB_NODE_FN (interface_punt) (vlib_main_t * vm,
1230                                vlib_node_runtime_t * node,
1231                                vlib_frame_t * frame)
1232 {
1233   return interface_drop_punt (vm, node, frame, VNET_ERROR_DISPOSITION_PUNT);
1234 }
1235
1236 /* *INDENT-OFF* */
1237 VLIB_REGISTER_NODE (interface_drop) = {
1238   .name = "error-drop",
1239   .vector_size = sizeof (u32),
1240   .format_trace = format_vnet_error_trace,
1241   .n_next_nodes = 1,
1242   .next_nodes = {
1243     [0] = "drop",
1244   },
1245 };
1246 /* *INDENT-ON* */
1247
1248 /* *INDENT-OFF* */
1249 VLIB_REGISTER_NODE (interface_punt) = {
1250   .name = "error-punt",
1251   .vector_size = sizeof (u32),
1252   .format_trace = format_vnet_error_trace,
1253   .n_next_nodes = 1,
1254   .next_nodes = {
1255     [0] = "punt",
1256   },
1257 };
1258 /* *INDENT-ON* */
1259
1260 /* *INDENT-OFF* */
1261 VLIB_REGISTER_NODE (vnet_per_buffer_interface_output_node) = {
1262   .name = "interface-output",
1263   .vector_size = sizeof (u32),
1264 };
1265 /* *INDENT-ON* */
1266
1267 static uword
1268 interface_tx_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1269                       vlib_frame_t * from_frame)
1270 {
1271   vnet_main_t *vnm = vnet_get_main ();
1272   u32 last_sw_if_index = ~0;
1273   vlib_frame_t *to_frame = 0;
1274   vnet_hw_interface_t *hw = 0;
1275   u32 *from, *to_next = 0;
1276   u32 n_left_from;
1277
1278   from = vlib_frame_vector_args (from_frame);
1279   n_left_from = from_frame->n_vectors;
1280   while (n_left_from > 0)
1281     {
1282       u32 bi0;
1283       vlib_buffer_t *b0;
1284       u32 sw_if_index0;
1285
1286       bi0 = from[0];
1287       from++;
1288       n_left_from--;
1289       b0 = vlib_get_buffer (vm, bi0);
1290       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
1291
1292       if (PREDICT_FALSE ((last_sw_if_index != sw_if_index0) || to_frame == 0))
1293         {
1294           if (to_frame)
1295             {
1296               hw = vnet_get_sup_hw_interface (vnm, last_sw_if_index);
1297               vlib_put_frame_to_node (vm, hw->tx_node_index, to_frame);
1298             }
1299           last_sw_if_index = sw_if_index0;
1300           hw = vnet_get_sup_hw_interface (vnm, sw_if_index0);
1301           to_frame = vlib_get_frame_to_node (vm, hw->tx_node_index);
1302           to_next = vlib_frame_vector_args (to_frame);
1303         }
1304
1305       to_next[0] = bi0;
1306       to_next++;
1307       to_frame->n_vectors++;
1308     }
1309   vlib_put_frame_to_node (vm, hw->tx_node_index, to_frame);
1310   return from_frame->n_vectors;
1311 }
1312
1313 /* *INDENT-OFF* */
1314 VLIB_REGISTER_NODE (interface_tx, static) = {
1315   .function = interface_tx_node_fn,
1316   .name = "interface-tx",
1317   .vector_size = sizeof (u32),
1318   .n_next_nodes = 1,
1319   .next_nodes = {
1320     [0] = "error-drop",
1321   },
1322 };
1323
1324 VNET_FEATURE_ARC_INIT (interface_output, static) =
1325 {
1326   .arc_name  = "interface-output",
1327   .start_nodes = VNET_FEATURES (0),
1328   .last_in_arc = "interface-tx",
1329   .arc_index_ptr = &vnet_main.interface_main.output_feature_arc_index,
1330 };
1331
1332 VNET_FEATURE_INIT (span_tx, static) = {
1333   .arc_name = "interface-output",
1334   .node_name = "span-output",
1335   .runs_before = VNET_FEATURES ("interface-tx"),
1336 };
1337
1338 VNET_FEATURE_INIT (ipsec_if_tx, static) = {
1339   .arc_name = "interface-output",
1340   .node_name = "ipsec-if-output",
1341   .runs_before = VNET_FEATURES ("interface-tx"),
1342 };
1343
1344 VNET_FEATURE_INIT (interface_tx, static) = {
1345   .arc_name = "interface-output",
1346   .node_name = "interface-tx",
1347   .runs_before = 0,
1348 };
1349 /* *INDENT-ON* */
1350
1351 #ifndef CLIB_MARCH_VARIANT
1352 clib_error_t *
1353 vnet_per_buffer_interface_output_hw_interface_add_del (vnet_main_t * vnm,
1354                                                        u32 hw_if_index,
1355                                                        u32 is_create)
1356 {
1357   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
1358   u32 next_index;
1359
1360   if (hi->output_node_index == 0)
1361     return 0;
1362
1363   next_index = vlib_node_add_next
1364     (vnm->vlib_main, vnet_per_buffer_interface_output_node.index,
1365      hi->output_node_index);
1366   hi->output_node_next_index = next_index;
1367
1368   return 0;
1369 }
1370
1371 VNET_HW_INTERFACE_ADD_DEL_FUNCTION
1372   (vnet_per_buffer_interface_output_hw_interface_add_del);
1373
1374 void
1375 vnet_set_interface_output_node (vnet_main_t * vnm,
1376                                 u32 hw_if_index, u32 node_index)
1377 {
1378   ASSERT (node_index);
1379   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
1380   u32 next_index = vlib_node_add_next
1381     (vnm->vlib_main, vnet_per_buffer_interface_output_node.index, node_index);
1382   hi->output_node_next_index = next_index;
1383   hi->output_node_index = node_index;
1384 }
1385 #endif /* CLIB_MARCH_VARIANT */
1386
1387 static clib_error_t *
1388 pcap_drop_trace_command_fn (vlib_main_t * vm,
1389                             unformat_input_t * input,
1390                             vlib_cli_command_t * cmd)
1391 {
1392   vnet_main_t *vnm = vnet_get_main ();
1393   vnet_interface_main_t *im = &vnm->interface_main;
1394   u8 *filename;
1395   u32 max;
1396   int matched = 0;
1397   clib_error_t *error = 0;
1398
1399   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1400     {
1401       if (unformat (input, "on"))
1402         {
1403           if (im->drop_pcap_enable == 0)
1404             {
1405               if (im->pcap_filename == 0)
1406                 im->pcap_filename = format (0, "/tmp/drop.pcap%c", 0);
1407
1408               clib_memset (&im->pcap_main, 0, sizeof (im->pcap_main));
1409               im->pcap_main.file_name = (char *) im->pcap_filename;
1410               im->pcap_main.n_packets_to_capture = 100;
1411               if (im->pcap_pkts_to_capture)
1412                 im->pcap_main.n_packets_to_capture = im->pcap_pkts_to_capture;
1413
1414               im->pcap_main.packet_type = PCAP_PACKET_TYPE_ethernet;
1415               im->drop_pcap_enable = 1;
1416               matched = 1;
1417               vlib_cli_output (vm, "pcap drop capture on...");
1418             }
1419           else
1420             {
1421               vlib_cli_output (vm, "pcap drop capture already on...");
1422             }
1423           matched = 1;
1424         }
1425       else if (unformat (input, "off"))
1426         {
1427           matched = 1;
1428
1429           if (im->drop_pcap_enable)
1430             {
1431               vlib_cli_output (vm, "captured %d pkts...",
1432                                im->pcap_main.n_packets_captured);
1433               if (im->pcap_main.n_packets_captured)
1434                 {
1435                   im->pcap_main.n_packets_to_capture =
1436                     im->pcap_main.n_packets_captured;
1437                   error = pcap_write (&im->pcap_main);
1438                   if (error)
1439                     clib_error_report (error);
1440                   else
1441                     vlib_cli_output (vm, "saved to %s...", im->pcap_filename);
1442                 }
1443             }
1444           else
1445             {
1446               vlib_cli_output (vm, "pcap drop capture already off...");
1447             }
1448
1449           im->drop_pcap_enable = 0;
1450         }
1451       else if (unformat (input, "max %d", &max))
1452         {
1453           im->pcap_pkts_to_capture = max;
1454           matched = 1;
1455         }
1456
1457       else if (unformat (input, "intfc %U",
1458                          unformat_vnet_sw_interface, vnm,
1459                          &im->pcap_sw_if_index))
1460         matched = 1;
1461       else if (unformat (input, "intfc any"))
1462         {
1463           im->pcap_sw_if_index = 0;
1464           matched = 1;
1465         }
1466       else if (unformat (input, "file %s", &filename))
1467         {
1468           u8 *chroot_filename;
1469           /* Brain-police user path input */
1470           if (strstr ((char *) filename, "..")
1471               || index ((char *) filename, '/'))
1472             {
1473               vlib_cli_output (vm, "illegal characters in filename '%s'",
1474                                filename);
1475               continue;
1476             }
1477
1478           chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1479           vec_free (filename);
1480
1481           if (im->pcap_filename)
1482             vec_free (im->pcap_filename);
1483           im->pcap_filename = chroot_filename;
1484           im->pcap_main.file_name = (char *) im->pcap_filename;
1485           matched = 1;
1486         }
1487       else if (unformat (input, "status"))
1488         {
1489           if (im->drop_pcap_enable == 0)
1490             {
1491               vlib_cli_output (vm, "pcap drop capture is off...");
1492               continue;
1493             }
1494
1495           vlib_cli_output (vm, "pcap drop capture: %d of %d pkts...",
1496                            im->pcap_main.n_packets_captured,
1497                            im->pcap_main.n_packets_to_capture);
1498           matched = 1;
1499         }
1500
1501       else
1502         break;
1503     }
1504
1505   if (matched == 0)
1506     return clib_error_return (0, "unknown input `%U'",
1507                               format_unformat_error, input);
1508
1509   return 0;
1510 }
1511
1512 /* *INDENT-OFF* */
1513 VLIB_CLI_COMMAND (pcap_trace_command, static) = {
1514   .path = "pcap drop trace",
1515   .short_help =
1516   "pcap drop trace on off max <nn> intfc <intfc> file <name> status",
1517   .function = pcap_drop_trace_command_fn,
1518 };
1519 /* *INDENT-ON* */
1520
1521 /*
1522  * fd.io coding-style-patch-verification: ON
1523  *
1524  * Local Variables:
1525  * eval: (c-set-style "gnu")
1526  * End:
1527  */