/* * l2_input.c : layer 2 input packet processing * * Copyright (c) 2013 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * @file * @brief Interface Input Mode (Layer 2 Cross-Connect or Bridge / Layer 3). * * This file contains the CLI Commands that modify the input mode of an * interface. For interfaces in a Layer 2 cross-connect, all packets * received on one interface will be transmitted to the other. For * interfaces in a bridge-domain, packets will be forwarded to other * interfaces in the same bridge-domain based on destination mac address. * For interfaces in Layer 3 mode, the packets will be routed. */ /* Feature graph node names */ static char *l2input_feat_names[] = { #define _(sym,name) name, foreach_l2input_feat #undef _ }; char ** l2input_get_feat_names (void) { return l2input_feat_names; } typedef struct { /* per-pkt trace data */ u8 src[6]; u8 dst[6]; u32 next_index; u32 sw_if_index; } l2input_trace_t; /* packet trace format function */ static u8 * format_l2input_trace (u8 * s, va_list * args) { CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); l2input_trace_t *t = va_arg (*args, l2input_trace_t *); s = format (s, "l2-input: sw_if_index %d dst %U src %U", t->sw_if_index, format_ethernet_address, t->dst, format_ethernet_address, t->src); return s; } l2input_main_t l2input_main; #define foreach_l2input_error \ _(L2INPUT, "L2 input packets") \ _(DROP, "L2 input drops") typedef enum { #define _(sym,str) L2INPUT_ERROR_##sym, foreach_l2input_error #undef _ L2INPUT_N_ERROR, } l2input_error_t; static char *l2input_error_strings[] = { #define _(sym,string) string, foreach_l2input_error #undef _ }; typedef enum { /* */ L2INPUT_NEXT_LEARN, L2INPUT_NEXT_FWD, L2INPUT_NEXT_DROP, L2INPUT_N_NEXT, } l2input_next_t; static_always_inline void classify_and_dispatch (vlib_main_t * vm, vlib_node_runtime_t * node, u32 thread_index, l2input_main_t * msm, vlib_buffer_t * b0, u32 * next0) { /* * Load L2 input feature struct * Load bridge domain struct * Parse ethernet header to determine unicast/mcast/broadcast * take L2 input stat * classify packet as IP/UDP/TCP, control, other * mask feature bitmap * go to first node in bitmap * Later: optimize VTM * * For L2XC, * set tx sw-if-handle */ u16 ethertype; u8 protocol; l2_input_config_t *config; l2_bridge_domain_t *bd_config; u16 bd_index0; u32 feature_bitmap; u32 feat_mask; ethernet_header_t *h0; u8 *l3h0; u32 sw_if_index0; #define get_u16(addr) ( *((u16 *)(addr)) ) sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; h0 = vlib_buffer_get_current (b0); l3h0 = (u8 *) h0 + vnet_buffer (b0)->l2.l2_len; ethertype = clib_net_to_host_u16 (get_u16 (l3h0 - 2)); feat_mask = ~0; /* Get config for the input interface */ config = vec_elt_at_index (msm->configs, sw_if_index0); /* Save split horizon group */ vnet_buffer (b0)->l2.shg = config->shg; /* determine layer2 kind for stat and mask */ if (PREDICT_FALSE (ethernet_address_cast (h0->dst_address))) { protocol = ((ip6_header_t *) l3h0)->protocol; /* Disable bridge forwarding (flooding will execute instead if not xconnect) */ feat_mask &= ~(L2INPUT_FEAT_FWD | L2INPUT_FEAT_UU_FLOOD); /* Disable ARP-term for non-ARP and non-ICMP6 packet */ if (ethertype != ETHERNET_TYPE_ARP && (ethertype != ETHERNET_TYPE_IP6 || protocol != IP_PROTOCOL_ICMP6)) feat_mask &= ~(L2INPUT_FEAT_ARP_TERM); } else { /* * Check for from-BVI processing - set SHG of unicast packets from BVI * to 0 so it is not dropped for VXLAN tunnels or other ports with the * same SHG as that of the BVI. */ if (PREDICT_FALSE (vnet_buffer (b0)->sw_if_index[VLIB_TX] == L2INPUT_BVI)) vnet_buffer (b0)->l2.shg = 0; } if (config->xconnect) { /* Set the output interface */ vnet_buffer (b0)->sw_if_index[VLIB_TX] = config->output_sw_if_index; } else { /* Do bridge-domain processing */ bd_index0 = config->bd_index; /* save BD ID for next feature graph nodes */ vnet_buffer (b0)->l2.bd_index = bd_index0; /* Get config for the bridge domain interface */ bd_config = vec_elt_at_index (msm->bd_configs, bd_index0); /* Save bridge domain and interface seq_num */ /* *INDENT-OFF* */ l2fib_seq_num_t sn = { .swif = config->seq_num, .bd = bd_config->seq_num, }; /* *INDENT-ON* */ vnet_buffer (b0)->l2.l2fib_sn = sn.as_u16;; /* * Process bridge domain feature enables. * To perform learning/flooding/forwarding, the corresponding bit * must be enabled in both the input interface config and in the * bridge domain config. In the bd_bitmap, bits for features other * than learning/flooding/forwarding should always be set. */ feat_mask = feat_mask & bd_config->feature_bitmap; } /* mask out features from bitmap using packet type and bd config */ feature_bitmap = config->feature_bitmap & feat_mask; /* save for next feature graph nodes */ vnet_buffer (b0)->l2.feature_bitmap = feature_bitmap; /* Determine the next node */ *next0 = feat_bitmap_get_next_node_index (msm->feat_next_node_index, feature_bitmap); } static_always_inline uword l2input_node_inline (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame, int do_trace) { u32 n_left_from, *from, *to_next; l2input_next_t next_index; l2input_main_t *msm = &l2input_main; u32 thread_index = vlib_get_thread_index (); from = vlib_frame_vector_args (frame); n_left_from = frame->n_vectors; /* number of packets to process */ next_index = node->cached_next_index; while (n_left_from > 0) { u32 n_left_to_next; /* get space to enqueue frame to graph node "next_index" */ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); while (n_left_from >= 8 && n_left_to_next >= 4) { u32 bi0, bi1, bi2, bi3; vlib_buffer_t *b0, *b1, *b2, *b3; u32 next0, next1, next2, next3; u32 sw_if_index0, sw_if_index1, sw_if_index2, sw_if_index3; /* Prefetch next iteration. */ { vlib_buffer_t *p4, *p5, *p6, *p7; p4 = vlib_get_buffer (vm, from[4]); p5 = vlib_get_buffer (vm, from[5]); p6 = vlib_get_buffer (vm, from[6]); p7 = vlib_get_buffer (vm, from[7]); /* Prefetch the buffer header and packet for the N+2 loop iteration */ vlib_prefetch_buffer_header (p4, LOAD); vlib_prefetch_buffer_header (p5, LOAD); vlib_prefetch_buffer_header (p6, LOAD); vlib_prefetch_buffer_header (p7, LOAD); CLIB_PREFETCH (p4->data, CLIB_CACHE_LINE_BYTES, STORE); CLIB_PREFETCH (p5->data, CLIB_CACHE_LINE_BYTES, STORE); CLIB_PREFETCH (p6->data, CLIB_CACHE_LINE_BYTES, STORE); CLIB_PREFETCH (p7->data, CLIB_CACHE_LINE_BYTES, STORE); /* * Don't bother prefetching the bridge-domain config (which * depends on the input config above). Only a small number of * bridge domains are expected. Plus the structure is small * and several fit in a cache line. */ } /* speculatively enqueue b0 and b1 to the current next frame */ /* bi is "buffer index", b is pointer to the buffer */ to_next[0] = bi0 = from[0]; to_next[1] = bi1 = from[1]; to_next[2] = bi2 = from[2]; to_next[3] = bi3 = from[3]; from += 4; to_next += 4; n_left_from -= 4; n_left_to_next -= 4; b0 = vlib_get_buffer (vm, bi0); b1 = vlib_get_buffer (vm, bi1); b2 = vlib_get_buffer (vm, bi2); b3 = vlib_get_buffer (vm, bi3); if (do_trace) { /* RX interface handles */ sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX]; sw_if_index2 = vnet_buffer (b2)->sw_if_index[VLIB_RX]; sw_if_index3 = vnet_buffer (b3)->sw_if_index[VLIB_RX]; if (b0->flags & VLIB_BUFFER_IS_TRACED) { ethernet_header_t *h0 = vlib_buffer_get_current (b0); l2input_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); t->sw_if_index = sw_if_index0; clib_memcpy (t->src, h0->src_address, 6); clib_memcpy (t->dst, h0->dst_address, 6); } if (b1->flags & VLIB_BUFFER_IS_TRACED) { ethernet_header_t *h1 = vlib_buffer_get_current (b1); l2input_trace_t *t = vlib_add_trace (vm, node, b1, sizeof (*t)); t->sw_if_index = sw_if_index1; clib_memcpy (t->src, h1->src_address, 6); clib_memcpy (t->dst, h1->dst_address, 6); } if (b2->flags & VLIB_BUFFER_IS_TRACED) { ethernet_header_t *h2 = vlib_buffer_get_current (b2); l2input_trace_t *t = vlib_add_trace (vm, node, b2, sizeof (*t)); t->sw_if_index = sw_if_index2; clib_memcpy (t->src, h2->src_address, 6); clib_memcpy (t->dst, h2->dst_address, 6); } if (b3->flags & VLIB_BUFFER_IS_TRACED) { ethernet_header_t *h3 = vlib_buffer_get_current (b3); l2input_trace_t *t = vlib_add_trace (vm, node, b3, sizeof (*t)); t->sw_if_index = sw_if_index3; clib_memcpy (t->src, h3->src_address, 6); clib_memcpy (t->dst, h3->dst_address, 6); } } vlib_node_increment_counter (vm, l2input_node.index, L2INPUT_ERROR_L2INPUT, 4); classify_and_dispatch (vm, node, thread_index, msm, b0, &next0); classify_and_dispatch (vm, node, thread_index, msm, b1, &next1); classify_and_dispatch (vm, node, thread_index, msm, b2, &next2); classify_and_dispatch (vm, node, thread_index, msm, b3, &next3); /* verify speculative enqueues, maybe switch current next frame */ /* if next0==next1==next_index then nothing special needs to be done */ vlib_validate_buffer_enqueue_x4 (vm, node, next_index, to_next, n_left_to_next, bi0, bi1, bi2, bi3, next0, next1, next2, next3); } while (n_left_from > 0 && n_left_to_next > 0) { u32 bi0; vlib_buffer_t *b0; u32 next0; u32 sw_if_index0; /* speculatively enqueue b0 to the current next frame */ bi0 = from[0]; to_next[0] = bi0; from += 1; to_next += 1; n_left_from -= 1; n_left_to_next -= 1; b0 = vlib_get_buffer (vm, bi0); if (do_trace && PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) { ethernet_header_t *h0 = vlib_buffer_get_current (b0); l2input_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t)); s
/*
 * node.c - - awkward chained buffer geometry test tool
 *
 * Copyright (c) 2019 by 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.
 */
#include <vlib/vlib.h>
#include <vnet/vnet.h>
#include <vppinfra/error.h>
#include <oddbuf/oddbuf.h>

typedef struct
{
  u32 sw_if_index;
  u32 next_index;
  u16 udp_checksum;
} oddbuf_trace_t;

#ifndef CLIB_MARCH_VARIANT

/* packet trace format function */
static u8 *
format_oddbuf_trace (u8 * s, va_list * args)
{
  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
  oddbuf_trace_t *t = va_arg (*args, oddbuf_trace_t *);

  s = format (s, "ODDBUF: sw_if_index %d, next index %d, udp checksum %04x\n",
	      t->sw_if_index, t->next_index, (u32) t->udp_checksum);
  return s;
}

vlib_node_registration_t oddbuf_node;

#endif /* CLIB_MARCH_VARIANT */

#define foreach_oddbuf_error \
_(SWAPPED, "Mac swap packets processed")

typedef enum
{
#define _(sym,str) ODDBUF_ERROR_##sym,
  foreach_oddbuf_error
#undef _
    ODDBUF_N_ERROR,
} oddbuf_error_t;

#ifndef CLIB_MARCH_VARIANT
static char *oddbuf_error_strings[] = {
#define _(sym,string) string,
  foreach_oddbuf_error
#undef _
};
#endif /* CLIB_MARCH_VARIANT */

typedef enum
{
  ODDBUF_NEXT_DROP,
  ODDBUF_N_NEXT,
} oddbuf_next_t;


always_inline uword
oddbuf_inline (vlib_main_t * vm,
	       vlib_node_runtime_t * node, vlib_frame_t * frame,
	       int is_ip4, int is_trace)
{
  oddbuf_main_t *om = &oddbuf_main;
  u32 n_left_from, *from;
  vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
  vlib_buffer_t *b0, *b0next;
  u32 bi;
  u16 nexts[VLIB_FRAME_SIZE], *next;
  u16 save_current_length;
  u32 next0;
  u8 *src, *dst;
  int i;
  ethernet_header_t *eh;
  ip4_header_t *ip;
  udp_header_t *udp;


  from = vlib_frame_vector_args (frame);
  n_left_from = frame->n_vectors;

  vlib_get_buffers (vm, from, bufs, n_left_from);
  b = bufs;
  next = nexts;

  while (n_left_from > 0)
    {
      b0 = b[0];
      vnet_feature_next (&next0, b0);
      nexts[0] = next0;

      if (vlib_buffer_alloc (vm, &bi, 1) != 1)
	{
	  clib_warning ("Buffer alloc fail, skipping");
	  goto skip;
	}

      if (om->first_chunk_offset)
	{
	  memmove (b0->data + b0->current_data + om->first_chunk_offset,
		   b0->data + b0->current_data, b0->current_length);
	  b0->current_data += om->first_chunk_offset;
	}

      eh = vlib_buffer_get_current (b0);
      ip = (ip4_header_t *) (eh + 1);
      udp = (udp_header_t *) (ip4_next_header (ip));

      if (1)
	{
	  save_current_length = vlib_buffer_length_in_chain (vm, b0);

	  b0next = vlib_get_buffer (vm, bi);
	  b0->flags |= VLIB_BUFFER_NEXT_PRESENT;
	  b0->flags &