span源码解析

CLI命令

vpp 端口镜像注册了两个命令:

  1. 设置端口镜像set interface span
VLIB_CLI_COMMAND (set_interface_span_command, static) = {
  .path = "set interface span",
  .short_help = "set interface span <if-name> [l2] {disable | destination <if-name> [both|rx|tx]}",
  .function = set_interface_span_command_fn,
};
  1. 显示端口镜像的设置信息show interface span
VLIB_CLI_COMMAND (show_interfaces_span_command, static) = {
  .path = "show interface span",
  .short_help = "Shows SPAN mirror table",
  .function = show_interfaces_span_command_fn,
};

源码

  1. 设置端口镜像
static clib_error_t *
set_interface_span_command_fn (vlib_main_t * vm,
			       unformat_input_t * input,
			       vlib_cli_command_t * cmd)
{
  span_main_t *sm = &span_main;
  u32 src_sw_if_index = ~0;
  u32 dst_sw_if_index = ~0;
  span_feat_t sf = SPAN_FEAT_DEVICE;
  span_state_t state = SPAN_BOTH;
  int state_set = 0;
//参数解析
  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "%U", unformat_vnet_sw_interface,
		    sm->vnet_main, &src_sw_if_index)) //源接口,直接转换为接口的索引
	;
      else if (unformat (input, "destination %U", unformat_vnet_sw_interface,
			 sm->vnet_main, &dst_sw_if_index))	//目标接口,直接转换为接口的索引
	;
      else if (unformat (input, "%U", unformat_span_state, &state)) //解析禁用,rx,tx或both,默认为both
	{
	  if (state_set)
	    return clib_error_return (0, "Multiple mirror states in input");
	  state_set = 1;
	}
      else if (unformat (input, "l2"))
	sf = SPAN_FEAT_L2;
      else
	return clib_error_return (0, "Invalid input");
    }

//调用添加或删除镜像函数
  int rv =
    span_add_delete_entry (vm, src_sw_if_index, dst_sw_if_index, state, sf);
  if (rv == VNET_API_ERROR_INVALID_INTERFACE)
    return clib_error_return (0, "Invalid interface");
  return 0;
}
//添加或删除镜像函数
int
span_add_delete_entry (vlib_main_t * vm,
		       u32 src_sw_if_index, u32 dst_sw_if_index, u8 state,
		       span_feat_t sf)
{
  span_main_t *sm = &span_main;
//state校验
  if (state > SPAN_BOTH)
    return VNET_API_ERROR_UNIMPLEMENTED;
    //接口索引校验
  if ((src_sw_if_index == ~0) || (dst_sw_if_index == ~0 && state > 0)
      || (src_sw_if_index == dst_sw_if_index))
    return VNET_API_ERROR_INVALID_INTERFACE;

  vec_validate_aligned (sm->interfaces, src_sw_if_index,
			CLIB_CACHE_LINE_BYTES);
//获取被镜像接口
  span_interface_t *si = vec_elt_at_index (sm->interfaces, src_sw_if_index);
//计算接口使能状态
  int rx = ! !(state & SPAN_RX);
  int tx = ! !(state & SPAN_TX);
//获取镜像接口
  span_mirror_t *rxm = &si->mirror_rxtx[sf][VLIB_RX];
  span_mirror_t *txm = &si->mirror_rxtx[sf][VLIB_TX];
//设置镜像接口
  u32 last_rx_ports_count = span_dst_set (rxm, dst_sw_if_index, rx);
  u32 last_tx_ports_count = span_dst_set (txm, dst_sw_if_index, tx);

  int enable_rx = last_rx_ports_count == 0 && rxm->num_mirror_ports == 1;
  int disable_rx = last_rx_ports_count > 0 && rxm->num_mirror_ports == 0;
  int enable_tx = last_tx_ports_count == 0 && txm->num_mirror_ports == 1;
  int disable_tx = last_tx_ports_count > 0 && txm->num_mirror_ports == 0;

  switch (sf)
    {
    case SPAN_FEAT_DEVICE:
    //根据设置使能或者禁用span-input或span-output节点
      if (enable_rx || disable_rx)
	vnet_feature_enable_disable ("device-input", "span-input",
				     src_sw_if_index, rx, 0, 0);
      if (enable_tx || disable_tx)
	vnet_feature_enable_disable ("interface-output", "span-output",
				     src_sw_if_index, tx, 0, 0);
      break;
    case SPAN_FEAT_L2:
    //根据设置在给定接口的位图中启用(或禁用)L2INPUT_FEAT_SPAN(或L2OUTPUT_FEAT_SPAN)
      if (enable_rx || disable_rx)
	l2input_intf_bitmap_enable (src_sw_if_index, L2INPUT_FEAT_SPAN, rx);
      if (enable_tx || disable_tx)
	l2output_intf_bitmap_enable (src_sw_if_index, L2OUTPUT_FEAT_SPAN, tx);
      break;
    default:
      return VNET_API_ERROR_UNIMPLEMENTED;
    }

  if (dst_sw_if_index != ~0 && dst_sw_if_index > sm->max_sw_if_index)
    sm->max_sw_if_index = dst_sw_if_index;

  return 0;
}

