IPSEC: no second lookup after tunnel encap
[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   u8 data[128 - sizeof (u32)];
51 }
52 interface_output_trace_t;
53
54 u8 *
55 format_vnet_interface_output_trace (u8 * s, va_list * va)
56 {
57   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *);
58   vlib_node_t *node = va_arg (*va, vlib_node_t *);
59   interface_output_trace_t *t = va_arg (*va, interface_output_trace_t *);
60   vnet_main_t *vnm = vnet_get_main ();
61   vnet_sw_interface_t *si;
62   u32 indent;
63
64   if (t->sw_if_index != (u32) ~ 0)
65     {
66       indent = format_get_indent (s);
67
68       if (pool_is_free_index
69           (vnm->interface_main.sw_interfaces, t->sw_if_index))
70         {
71           /* the interface may have been deleted by the time the trace is printed */
72           s = format (s, "sw_if_index: %d\n%U%U",
73                       t->sw_if_index,
74                       format_white_space, indent,
75                       node->format_buffer ? node->
76                       format_buffer : format_hex_bytes, t->data,
77                       sizeof (t->data));
78         }
79       else
80         {
81           si = vnet_get_sw_interface (vnm, t->sw_if_index);
82
83           s = format (s, "%U\n%U%U",
84                       format_vnet_sw_interface_name, vnm, si,
85                       format_white_space, indent,
86                       node->format_buffer ? node->
87                       format_buffer : format_hex_bytes, t->data,
88                       sizeof (t->data));
89         }
90     }
91   return s;
92 }
93
94 static void
95 vnet_interface_output_trace (vlib_main_t * vm,
96                              vlib_node_runtime_t * node,
97                              vlib_frame_t * frame, uword n_buffers)
98 {
99   u32 n_left, *from;
100
101   n_left = n_buffers;
102   from = vlib_frame_vector_args (frame);
103
104   while (n_left >= 4)
105     {
106       u32 bi0, bi1;
107       vlib_buffer_t *b0, *b1;
108       interface_output_trace_t *t0, *t1;
109
110       /* Prefetch next iteration. */
111       vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
112       vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
113
114       bi0 = from[0];
115       bi1 = from[1];
116
117       b0 = vlib_get_buffer (vm, bi0);
118       b1 = vlib_get_buffer (vm, bi1);
119
120       if (b0->flags & VLIB_BUFFER_IS_TRACED)
121         {
122           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
123           t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
124           clib_memcpy_fast (t0->data, vlib_buffer_get_current (b0),
125                             sizeof (t0->data));
126         }
127       if (b1->flags & VLIB_BUFFER_IS_TRACED)
128         {
129           t1 = vlib_add_trace (vm, node, b1, sizeof (t1[0]));
130           t1->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_TX];
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           clib_memcpy_fast (t0->data, vlib_buffer_get_current (b0),
153                             sizeof (t0->data));
154         }
155       from += 1;
156       n_left -= 1;
157     }
158 }
159
160 static_always_inline void
161 calc_checksums (vlib_main_t * vm, vlib_buffer_t * b)
162 {
163   ip4_header_t *ip4;
164   ip6_header_t *ip6;
165   tcp_header_t *th;
166   udp_header_t *uh;
167
168   int is_ip4 = (b->flags & VNET_BUFFER_F_IS_IP4) != 0;
169   int is_ip6 = (b->flags & VNET_BUFFER_F_IS_IP6) != 0;
170
171   ASSERT (!(is_ip4 && is_ip6));
172
173   ip4 = (ip4_header_t *) (b->data + vnet_buffer (b)->l3_hdr_offset);
174   ip6 = (ip6_header_t *) (b->data + vnet_buffer (b)->l3_hdr_offset);
175   th = (tcp_header_t *) (b->data + vnet_buffer (b)->l4_hdr_offset);
176   uh = (udp_header_t *) (b->data + vnet_buffer (b)->l4_hdr_offset);
177
178   if (is_ip4)
179     {
180       ip4 = (ip4_header_t *) (b->data + vnet_buffer (b)->l3_hdr_offset);
181       if (b->flags & VNET_BUFFER_F_OFFLOAD_IP_CKSUM)
182         ip4->checksum = ip4_header_checksum (ip4);
183       if (b->flags & VNET_BUFFER_F_OFFLOAD_TCP_CKSUM)
184         {
185           th->checksum = 0;
186           th->checksum = ip4_tcp_udp_compute_checksum (vm, b, ip4);
187         }
188       if (b->flags & VNET_BUFFER_F_OFFLOAD_UDP_CKSUM)
189         uh->checksum = ip4_tcp_udp_compute_checksum (vm, b, ip4);
190     }
191   if (is_ip6)
192     {
193       int bogus;
194       if (b->flags & VNET_BUFFER_F_OFFLOAD_TCP_CKSUM)
195         th->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b, ip6, &bogus);
196       if (b->flags & VNET_BUFFER_F_OFFLOAD_UDP_CKSUM)
197         uh->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b, ip6, &bogus);
198     }
199
200   b->flags &= ~VNET_BUFFER_F_OFFLOAD_TCP_CKSUM;
201   b->flags &= ~VNET_BUFFER_F_OFFLOAD_UDP_CKSUM;
202   b->flags &= ~VNET_BUFFER_F_OFFLOAD_IP_CKSUM;
203 }
204
205 static_always_inline uword
206 vnet_interface_output_node_inline (vlib_main_t * vm,
207                                    vlib_node_runtime_t * node,
208                                    vlib_frame_t * frame, vnet_main_t * vnm,
209                                    vnet_hw_interface_t * hi,
210                                    int do_tx_offloads)
211 {
212   vnet_interface_output_runtime_t *rt = (void *) node->runtime_data;
213   vnet_sw_interface_t *si;
214   u32 n_left_to_tx, *from, *from_end, *to_tx;
215   u32 n_bytes, n_buffers, n_packets;
216   u32 n_bytes_b0, n_bytes_b1, n_bytes_b2, n_bytes_b3;
217   u32 thread_index = vm->thread_index;
218   vnet_interface_main_t *im = &vnm->interface_main;
219   u32 next_index = VNET_INTERFACE_OUTPUT_NEXT_TX;
220   u32 current_config_index = ~0;
221   u8 arc = im->output_feature_arc_index;
222
223   n_buffers = frame->n_vectors;
224
225   if (node->flags & VLIB_NODE_FLAG_TRACE)
226     vnet_interface_output_trace (vm, node, frame, n_buffers);
227
228   from = vlib_frame_vector_args (frame);
229
230   if (rt->is_deleted)
231     return vlib_error_drop_buffers (vm, node, from,
232                                     /* buffer stride */ 1,
233                                     n_buffers,
234                                     VNET_INTERFACE_OUTPUT_NEXT_DROP,
235                                     node->node_index,
236                                     VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DELETED);
237
238   si = vnet_get_sw_interface (vnm, rt->sw_if_index);
239   hi = vnet_get_sup_hw_interface (vnm, rt->sw_if_index);
240   if (!(si->flags & (VNET_SW_INTERFACE_FLAG_ADMIN_UP |
241                      VNET_SW_INTERFACE_FLAG_BOND_SLAVE)) ||
242       !(hi->flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
243     {
244       vlib_simple_counter_main_t *cm;
245
246       cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
247                              VNET_INTERFACE_COUNTER_TX_ERROR);
248       vlib_increment_simple_counter (cm, thread_index,
249                                      rt->sw_if_index, n_buffers);
250
251       return vlib_error_drop_buffers (vm, node, from,
252                                       /* buffer stride */ 1,
253                                       n_buffers,
254                                       VNET_INTERFACE_OUTPUT_NEXT_DROP,
255                                       node->node_index,
256                                       VNET_INTERFACE_OUTPUT_ERROR_INTERFACE_DOWN);
257     }
258
259   from_end = from + n_buffers;
260
261   /* Total byte count of all buffers. */
262   n_bytes = 0;
263   n_packets = 0;
264
265   /* interface-output feature arc handling */
266   if (PREDICT_FALSE (vnet_have_features (arc, rt->sw_if_index)))
267     {
268       vnet_feature_config_main_t *fcm;
269       fcm = vnet_feature_get_config_main (arc);
270       current_config_index = vnet_get_feature_config_index (arc,
271                                                             rt->sw_if_index);
272       vnet_get_config_data (&fcm->config_main, &current_config_index,
273                             &next_index, 0);
274     }
275
276   while (from < from_end)
277     {
278       /* Get new next frame since previous incomplete frame may have less
279          than VNET_FRAME_SIZE vectors in it. */
280       vlib_get_new_next_frame (vm, node, next_index, to_tx, n_left_to_tx);
281
282       while (from + 8 <= from_end && n_left_to_tx >= 4)
283         {
284           u32 bi0, bi1, bi2, bi3;
285           vlib_buffer_t *b0, *b1, *b2, *b3;
286           u32 tx_swif0, tx_swif1, tx_swif2, tx_swif3;
287           u32 or_flags;
288
289           /* Prefetch next iteration. */
290           vlib_prefetch_buffer_with_index (vm, from[4], LOAD);
291           vlib_prefetch_buffer_with_index (vm, from[5], LOAD);
292           vlib_prefetch_buffer_with_index (vm, from[6], LOAD);
293           vlib_prefetch_buffer_with_index (vm, from[7], LOAD);
294
295           bi0 = from[0];
296           bi1 = from[1];
297           bi2 = from[2];
298           bi3 = from[3];
299           to_tx[0] = bi0;
300           to_tx[1] = bi1;
301           to_tx[2] = bi2;
302           to_tx[3] = bi3;
303           from += 4;
304           to_tx += 4;
305           n_left_to_tx -= 4;
306
307           b0 = vlib_get_buffer (vm, bi0);
308           b1 = vlib_get_buffer (vm, bi1);
309           b2 = vlib_get_buffer (vm, bi2);
310           b3 = vlib_get_buffer (vm, bi3);
311
312           /* Be grumpy about zero length buffers for benefit of
313              driver tx function. */
314           ASSERT (b0->current_length > 0);
315           ASSERT (b1->current_length > 0);
316           ASSERT (b2->current_length > 0);
317           ASSERT (b3->current_length > 0);
318
319           n_bytes_b0 = vlib_buffer_length_in_chain (vm, b0);
320           n_bytes_b1 = vlib_buffer_length_in_chain (vm, b1);
321           n_bytes_b2 = vlib_buffer_length_in_chain (vm, b2);
322           n_bytes_b3 = vlib_buffer_length_in_chain (vm, b3);
323           tx_swif0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
324           tx_swif1 = vnet_buffer (b1)->sw_if_index[VLIB_TX];
325           tx_swif2 = vnet_buffer (b2)->sw_if_index[VLIB_TX];
326           tx_swif3 = vnet_buffer (b3)->sw_if_index[VLIB_TX];
327
328           n_bytes += n_bytes_b0 + n_bytes_b1;
329           n_bytes += n_bytes_b2 + n_bytes_b3;
330           n_packets += 4;
331
332           if (PREDICT_FALSE (current_config_index != ~0))
333             {
334               vnet_buffer (b0)->feature_arc_index = arc;
335               vnet_buffer (b1)->feature_arc_index = arc;
336               vnet_buffer (b2)->feature_arc_index = arc;
337               vnet_buffer (b3)->feature_arc_index = arc;
338               b0->current_config_index = current_config_index;
339               b1->current_config_index = current_config_index;
340               b2->current_config_index = current_config_index;
341               b3->current_config_index = current_config_index;
342             }
343
344           /* update vlan subif tx counts, if required */
345           if (PREDICT_FALSE (tx_swif0 != rt->sw_if_index))
346             {
347               vlib_increment_combined_counter (im->combined_sw_if_counters +
348                                                VNET_INTERFACE_COUNTER_TX,
349                                                thread_index, tx_swif0, 1,
350                                                n_bytes_b0);
351             }
352
353           if (PREDICT_FALSE (tx_swif1 != rt->sw_if_index))
354             {
355
356               vlib_increment_combined_counter (im->combined_sw_if_counters +
357                                                VNET_INTERFACE_COUNTER_TX,
358                                                thread_index, tx_swif1, 1,
359                                                n_bytes_b1);
360             }
361
362           if (PREDICT_FALSE (tx_swif2 != rt->sw_if_index))
363             {
364
365               vlib_increment_combined_counter (im->combined_sw_if_counters +
366                                                VNET_INTERFACE_COUNTER_TX,
367                                                thread_index, tx_swif2, 1,
368                                                n_bytes_b2);
369             }
370           if (PREDICT_FALSE (tx_swif3 != 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_swif3, 1,
376                                                n_bytes_b3);
377             }
378
379           or_flags = b0->flags | b1->flags | b2->flags | b3->flags;
380
381           if (do_tx_offloads)
382             {
383               if (or_flags &
384                   (VNET_BUFFER_F_OFFLOAD_TCP_CKSUM |
385                    VNET_BUFFER_F_OFFLOAD_UDP_CKSUM |
386                    VNET_BUFFER_F_OFFLOAD_IP_CKSUM))
387                 {
388                   calc_checksums (vm, b0);
389                   calc_checksums (vm, b1);
390                   calc_checksums (vm, b2);
391                   calc_checksums (vm, b3);
392                 }
393             }
394         }
395
396       while (from + 1 <= from_end && n_left_to_tx >= 1)
397         {
398           u32 bi0;
399           vlib_buffer_t *b0;
400           u32 tx_swif0;
401
402           bi0 = from[0];
403           to_tx[0] = bi0;
404           from += 1;
405           to_tx += 1;
406           n_left_to_tx -= 1;
407
408           b0 = vlib_get_buffer (vm, bi0);
409
410           /* Be grumpy about zero length buffers for benefit of
411              driver tx function. */
412           ASSERT (b0->current_length > 0);
413
414           n_bytes_b0 = vlib_buffer_length_in_chain (vm, b0);
415           tx_swif0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
416           n_bytes += n_bytes_b0;
417           n_packets += 1;
418
419           if (PREDICT_FALSE (current_config_index != ~0))
420             {
421               vnet_buffer (b0)->feature_arc_index = arc;
422               b0->current_config_index = current_config_index;
423             }
424
425           if (PREDICT_FALSE (tx_swif0 != rt->sw_if_index))
426             {
427
428               vlib_increment_combined_counter (im->combined_sw_if_counters +
429                                                VNET_INTERFACE_COUNTER_TX,
430                                                thread_index, tx_swif0, 1,
431                                                n_bytes_b0);
432             }
433
434           if (do_tx_offloads)
435             calc_checksums (vm, b0);
436         }
437
438       vlib_put_next_frame (vm, node, next_index, n_left_to_tx);
439     }
440
441   /* Update main interface stats. */
442   vlib_increment_combined_counter (im->combined_sw_if_counters
443                                    + VNET_INTERFACE_COUNTER_TX,
444                                    thread_index,
445                                    rt->sw_if_index, n_packets, n_bytes);
446   return n_buffers;
447 }
448
449 uword
450 vnet_interface_output_node (vlib_main_t * vm, vlib_node_runtime_t * node,
451                             vlib_frame_t * frame)
452 {
453   vnet_main_t *vnm = vnet_get_main ();
454   vnet_hw_interface_t *hi;
455   vnet_interface_output_runtime_t *rt = (void *) node->runtime_data;
456   hi = vnet_get_sup_hw_interface (vnm, rt->sw_if_index);
457
458   if (hi->flags & VNET_HW_INTERFACE_FLAG_SUPPORTS_TX_L4_CKSUM_OFFLOAD)
459     return vnet_interface_output_node_inline (vm, node, frame, vnm, hi,
460                                               /* do_tx_offloads */ 0);
461   else
462     return vnet_interface_output_node_inline (vm, node, frame, vnm, hi,
463                                               /* do_tx_offloads */ 1);
464 }
465
466 /* Use buffer's sw_if_index[VNET_TX] to choose output interface. */
467 static uword
468 vnet_per_buffer_interface_output (vlib_main_t * vm,
469                                   vlib_node_runtime_t * node,
470                                   vlib_frame_t * frame)
471 {
472   vnet_main_t *vnm = vnet_get_main ();
473   u32 n_left_to_next, *from, *to_next;
474   u32 n_left_from, next_index;
475
476   n_left_from = frame->n_vectors;
477
478   from = vlib_frame_vector_args (frame);
479   next_index = node->cached_next_index;
480
481   while (n_left_from > 0)
482     {
483       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
484
485       while (n_left_from >= 4 && n_left_to_next >= 2)
486         {
487           u32 bi0, bi1, next0, next1;
488           vlib_buffer_t *b0, *b1;
489           vnet_hw_interface_t *hi0, *hi1;
490
491           /* Prefetch next iteration. */
492           vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
493           vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
494
495           bi0 = from[0];
496           bi1 = from[1];
497           to_next[0] = bi0;
498           to_next[1] = bi1;
499           from += 2;
500           to_next += 2;
501           n_left_to_next -= 2;
502           n_left_from -= 2;
503
504           b0 = vlib_get_buffer (vm, bi0);
505           b1 = vlib_get_buffer (vm, bi1);
506
507           hi0 =
508             vnet_get_sup_hw_interface (vnm,
509                                        vnet_buffer (b0)->sw_if_index
510                                        [VLIB_TX]);
511           hi1 =
512             vnet_get_sup_hw_interface (vnm,
513                                        vnet_buffer (b1)->sw_if_index
514                                        [VLIB_TX]);
515
516           next0 = hi0->output_node_next_index;
517           next1 = hi1->output_node_next_index;
518
519           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
520                                            n_left_to_next, bi0, bi1, next0,
521                                            next1);
522         }
523
524       while (n_left_from > 0 && n_left_to_next > 0)
525         {
526           u32 bi0, next0;
527           vlib_buffer_t *b0;
528           vnet_hw_interface_t *hi0;
529
530           bi0 = from[0];
531           to_next[0] = bi0;
532           from += 1;
533           to_next += 1;
534           n_left_to_next -= 1;
535           n_left_from -= 1;
536
537           b0 = vlib_get_buffer (vm, bi0);
538
539           hi0 =
540             vnet_get_sup_hw_interface (vnm,
541                                        vnet_buffer (b0)->sw_if_index
542                                        [VLIB_TX]);
543
544           next0 = hi0->output_node_next_index;
545
546           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
547                                            n_left_to_next, bi0, next0);
548         }
549
550       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
551     }
552
553   return frame->n_vectors;
554 }
555
556 always_inline u32
557 counter_index (vlib_main_t * vm, vlib_error_t e)
558 {
559   vlib_node_t *n;
560   u32 ci, ni;
561
562   ni = vlib_error_get_node (e);
563   n = vlib_get_node (vm, ni);
564
565   ci = vlib_error_get_code (e);
566   ASSERT (ci < n->n_errors);
567
568   ci += n->error_heap_index;
569
570   return ci;
571 }
572
573 static u8 *
574 format_vnet_error_trace (u8 * s, va_list * va)
575 {
576   vlib_main_t *vm = va_arg (*va, vlib_main_t *);
577   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
578   vlib_error_t *e = va_arg (*va, vlib_error_t *);
579   vlib_node_t *error_node;
580   vlib_error_main_t *em = &vm->error_main;
581   u32 i;
582
583   error_node = vlib_get_node (vm, vlib_error_get_node (e[0]));
584   i = counter_index (vm, e[0]);
585   s = format (s, "%v: %s", error_node->name, em->error_strings_heap[i]);
586
587   return s;
588 }
589
590 static void
591 trace_errors_with_buffers (vlib_main_t * vm,
592                            vlib_node_runtime_t * node, vlib_frame_t * frame)
593 {
594   u32 n_left, *buffers;
595
596   buffers = vlib_frame_vector_args (frame);
597   n_left = frame->n_vectors;
598
599   while (n_left >= 4)
600     {
601       u32 bi0, bi1;
602       vlib_buffer_t *b0, *b1;
603       vlib_error_t *t0, *t1;
604
605       /* Prefetch next iteration. */
606       vlib_prefetch_buffer_with_index (vm, buffers[2], LOAD);
607       vlib_prefetch_buffer_with_index (vm, buffers[3], LOAD);
608
609       bi0 = buffers[0];
610       bi1 = buffers[1];
611
612       b0 = vlib_get_buffer (vm, bi0);
613       b1 = vlib_get_buffer (vm, bi1);
614
615       if (b0->flags & VLIB_BUFFER_IS_TRACED)
616         {
617           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
618           t0[0] = b0->error;
619         }
620       if (b1->flags & VLIB_BUFFER_IS_TRACED)
621         {
622           t1 = vlib_add_trace (vm, node, b1, sizeof (t1[0]));
623           t1[0] = b1->error;
624         }
625       buffers += 2;
626       n_left -= 2;
627     }
628
629   while (n_left >= 1)
630     {
631       u32 bi0;
632       vlib_buffer_t *b0;
633       vlib_error_t *t0;
634
635       bi0 = buffers[0];
636
637       b0 = vlib_get_buffer (vm, bi0);
638
639       if (b0->flags & VLIB_BUFFER_IS_TRACED)
640         {
641           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
642           t0[0] = b0->error;
643         }
644       buffers += 1;
645       n_left -= 1;
646     }
647 }
648
649 static u8 *
650 validate_error (vlib_main_t * vm, vlib_error_t * e, u32 index)
651 {
652   uword node_index = vlib_error_get_node (e[0]);
653   uword code = vlib_error_get_code (e[0]);
654   vlib_node_t *n;
655
656   if (node_index >= vec_len (vm->node_main.nodes))
657     return format (0, "[%d], node index out of range 0x%x, error 0x%x",
658                    index, node_index, e[0]);
659
660   n = vlib_get_node (vm, node_index);
661   if (code >= n->n_errors)
662     return format (0, "[%d], code %d out of range for node %v",
663                    index, code, n->name);
664
665   return 0;
666 }
667
668 static u8 *
669 validate_error_frame (vlib_main_t * vm,
670                       vlib_node_runtime_t * node, vlib_frame_t * f)
671 {
672   u32 *buffers = vlib_frame_vector_args (f);
673   vlib_buffer_t *b;
674   u8 *msg = 0;
675   uword i;
676
677   for (i = 0; i < f->n_vectors; i++)
678     {
679       b = vlib_get_buffer (vm, buffers[i]);
680       msg = validate_error (vm, &b->error, i);
681       if (msg)
682         return msg;
683     }
684
685   return msg;
686 }
687
688 typedef enum
689 {
690   VNET_ERROR_DISPOSITION_DROP,
691   VNET_ERROR_DISPOSITION_PUNT,
692   VNET_ERROR_N_DISPOSITION,
693 } vnet_error_disposition_t;
694
695 always_inline void
696 do_packet (vlib_main_t * vm, vlib_error_t a)
697 {
698   vlib_error_main_t *em = &vm->error_main;
699   u32 i = counter_index (vm, a);
700   em->counters[i] += 1;
701   vlib_error_elog_count (vm, i, 1);
702 }
703
704 static_always_inline uword
705 process_drop_punt (vlib_main_t * vm,
706                    vlib_node_runtime_t * node,
707                    vlib_frame_t * frame, vnet_error_disposition_t disposition)
708 {
709   vnet_main_t *vnm = vnet_get_main ();
710   vlib_error_main_t *em = &vm->error_main;
711   u32 *buffers, *first_buffer;
712   vlib_error_t current_error;
713   u32 current_counter_index, n_errors_left;
714   u32 current_sw_if_index, n_errors_current_sw_if_index;
715   u64 current_counter;
716   vlib_simple_counter_main_t *cm;
717   u32 thread_index = vm->thread_index;
718
719   static vlib_error_t memory[VNET_ERROR_N_DISPOSITION];
720   static char memory_init[VNET_ERROR_N_DISPOSITION];
721
722   buffers = vlib_frame_vector_args (frame);
723   first_buffer = buffers;
724
725   {
726     vlib_buffer_t *b = vlib_get_buffer (vm, first_buffer[0]);
727
728     if (!memory_init[disposition])
729       {
730         memory_init[disposition] = 1;
731         memory[disposition] = b->error;
732       }
733
734     current_sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
735     n_errors_current_sw_if_index = 0;
736   }
737
738   current_error = memory[disposition];
739   current_counter_index = counter_index (vm, memory[disposition]);
740   current_counter = em->counters[current_counter_index];
741
742   if (node->flags & VLIB_NODE_FLAG_TRACE)
743     trace_errors_with_buffers (vm, node, frame);
744
745   n_errors_left = frame->n_vectors;
746   cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
747                          (disposition == VNET_ERROR_DISPOSITION_PUNT
748                           ? VNET_INTERFACE_COUNTER_PUNT
749                           : VNET_INTERFACE_COUNTER_DROP));
750
751   while (n_errors_left >= 2)
752     {
753       vlib_buffer_t *b0, *b1;
754       vnet_sw_interface_t *sw_if0, *sw_if1;
755       vlib_error_t e0, e1;
756       u32 bi0, bi1;
757       u32 sw_if_index0, sw_if_index1;
758
759       bi0 = buffers[0];
760       bi1 = buffers[1];
761
762       buffers += 2;
763       n_errors_left -= 2;
764
765       b0 = vlib_get_buffer (vm, bi0);
766       b1 = vlib_get_buffer (vm, bi1);
767
768       e0 = b0->error;
769       e1 = b1->error;
770
771       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
772       sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
773
774       /* Speculate that sw_if_index == sw_if_index[01]. */
775       n_errors_current_sw_if_index += 2;
776
777       /* Speculatively assume all 2 (node, code) pairs are equal
778          to current (node, code). */
779       current_counter += 2;
780
781       if (PREDICT_FALSE (e0 != current_error
782                          || e1 != current_error
783                          || sw_if_index0 != current_sw_if_index
784                          || sw_if_index1 != current_sw_if_index))
785         {
786           current_counter -= 2;
787           n_errors_current_sw_if_index -= 2;
788
789           vlib_increment_simple_counter (cm, thread_index, sw_if_index0, 1);
790           vlib_increment_simple_counter (cm, thread_index, sw_if_index1, 1);
791
792           /* Increment super-interface drop/punt counters for
793              sub-interfaces. */
794           sw_if0 = vnet_get_sw_interface (vnm, sw_if_index0);
795           vlib_increment_simple_counter
796             (cm, thread_index, sw_if0->sup_sw_if_index,
797              sw_if0->sup_sw_if_index != sw_if_index0);
798
799           sw_if1 = vnet_get_sw_interface (vnm, sw_if_index1);
800           vlib_increment_simple_counter
801             (cm, thread_index, sw_if1->sup_sw_if_index,
802              sw_if1->sup_sw_if_index != sw_if_index1);
803
804           em->counters[current_counter_index] = current_counter;
805           do_packet (vm, e0);
806           do_packet (vm, e1);
807
808           /* For 2 repeated errors, change current error. */
809           if (e0 == e1 && e1 != current_error)
810             {
811               current_error = e0;
812               current_counter_index = counter_index (vm, e0);
813             }
814           current_counter = em->counters[current_counter_index];
815         }
816     }
817
818   while (n_errors_left >= 1)
819     {
820       vlib_buffer_t *b0;
821       vnet_sw_interface_t *sw_if0;
822       vlib_error_t e0;
823       u32 bi0, sw_if_index0;
824
825       bi0 = buffers[0];
826
827       buffers += 1;
828       n_errors_left -= 1;
829       current_counter += 1;
830
831       b0 = vlib_get_buffer (vm, bi0);
832       e0 = b0->error;
833
834       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
835
836       /* Increment drop/punt counters. */
837       vlib_increment_simple_counter (cm, thread_index, sw_if_index0, 1);
838
839       /* Increment super-interface drop/punt counters for sub-interfaces. */
840       sw_if0 = vnet_get_sw_interface (vnm, sw_if_index0);
841       vlib_increment_simple_counter (cm, thread_index,
842                                      sw_if0->sup_sw_if_index,
843                                      sw_if0->sup_sw_if_index != sw_if_index0);
844
845       if (PREDICT_FALSE (e0 != current_error))
846         {
847           current_counter -= 1;
848
849           vlib_error_elog_count (vm, current_counter_index,
850                                  (current_counter
851                                   - em->counters[current_counter_index]));
852
853           em->counters[current_counter_index] = current_counter;
854
855           do_packet (vm, e0);
856           current_error = e0;
857           current_counter_index = counter_index (vm, e0);
858           current_counter = em->counters[current_counter_index];
859         }
860     }
861
862   if (n_errors_current_sw_if_index > 0)
863     {
864       vnet_sw_interface_t *si;
865
866       vlib_increment_simple_counter (cm, thread_index, current_sw_if_index,
867                                      n_errors_current_sw_if_index);
868
869       si = vnet_get_sw_interface (vnm, current_sw_if_index);
870       if (si->sup_sw_if_index != current_sw_if_index)
871         vlib_increment_simple_counter (cm, thread_index, si->sup_sw_if_index,
872                                        n_errors_current_sw_if_index);
873     }
874
875   vlib_error_elog_count (vm, current_counter_index,
876                          (current_counter
877                           - em->counters[current_counter_index]));
878
879   /* Return cached counter. */
880   em->counters[current_counter_index] = current_counter;
881
882   /* Save memory for next iteration. */
883   memory[disposition] = current_error;
884
885   if (disposition == VNET_ERROR_DISPOSITION_DROP || !vm->os_punt_frame)
886     {
887       vlib_buffer_free (vm, first_buffer, frame->n_vectors);
888
889       /* If there is no punt function, free the frame as well. */
890       if (disposition == VNET_ERROR_DISPOSITION_PUNT && !vm->os_punt_frame)
891         vlib_frame_free (vm, node, frame);
892     }
893   else
894     vm->os_punt_frame (vm, node, frame);
895
896   return frame->n_vectors;
897 }
898
899 static inline void
900 pcap_drop_trace (vlib_main_t * vm,
901                  vnet_interface_main_t * im, vlib_frame_t * f)
902 {
903   u32 *from;
904   u32 n_left = f->n_vectors;
905   vlib_buffer_t *b0, *p1;
906   u32 bi0;
907   i16 save_current_data;
908   u16 save_current_length;
909
910   from = vlib_frame_vector_args (f);
911
912   while (n_left > 0)
913     {
914       if (PREDICT_TRUE (n_left > 1))
915         {
916           p1 = vlib_get_buffer (vm, from[1]);
917           vlib_prefetch_buffer_header (p1, LOAD);
918         }
919
920       bi0 = from[0];
921       b0 = vlib_get_buffer (vm, bi0);
922       from++;
923       n_left--;
924
925       /* See if we're pointedly ignoring this specific error */
926       if (im->pcap_drop_filter_hash
927           && hash_get (im->pcap_drop_filter_hash, b0->error))
928         continue;
929
930       /* Trace all drops, or drops received on a specific interface */
931       if (im->pcap_sw_if_index == 0 ||
932           im->pcap_sw_if_index == vnet_buffer (b0)->sw_if_index[VLIB_RX])
933         {
934           save_current_data = b0->current_data;
935           save_current_length = b0->current_length;
936
937           /*
938            * Typically, we'll need to rewind the buffer
939            */
940           if (b0->current_data > 0)
941             vlib_buffer_advance (b0, (word) - b0->current_data);
942
943           pcap_add_buffer (&im->pcap_main, vm, bi0, 512);
944
945           b0->current_data = save_current_data;
946           b0->current_length = save_current_length;
947         }
948     }
949 }
950
951 void
952 vnet_pcap_drop_trace_filter_add_del (u32 error_index, int is_add)
953 {
954   vnet_interface_main_t *im = &vnet_get_main ()->interface_main;
955
956   if (im->pcap_drop_filter_hash == 0)
957     im->pcap_drop_filter_hash = hash_create (0, sizeof (uword));
958
959   if (is_add)
960     hash_set (im->pcap_drop_filter_hash, error_index, 1);
961   else
962     hash_unset (im->pcap_drop_filter_hash, error_index);
963 }
964
965 static uword
966 process_drop (vlib_main_t * vm,
967               vlib_node_runtime_t * node, vlib_frame_t * frame)
968 {
969   vnet_interface_main_t *im = &vnet_get_main ()->interface_main;
970
971   if (PREDICT_FALSE (im->drop_pcap_enable))
972     pcap_drop_trace (vm, im, frame);
973
974   return process_drop_punt (vm, node, frame, VNET_ERROR_DISPOSITION_DROP);
975 }
976
977 static uword
978 process_punt (vlib_main_t * vm,
979               vlib_node_runtime_t * node, vlib_frame_t * frame)
980 {
981   return process_drop_punt (vm, node, frame, VNET_ERROR_DISPOSITION_PUNT);
982 }
983
984 /* *INDENT-OFF* */
985 VLIB_REGISTER_NODE (drop_buffers,static) = {
986   .function = process_drop,
987   .name = "error-drop",
988   .flags = VLIB_NODE_FLAG_IS_DROP,
989   .vector_size = sizeof (u32),
990   .format_trace = format_vnet_error_trace,
991   .validate_frame = validate_error_frame,
992 };
993 /* *INDENT-ON* */
994
995 VLIB_NODE_FUNCTION_MULTIARCH (drop_buffers, process_drop);
996
997 /* *INDENT-OFF* */
998 VLIB_REGISTER_NODE (punt_buffers,static) = {
999   .function = process_punt,
1000   .flags = (VLIB_NODE_FLAG_FRAME_NO_FREE_AFTER_DISPATCH
1001             | VLIB_NODE_FLAG_IS_PUNT),
1002   .name = "error-punt",
1003   .vector_size = sizeof (u32),
1004   .format_trace = format_vnet_error_trace,
1005   .validate_frame = validate_error_frame,
1006 };
1007 /* *INDENT-ON* */
1008
1009 VLIB_NODE_FUNCTION_MULTIARCH (punt_buffers, process_punt);
1010
1011 /* *INDENT-OFF* */
1012 VLIB_REGISTER_NODE (vnet_per_buffer_interface_output_node,static) = {
1013   .function = vnet_per_buffer_interface_output,
1014   .name = "interface-output",
1015   .vector_size = sizeof (u32),
1016 };
1017 /* *INDENT-ON* */
1018
1019 /* Convenience node to drop a vector of buffers with a "misc error". */
1020 static uword
1021 misc_drop_buffers (vlib_main_t * vm,
1022                    vlib_node_runtime_t * node, vlib_frame_t * frame)
1023 {
1024   return vlib_error_drop_buffers (vm, node, vlib_frame_vector_args (frame),
1025                                   /* buffer stride */ 1,
1026                                   frame->n_vectors,
1027                                   /* next */ 0,
1028                                   node->node_index,
1029                                   /* error */ 0);
1030 }
1031
1032 static char *misc_drop_buffers_error_strings[] = {
1033   [0] = "misc. errors",
1034 };
1035
1036 /* *INDENT-OFF* */
1037 VLIB_REGISTER_NODE (misc_drop_buffers_node,static) = {
1038   .function = misc_drop_buffers,
1039   .name = "misc-drop-buffers",
1040   .vector_size = sizeof (u32),
1041   .n_errors = 1,
1042   .n_next_nodes = 1,
1043   .next_nodes = {
1044       "error-drop",
1045   },
1046   .error_strings = misc_drop_buffers_error_strings,
1047 };
1048 /* *INDENT-ON* */
1049
1050 VLIB_NODE_FUNCTION_MULTIARCH (vnet_per_buffer_interface_output_node,
1051                               vnet_per_buffer_interface_output);
1052
1053 static uword
1054 interface_tx_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1055                       vlib_frame_t * from_frame)
1056 {
1057   vnet_main_t *vnm = vnet_get_main ();
1058   u32 last_sw_if_index = ~0;
1059   vlib_frame_t *to_frame = 0;
1060   vnet_hw_interface_t *hw = 0;
1061   u32 *from, *to_next = 0;
1062   u32 n_left_from;
1063
1064   from = vlib_frame_vector_args (from_frame);
1065   n_left_from = from_frame->n_vectors;
1066   while (n_left_from > 0)
1067     {
1068       u32 bi0;
1069       vlib_buffer_t *b0;
1070       u32 sw_if_index0;
1071
1072       bi0 = from[0];
1073       from++;
1074       n_left_from--;
1075       b0 = vlib_get_buffer (vm, bi0);
1076       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
1077
1078       if (PREDICT_FALSE ((last_sw_if_index != sw_if_index0) || to_frame == 0))
1079         {
1080           if (to_frame)
1081             {
1082               hw = vnet_get_sup_hw_interface (vnm, last_sw_if_index);
1083               vlib_put_frame_to_node (vm, hw->tx_node_index, to_frame);
1084             }
1085           last_sw_if_index = sw_if_index0;
1086           hw = vnet_get_sup_hw_interface (vnm, sw_if_index0);
1087           to_frame = vlib_get_frame_to_node (vm, hw->tx_node_index);
1088           to_next = vlib_frame_vector_args (to_frame);
1089         }
1090
1091       to_next[0] = bi0;
1092       to_next++;
1093       to_frame->n_vectors++;
1094     }
1095   vlib_put_frame_to_node (vm, hw->tx_node_index, to_frame);
1096   return from_frame->n_vectors;
1097 }
1098
1099 /* *INDENT-OFF* */
1100 VLIB_REGISTER_NODE (interface_tx, static) = {
1101   .function = interface_tx_node_fn,
1102   .name = "interface-tx",
1103   .vector_size = sizeof (u32),
1104   .n_next_nodes = 1,
1105   .next_nodes = {
1106     [0] = "error-drop",
1107   },
1108 };
1109
1110 VNET_FEATURE_ARC_INIT (interface_output, static) =
1111 {
1112   .arc_name  = "interface-output",
1113   .start_nodes = VNET_FEATURES (0),
1114   .last_in_arc = "interface-tx",
1115   .arc_index_ptr = &vnet_main.interface_main.output_feature_arc_index,
1116 };
1117
1118 VNET_FEATURE_INIT (span_tx, static) = {
1119   .arc_name = "interface-output",
1120   .node_name = "span-output",
1121   .runs_before = VNET_FEATURES ("interface-tx"),
1122 };
1123
1124 VNET_FEATURE_INIT (ipsec_if_tx, static) = {
1125   .arc_name = "interface-output",
1126   .node_name = "ipsec-if-output",
1127   .runs_before = VNET_FEATURES ("interface-tx"),
1128 };
1129
1130 VNET_FEATURE_INIT (interface_tx, static) = {
1131   .arc_name = "interface-output",
1132   .node_name = "interface-tx",
1133   .runs_before = 0,
1134 };
1135 /* *INDENT-ON* */
1136
1137 clib_error_t *
1138 vnet_per_buffer_interface_output_hw_interface_add_del (vnet_main_t * vnm,
1139                                                        u32 hw_if_index,
1140                                                        u32 is_create)
1141 {
1142   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
1143   u32 next_index;
1144
1145   if (hi->output_node_index == 0)
1146     return 0;
1147
1148   next_index = vlib_node_add_next
1149     (vnm->vlib_main, vnet_per_buffer_interface_output_node.index,
1150      hi->output_node_index);
1151   hi->output_node_next_index = next_index;
1152
1153   return 0;
1154 }
1155
1156 VNET_HW_INTERFACE_ADD_DEL_FUNCTION
1157   (vnet_per_buffer_interface_output_hw_interface_add_del);
1158
1159 void
1160 vnet_set_interface_output_node (vnet_main_t * vnm,
1161                                 u32 hw_if_index, u32 node_index)
1162 {
1163   ASSERT (node_index);
1164   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
1165   u32 next_index = vlib_node_add_next
1166     (vnm->vlib_main, vnet_per_buffer_interface_output_node.index, node_index);
1167   hi->output_node_next_index = next_index;
1168   hi->output_node_index = node_index;
1169 }
1170
1171 static clib_error_t *
1172 pcap_drop_trace_command_fn (vlib_main_t * vm,
1173                             unformat_input_t * input,
1174                             vlib_cli_command_t * cmd)
1175 {
1176   vnet_main_t *vnm = vnet_get_main ();
1177   vnet_interface_main_t *im = &vnm->interface_main;
1178   u8 *filename;
1179   u32 max;
1180   int matched = 0;
1181   clib_error_t *error = 0;
1182
1183   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1184     {
1185       if (unformat (input, "on"))
1186         {
1187           if (im->drop_pcap_enable == 0)
1188             {
1189               if (im->pcap_filename == 0)
1190                 im->pcap_filename = format (0, "/tmp/drop.pcap%c", 0);
1191
1192               clib_memset (&im->pcap_main, 0, sizeof (im->pcap_main));
1193               im->pcap_main.file_name = (char *) im->pcap_filename;
1194               im->pcap_main.n_packets_to_capture = 100;
1195               if (im->pcap_pkts_to_capture)
1196                 im->pcap_main.n_packets_to_capture = im->pcap_pkts_to_capture;
1197
1198               im->pcap_main.packet_type = PCAP_PACKET_TYPE_ethernet;
1199               im->drop_pcap_enable = 1;
1200               matched = 1;
1201               vlib_cli_output (vm, "pcap drop capture on...");
1202             }
1203           else
1204             {
1205               vlib_cli_output (vm, "pcap drop capture already on...");
1206             }
1207           matched = 1;
1208         }
1209       else if (unformat (input, "off"))
1210         {
1211           matched = 1;
1212
1213           if (im->drop_pcap_enable)
1214             {
1215               vlib_cli_output (vm, "captured %d pkts...",
1216                                im->pcap_main.n_packets_captured);
1217               if (im->pcap_main.n_packets_captured)
1218                 {
1219                   im->pcap_main.n_packets_to_capture =
1220                     im->pcap_main.n_packets_captured;
1221                   error = pcap_write (&im->pcap_main);
1222                   if (error)
1223                     clib_error_report (error);
1224                   else
1225                     vlib_cli_output (vm, "saved to %s...", im->pcap_filename);
1226                 }
1227             }
1228           else
1229             {
1230               vlib_cli_output (vm, "pcap drop capture already off...");
1231             }
1232
1233           im->drop_pcap_enable = 0;
1234         }
1235       else if (unformat (input, "max %d", &max))
1236         {
1237           im->pcap_pkts_to_capture = max;
1238           matched = 1;
1239         }
1240
1241       else if (unformat (input, "intfc %U",
1242                          unformat_vnet_sw_interface, vnm,
1243                          &im->pcap_sw_if_index))
1244         matched = 1;
1245       else if (unformat (input, "intfc any"))
1246         {
1247           im->pcap_sw_if_index = 0;
1248           matched = 1;
1249         }
1250       else if (unformat (input, "file %s", &filename))
1251         {
1252           u8 *chroot_filename;
1253           /* Brain-police user path input */
1254           if (strstr ((char *) filename, "..")
1255               || index ((char *) filename, '/'))
1256             {
1257               vlib_cli_output (vm, "illegal characters in filename '%s'",
1258                                filename);
1259               continue;
1260             }
1261
1262           chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1263           vec_free (filename);
1264
1265           if (im->pcap_filename)
1266             vec_free (im->pcap_filename);
1267           im->pcap_filename = chroot_filename;
1268           im->pcap_main.file_name = (char *) im->pcap_filename;
1269           matched = 1;
1270         }
1271       else if (unformat (input, "status"))
1272         {
1273           if (im->drop_pcap_enable == 0)
1274             {
1275               vlib_cli_output (vm, "pcap drop capture is off...");
1276               continue;
1277             }
1278
1279           vlib_cli_output (vm, "pcap drop capture: %d of %d pkts...",
1280                            im->pcap_main.n_packets_captured,
1281                            im->pcap_main.n_packets_to_capture);
1282           matched = 1;
1283         }
1284
1285       else
1286         break;
1287     }
1288
1289   if (matched == 0)
1290     return clib_error_return (0, "unknown input `%U'",
1291                               format_unformat_error, input);
1292
1293   return 0;
1294 }
1295
1296 /* *INDENT-OFF* */
1297 VLIB_CLI_COMMAND (pcap_trace_command, static) = {
1298   .path = "pcap drop trace",
1299   .short_help =
1300   "pcap drop trace on off max <nn> intfc <intfc> file <name> status",
1301   .function = pcap_drop_trace_command_fn,
1302 };
1303 /* *INDENT-ON* */
1304
1305 /*
1306  * fd.io coding-style-patch-verification: ON
1307  *
1308  * Local Variables:
1309  * eval: (c-set-style "gnu")
1310  * End:
1311  */