/* * Copyright (c) 2015 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * interface_output.c: interface output node * * Copyright (c) 2008 Eliot Dresselhaus * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include typedef struct { u32 sw_if_index; u32 flags; u16 gso_size; u8 gso_l4_hdr_sz; u8 data[128 - 3 * sizeof (u32)]; } interface_output_trace_t; #ifndef CLIB_MARCH_VARIANT u8 * format_vnet_interface_output_trace (u8 * s, va_list * va) { CLIB_UNUSED (vlib_main_t * vm) = va_arg (*va, vlib_main_t *); vlib_node_t *node = va_arg (*va, vlib_node_t *); interface_output_trace_t *t = va_arg (*va, interface_output_trace_t *); vnet_main_t *vnm = vnet_get_main (); vnet_sw_interface_t *si; u32 indent; if (t->sw_if_index != (u32) ~ 0) { indent = format_get_indent (s); if (pool_is_free_index (vnm->interface_main.sw_interfaces, t->sw_if_index)) { /* the interface may have been deleted by the time the trace is printed */ s = format (s, "sw_if_index: %d ", t->sw_if_index); } else { si = vnet_get_sw_interface (vnm, t->sw_if_index); s = format (s, "%U ", format_vnet_sw_interface_name, vnm, si, t->flags); } #define _(bit, name, v, x) \ if (v && (t->flags & VNET_BUFFER_F_##name)) \ s = format (s, "%s ", v); foreach_vnet_buffer_flag #undef _ if (t->flags & VNET_BUFFER_F_GSO) { s = format (s, "\n%Ugso_sz %d gso_l4_hdr_sz %d", format_white_space, indent + 2, t->gso_size, t->gso_l4_hdr_sz); } s = format (s, "\n%U%U", format_white_space, indent, node->format_buffer ? node->format_buffer : format_hex_bytes, t->data, sizeof (t->data)); } return s; } static void vnet_interface_output_trace (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame, uword n_buffers) { u32 n_left, *from; n_left = n_buffers; from = vlib_frame_vector_args (frame); while (n_left >= 4) { u32 bi0, bi1; vlib_buffer_t *b0, *b1; interface_output_trace_t *t0, *t1; /* Prefetch next iteration. */ vlib_prefetch_buffer_with_index (vm, from[2], LOAD); vlib_prefetch_buffer_with_index (vm, from[3], LOAD); bi0 = from[0]; bi1 = from[1]; b0 = vlib_get_buffer (vm, bi0); b1 = vlib_get_buffer (vm, bi1); if (b0->flags & VLIB_BUFFER_IS_TRACED) { t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0])); t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX]; t0->flags = b0->flags; t0->gso_size = vnet_buffer2 (b0)->gso_size; t0->gso_l4_hdr_sz = vnet_buffer2 (b0)->gso_l4_hdr_sz; clib_memcpy_fast (t0->data, vlib_buffer_get_current (b0), sizeof (t0->data)); } if (b1->flags & VLIB_BUFFER_IS_TRACED) { t1 = vlib_add_trace (vm, node, b1, sizeof (t1[0])); t1->sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_TX]; t1->flags = b1->flags; t1->gso_size = vnet_buffer2 (b1)->gso_size; t1->gso_l4_hdr_sz = vnet_buffer2 (b1)->gso_l4_hdr_sz; clib_memcpy_fast (t1->data, vlib_buffer_get_current (b1), sizeof (t1->data)); } from += 2; n_left -= 2; } while (n_left >= 1) { u32 bi0; vlib_buffer_t *b0; interface_output_trace_t *t0; bi0 = from[0]; b0 = vlib_get_buffer (vm, bi0); if (b0->flags & VLIB_BUFFER_IS_TRACED) { t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0])); t0->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX]; t0->flags = b0->flags; t0->gso_size = vnet_buffer2 (b0)->gso_size; t0->gso_l4_hdr_sz = vnet_buffer2 (b0)->gso_l4_hdr_sz; clib_memcpy_fast (t0->data, vlib_buffer_get_current (b0), sizeof (t0->data)); } from += 1; n_left -= 1; } } static_always_inline void calc_checksums (vlib_main_t * vm, vlib_buffer_t * b) { tcp_header_t *th; udp_header_t *uh; int is_ip4 = (b->flags & VNET_BUFFER_F_IS_IP4) != 0; int is_ip6 = (b->flags & VNET_BUFFER_F_IS_IP6) != 0; ASSERT (!(is_ip4 && is_ip6)); th = (tcp_header_t *) (b->data + vnet_buffer (b)->l4_hdr_offset); uh = (udp_header_t *) (b->data + vnet_buffer (b)->l4_hdr_offset); if (is_ip4) { ip4_header_t *ip4; ip4 = (ip4_header_t *) (b->data + vnet_buffer (b)->l3_hdr_offset); if (b->flags & VNET_BUFFER_F_OFFLOAD_IP_CKSUM) ip4->checksum = ip4_header_checksum (ip4); if (b->flags & VNET_BUFFER_F_OFFLOAD_TCP_CKSUM) { th->checksum = 0; th->checksum = ip4_tcp_udp_compute_checksum (vm, b, ip4); } else if (b->flags & VNET_BUFFER_F_OFFLOAD_UDP_CKSUM) uh->checksum = ip4_tcp_udp_compute_checksum (vm, b, ip4); } else if (is_ip6) { int bogus; ip6_header_t *ip6; ip6 = (ip6_header_t *) (b->data + vnet_buffer (b)->l3_hdr_offset); if (b->flags & VNET_BUFFER_F_OFFLOAD_TCP_CKSUM) { th->checksum = 0; th->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b, ip6, &bogus); } else if (b->flags & VNET_BUFFER_F_OFFLOAD_UDP_CKSUM) { uh->checksum = 0; uh->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b, ip6, &bogus); } } b->flags &= ~VNET_BUFFER_F_OFFLOAD_TCP_CKSUM; b->flags &= ~VNET_BUFFER_F_OFFLOAD_UDP_CKSUM; b->flags &= ~VNET_BUFFER_F_OFFLOAD_IP_CKSUM; } static_always_inline u16 tso_alloc_tx_bufs (vlib_main_t * vm, vnet_interface_per_thread_data_t * ptd, vlib_buffer_t * b0, u32 n_bytes_b0, u16 l234_sz, u16 gso_size) { u16 size = clib_min (gso_size, vlib_buffer_get_default_data_size (vm) - l234_sz); /* rounded-up division */ u16 n_bufs = (n_bytes_b0 - l234_sz + (size - 1)) / size; u16 n_alloc; ASSERT (n_bufs > 0); vec_validate (ptd->split_buffers, n_bufs - 1); n_alloc = vlib_buffer_alloc (vm, ptd->split_buffers, n_bufs); if (n_alloc < n_bufs) { vlib_buffer_free (vm, ptd->split_buffers, n_alloc); return 0; } return n_alloc; } static_always_inline void tso_init_buf_from_template_base (vlib_buffer_t * nb0, vlib_buffer_t * b0, u32 flags, u16 length) { nb0->current_data = b0->current_data; nb0->total_length_not_including_first_buffer = 0; nb0->flags = VLIB_BUFFER_TOTAL_LENGTH_VALID | flags; clib_memcpy_fast (&nb0->opaque, &b0->opaque, sizeof (nb0->opaque)); clib_memcpy_fast (vlib_buffer_get_current (nb0), vlib_buffer_get_current (b0), length); nb0->current_length = length; } static_always_inline void tso_init_buf_from_template (vlib_main_t * vm, vlib_buffer_t * nb0, vlib_buffer_t * b0, u16 template_data_sz, u16 gso_size, u8 ** p_dst_ptr, u16 * p_dst_left, u32 next_tcp_seq, u32 flags) { tso_init_buf_from_template_base (nb0, b0, flags, template_data_sz); *p_dst_left = clib_min (gso_size, vlib_buffer_get_default_data_size (vm) - (template_data_sz + nb0->current_data)); *p_dst_ptr = vlib_buffer_get_current (nb0) + template_data_sz; tcp_header_t *tcp = (tcp_header_t *) (nb0->data + vnet_buffer (nb0)->l4_hdr_offset); tcp->seq_number = clib_host_to_net_u32 (next_tcp_seq); } static_always_inline void tso_fixup_segmented_buf (vlib_buffer_t * b0, u8 tcp_flags, int is_ip6) { u16 l3_hdr_offset = vnet_buffer (b0)->l3_hdr_offset; u16 l4_hdr_offset = vnet_buffer (b0)->l4_hdr_offset; ip4_header_t *ip4 = (ip4_header_t *) (b0->data + l3_hdr_offset); ip6_header_t *ip6 = (ip6_header_t *) (b0->data + l3_hdr_offset); tcp_header_t *tcp = (tcp_header_t *) (b0->data + l4_hdr_offset); tcp->flags = tcp_flags; if (is_ip6) ip6->payload_length = clib_host_to_net_u16 (b0->current_length - (l4_hdr_offset - b0->current_data)); else ip4->length = clib_host_to_net_u16 (b0->current_length - (l3_hdr_offset - b0->current_data)); } /** * Allocate the necessary number of ptd->split_buffers, * and segment the possibly chained buffer(s) from b0 into * there. * * Return the cumulative number of bytes sent or zero * if allocation failed. */ static_always_inline u32 tso_segment_buffer (vlib_main_t * vm, vnet_interface_per_thread_data_t * ptd, int do_tx_offloads, u32 sbi0, vlib_buffer_t * sb0, u32 n_bytes_b0) { u32 n_tx_bytes = 0; int is_ip4 = sb0->flags & VNET_BUFFER_F_IS_IP4; int is_ip6 = sb0->flags & VNET_BUFFER_F_IS_IP6; ASSERT (is_ip4 || is_ip6); ASSERT (sb0->flags & VNET_BUFFER_F_L2_HDR_OFFSET_VALID); ASSERT (sb0->flags & VNET_BUFFER_F_L3_HDR_OFFSET_VALID); ASSERT (sb0->flags & VNET_BUFFER_F_L4_HDR_OFFSET_VALID); u16 gso_size = vnet_buffer2 (sb0)->gso_size; int l4_hdr_sz = vnet_buffer2 (sb0)->gso_l4_hdr_sz; u8 save_tcp_flags = 0; u8 tcp_flags_no_fin_psh = 0; u32 next_tcp_seq = 0; tcp_header_t *tcp = (tcp_header_t *) (sb0->data + vnet_buffer (sb0)->l4_hdr_offset); next_tcp_seq = clib_net_to_host_u32 (tcp->seq_number); /* store original flags for last packet and reset FIN and PSH */ save_tcp_flags = tcp->flags; tcp_flags_no_fin_psh = tcp->flags & ~(TCP_FLAG_FIN | TCP_FLAG_PSH); tcp->checksum = 0; u32 default_bflags = sb0->flags & ~(VNET_BUFFER_F_GSO | VLIB_BUFFER_NEXT_PRESENT); u16 l234_sz = vnet_buffer (sb0)->l4_hdr_offset + l4_hdr_sz - sb0->current_data; int first_data_size = clib_min (gso_size, sb0->current_length - l234_sz); next_tcp_seq += first_data_size; if (PREDICT_FALSE (!tso_alloc_tx_bufs (vm, ptd, sb0, n_bytes_b0, l234_sz, gso_size))) return 0; vlib_buffer_t *b0 = vlib_get_buffer (vm, ptd->split_buffers[0]); tso_init_buf_from_template_base (b0, sb0, default_bflags, l234_sz + first_data_size); u32 total_src_left = n_bytes_b0 - l234_sz - first_data_size; if (total_src_left) { /* Need to copy more segments */ u8 *src_ptr, *dst_ptr; u16 src_left, dst_left; /* current source buffer */ vlib_buffer_t *csb0 = sb0; u32 csbi0 = sbi0; /* current dest buffer */ vlib_buffer_t *cdb0; u16 dbi = 1; /* the buffer
;;; plugin-test-skel.el - vpp-api-test plug-in skeleton
;;;
;;; Copyright (c) 2016 Cisco and/or its affiliates.
;;; Licensed under the Apache License, Version 2.0 (the "License");
;;; you may not use this file except in compliance with the License.
;;; You may obtain a copy of the License at:
;;;
;;;     http://www.apache.org/licenses/LICENSE-2.0
;;;
;;; Unless required by applicable law or agreed to in writing, software
;;; distributed under the License is distributed on an "AS IS" BASIS,
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;; See the License for the specific language governing permissions and
;;; limitations under the License.

