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