设置镜像端口将镜像信息保存在span_main.interfaces中,并且使能了镜像节点,开始镜像。
镜像节点处理过程源码:

static_always_inline uword
span_node_inline_fn (vlib_main_t * vm, vlib_node_runtime_t * node,
		     vlib_frame_t * frame, vlib_rx_or_tx_t rxtx,
		     span_feat_t sf)
{
  span_main_t *sm = &span_main;
  vnet_main_t *vnm = vnet_get_main ();
  u32 n_left_from, *from, *to_next;
  u32 next_index;
  u32 sw_if_index;
  static __thread vlib_frame_t **mirror_frames = 0;

//frame起始位置
  from = vlib_frame_vector_args (frame);
  //frame中数据包个数
  n_left_from = frame->n_vectors;
  //节点之后的处理节点
  next_index = node->cached_next_index;

  vec_validate_aligned (mirror_frames, sm->max_sw_if_index,
			CLIB_CACHE_LINE_BYTES);

  while (n_left_from > 0)
    {
      u32 n_left_to_next;

/* vlib_get_next_frame,vlib_put_next_frame几乎每个node中必定出现的一对好基友。 vlib_get_next_frame获取传递给下一个node的数据包将驻留的内存结构。vlib_put_next_frame把传递给下一个node的数据包写入特定位置。
	  这样下一个node将正式可以被调度框架调度,并处理传给他的数据包
	  */
      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);

		/*
        * n_left_from表示vector拿到多少个数据包
        * n_left_to_next表示上次缓存的下一级node剩下的vector位置
        * 所以循环表示的是当前node还能拿到多少个包,
        * 下一级能接收多少包,以少的为准。
        */
      while (n_left_from >= 4 && n_left_to_next >= 2)
	{
	  u32 bi0;
	  u32 bi1;
	  vlib_buffer_t *b0;
	  vlib_buffer_t *b1;
	  u32 sw_if_index0;
	  u32 next0 = 0;
	  u32 sw_if_index1;
	  u32 next1 = 0;

	  /* speculatively enqueue b0, b1 to the current next frame */
	  to_next[0] = bi0 = from[0];
	  to_next[1] = bi1 = from[1];
	  to_next += 2;
	  n_left_to_next -= 2;
	  from += 2;
	  n_left_from -= 2;
    //获取两个数据包
	  b0 = vlib_get_buffer (vm, bi0);
	  b1 = vlib_get_buffer (vm, bi1);
	  //获取数据包的源端口
	  sw_if_index0 = vnet_buffer (b0)->sw_if_index[rxtx];
	  sw_if_index1 = vnet_buffer (b1)->sw_if_index[rxtx];
	
     //镜像当前端口到指定端口
	  span_mirror (vm, node, sw_if_index0, b0, mirror_frames, rxtx, sf);
	  span_mirror (vm, node, sw_if_index1, b1, mirror_frames, rxtx, sf);

	  switch (sf)
	    {
	    case SPAN_FEAT_L2:
	      if (rxtx == VLIB_RX)
		{
		  next0 = vnet_l2_feature_next (b0, sm->l2_input_next,
						L2INPUT_FEAT_SPAN);
		  next1 = vnet_l2_feature_next (b1, sm->l2_input_next,
						L2INPUT_FEAT_SPAN);
		}
	      else
		{
		  next0 = vnet_l2_feature_next (b0, sm->l2_output_next,
						L2OUTPUT_FEAT_SPAN);
		  next1 = vnet_l2_feature_next (b1, sm->l2_output_next,
						L2OUTPUT_FEAT_SPAN);
		}
	      break;
	    case SPAN_FEAT_DEVICE:
	    default:
	      vnet_feature_next (&next0, b0);
	      vnet_feature_next (&next1, b1);
	      break;
	    }

		/* 验证处理结果,当next0 == next_index时,说明该包被正确处理,该宏将do nothing
	     否则,说明本来该包应去next_index但是发生错误,使得next0 != next_index。该宏会将该错误包索引bi0发往到next0实际的下一个结点(next_index) */
	  /* verify speculative enqueue, maybe switch current next frame */
	  vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
					   to_next, n_left_to_next,
					   bi0, bi1, next0, next1);
	}
	//单个包处理
      while (n_left_from > 0 && n_left_to_next > 0)
	{
	  u32 bi0;
	  vlib_buffer_t *b0;
	  u32 sw_if_index0;
	  u32 next0 = 0;

	  /* speculatively enqueue b0 to the current next frame */
	  to_next[0] = bi0 = from[0];
	  to_next += 1;
	  n_left_to_next -= 1;
	  from += 1;
	  n_left_from -= 1;

	  b0 = vlib_get_buffer (vm, bi0);
	  sw_if_index0 = vnet_buffer (b0)->sw_if_index[rxtx];

	  span_mirror (vm, node, sw_if_index0, b0, mirror_frames, rxtx, sf);

	  switch (sf)
	    {
	    case SPAN_FEAT_L2:
	      if (rxtx == VLIB_RX)
		next0 = vnet_l2_feature_next (b0, sm->l2_input_next,
					      L2INPUT_FEAT_SPAN);
	      else
		next0 = vnet_l2_feature_next (b0, sm->l2_output_next,
					      L2OUTPUT_FEAT_SPAN);
	      break;
	    case SPAN_FEAT_DEVICE:
	    default:
	      vnet_feature_next (&next0, b0);
	      break;
	    }

	  /* verify speculative enqueue, maybe switch current next frame */
	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
					   n_left_to_next, bi0, next0);
	}
     /* 所有流程都正确处理完毕后,下一结点的frame上已经有本结点处理过后的数据索引
         执行该函数,将相关信息登记到vlib_pending_frame_t中,准备开始调度处理 
	  */
      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
    }


  for (sw_if_index = 0; sw_if_index < vec_len (mirror_frames); sw_if_index++)
    {
      vlib_frame_t *f = mirror_frames[sw_if_index];
      if (f == 0)
	continue;

      if (sf == SPAN_FEAT_L2)
	vlib_put_frame_to_node (vm, l2output_node.index, f);
      else
	vnet_put_frame_to_sw_interface (vnm, sw_if_index, f);
      mirror_frames[sw_if_index] = 0;
    }