(require 'skeleton)

(define-skeleton skel-plugin-test
"Insert a plug-in vpp-api-test skeleton "
nil
'(if (not (boundp 'plugin-name))
     (setq plugin-name (read-string "Plugin name: ")))
'(setq PLUGIN-NAME (upcase plugin-name))
'(setq capital-oh-en "ON")
'(setq main-p (concat (substring plugin-name 0 1) "tmp"))
"/*
 * " plugin-name ".c - skeleton vpp-api-test plug-in 
 *
 * Copyright (c) <current-year> <your-organization>
 * Licensed under the Apache License, Version 2.0 (the \"License\");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an \"AS IS\" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <vat/vat.h>
#include <vlibapi/api.h>
#include <vlibmemory/api.h>
#include <vppinfra/error.h>

uword unformat_sw_if_index (unformat_input_t * input, va_list * args);

/* Declare message IDs */
#include <" plugin-name "/" plugin-name "_msg_enum.h>

/* define message structures */
#define vl_typedefs
#include <" plugin-name "/" plugin-name "_all_api_h.h> 
#undef vl_typedefs

/* declare message handlers for each api */

#define vl_endianfun             /* define message structures */
#include <" plugin-name "/" plugin-name "_all_api_h.h> 
#undef vl_endianfun

/* instantiate all the print functions we know about */
#define vl_print(handle, ...)
#define vl_printfun
#include <" plugin-name "/" plugin-name "_all_api_h.h> 
#undef vl_printfun

/* Get the API version number. */
#define vl_api_version(n,v) static u32 api_version=(v);
#include <" plugin-name "/" plugin-name "_all_api_h.h>
#undef vl_api_version


typedef struct 
{
  /* API message ID base */
  u16 msg_id_base;
  vat_main_t *vat_main;
} " plugin-name "_test_main_t;

" plugin-name "_test_main_t " plugin-name "_test_main;

#define __plugin_msg_base " plugin-name"_test_main.msg_id_base
#include <vlibapi/vat_helper_macros.h>

#define foreach_standard_reply_retval_handler   \\
_(" plugin-name "_enable_disable_reply)

#define _(n)                                            \\
    static void vl_api_##n##_t_handler                  \\
    (vl_api_##n##_t * mp)                               \\
    {                                                   \\
        vat_main_t * vam = " plugin-name "_test_main.vat_main;   \\
        i32 retval = ntohl(mp->retval);                 \\
        if (vam->async_mode) {                          \\
            vam->async_errors += (retval < 0);          \\
        } else {                                        \\
            vam->retval = retval;                       \\
            vam->result_ready = 1;                      \\
        }                                               \\
    }
foreach_standard_reply_retval_handler;
#undef _

/* 
 * Table of message reply handlers, must include boilerplate handlers
 * we just generated
 */
#define foreach_vpe_api_reply_msg                                       \\
_(" PLUGIN-NAME "_ENABLE_DISABLE_REPLY, " plugin-name "_enable_disable_reply)


static int api_" plugin-name "_enable_disable (vat_main_t * vam)
{
  unformat_input_t * i = vam->input;
  int enable_disable = 1;
  u32 sw_if_index = ~0;
  vl_api_" plugin-name "_enable_disable_t * mp;
  int ret;

  /* Parse args required to build the message */
  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) 
    {
      if (unformat (i, \"%U\", unformat_sw_if_index, vam, &sw_if_index))
          ;
        else if (unformat (i, \"sw_if_index %d\", &sw_if_index))
          ;
      else if (unformat (i, \"disable\"))
          enable_disable = 0;
      else
          break;
    }
  
  if (sw_if_index == ~0) 
    {
      errmsg (\"missing interface name / explicit sw_if_index number \\n\");
      return -99;
    }
  
  /* Construct the API message */
  M(" PLUGIN-NAME "_ENABLE_DISABLE, mp);
  mp->sw_if_index = ntohl (sw_if_index);
  mp->enable_disable = enable_disable;

  /* send it... */
  S(mp);

  /* Wait for a reply... */
  W (ret);
  return ret;
}

/* 
 * List of messages that the api test plugin sends,
 * and that the data plane plugin processes
 */
#define foreach_vpe_api_msg \\
_(" plugin-name "_enable_disable, \"<intfc> [disable]\")

static void " plugin-name "_api_hookup (vat_main_t *vam)
{
    " plugin-name "_test_main_t * " main-p " = &" plugin-name "_test_main;
    /* Hook up handlers for replies from the data plane plug-in */
#define _(N,n)                                                  \\
    vl_msg_api_set_handlers((VL_API_##N + " main-p "->msg_id_base),     \\
                           #n,                                  \\
                           vl_api_##n##_t_handler,              \\
                           vl_noop_handler,                     \\
                           vl_api_##n##_t_endian,               \\
                           vl_api_##n##_t_print,                \\
                           sizeof(vl_api_##n##_t), 1); 
    foreach_vpe_api_reply_msg;
#undef _

    /* API messages we can send */
#define _(n,h) hash_set_mem (vam->function_by_name, #n, api_##n);
    foreach_vpe_api_msg;
#undef _    
    
    /* Help strings */
#define _(n,h) hash_set_mem (vam->help_by_name, #n, h);
    foreach_vpe_api_msg;
#undef _
}

clib_error_t * vat_plugin_register (vat_main_t *vam)
{
  " plugin-name "_test_main_t * " main-p " = &" plugin-name "_test_main;
  u8 * name;

  " main-p "->vat_main = vam;

  /* Ask the vpp engine for the first assigned message-id */
  name = format (0, \"" plugin-name "_%08x%c\", api_version, 0);
  " main-p "->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name);

  if (" main-p "->msg_id_base != (u16) ~0)
    " plugin-name "_api_hookup (vam);
  
  vec_free(name);
  
  return 0;
}
/*
 * fd.io coding-style-patch-verification: " capital-oh-en "
 *
 * Local Variables:
 * eval: (c-set-style \"gnu\")
 * End:
 */
")
g up the drop node */ error_node_index = vm->node_main.node_by_error[b0->error]; n = vlib_get_node (vm, error_node_index); /* Length of full drop string, w/ "nodename: " prepended */ drop_string_len = error_string_len + vec_len (n->name) + 2; /* Find the last buffer in the chain */ while (last->flags & VLIB_BUFFER_NEXT_PRESENT) last = vlib_get_buffer (vm, last->next_buffer); /* * Append : to the capture, * only if we can do that without allocating a new buffer. */ if (PREDICT_TRUE ((last->current_data + last->current_length) < (VLIB_BUFFER_DEFAULT_DATA_SIZE - drop_string_len))) { clib_memcpy_fast (last->data + last->current_data + last->current_length, n->name, vec_len (n->name)); clib_memcpy_fast (last->data + last->current_data + last->current_length + vec_len (n->name), ": ", 2); clib_memcpy_fast (last->data + last->current_data + last->current_length + vec_len (n->name) + 2, em->error_strings_heap[b0->error], error_string_len); last->current_length += drop_string_len; b0->flags &= ~(VLIB_BUFFER_TOTAL_LENGTH_VALID); pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt); last->current_length -= drop_string_len; b0->current_data = save_current_data; b0->current_length = save_current_length; continue; } } /* * Didn't have space in the last buffer, here's the dropped * packet as-is */ pcap_add_buffer (&pp->pcap_main, vm, bi0, pp->max_bytes_per_pkt); b0->current_data = save_current_data; b0->current_length = save_current_length; } } } #ifndef CLIB_MARCH_VARIANT void vnet_pcap_drop_trace_filter_add_del (u32 error_index, int is_add) { vnet_interface_main_t *im = &vnet_get_main ()->interface_main; if (im->pcap_drop_filter_hash == 0) im->pcap_drop_filter_hash = hash_create (0, sizeof (uword)); if (is_add) hash_set (im->pcap_drop_filter_hash, error_index, 1); else hash_unset (im->pcap_drop_filter_hash, error_index); } #endif /* CLIB_MARCH_VARIANT */ VLIB_NODE_FN (interface_drop) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { vnet_interface_main_t *im = &vnet_get_main ()->interface_main; vnet_pcap_t *pp = &vlib_global_main.pcap; if (PREDICT_FALSE (pp->pcap_drop_enable)) pcap_drop_trace (vm, im, pp, frame); return interface_drop_punt (vm, node, frame, VNET_ERROR_DISPOSITION_DROP); } VLIB_NODE_FN (interface_punt) (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { return interface_drop_punt (vm, node, frame, VNET_ERROR_DISPOSITION_PUNT); } /* *INDENT-OFF* */ VLIB_REGISTER_NODE (interface_drop) = { .name = "error-drop", .vector_size = sizeof (u32), .format_trace = format_vnet_error_trace, .n_next_nodes = 1, .next_nodes = { [0] = "drop", }, }; /* *INDENT-ON* */ /* *INDENT-OFF* */ VLIB_REGISTER_NODE (interface_punt) = { .name = "error-punt", .vector_size = sizeof (u32), .format_trace = format_vnet_error_trace, .n_next_nodes = 1, .next_nodes = { [0] = "punt", }, }; /* *INDENT-ON* */ /* *INDENT-OFF* */ VLIB_REGISTER_NODE (vnet_per_buffer_interface_output_node) = { .name = "interface-output", .vector_size = sizeof (u32), }; /* *INDENT-ON* */ static uword interface_tx_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame) { vnet_main_t *vnm = vnet_get_main (); u32 last_sw_if_index = ~0; vlib_frame_t *to_frame = 0; vnet_hw_interface_t *hw = 0; u32 *from, *to_next = 0; u32 n_left_from; from = vlib_frame_vector_args (from_frame); n_left_from = from_frame->n_vectors; while (n_left_from > 0) { u32 bi0; vlib_buffer_t *b0; u32 sw_if_index0; bi0 = from[0]; from++; n_left_from--; b0 = vlib_get_buffer (vm, bi0); sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX]; if (PREDICT_FALSE ((last_sw_if_index != sw_if_index0) || to_frame == 0)) { if (to_frame) { hw = vnet_get_sup_hw_interface (vnm, last_sw_if_index); vlib_put_frame_to_node (vm, hw->tx_node_index, to_frame); } last_sw_if_index = sw_if_index0; hw = vnet_get_sup_hw_interface (vnm, sw_if_index0); to_frame = vlib_get_frame_to_node (vm, hw->tx_node_index); to_next = vlib_frame_vector_args (to_frame); } to_next[0] = bi0; to_next++; to_frame->n_vectors++; } vlib_put_frame_to_node (vm, hw->tx_node_index, to_frame); return from_frame->n_vectors; } /* *INDENT-OFF* */ VLIB_REGISTER_NODE (interface_tx) = { .function = interface_tx_node_fn, .name = "interface-tx", .vector_size = sizeof (u32), .n_next_nodes = 1, .next_nodes = { [0] = "error-drop", }, }; VNET_FEATURE_ARC_INIT (interface_output, static) = { .arc_name = "interface-output", .start_nodes = VNET_FEATURES (0), .last_in_arc = "interface-tx", .arc_index_ptr = &vnet_main.interface_main.output_feature_arc_index, }; VNET_FEATURE_INIT (span_tx, static) = { .arc_name = "interface-output", .node_name = "span-output", .runs_before = VNET_FEATURES ("interface-tx"), }; VNET_FEATURE_INIT (ipsec_if_tx, static) = { .arc_name = "interface-output", .node_name = "ipsec-if-output", .runs_before = VNET_FEATURES ("interface-tx"), }; VNET_FEATURE_INIT (interface_tx, static) = { .arc_name = "interface-output", .node_name = "interface-tx", .runs_before = 0, }; /* *INDENT-ON* */ #ifndef CLIB_MARCH_VARIANT clib_error_t * vnet_per_buffer_interface_output_hw_interface_add_del (vnet_main_t * vnm, u32 hw_if_index, u32 is_create) { vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index); u32 next_index; if (hi->output_node_index == 0) return 0; next_index = vlib_node_add_next (vnm->vlib_main, vnet_per_buffer_interface_output_node.index, hi->output_node_index); hi->output_node_next_index = next_index; return 0; } VNET_HW_INTERFACE_ADD_DEL_FUNCTION (vnet_per_buffer_interface_output_hw_interface_add_del); void vnet_set_interface_output_node (vnet_main_t * vnm, u32 hw_if_index, u32 node_index) { ASSERT (node_index); vnet_hw_interface_t *hi = vnet_get_hw_interface (vnm, hw_if_index); u32 next_index = vlib_node_add_next (vnm->vlib_main, vnet_per_buffer_interface_output_node.index, node_index); hi->output_node_next_index = next_index; hi->output_node_index = node_index; } #endif /* CLIB_MARCH_VARIANT */ /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */