Fix TCP checksum.
[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 static 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 VLIB_NODE_FUNCTION_MULTIARCH_CLONE (vnet_interface_output_node);
467 CLIB_MULTIARCH_SELECT_FN (vnet_interface_output_node);
468
469 /* Use buffer's sw_if_index[VNET_TX] to choose output interface. */
470 static uword
471 vnet_per_buffer_interface_output (vlib_main_t * vm,
472                                   vlib_node_runtime_t * node,
473                                   vlib_frame_t * frame)
474 {
475   vnet_main_t *vnm = vnet_get_main ();
476   u32 n_left_to_next, *from, *to_next;
477   u32 n_left_from, next_index;
478
479   n_left_from = frame->n_vectors;
480
481   from = vlib_frame_vector_args (frame);
482   next_index = node->cached_next_index;
483
484   while (n_left_from > 0)
485     {
486       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
487
488       while (n_left_from >= 4 && n_left_to_next >= 2)
489         {
490           u32 bi0, bi1, next0, next1;
491           vlib_buffer_t *b0, *b1;
492           vnet_hw_interface_t *hi0, *hi1;
493
494           /* Prefetch next iteration. */
495           vlib_prefetch_buffer_with_index (vm, from[2], LOAD);
496           vlib_prefetch_buffer_with_index (vm, from[3], LOAD);
497
498           bi0 = from[0];
499           bi1 = from[1];
500           to_next[0] = bi0;
501           to_next[1] = bi1;
502           from += 2;
503           to_next += 2;
504           n_left_to_next -= 2;
505           n_left_from -= 2;
506
507           b0 = vlib_get_buffer (vm, bi0);
508           b1 = vlib_get_buffer (vm, bi1);
509
510           hi0 =
511             vnet_get_sup_hw_interface (vnm,
512                                        vnet_buffer (b0)->sw_if_index
513                                        [VLIB_TX]);
514           hi1 =
515             vnet_get_sup_hw_interface (vnm,
516                                        vnet_buffer (b1)->sw_if_index
517                                        [VLIB_TX]);
518
519           next0 = hi0->output_node_next_index;
520           next1 = hi1->output_node_next_index;
521
522           vlib_validate_buffer_enqueue_x2 (vm, node, next_index, to_next,
523                                            n_left_to_next, bi0, bi1, next0,
524                                            next1);
525         }
526
527       while (n_left_from > 0 && n_left_to_next > 0)
528         {
529           u32 bi0, next0;
530           vlib_buffer_t *b0;
531           vnet_hw_interface_t *hi0;
532
533           bi0 = from[0];
534           to_next[0] = bi0;
535           from += 1;
536           to_next += 1;
537           n_left_to_next -= 1;
538           n_left_from -= 1;
539
540           b0 = vlib_get_buffer (vm, bi0);
541
542           hi0 =
543             vnet_get_sup_hw_interface (vnm,
544                                        vnet_buffer (b0)->sw_if_index
545                                        [VLIB_TX]);
546
547           next0 = hi0->output_node_next_index;
548
549           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
550                                            n_left_to_next, bi0, next0);
551         }
552
553       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
554     }
555
556   return frame->n_vectors;
557 }
558
559 always_inline u32
560 counter_index (vlib_main_t * vm, vlib_error_t e)
561 {
562   vlib_node_t *n;
563   u32 ci, ni;
564
565   ni = vlib_error_get_node (e);
566   n = vlib_get_node (vm, ni);
567
568   ci = vlib_error_get_code (e);
569   ASSERT (ci < n->n_errors);
570
571   ci += n->error_heap_index;
572
573   return ci;
574 }
575
576 static u8 *
577 format_vnet_error_trace (u8 * s, va_list * va)
578 {
579   vlib_main_t *vm = va_arg (*va, vlib_main_t *);
580   CLIB_UNUSED (vlib_node_t * node) = va_arg (*va, vlib_node_t *);
581   vlib_error_t *e = va_arg (*va, vlib_error_t *);
582   vlib_node_t *error_node;
583   vlib_error_main_t *em = &vm->error_main;
584   u32 i;
585
586   error_node = vlib_get_node (vm, vlib_error_get_node (e[0]));
587   i = counter_index (vm, e[0]);
588   s = format (s, "%v: %s", error_node->name, em->error_strings_heap[i]);
589
590   return s;
591 }
592
593 static void
594 trace_errors_with_buffers (vlib_main_t * vm,
595                            vlib_node_runtime_t * node, vlib_frame_t * frame)
596 {
597   u32 n_left, *buffers;
598
599   buffers = vlib_frame_vector_args (frame);
600   n_left = frame->n_vectors;
601
602   while (n_left >= 4)
603     {
604       u32 bi0, bi1;
605       vlib_buffer_t *b0, *b1;
606       vlib_error_t *t0, *t1;
607
608       /* Prefetch next iteration. */
609       vlib_prefetch_buffer_with_index (vm, buffers[2], LOAD);
610       vlib_prefetch_buffer_with_index (vm, buffers[3], LOAD);
611
612       bi0 = buffers[0];
613       bi1 = buffers[1];
614
615       b0 = vlib_get_buffer (vm, bi0);
616       b1 = vlib_get_buffer (vm, bi1);
617
618       if (b0->flags & VLIB_BUFFER_IS_TRACED)
619         {
620           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
621           t0[0] = b0->error;
622         }
623       if (b1->flags & VLIB_BUFFER_IS_TRACED)
624         {
625           t1 = vlib_add_trace (vm, node, b1, sizeof (t1[0]));
626           t1[0] = b1->error;
627         }
628       buffers += 2;
629       n_left -= 2;
630     }
631
632   while (n_left >= 1)
633     {
634       u32 bi0;
635       vlib_buffer_t *b0;
636       vlib_error_t *t0;
637
638       bi0 = buffers[0];
639
640       b0 = vlib_get_buffer (vm, bi0);
641
642       if (b0->flags & VLIB_BUFFER_IS_TRACED)
643         {
644           t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0]));
645           t0[0] = b0->error;
646         }
647       buffers += 1;
648       n_left -= 1;
649     }
650 }
651
652 static u8 *
653 validate_error (vlib_main_t * vm, vlib_error_t * e, u32 index)
654 {
655   uword node_index = vlib_error_get_node (e[0]);
656   uword code = vlib_error_get_code (e[0]);
657   vlib_node_t *n;
658
659   if (node_index >= vec_len (vm->node_main.nodes))
660     return format (0, "[%d], node index out of range 0x%x, error 0x%x",
661                    index, node_index, e[0]);
662
663   n = vlib_get_node (vm, node_index);
664   if (code >= n->n_errors)
665     return format (0, "[%d], code %d out of range for node %v",
666                    index, code, n->name);
667
668   return 0;
669 }
670
671 static u8 *
672 validate_error_frame (vlib_main_t * vm,
673                       vlib_node_runtime_t * node, vlib_frame_t * f)
674 {
675   u32 *buffers = vlib_frame_vector_args (f);
676   vlib_buffer_t *b;
677   u8 *msg = 0;
678   uword i;
679
680   for (i = 0; i < f->n_vectors; i++)
681     {
682       b = vlib_get_buffer (vm, buffers[i]);
683       msg = validate_error (vm, &b->error, i);
684       if (msg)
685         return msg;
686     }
687
688   return msg;
689 }
690
691 typedef enum
692 {
693   VNET_ERROR_DISPOSITION_DROP,
694   VNET_ERROR_DISPOSITION_PUNT,
695   VNET_ERROR_N_DISPOSITION,
696 } vnet_error_disposition_t;
697
698 always_inline void
699 do_packet (vlib_main_t * vm, vlib_error_t a)
700 {
701   vlib_error_main_t *em = &vm->error_main;
702   u32 i = counter_index (vm, a);
703   em->counters[i] += 1;
704   vlib_error_elog_count (vm, i, 1);
705 }
706
707 static_always_inline uword
708 process_drop_punt (vlib_main_t * vm,
709                    vlib_node_runtime_t * node,
710                    vlib_frame_t * frame, vnet_error_disposition_t disposition)
711 {
712   vnet_main_t *vnm = vnet_get_main ();
713   vlib_error_main_t *em = &vm->error_main;
714   u32 *buffers, *first_buffer;
715   vlib_error_t current_error;
716   u32 current_counter_index, n_errors_left;
717   u32 current_sw_if_index, n_errors_current_sw_if_index;
718   u64 current_counter;
719   vlib_simple_counter_main_t *cm;
720   u32 thread_index = vm->thread_index;
721
722   static vlib_error_t memory[VNET_ERROR_N_DISPOSITION];
723   static char memory_init[VNET_ERROR_N_DISPOSITION];
724
725   buffers = vlib_frame_vector_args (frame);
726   first_buffer = buffers;
727
728   {
729     vlib_buffer_t *b = vlib_get_buffer (vm, first_buffer[0]);
730
731     if (!memory_init[disposition])
732       {
733         memory_init[disposition] = 1;
734         memory[disposition] = b->error;
735       }
736
737     current_sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX];
738     n_errors_current_sw_if_index = 0;
739   }
740
741   current_error = memory[disposition];
742   current_counter_index = counter_index (vm, memory[disposition]);
743   current_counter = em->counters[current_counter_index];
744
745   if (node->flags & VLIB_NODE_FLAG_TRACE)
746     trace_errors_with_buffers (vm, node, frame);
747
748   n_errors_left = frame->n_vectors;
749   cm = vec_elt_at_index (vnm->interface_main.sw_if_counters,
750                          (disposition == VNET_ERROR_DISPOSITION_PUNT
751                           ? VNET_INTERFACE_COUNTER_PUNT
752                           : VNET_INTERFACE_COUNTER_DROP));
753
754   while (n_errors_left >= 2)
755     {
756       vlib_buffer_t *b0, *b1;
757       vnet_sw_interface_t *sw_if0, *sw_if1;
758       vlib_error_t e0, e1;
759       u32 bi0, bi1;
760       u32 sw_if_index0, sw_if_index1;
761
762       bi0 = buffers[0];
763       bi1 = buffers[1];
764
765       buffers += 2;
766       n_errors_left -= 2;
767
768       b0 = vlib_get_buffer (vm, bi0);
769       b1 = vlib_get_buffer (vm, bi1);
770
771       e0 = b0->error;
772       e1 = b1->error;
773
774       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
775       sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
776
777       /* Speculate that sw_if_index == sw_if_index[01]. */
778       n_errors_current_sw_if_index += 2;
779
780       /* Speculatively assume all 2 (node, code) pairs are equal
781          to current (node, code). */
782       current_counter += 2;
783
784       if (PREDICT_FALSE (e0 != current_error
785                          || e1 != current_error
786                          || sw_if_index0 != current_sw_if_index
787                          || sw_if_index1 != current_sw_if_index))
788         {
789           current_counter -= 2;
790           n_errors_current_sw_if_index -= 2;
791
792           vlib_increment_simple_counter (cm, thread_index, sw_if_index0, 1);
793           vlib_increment_simple_counter (cm, thread_index, sw_if_index1, 1);
794
795           /* Increment super-interface drop/punt counters for
796              sub-interfaces. */
797           sw_if0 = vnet_get_sw_interface (vnm, sw_if_index0);
798           vlib_increment_simple_counter
799             (cm, thread_index, sw_if0->sup_sw_if_index,
800              sw_if0->sup_sw_if_index != sw_if_index0);
801
802           sw_if1 = vnet_get_sw_interface (vnm, sw_if_index1);
803           vlib_increment_simple_counter
804             (cm, thread_index, sw_if1->sup_sw_if_index,
805              sw_if1->sup_sw_if_index != sw_if_index1);
806
807           em->counters[current_counter_index] = current_counter;
808           do_packet (vm, e0);
809           do_packet (vm, e1);
810
811           /* For 2 repeated errors, change current error. */
812           if (e0 == e1 && e1 != current_error)
813             {
814               current_error = e0;
815               current_counter_index = counter_index (vm, e0);
816             }
817           current_counter = em->counters[current_counter_index];
818         }
819     }
820
821   while (n_errors_left >= 1)
822     {
823       vlib_buffer_t *b0;
824       vnet_sw_interface_t *sw_if0;
825       vlib_error_t e0;
826       u32 bi0, sw_if_index0;
827
828       bi0 = buffers[0];
829
830       buffers += 1;
831       n_errors_left -= 1;
832       current_counter += 1;
833
834       b0 = vlib_get_buffer (vm, bi0);
835       e0 = b0->error;
836
837       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
838
839       /* Increment drop/punt counters. */
840       vlib_increment_simple_counter (cm, thread_index, sw_if_index0, 1);
841
842       /* Increment super-interface drop/punt counters for sub-interfaces. */
843       sw_if0 = vnet_get_sw_interface (vnm, sw_if_index0);
844       vlib_increment_simple_counter (cm, thread_index,
845                                      sw_if0->sup_sw_if_index,
846                                      sw_if0->sup_sw_if_index != sw_if_index0);
847
848       if (PREDICT_FALSE (e0 != current_error))
849         {
850           current_counter -= 1;
851
852           vlib_error_elog_count (vm, current_counter_index,
853                                  (current_counter
854                                   - em->counters[current_counter_index]));
855
856           em->counters[current_counter_index] = current_counter;
857
858           do_packet (vm, e0);
859           current_error = e0;
860           current_counter_index = counter_index (vm, e0);
861           current_counter = em->counters[current_counter_index];
862         }
863     }
864
865   if (n_errors_current_sw_if_index > 0)
866     {
867       vnet_sw_interface_t *si;
868
869       vlib_increment_simple_counter (cm, thread_index, current_sw_if_index,
870                                      n_errors_current_sw_if_index);
871
872       si = vnet_get_sw_interface (vnm, current_sw_if_index);
873       if (si->sup_sw_if_index != current_sw_if_index)
874         vlib_increment_simple_counter (cm, thread_index, si->sup_sw_if_index,
875                                        n_errors_current_sw_if_index);
876     }
877
878   vlib_error_elog_count (vm, current_counter_index,
879                          (current_counter
880                           - em->counters[current_counter_index]));
881
882   /* Return cached counter. */
883   em->counters[current_counter_index] = current_counter;
884
885   /* Save memory for next iteration. */
886   memory[disposition] = current_error;
887
888   if (disposition == VNET_ERROR_DISPOSITION_DROP || !vm->os_punt_frame)
889     {
890       vlib_buffer_free (vm, first_buffer, frame->n_vectors);
891
892       /* If there is no punt function, free the frame as well. */
893       if (disposition == VNET_ERROR_DISPOSITION_PUNT && !vm->os_punt_frame)
894         vlib_frame_free (vm, node, frame);
895     }
896   else
897     vm->os_punt_frame (vm, node, frame);
898
899   return frame->n_vectors;
900 }
901
902 static inline void
903 pcap_drop_trace (vlib_main_t * vm,
904                  vnet_interface_main_t * im, vlib_frame_t * f)
905 {
906   u32 *from;
907   u32 n_left = f->n_vectors;
908   vlib_buffer_t *b0, *p1;
909   u32 bi0;
910   i16 save_current_data;
911   u16 save_current_length;
912
913   from = vlib_frame_vector_args (f);
914
915   while (n_left > 0)
916     {
917       if (PREDICT_TRUE (n_left > 1))
918         {
919           p1 = vlib_get_buffer (vm, from[1]);
920           vlib_prefetch_buffer_header (p1, LOAD);
921         }
922
923       bi0 = from[0];
924       b0 = vlib_get_buffer (vm, bi0);
925       from++;
926       n_left--;
927
928       /* See if we're pointedly ignoring this specific error */
929       if (im->pcap_drop_filter_hash
930           && hash_get (im->pcap_drop_filter_hash, b0->error))
931         continue;
932
933       /* Trace all drops, or drops received on a specific interface */
934       if (im->pcap_sw_if_index == 0 ||
935           im->pcap_sw_if_index == vnet_buffer (b0)->sw_if_index[VLIB_RX])
936         {
937           save_current_data = b0->current_data;
938           save_current_length = b0->current_length;
939
940           /*
941            * Typically, we'll need to rewind the buffer
942            */
943           if (b0->current_data > 0)
944             vlib_buffer_advance (b0, (word) - b0->current_data);
945
946           pcap_add_buffer (&im->pcap_main, vm, bi0, 512);
947
948           b0->current_data = save_current_data;
949           b0->current_length = save_current_length;
950         }
951     }
952 }
953
954 void
955 vnet_pcap_drop_trace_filter_add_del (u32 error_index, int is_add)
956 {
957   vnet_interface_main_t *im = &vnet_get_main ()->interface_main;
958
959   if (im->pcap_drop_filter_hash == 0)
960     im->pcap_drop_filter_hash = hash_create (0, sizeof (uword));
961
962   if (is_add)
963     hash_set (im->pcap_drop_filter_hash, error_index, 1);
964   else
965     hash_unset (im->pcap_drop_filter_hash, error_index);
966 }
967
968 static uword
969 process_drop (vlib_main_t * vm,
970               vlib_node_runtime_t * node, vlib_frame_t * frame)
971 {
972   vnet_interface_main_t *im = &vnet_get_main ()->interface_main;
973
974   if (PREDICT_FALSE (im->drop_pcap_enable))
975     pcap_drop_trace (vm, im, frame);
976
977   return process_drop_punt (vm, node, frame, VNET_ERROR_DISPOSITION_DROP);
978 }
979
980 static uword
981 process_punt (vlib_main_t * vm,
982               vlib_node_runtime_t * node, vlib_frame_t * frame)
983 {
984   return process_drop_punt (vm, node, frame, VNET_ERROR_DISPOSITION_PUNT);
985 }
986
987 /* *INDENT-OFF* */
988 VLIB_REGISTER_NODE (drop_buffers,static) = {
989   .function = process_drop,
990   .name = "error-drop",
991   .flags = VLIB_NODE_FLAG_IS_DROP,
992   .vector_size = sizeof (u32),
993   .format_trace = format_vnet_error_trace,
994   .validate_frame = validate_error_frame,
995 };
996 /* *INDENT-ON* */
997
998 VLIB_NODE_FUNCTION_MULTIARCH (drop_buffers, process_drop);
999
1000 /* *INDENT-OFF* */
1001 VLIB_REGISTER_NODE (punt_buffers,static) = {
1002   .function = process_punt,
1003   .flags = (VLIB_NODE_FLAG_FRAME_NO_FREE_AFTER_DISPATCH
1004             | VLIB_NODE_FLAG_IS_PUNT),
1005   .name = "error-punt",
1006   .vector_size = sizeof (u32),
1007   .format_trace = format_vnet_error_trace,
1008   .validate_frame = validate_error_frame,
1009 };
1010 /* *INDENT-ON* */
1011
1012 VLIB_NODE_FUNCTION_MULTIARCH (punt_buffers, process_punt);
1013
1014 /* *INDENT-OFF* */
1015 VLIB_REGISTER_NODE (vnet_per_buffer_interface_output_node,static) = {
1016   .function = vnet_per_buffer_interface_output,
1017   .name = "interface-output",
1018   .vector_size = sizeof (u32),
1019 };
1020 /* *INDENT-ON* */
1021
1022 VLIB_NODE_FUNCTION_MULTIARCH (vnet_per_buffer_interface_output_node,
1023                               vnet_per_buffer_interface_output);
1024
1025 static uword
1026 interface_tx_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
1027                       vlib_frame_t * from_frame)
1028 {
1029   vnet_main_t *vnm = vnet_get_main ();
1030   u32 last_sw_if_index = ~0;
1031   vlib_frame_t *to_frame = 0;
1032   vnet_hw_interface_t *hw = 0;
1033   u32 *from, *to_next = 0;
1034   u32 n_left_from;
1035
1036   from = vlib_frame_vector_args (from_frame);
1037   n_left_from = from_frame->n_vectors;
1038   while (n_left_from > 0)
1039     {
1040       u32 bi0;
1041       vlib_buffer_t *b0;
1042       u32 sw_if_index0;
1043
1044       bi0 = from[0];
1045       from++;
1046       n_left_from--;
1047       b0 = vlib_get_buffer (vm, bi0);
1048       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
1049
1050       if (PREDICT_FALSE ((last_sw_if_index != sw_if_index0) || to_frame == 0))
1051         {
1052           if (to_frame)
1053             {
1054               hw = vnet_get_sup_hw_interface (vnm, last_sw_if_index);
1055               vlib_put_frame_to_node (vm, hw->tx_node_index, to_frame);
1056             }
1057           last_sw_if_index = sw_if_index0;
1058           hw = vnet_get_sup_hw_interface (vnm, sw_if_index0);
1059           to_frame = vlib_get_frame_to_node (vm, hw->tx_node_index);
1060           to_next = vlib_frame_vector_args (to_frame);
1061         }
1062
1063       to_next[0] = bi0;
1064       to_next++;
1065       to_frame->n_vectors++;
1066     }
1067   vlib_put_frame_to_node (vm, hw->tx_node_index, to_frame);
1068   return from_frame->n_vectors;
1069 }
1070
1071 /* *INDENT-OFF* */
1072 VLIB_REGISTER_NODE (interface_tx, static) = {
1073   .function = interface_tx_node_fn,
1074   .name = "interface-tx",
1075   .vector_size = sizeof (u32),
1076   .n_next_nodes = 1,
1077   .next_nodes = {
1078     [0] = "error-drop",
1079   },
1080 };
1081
1082 VNET_FEATURE_ARC_INIT (interface_output, static) =
1083 {
1084   .arc_name  = "interface-output",
1085   .start_nodes = VNET_FEATURES (0),
1086   .last_in_arc = "interface-tx",
1087   .arc_index_ptr = &vnet_main.interface_main.output_feature_arc_index,
1088 };
1089
1090 VNET_FEATURE_INIT (span_tx, static) = {
1091   .arc_name = "interface-output",
1092   .node_name = "span-output",
1093   .runs_before = VNET_FEATURES ("interface-tx"),
1094 };
1095
1096 VNET_FEATURE_INIT (ipsec_if_tx, static) = {
1097   .arc_name = "interface-output",
1098   .node_name = "ipsec-if-output",
1099   .runs_before = VNET_FEATURES ("interface-tx"),
1100 };
1101
1102 VNET_FEATURE_INIT (interface_tx, static) = {
1103   .arc_name = "interface-output",
1104   .node_name = "interface-tx",
1105   .runs_before = 0,
1106 };
1107 /* *INDENT-ON* */
1108
1109 clib_error_t *
1110 vnet_per_buffer_interface_output_hw_interface_add_del (vnet_main_t * vnm,
1111                                                        u32 hw_if_index,
1112                                                        u32 is_create)
1113 {
1114   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
1115   u32 next_index;
1116
1117   if (hi->output_node_index == 0)
1118     return 0;
1119
1120   next_index = vlib_node_add_next
1121     (vnm->vlib_main, vnet_per_buffer_interface_output_node.index,
1122      hi->output_node_index);
1123   hi->output_node_next_index = next_index;
1124
1125   return 0;
1126 }
1127
1128 VNET_HW_INTERFACE_ADD_DEL_FUNCTION
1129   (vnet_per_buffer_interface_output_hw_interface_add_del);
1130
1131 void
1132 vnet_set_interface_output_node (vnet_main_t * vnm,
1133                                 u32 hw_if_index, u32 node_index)
1134 {
1135   ASSERT (node_index);
1136   vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index);
1137   u32 next_index = vlib_node_add_next
1138     (vnm->vlib_main, vnet_per_buffer_interface_output_node.index, node_index);
1139   hi->output_node_next_index = next_index;
1140   hi->output_node_index = node_index;
1141 }
1142
1143 static clib_error_t *
1144 pcap_drop_trace_command_fn (vlib_main_t * vm,
1145                             unformat_input_t * input,
1146                             vlib_cli_command_t * cmd)
1147 {
1148   vnet_main_t *vnm = vnet_get_main ();
1149   vnet_interface_main_t *im = &vnm->interface_main;
1150   u8 *filename;
1151   u32 max;
1152   int matched = 0;
1153   clib_error_t *error = 0;
1154
1155   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1156     {
1157       if (unformat (input, "on"))
1158         {
1159           if (im->drop_pcap_enable == 0)
1160             {
1161               if (im->pcap_filename == 0)
1162                 im->pcap_filename = format (0, "/tmp/drop.pcap%c", 0);
1163
1164               clib_memset (&im->pcap_main, 0, sizeof (im->pcap_main));
1165               im->pcap_main.file_name = (char *) im->pcap_filename;
1166               im->pcap_main.n_packets_to_capture = 100;
1167               if (im->pcap_pkts_to_capture)
1168                 im->pcap_main.n_packets_to_capture = im->pcap_pkts_to_capture;
1169
1170               im->pcap_main.packet_type = PCAP_PACKET_TYPE_ethernet;
1171               im->drop_pcap_enable = 1;
1172               matched = 1;
1173               vlib_cli_output (vm, "pcap drop capture on...");
1174             }
1175           else
1176             {
1177               vlib_cli_output (vm, "pcap drop capture already on...");
1178             }
1179           matched = 1;
1180         }
1181       else if (unformat (input, "off"))
1182         {
1183           matched = 1;
1184
1185           if (im->drop_pcap_enable)
1186             {
1187               vlib_cli_output (vm, "captured %d pkts...",
1188                                im->pcap_main.n_packets_captured);
1189               if (im->pcap_main.n_packets_captured)
1190                 {
1191                   im->pcap_main.n_packets_to_capture =
1192                     im->pcap_main.n_packets_captured;
1193                   error = pcap_write (&im->pcap_main);
1194                   if (error)
1195                     clib_error_report (error);
1196                   else
1197                     vlib_cli_output (vm, "saved to %s...", im->pcap_filename);
1198                 }
1199             }
1200           else
1201             {
1202               vlib_cli_output (vm, "pcap drop capture already off...");
1203             }
1204
1205           im->drop_pcap_enable = 0;
1206         }
1207       else if (unformat (input, "max %d", &max))
1208         {
1209           im->pcap_pkts_to_capture = max;
1210           matched = 1;
1211         }
1212
1213       else if (unformat (input, "intfc %U",
1214                          unformat_vnet_sw_interface, vnm,
1215                          &im->pcap_sw_if_index))
1216         matched = 1;
1217       else if (unformat (input, "intfc any"))
1218         {
1219           im->pcap_sw_if_index = 0;
1220           matched = 1;
1221         }
1222       else if (unformat (input, "file %s", &filename))
1223         {
1224           u8 *chroot_filename;
1225           /* Brain-police user path input */
1226           if (strstr ((char *) filename, "..")
1227               || index ((char *) filename, '/'))
1228             {
1229               vlib_cli_output (vm, "illegal characters in filename '%s'",
1230                                filename);
1231               continue;
1232             }
1233
1234           chroot_filename = format (0, "/tmp/%s%c", filename, 0);
1235           vec_free (filename);
1236
1237           if (im->pcap_filename)
1238             vec_free (im->pcap_filename);
1239           im->pcap_filename = chroot_filename;
1240           im->pcap_main.file_name = (char *) im->pcap_filename;
1241           matched = 1;
1242         }
1243       else if (unformat (input, "status"))
1244         {
1245           if (im->drop_pcap_enable == 0)
1246             {
1247               vlib_cli_output (vm, "pcap drop capture is off...");
1248               continue;
1249             }
1250
1251           vlib_cli_output (vm, "pcap drop capture: %d of %d pkts...",
1252                            im->pcap_main.n_packets_captured,
1253                            im->pcap_main.n_packets_to_capture);
1254           matched = 1;
1255         }
1256
1257       else
1258         break;
1259     }
1260
1261   if (matched == 0)
1262     return clib_error_return (0, "unknown input `%U'",
1263                               format_unformat_error, input);
1264
1265   return 0;
1266 }
1267
1268 /* *INDENT-OFF* */
1269 VLIB_CLI_COMMAND (pcap_trace_command, static) = {
1270   .path = "pcap drop trace",
1271   .short_help =
1272   "pcap drop trace on off max <nn> intfc <intfc> file <name> status",
1273   .function = pcap_drop_trace_command_fn,
1274 };
1275 /* *INDENT-ON* */
1276
1277 /*
1278  * fd.io coding-style-patch-verification: ON
1279  *
1280  * Local Variables:
1281  * eval: (c-set-style "gnu")
1282  * End:
1283  */