classify: per-interface rx/tx pcap capture filters
[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 #include <vnet/classify/trace_classify.h>
47
48 typedef struct
49 {
50   u32 sw_if_index;
51   u32 flags;
52   u8 data[128 - 2 * sizeof (u32)];
53 }
54 interface_output_trace_t;
55
56 #ifndef CLIB_MARCH_VARIANT
57 u8 *
58 format_vnet_interface_output_trace (u8 * s, va_list * va)
59 {
60   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
61   vlib_node_t *node = va_arg (*va, vlib_node_t *);
62   interface_output_trace_t *t = va_arg (*va, interface_output_trace_t *);
63   vnet_main_t *vnm = vnet_get_main ();
64   vnet_sw_interface_t *si;
65   u32 indent;
66
67   if (t->sw_if_index != (u32) ~ 0)
68     {
69       indent = format_get_indent (s);
70
71       if (pool_is_free_index
72           (vnm->interface_main.sw_interfaces, t->sw_if_index))
73         {
74           /* the interface may have been deleted by the time the trace is printed */
75           s = format (s, "sw_if_index: %d ", t->sw_if_index);
76         }
77       else
78         {
79           si = vnet_get_sw_interface (vnm, t->sw_if_index);
80           s =
81             format (s, "%U ", format_vnet_sw_interface_name, vnm, si,
82                     t->flags);
83         }
84       s =
85         format (s, "\n%U%U", format_white_space, indent,
86                 node->format_buffer ? node->format_buffer : format_hex_bytes,
87                 t->data, sizeof (t->data));
88     }
89   return s;
90 }
91
92 static void
93 vnet_interface_output_trace (vlib_main_t * vm,
94                              vlib_node_runtime_t * node,
95                              vlib_frame_t * frame, uword n_buffers)
96 {
97   u32 n_left, *from;
98
99   n_left = n_buffers;
100   from = vlib_frame_vector_args (frame);
101
102   while (n_left >= 4)
103     {
104       u32 bi0, bi1;
105       vlib_buffer_t *b0, *b1;
106       interface_output_trace_t *t0, *t1;
107
108       /* Prefetch next iteration. */
109       vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
110       vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
111
112       bi0 = from[0];
113       bi1 = from[1];
114
115       b0 = vlib_get_buffer (vm, bi0);
116       b1 = vlib_get_buffer (vm, bi1);
117
118       if (b0->flags & VLIB_BUFFER_IS_TRACED)
119         {
120           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
121           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
122           t0->flags = b0->flags;
123           clib_memcpy_fast (t0->data, vlib_buffer_get_current (b0),
124                             sizeof (t0->data));
125         }
126       if (b1->flags & VLIB_BUFFER_IS_TRACED)
127         {
128           t1 = vlib_add_trace (vm, node, b1, sizeof (t1[0]));
129           t1->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_TX];
130           t1->flags = b1->flags;
131           clib_memcpy_fast (t1->data, vlib_buffer_get_current (b1),
132                             sizeof (t1->data));
133         }
134       from += 2;
135       n_left -= 2;
136     }
137
138   while (n_left >= 1)
139     {
140       u32 bi0;
141       vlib_buffer_t *b0;
142       interface_output_trace_t *t0;
143
144       bi0 = from[0];
145
146       b0 = vlib_get_buffer (vm, bi0);
147
148       if (b0->flags & VLIB_BUFFER_IS_TRACED)
149         {
150           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
151           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
152           t0->flags = b0->flags;
153           clib_memcpy_fast (t0->data, vlib_buffer_get_current (b0),
154                             sizeof (t0->data));
155         }
156       from += 1;
157       n_left -= 1;
158     }
159 }
160
161 static_always_inline void
162 calc_checksums (vlib_main_t * vm, vlib_buffer_t * b)
163 {
164   tcp_header_t *th;
165   udp_header_t *uh;
166
167   int is_ip4 = (b->flags & VNET_BUFFER_F_IS_IP4) != 0;
168   int is_ip6 = (b->flags & VNET_BUFFER_F_IS_IP6) != 0;
169
170   ASSERT (!(is_ip4 && is_ip6));
171
172   th = (tcp_header_t *) (b->data + vnet_buffer (b)->l4_hdr_offset);
173   uh = (udp_header_t *) (b->data + vnet_buffer (b)->l4_hdr_offset);
174
175   if (is_ip4)
176     {
177       ip4_header_t *ip4;
178
179       ip4 = (ip4_header_t *) (b->data + vnet_buffer (b)->l3_hdr_offset);
180       if (b->flags & VNET_BUFFER_F_OFFLOAD_IP_CKSUM)
181         ip4->checksum = ip4_header_checksum (ip4);
182       if (b->flags & VNET_BUFFER_F_OFFLOAD_TCP_CKSUM)
183         {
184           th->checksum = 0;
185           th->checksum = ip4_tcp_udp_compute_checksum (vm, b, ip4);
186         }
187       else if (b->flags & VNET_BUFFER_F_OFFLOAD_UDP_CKSUM)
188         uh->checksum = ip4_tcp_udp_compute_checksum (vm, b, ip4);
189     }
190   else if (is_ip6)
191     {
192       int bogus;
193       ip6_header_t *ip6;
194
195       ip6 = (ip6_header_t *) (b->data + vnet_buffer (b)->l3_hdr_offset);
196       if (b->flags & VNET_BUFFER_F_OFFLOAD_TCP_CKSUM)
197         {
198           th->checksum = 0;
199           th->checksum =
200             ip6_tcp_udp_icmp_compute_checksum (vm, b, ip6, &bogus);
201         }
202       else if (b->flags & VNET_BUFFER_F_OFFLOAD_UDP_CKSUM)
203         {
204           uh->checksum = 0;
205           uh->checksum =
206             ip6_tcp_udp_icmp_compute_checksum (vm, b, ip6, &bogus);
207         }
208     }
209   b->flags &= ~VNET_BUFFER_F_OFFLOAD_TCP_CKSUM;
210   b->flags &= ~VNET_BUFFER_F_OFFLOAD_UDP_CKSUM;
211   b->flags &= ~VNET_BUFFER_F_OFFLOAD_IP_CKSUM;
212 }
213
214 static_always_inline uword
215 vnet_interface_output_node_inline (vlib_main_t * vm,
216                                    vlib_node_runtime_t * node,
217                                    vlib_frame_t * frame,
218                                    vnet_main_t * vnm,
219                                    vnet_hw_interface_t * hi,
220                                    int do_tx_offloads)
221 {
222   vnet_interface_output_runtime_t *rt = (void *) node->runtime_data;
223   vnet_sw_interface_t *si;
224   u32 n_left_to_tx, *from, *from_end, *to_tx;
225   u32 n_bytes, n_buffers, n_packets;
226   u32 n_bytes_b0, n_bytes_b1, n_bytes_b2, n_bytes_b3;
227   u32 thread_index = vm->thread_index;
228   vnet_interface_main_t *im = &vnm->interface_main;
229   u32 next_index = VNET_INTERFACE_OUTPUT_NEXT_TX;
230   u32 current_config_index = ~0;
231   u8 arc = im->output_feature_arc_index;
232   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
233
234   n_buffers = frame->n_vectors;
235
236   if (node->flags & VLIB_NODE_FLAG_TRACE)
237     vnet_interface_output_trace (vm, node, frame, n_buffers);
238
239   from = vlib_frame_vector_args (frame);
240   vlib_get_buffers (vm, from, b, n_buffers);
241
242   if (rt->is_deleted)
243     return vlib_error_drop_buffers (vm, node, from,
244                                     /* buffer stride */ 1,
245                                     n_buffers,
246                                     VNET_INTERFACE_OUTPUT_NEXT_DROP,
247                                     node->node_index,
248                                     VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DELETED);
249
250   si = vnet_get_sw_interface (vnm, rt->sw_if_index);
251   hi = vnet_get_sup_hw_interface (vnm, rt->sw_if_index);
252   if (!(si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ||
253       !(hi->flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
254     {
255       vlib_simple_counter_main_t *cm;
256
257       cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
258                              VNET_INTERFACE_COUNTER_TX_ERROR);
259       vlib_increment_simple_counter (cm, thread_index,
260                                      rt->sw_if_index, n_buffers);
261
262       return vlib_error_drop_buffers (vm, node, from,
263                                       /* buffer stride */ 1,
264                                       n_buffers,
265                                       VNET_INTERFACE_OUTPUT_NEXT_DROP,
266                                       node->node_index,
267                                       VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DOWN);
268     }
269
270   from_end = from + n_buffers;
271
272   /* Total byte count of all buffers. */
273   n_bytes = 0;
274   n_packets = 0;
275
276   /* interface-output feature arc handling */
277   if (PREDICT_FALSE (vnet_have_features (arc, rt->sw_if_index)))
278     {
279       vnet_feature_config_main_t *fcm;
280       fcm = vnet_feature_get_config_main (arc);
281       current_config_index = vnet_get_feature_config_index (arc,
282                                                             rt->sw_if_index);
283       vnet_get_config_data (&fcm->config_main, &current_config_index,
284                             &next_index, 0);
285     }
286
287   while (from < from_end)
288     {
289       /* Get new next frame since previous incomplete frame may have less
290          than VNET_FRAME_SIZE vectors in it. */
291       vlib_get_new_next_frame (vm, node, next_index, to_tx, n_left_to_tx);
292
293       while (from + 8 <= from_end && n_left_to_tx >= 4)
294         {
295           u32 bi0, bi1, bi2, bi3;
296           u32 tx_swif0, tx_swif1, tx_swif2, tx_swif3;
297           u32 or_flags;
298
299           /* Prefetch next iteration. */
300           vlib_prefetch_buffer_header (b[4], LOAD);
301           vlib_prefetch_buffer_header (b[5], LOAD);
302           vlib_prefetch_buffer_header (b[6], LOAD);
303           vlib_prefetch_buffer_header (b[7], LOAD);
304
305           bi0 = from[0];
306           bi1 = from[1];
307           bi2 = from[2];
308           bi3 = from[3];
309           to_tx[0] = bi0;
310           to_tx[1] = bi1;
311           to_tx[2] = bi2;
312           to_tx[3] = bi3;
313
314           or_flags = b[0]->flags | b[1]->flags | b[2]->flags | b[3]->flags;
315
316           from += 4;
317           to_tx += 4;
318           n_left_to_tx -= 4;
319
320           /* Be grumpy about zero length buffers for benefit of
321              driver tx function. */
322           ASSERT (b[0]->current_length > 0);
323           ASSERT (b[1]->current_length > 0);
324           ASSERT (b[2]->current_length > 0);
325           ASSERT (b[3]->current_length > 0);
326
327           n_bytes_b0 = vlib_buffer_length_in_chain (vm, b[0]);
328           n_bytes_b1 = vlib_buffer_length_in_chain (vm, b[1]);
329           n_bytes_b2 = vlib_buffer_length_in_chain (vm, b[2]);
330           n_bytes_b3 = vlib_buffer_length_in_chain (vm, b[3]);
331           tx_swif0 = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
332           tx_swif1 = vnet_buffer (b[1])->sw_if_index[VLIB_TX];
333           tx_swif2 = vnet_buffer (b[2])->sw_if_index[VLIB_TX];
334           tx_swif3 = vnet_buffer (b[3])->sw_if_index[VLIB_TX];
335
336           n_bytes += n_bytes_b0 + n_bytes_b1;
337           n_bytes += n_bytes_b2 + n_bytes_b3;
338           n_packets += 4;
339
340           if (PREDICT_FALSE (current_config_index != ~0))
341             {
342               vnet_buffer (b[0])->feature_arc_index = arc;
343               vnet_buffer (b[1])->feature_arc_index = arc;
344               vnet_buffer (b[2])->feature_arc_index = arc;
345               vnet_buffer (b[3])->feature_arc_index = arc;
346               b[0]->current_config_index = current_config_index;
347               b[1]->current_config_index = current_config_index;
348               b[2]->current_config_index = current_config_index;
349               b[3]->current_config_index = current_config_index;
350             }
351
352           /* update vlan subif tx counts, if required */
353           if (PREDICT_FALSE (tx_swif0 != rt->sw_if_index))
354             {
355               vlib_increment_combined_counter (im->combined_sw_if_counters +
356                                                VNET_INTERFACE_COUNTER_TX,
357                                                thread_index, tx_swif0, 1,
358                                                n_bytes_b0);
359             }
360
361           if (PREDICT_FALSE (tx_swif1 != rt->sw_if_index))
362             {
363
364               vlib_increment_combined_counter (im->combined_sw_if_counters +
365                                                VNET_INTERFACE_COUNTER_TX,
366                                                thread_index, tx_swif1, 1,
367                                                n_bytes_b1);
368             }
369
370           if (PREDICT_FALSE (tx_swif2 != rt->sw_if_index))
371             {
372
373               vlib_increment_combined_counter (im->combined_sw_if_counters +
374                                                VNET_INTERFACE_COUNTER_TX,
375                                                thread_index, tx_swif2, 1,
376                                                n_bytes_b2);
377             }
378           if (PREDICT_FALSE (tx_swif3 != rt->sw_if_index))
379             {
380
381               vlib_increment_combined_counter (im->combined_sw_if_counters +
382                                                VNET_INTERFACE_COUNTER_TX,
383                                                thread_index, tx_swif3, 1,
384                                                n_bytes_b3);
385             }
386
387           if (do_tx_offloads)
388             {
389               if (or_flags &
390                   (VNET_BUFFER_F_OFFLOAD_TCP_CKSUM |
391                    VNET_BUFFER_F_OFFLOAD_UDP_CKSUM |
392                    VNET_BUFFER_F_OFFLOAD_IP_CKSUM))
393                 {
394                   calc_checksums (vm, b[0]);
395                   calc_checksums (vm, b[1]);
396                   calc_checksums (vm, b[2]);
397                   calc_checksums (vm, b[3]);
398                 }
399             }
400           b += 4;
401
402         }
403
404       while (from + 1 <= from_end && n_left_to_tx >= 1)
405         {
406           u32 bi0;
407           u32 tx_swif0;
408
409           bi0 = from[0];
410           to_tx[0] = bi0;
411           from += 1;
412           to_tx += 1;
413           n_left_to_tx -= 1;
414
415           /* Be grumpy about zero length buffers for benefit of
416              driver tx function. */
417           ASSERT (b[0]->current_length > 0);
418
419           n_bytes_b0 = vlib_buffer_length_in_chain (vm, b[0]);
420           tx_swif0 = vnet_buffer (b[0])->sw_if_index[VLIB_TX];
421           n_bytes += n_bytes_b0;
422           n_packets += 1;
423
424           if (PREDICT_FALSE (current_config_index != ~0))
425             {
426               vnet_buffer (b[0])->feature_arc_index = arc;
427               b[0]->current_config_index = current_config_index;
428             }
429
430           if (PREDICT_FALSE (tx_swif0 != rt->sw_if_index))
431             {
432
433               vlib_increment_combined_counter (im->combined_sw_if_counters +
434                                                VNET_INTERFACE_COUNTER_TX,
435                                                thread_index, tx_swif0, 1,
436                                                n_bytes_b0);
437             }
438
439           if (do_tx_offloads)
440             {
441               if (b[0]->flags &
442                   (VNET_BUFFER_F_OFFLOAD_TCP_CKSUM |
443                    VNET_BUFFER_F_OFFLOAD_UDP_CKSUM |
444                    VNET_BUFFER_F_OFFLOAD_IP_CKSUM))
445                 calc_checksums (vm, b[0]);
446             }
447           b += 1;
448         }
449
450       vlib_put_next_frame (vm, node, next_index, n_left_to_tx);
451     }
452
453   /* Update main interface stats. */
454   vlib_increment_combined_counter (im->combined_sw_if_counters
455                                    + VNET_INTERFACE_COUNTER_TX,
456                                    thread_index,
457                                    rt->sw_if_index, n_packets, n_bytes);
458   return n_buffers;
459 }
460 #endif /* CLIB_MARCH_VARIANT */
461
462 static_always_inline void vnet_interface_pcap_tx_trace
463   (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame,
464    int sw_if_index_from_buffer)
465 {
466   u32 n_left_from, *from;
467   u32 sw_if_index;
468   vnet_pcap_t *pp = &vlib_global_main.pcap;
469
470   if (PREDICT_TRUE (pp->pcap_tx_enable == 0))
471     return;
472
473   if (sw_if_index_from_buffer == 0)
474     {
475       vnet_interface_output_runtime_t *rt = (void *) node->runtime_data;
476       sw_if_index = rt->sw_if_index;
477     }
478   else
479     sw_if_index = ~0;
480
481   n_left_from = frame->n_vectors;
482   from = vlib_frame_vector_args (frame);
483
484   while (n_left_from > 0)
485     {
486       int classify_filter_result;
487       u32 bi0 = from[0];
488       vlib_buffer_t *b0 = vlib_get_buffer (vm, bi0);
489       from++;
490       n_left_from--;
491
492       if (pp->filter_classify_table_index != ~0)
493         {
494           classify_filter_result =
495             vnet_is_packet_traced_inline
496             (b0, pp->filter_classify_table_index, 0 /* full classify */ );
497           if (classify_filter_result)
498             pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt);
499           continue;
500         }
501
502       if (sw_if_index_from_buffer)
503         sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
504
505       if (pp->pcap_sw_if_index == 0 || pp->pcap_sw_if_index == sw_if_index)
506         {
507           vnet_main_t *vnm = vnet_get_main ();
508           vnet_hw_interface_t *hi =
509             vnet_get_sup_hw_interface (vnm, sw_if_index);
510           /* Capture pkt if not filtered, or if filter hits */
511           if (hi->trace_classify_table_index == ~0 ||
512               vnet_is_packet_traced_inline
513               (b0, hi->trace_classify_table_index, 0 /* full classify */ ))
514             pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt);
515         }
516     }
517 }
518
519 #ifndef CLIB_MARCH_VARIANT
520
521 uword
522 vnet_interface_output_node (vlib_main_t * vm, vlib_node_runtime_t * node,
523                             vlib_frame_t * frame)
524 {
525   vnet_main_t *vnm = vnet_get_main ();
526   vnet_hw_interface_t *hi;
527   vnet_interface_output_runtime_t *rt = (void *) node->runtime_data;
528   hi = vnet_get_sup_hw_interface (vnm, rt->sw_if_index);
529
530   vnet_interface_pcap_tx_trace (vm, node, frame,
531                                 0 /* sw_if_index_from_buffer */ );
532
533   if (hi->flags & VNET_HW_INTERFACE_FLAG_SUPPORTS_TX_L4_CKSUM_OFFLOAD)
534     return vnet_interface_output_node_inline (vm, node, frame, vnm, hi,
535                                               /* do_tx_offloads */ 0);
536   else
537     return vnet_interface_output_node_inline (vm, node, frame, vnm, hi,
538                                               /* do_tx_offloads */ 1);
539 }
540 #endif /* CLIB_MARCH_VARIANT */
541
542 /* Use buffer's sw_if_index[VNET_TX] to choose output interface. */
543 VLIB_NODE_FN (vnet_per_buffer_interface_output_node) (vlib_main_t * vm,
544                                                       vlib_node_runtime_t *
545                                                       node,
546                                                       vlib_frame_t * frame)
547 {
548   vnet_main_t *vnm = vnet_get_main ();
549   u32 n_left_to_next, *from, *to_next;
550   u32 n_left_from, next_index;
551
552   vnet_interface_pcap_tx_trace (vm, node, frame,
553                                 1 /* sw_if_index_from_buffer */ );
554
555   n_left_from = frame->n_vectors;
556
557   from = vlib_frame_vector_args (frame);
558   next_index = node->cached_next_index;
559
560   while (n_left_from > 0)
561     {
562       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
563
564       while (n_left_from >= 4 && n_left_to_next >= 2)
565         {
566           u32 bi0, bi1, next0, next1;
567           vlib_buffer_t *b0, *b1;
568           vnet_hw_interface_t *hi0, *hi1;
569
570           /* Prefetch next iteration. */
571           vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
572           vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
573
574           bi0 = from[0];
575           bi1 = from[1];
576           to_next[0] = bi0;
577           to_next[1] = bi1;
578           from += 2;
579           to_next += 2;
580           n_left_to_next -= 2;
581           n_left_from -= 2;
582
583           b0 = vlib_get_buffer (vm, bi0);
584           b1 = vlib_get_buffer (vm, bi1);
585
586           hi0 =
587             vnet_get_sup_hw_interface (vnm,
588                                        vnet_buffer (b0)->sw_if_index
589                                        [VLIB_TX]);
590           hi1 =
591             vnet_get_sup_hw_interface (vnm,
592                                        vnet_buffer (b1)->sw_if_index
593                                        [VLIB_TX]);
594
595           next0 = hi0->output_node_next_index;
596           next1 = hi1->output_node_next_index;
597
598           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
599                                            n_left_to_next, bi0, bi1, next0,
600                                            next1);
601         }
602
603       while (n_left_from > 0 && n_left_to_next > 0)
604         {
605           u32 bi0, next0;
606           vlib_buffer_t *b0;
607           vnet_hw_interface_t *hi0;
608
609           bi0 = from[0];
610           to_next[0] = bi0;
611           from += 1;
612           to_next += 1;
613           n_left_to_next -= 1;
614           n_left_from -= 1;
615
616           b0 = vlib_get_buffer (vm, bi0);
617
618           hi0 =
619             vnet_get_sup_hw_interface (vnm,
620                                        vnet_buffer (b0)->sw_if_index
621                                        [VLIB_TX]);
622
623           next0 = hi0->output_node_next_index;
624
625           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
626                                            n_left_to_next, bi0, next0);
627         }
628
629       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
630     }
631
632   return frame->n_vectors;
633 }
634
635 typedef struct vnet_error_trace_t_
636 {
637   u32 sw_if_index;
638 } vnet_error_trace_t;
639
640
641 static u8 *
642 format_vnet_error_trace (u8 * s, va_list * va)
643 {
644   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
645   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
646   vnet_error_trace_t *t = va_arg (*va, vnet_error_trace_t *);
647
648   s = format (s, "rx:%U", format_vnet_sw_if_index_name,
649               vnet_get_main (), t->sw_if_index);
650
651   return s;
652 }
653
654 static void
655 interface_trace_buffers (vlib_main_t * vm,
656                          vlib_node_runtime_t * node, vlib_frame_t * frame)
657 {
658   u32 n_left, *buffers;
659
660   buffers = vlib_frame_vector_args (frame);
661   n_left = frame->n_vectors;
662
663   while (n_left >= 4)
664     {
665       u32 bi0, bi1;
666       vlib_buffer_t *b0, *b1;
667       vnet_error_trace_t *t0, *t1;
668
669       /* Prefetch next iteration. */
670       vlib_prefetch_buffer_with_index (vm, buffers[2], LOAD);
671       vlib_prefetch_buffer_with_index (vm, buffers[3], LOAD);
672
673       bi0 = buffers[0];
674       bi1 = buffers[1];
675
676       b0 = vlib_get_buffer (vm, bi0);
677       b1 = vlib_get_buffer (vm, bi1);
678
679       if (b0->flags & VLIB_BUFFER_IS_TRACED)
680         {
681           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
682           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
683         }
684       if (b1->flags & VLIB_BUFFER_IS_TRACED)
685         {
686           t1 = vlib_add_trace (vm, node, b1, sizeof (t1[0]));
687           t1->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX];
688         }
689       buffers += 2;
690       n_left -= 2;
691     }
692
693   while (n_left >= 1)
694     {
695       u32 bi0;
696       vlib_buffer_t *b0;
697       vnet_error_trace_t *t0;
698
699       bi0 = buffers[0];
700
701       b0 = vlib_get_buffer (vm, bi0);
702
703       if (b0->flags & VLIB_BUFFER_IS_TRACED)
704         {
705           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
706           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
707         }
708       buffers += 1;
709       n_left -= 1;
710     }
711 }
712
713 typedef enum
714 {
715   VNET_ERROR_DISPOSITION_DROP,
716   VNET_ERROR_DISPOSITION_PUNT,
717   VNET_ERROR_N_DISPOSITION,
718 } vnet_error_disposition_t;
719
720 static_always_inline uword
721 interface_drop_punt (vlib_main_t * vm,
722                      vlib_node_runtime_t * node,
723                      vlib_frame_t * frame,
724                      vnet_error_disposition_t disposition)
725 {
726   u32 *from, n_left, thread_index, *sw_if_index;
727   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
728   u32 sw_if_indices[VLIB_FRAME_SIZE];
729   vlib_simple_counter_main_t *cm;
730   u16 nexts[VLIB_FRAME_SIZE];
731   vnet_main_t *vnm;
732
733   vnm = vnet_get_main ();
734   thread_index = vm->thread_index;
735   from = vlib_frame_vector_args (frame);
736   n_left = frame->n_vectors;
737   b = bufs;
738   sw_if_index = sw_if_indices;
739
740   vlib_get_buffers (vm, from, bufs, n_left);
741
742   if (node->flags & VLIB_NODE_FLAG_TRACE)
743     interface_trace_buffers (vm, node, frame);
744
745   /* All going to drop regardless, this is just a counting exercise */
746   clib_memset (nexts, 0, sizeof (nexts));
747
748   cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
749                          (disposition == VNET_ERROR_DISPOSITION_PUNT
750                           ? VNET_INTERFACE_COUNTER_PUNT
751                           : VNET_INTERFACE_COUNTER_DROP));
752
753   /* collect the array of interfaces first ... */
754   while (n_left >= 4)
755     {
756       if (n_left >= 12)
757         {
758           /* Prefetch 8 ahead - there's not much going on in each iteration */
759           vlib_prefetch_buffer_header (b[4], LOAD);
760           vlib_prefetch_buffer_header (b[5], LOAD);
761           vlib_prefetch_buffer_header (b[6], LOAD);
762           vlib_prefetch_buffer_header (b[7], LOAD);
763         }
764       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
765       sw_if_index[1] = vnet_buffer (b[1])->sw_if_index[VLIB_RX];
766       sw_if_index[2] = vnet_buffer (b[2])->sw_if_index[VLIB_RX];
767       sw_if_index[3] = vnet_buffer (b[3])->sw_if_index[VLIB_RX];
768
769       sw_if_index += 4;
770       n_left -= 4;
771       b += 4;
772     }
773   while (n_left)
774     {
775       sw_if_index[0] = vnet_buffer (b[0])->sw_if_index[VLIB_RX];
776
777       sw_if_index += 1;
778       n_left -= 1;
779       b += 1;
780     }
781
782   /* ... then count against them in blocks */
783   n_left = frame->n_vectors;
784
785   while (n_left)
786     {
787       vnet_sw_interface_t *sw_if0;
788       u16 off, count;
789
790       off = frame->n_vectors - n_left;
791
792       sw_if_index = sw_if_indices + off;
793
794       count = clib_count_equal_u32 (sw_if_index, n_left);
795       n_left -= count;
796
797       vlib_increment_simple_counter (cm, thread_index, sw_if_index[0], count);
798
799       /* Increment super-interface drop/punt counters for
800          sub-interfaces. */
801       sw_if0 = vnet_get_sw_interface (vnm, sw_if_index[0]);
802       if (sw_if0->sup_sw_if_index != sw_if_index[0])
803         vlib_increment_simple_counter
804           (cm, thread_index, sw_if0->sup_sw_if_index, count);
805     }
806
807   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
808
809   return frame->n_vectors;
810 }
811
812 static inline void
813 pcap_drop_trace (vlib_main_t * vm,
814                  vnet_interface_main_t * im,
815                  vnet_pcap_t * pp, vlib_frame_t * f)
816 {
817   u32 *from;
818   u32 n_left = f->n_vectors;
819   vlib_buffer_t *b0, *p1;
820   u32 bi0;
821   i16 save_current_data;
822   u16 save_current_length;
823   vlib_error_main_t *em = &vm->error_main;
824   int do_trace = 0;
825
826
827   from = vlib_frame_vector_args (f);
828
829   while (n_left > 0)
830     {
831       if (PREDICT_TRUE (n_left > 1))
832         {
833           p1 = vlib_get_buffer (vm, from[1]);
834           vlib_prefetch_buffer_header (p1, LOAD);
835         }
836
837       bi0 = from[0];
838       b0 = vlib_get_buffer (vm, bi0);
839       from++;
840       n_left--;
841
842       /* See if we're pointedly ignoring this specific error */
843       if (im->pcap_drop_filter_hash
844           && hash_get (im->pcap_drop_filter_hash, b0->error))
845         continue;
846
847       do_trace = (pp->pcap_sw_if_index == 0) ||
848         pp->pcap_sw_if_index == vnet_buffer (b0)->sw_if_index[VLIB_RX];
849
850       if (PREDICT_FALSE
851           (do_trace == 0 && pp->filter_classify_table_index != ~0))
852         {
853           do_trace = vnet_is_packet_traced_inline
854             (b0, pp->filter_classify_table_index, 0 /* full classify */ );
855         }
856
857       /* Trace all drops, or drops received on a specific interface */
858       if (do_trace)
859         {
860           save_current_data = b0->current_data;
861           save_current_length = b0->current_length;
862
863           /*
864            * Typically, we'll need to rewind the buffer
865            * if l2_hdr_offset is valid, make sure to rewind to the start of
866            * the L2 header. This may not be the buffer start in case we pop-ed
867            * vlan tags.
868            * Otherwise, rewind to buffer start and hope for the best.
869            */
870           if (b0->flags & VNET_BUFFER_F_L2_HDR_OFFSET_VALID)
871             {
872               if (b0->current_data > vnet_buffer (b0)->l2_hdr_offset)
873                 vlib_buffer_advance (b0,
874                                      vnet_buffer (b0)->l2_hdr_offset -
875                                      b0->current_data);
876             }
877           else if (b0->current_data > 0)
878             vlib_buffer_advance (b0, (word) - b0->current_data);
879
880           {
881             vlib_buffer_t *last = b0;
882             u32 error_node_index;
883             int drop_string_len;
884             vlib_node_t *n;
885             /* Length of the error string */
886             int error_string_len =
887               clib_strnlen (em->error_strings_heap[b0->error], 128);
888
889             /* Dig up the drop node */
890             error_node_index = vm->node_main.node_by_error[b0->error];
891             n = vlib_get_node (vm, error_node_index);
892
893             /* Length of full drop string, w/ "nodename: " prepended */
894             drop_string_len = error_string_len + vec_len (n->name) + 2;
895
896             /* Find the last buffer in the chain */
897             while (last->flags & VLIB_BUFFER_NEXT_PRESENT)
898               last = vlib_get_buffer (vm, last->next_buffer);
899
900             /*
901              * Append <nodename>: <error-string> to the capture,
902              * only if we can do that without allocating a new buffer.
903              */
904             if (PREDICT_TRUE ((last->current_data + last->current_length)
905                               < (VLIB_BUFFER_DEFAULT_DATA_SIZE
906                                  - drop_string_len)))
907               {
908                 clib_memcpy_fast (last->data + last->current_data +
909                                   last->current_length, n->name,
910                                   vec_len (n->name));
911                 clib_memcpy_fast (last->data + last->current_data +
912                                   last->current_length + vec_len (n->name),
913                                   ": ", 2);
914                 clib_memcpy_fast (last->data + last->current_data +
915                                   last->current_length + vec_len (n->name) +
916                                   2, em->error_strings_heap[b0->error],
917                                   error_string_len);
918                 last->current_length += drop_string_len;
919                 b0->flags &= ~(VLIB_BUFFER_TOTAL_LENGTH_VALID);
920                 pcap_add_buffer (&pp->pcap_main, vm, bi0,
921                                  pp->max_bytes_per_pkt);
922                 last->current_length -= drop_string_len;
923                 b0->current_data = save_current_data;
924                 b0->current_length = save_current_length;
925                 continue;
926               }
927           }
928
929           /*
930            * Didn't have space in the last buffer, here's the dropped
931            * packet as-is
932            */
933           pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt);
934
935           b0->current_data = save_current_data;
936           b0->current_length = save_current_length;
937         }
938     }
939 }
940
941 #ifndef CLIB_MARCH_VARIANT
942 void
943 vnet_pcap_drop_trace_filter_add_del (u32 error_index, int is_add)
944 {
945   vnet_interface_main_t *im = &vnet_get_main ()->interface_main;
946
947   if (im->pcap_drop_filter_hash == 0)
948     im->pcap_drop_filter_hash = hash_create (0, sizeof (uword));
949
950   if (is_add)
951     hash_set (im->pcap_drop_filter_hash, error_index, 1);
952   else
953     hash_unset (im->pcap_drop_filter_hash, error_index);
954 }
955 #endif /* CLIB_MARCH_VARIANT */
956
957 VLIB_NODE_FN (interface_drop) (vlib_main_t * vm,
958                                vlib_node_runtime_t * node,
959                                vlib_frame_t * frame)
960 {
961   vnet_interface_main_t *im = &vnet_get_main ()->interface_main;
962   vnet_pcap_t *pp = &vlib_global_main.pcap;
963
964   if (PREDICT_FALSE (pp->pcap_drop_enable))
965     pcap_drop_trace (vm, im, pp, frame);
966
967   return interface_drop_punt (vm, node, frame, VNET_ERROR_DISPOSITION_DROP);
968 }
969
970 VLIB_NODE_FN (interface_punt) (vlib_main_t * vm,
971                                vlib_node_runtime_t * node,
972                                vlib_frame_t * frame)
973 {
974   return interface_drop_punt (vm, node, frame, VNET_ERROR_DISPOSITION_PUNT);
975 }
976
977 /* *INDENT-OFF* */
978 VLIB_REGISTER_NODE (interface_drop) = {
979   .name = "error-drop",
980   .vector_size = sizeof (u32),
981   .format_trace = format_vnet_error_trace,
982   .n_next_nodes = 1,
983   .next_nodes = {
984     [0] = "drop",
985   },
986 };
987 /* *INDENT-ON* */
988
989 /* *INDENT-OFF* */
990 VLIB_REGISTER_NODE (interface_punt) = {
991   .name = "error-punt",
992   .vector_size = sizeof (u32),
993   .format_trace = format_vnet_error_trace,
994   .n_next_nodes = 1,
995   .next_nodes = {
996     [0] = "punt",
997   },
998 };
999 /* *INDENT-ON* */
1000
1001 /* *INDENT-OFF* */
1002 VLIB_REGISTER_NODE (vnet_per_buffer_interface_output_node) = {
1003   .name = "interface-output",
1004   .vector_size = sizeof (u32),
1005 };
1006 /* *INDENT-ON* */
1007
1008 static uword
1009 interface_tx_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1010                       vlib_frame_t * from_frame)
1011 {
1012   vnet_main_t *vnm = vnet_get_main ();
1013   u32 last_sw_if_index = ~0;
1014   vlib_frame_t *to_frame = 0;
1015   vnet_hw_interface_t *hw = 0;
1016   u32 *from, *to_next = 0;
1017   u32 n_left_from;
1018
1019   from = vlib_frame_vector_args (from_frame);
1020   n_left_from = from_frame->n_vectors;
1021   while (n_left_from > 0)
1022     {
1023       u32 bi0;
1024       vlib_buffer_t *b0;
1025       u32 sw_if_index0;
1026
1027       bi0 = from[0];
1028       from++;
1029       n_left_from--;
1030       b0 = vlib_get_buffer (vm, bi0);
1031       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
1032
1033       if (PREDICT_FALSE ((last_sw_if_index != sw_if_index0) || to_frame == 0))
1034         {
1035           if (to_frame)
1036             {
1037               hw = vnet_get_sup_hw_interface (vnm, last_sw_if_index);
1038               vlib_put_frame_to_node (vm, hw->tx_node_index, to_frame);
1039             }
1040           last_sw_if_index = sw_if_index0;
1041           hw = vnet_get_sup_hw_interface (vnm, sw_if_index0);
1042           to_frame = vlib_get_frame_to_node (vm, hw->tx_node_index);
1043           to_next = vlib_frame_vector_args (to_frame);
1044         }
1045
1046       to_next[0] = bi0;
1047       to_next++;
1048       to_frame->n_vectors++;
1049     }
1050   vlib_put_frame_to_node (vm, hw->tx_node_index, to_frame);
1051   return from_frame->n_vectors;
1052 }
1053
1054 /* *INDENT-OFF* */
1055 VLIB_REGISTER_NODE (interface_tx) = {
1056   .function = interface_tx_node_fn,
1057   .name = "interface-tx",
1058   .vector_size = sizeof (u32),
1059   .n_next_nodes = 1,
1060   .next_nodes = {
1061     [0] = "error-drop",
1062   },
1063 };
1064
1065 VNET_FEATURE_ARC_INIT (interface_output, static) =
1066 {
1067   .arc_name  = "interface-output",
1068   .start_nodes = VNET_FEATURES (0),
1069   .last_in_arc = "interface-tx",
1070   .arc_index_ptr = &vnet_main.interface_main.output_feature_arc_index,
1071 };
1072
1073 VNET_FEATURE_INIT (span_tx, static) = {
1074   .arc_name = "interface-output",
1075   .node_name = "span-output",
1076   .runs_before = VNET_FEATURES ("interface-tx"),
1077 };
1078
1079 VNET_FEATURE_INIT (ipsec_if_tx, static) = {
1080   .arc_name = "interface-output",
1081   .node_name = "ipsec-if-output",
1082   .runs_before = VNET_FEATURES ("interface-tx"),
1083 };
1084
1085 VNET_FEATURE_INIT (interface_tx, static) = {
1086   .arc_name = "interface-output",
1087   .node_name = "interface-tx",
1088   .runs_before = 0,
1089 };
1090 /* *INDENT-ON* */
1091
1092 #ifndef CLIB_MARCH_VARIANT
1093 clib_error_t *
1094 vnet_per_buffer_interface_output_hw_interface_add_del (vnet_main_t * vnm,
1095                                                        u32 hw_if_index,
1096                                                        u32 is_create)
1097 {
1098   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
1099   u32 next_index;
1100
1101   if (hi->output_node_index == 0)
1102     return 0;
1103
1104   next_index = vlib_node_add_next
1105     (vnm->vlib_main, vnet_per_buffer_interface_output_node.index,
1106      hi->output_node_index);
1107   hi->output_node_next_index = next_index;
1108
1109   return 0;
1110 }
1111
1112 VNET_HW_INTERFACE_ADD_DEL_FUNCTION
1113   (vnet_per_buffer_interface_output_hw_interface_add_del);
1114
1115 void
1116 vnet_set_interface_output_node (vnet_main_t * vnm,
1117                                 u32 hw_if_index, u32 node_index)
1118 {
1119   ASSERT (node_index);
1120   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
1121   u32 next_index = vlib_node_add_next
1122     (vnm->vlib_main, vnet_per_buffer_interface_output_node.index, node_index);
1123   hi->output_node_next_index = next_index;
1124   hi->output_node_index = node_index;
1125 }
1126 #endif /* CLIB_MARCH_VARIANT */
1127
1128 /*
1129  * fd.io coding-style-patch-verification: ON
1130  *
1131  * Local Variables:
1132  * eval: (c-set-style "gnu")
1133  * End:
1134  */