/* 返回frame中数据包个数 */
  return frame->n_vectors;
}
//端口镜像
static_always_inline void
span_mirror (vlib_main_t * vm, vlib_node_runtime_t * node, u32 sw_if_index0,
	     vlib_buffer_t * b0, vlib_frame_t ** mirror_frames,
	     vlib_rx_or_tx_t rxtx, span_feat_t sf)
{
  vlib_buffer_t *c0;
  span_main_t *sm = &span_main;
  vnet_main_t *vnm = vnet_get_main ();
  u32 *to_mirror_next = 0;
  u32 i;
  span_interface_t *si0;
  span_mirror_t *sm0;

  if (sw_if_index0 >= vec_len (sm->interfaces))
    return;
  //从源接口队列中取出当前端口
  si0 = vec_elt_at_index (sm->interfaces, sw_if_index0);
  //获取源接口的镜像信息
  sm0 = &si0->mirror_rxtx[sf][rxtx];
  //判断目的镜像队列是否为空
  if (sm0->num_mirror_ports == 0)
    return;

  /* Don't do it again */
  if (PREDICT_FALSE (b0->flags & VNET_BUFFER_F_SPAN_CLONE))
    return;
//遍历目的接口,镜像数据包
  /* *INDENT-OFF* */
  clib_bitmap_foreach (i, sm0->mirror_ports, (
    {
    //获取目的接口的frame
      if (mirror_frames[i] == 0)
        {
          if (sf == SPAN_FEAT_L2)
            mirror_frames[i] = vlib_get_frame_to_node (vm, l2output_node.index);
          else
            mirror_frames[i] = vnet_get_frame_to_sw_interface (vnm, i);
	   }
	   //指针指向frame队列
      to_mirror_next = vlib_frame_vector_args (mirror_frames[i]);
      //指针指向frame队列结尾
      to_mirror_next += mirror_frames[i]->n_vectors;
      /* This can fail */
      c0 = vlib_buffer_copy (vm, b0);
      if (PREDICT_TRUE(c0 != 0))
        {
          vnet_buffer (c0)->sw_if_index[VLIB_TX] = i;
          c0->flags |= VNET_BUFFER_F_SPAN_CLONE;
          if (sf == SPAN_FEAT_L2)
	    vnet_buffer (c0)->l2.feature_bitmap = L2OUTPUT_FEAT_OUTPUT;
	    //将被拷贝frame的index赋值给目的接口的frame队列的结尾
          to_mirror_next[0] = vlib_get_buffer_index (vm, c0);
          //增加队列长度计数
          mirror_frames[i]->n_vectors++;
          if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
            {
              span_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
              t->src_sw_if_index = sw_if_index0;
              t->mirror_sw_if_index = i;
#if 0
	      /* Enable this path to allow packet trace of SPAN packets.
	         Note that all SPAN packets will show up on the trace output
	         with the first SPAN packet (since they are in the same frame)
	         thus making trace output of the original packet confusing */
	      mirror_frames[i]->flags |= VLIB_FRAME_TRACE;
	      c0->flags |= VLIB_BUFFER_IS_TRACED;
#endif
	        }
	    }
    }));
  /* *INDENT-ON* */
}
  1. 显示端口镜像的设置信息show interface span
    show interface span比较简单,遍历一下span_main.interfaces,打印相关信息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值