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