Linux-cp创建接口对

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

为了能够将数据报文由VPP送到Linux中Linux-cp的如下命令为VPP中的接口创建对应的linux中映射接口host-ifhost-if默认为tap类型接口可通过关键字tun改变接口类型创建tun类型的映射接口。

VLIB_CLI_COMMAND (lcp_itf_pair_create_command, static) = {
  .path = "lcp create",
  .short_help = "lcp create <sw_if_index>|<if-name> host-if <host-if-name> "
        "netns <namespace> [tun]",
  .function = lcp_itf_pair_create_command_fn,
};

解析lcp命令行参数交由函数lcp_itf_pair_create处理。

static clib_error_t *
lcp_itf_pair_create_command_fn (vlib_main_t *vm, unformat_input_t *input,
                vlib_cli_command_t *cmd)
{
  unformat_input_t _line_input, *line_input = &_line_input;
  vnet_main_t *vnm = vnet_get_main ();

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "%d", &sw_if_index));
      else if (unformat (line_input, "%U", unformat_vnet_sw_interface, vnm,
             &sw_if_index)) ;
      else if (unformat (line_input, "host-if %s", &host_if_name)) ;
      else if (unformat (line_input, "netns %s", &ns)) ;
      else if (unformat (line_input, "tun"))
    host_if_type = LCP_ITF_HOST_TUN;
      else
    {
      unformat_free (line_input);
      vec_free (host_if_name);
      vec_free (ns);
      return clib_error_return (0, "unknown input `%U'", format_unformat_error, input);
    }
    }
  r = lcp_itf_pair_create (sw_if_index, host_if_name, host_if_type, ns, NULL);

首先检查指定的VPP接口是否有效以及要创建的linux接口的名称是否合法。

int
lcp_itf_pair_create (u32 phy_sw_if_index, u8 *host_if_name,
             lip_host_type_t host_if_type, u8 *ns,
             u32 *host_sw_if_indexp)
{
  if (!vnet_sw_if_index_is_api_valid (phy_sw_if_index))
    {
      LCP_ITF_PAIR_ERR ("pair_create: invalid phy index %u", phy_sw_if_index);
      return VNET_API_ERROR_INVALID_SW_IF_INDEX;
    }     
  if (!lcp_validate_if_name (host_if_name))
    {
      LCP_ITF_PAIR_ERR ("pair_create: invalid host-if-name '%s'",
            host_if_name);
      return VNET_API_ERROR_INVALID_ARGUMENT;
    }

其次检查VPP接口是否存在。对于未指定命名空间的情况使用默认的命名空间默认命名空间也可能为空。

  vnm = vnet_get_main ();
  sw = vnet_get_sw_interface (vnm, phy_sw_if_index);
  hw = vnet_get_sup_hw_interface (vnm, phy_sw_if_index);
  if (!sw || !hw)
    {
      LCP_ITF_PAIR_ERR ("pair_create: invalid interface");
      return VNET_API_ERROR_INVALID_SW_IF_INDEX;
    }

  /*
   * Use interface-specific netns if supplied.
   * Otherwise, use netns if defined, otherwise use the OS default.
   */
  if (ns == 0 || ns[0] == 0)
    ns = lcp_get_default_ns ();

VLAN接口

对于VLAN接口如果其不支持IP地址配置表明其为L2层接口不进行处理返回错误。

  /* sub interfaces do not need a tap created */
  if (vnet_sw_interface_is_sub (vnm, phy_sw_if_index))
    {
      err = vnet_sw_interface_supports_addressing (vnm, phy_sw_if_index);
      if (err)
    {
      LCP_ITF_PAIR_ERR ("pair_create: can't create LCP for a "
                "sub-interface without exact-match set");
      return VNET_API_ERROR_INVALID_ARGUMENT;
    }

对于VLAN接口查找其父接口是否已经创建Linux映射接口如果父接口没有linux映射接口就不能为其vlan子接口创建linux映射接口。

      outer_vlan = sw->sub.eth.outer_vlan_id;
      inner_vlan = sw->sub.eth.inner_vlan_id;
      outer_proto = inner_proto = ETH_P_8021Q;
      if (1 == sw->sub.eth.flags.dot1ad)
    outer_proto = ETH_P_8021AD;

      LCP_ITF_PAIR_INFO ("pair_create: subif: dot1%s outer %d inner %d on %U",
             sw->sub.eth.flags.dot1ad ? "ad" : "q", outer_vlan,
             inner_vlan, format_vnet_sw_if_index_name, vnm,
             hw->sw_if_index);

      parent_if_index = lcp_itf_pair_find_by_phy (sw->sup_sw_if_index);
      if (INDEX_INVALID == parent_if_index)
    {
      LCP_ITF_PAIR_ERR ("pair_create: can't find LCP for %U",
                format_vnet_sw_if_index_name, vnet_get_main (),
                sw->sup_sw_if_index);
      return VNET_API_ERROR_INVALID_SW_IF_INDEX;
    }
      lip = lcp_itf_pair_get (parent_if_index);
      if (!lip)
    {
      LCP_ITF_PAIR_ERR ("pair_create: can't create LCP for a "
                "sub-interface without an LCP on the parent");
      return VNET_API_ERROR_INVALID_ARGUMENT;
    }

如果指定了命名空间切换到指定命名空间检查命令行指定的linux接口名称是否存在如下检查其名称对应的linux接口索引值是否有效。

      LCP_ITF_PAIR_DBG ("pair_create: parent %U", format_lcp_itf_pair, lip);
      parent_vif_index = lip->lip_vif_index;

      /*
       * see if the requested host interface has already been created
       */
      orig_ns_fd = ns_fd = -1;
      err = NULL;

      if (ns && ns[0] != 0)
    {
      orig_ns_fd = clib_netns_open (NULL /* self */);
      ns_fd = clib_netns_open (ns);
      if (orig_ns_fd == -1 || ns_fd == -1)
        goto socket_close;
             
      clib_setns (ns_fd);
    } 
      
      vif_index = if_nametoindex ((const char *) host_if_name);

对于指定的linux接口不存在的情况如果VPP接口的内层VLAN有值表明此接口有两层VLAN标签查找外出VLAN对应的接口是否已经创建linux映射接口如果不存在返回错误。否则记录外层VLAN对应的linux映射接口作为要新创建接口的父接口。

      if (!vif_index)
    {           
      if (inner_vlan)
        {
          vlan = inner_vlan;
          proto = inner_proto;
          linux_parent_if_index = lcp_itf_pair_find_by_outer_vlan (
        hw->sw_if_index, sw->sub.eth.outer_vlan_id, sw->sub.eth.flags.dot1ad);
          if (INDEX_INVALID == linux_parent_if_index ||
          !(llip = lcp_itf_pair_get (linux_parent_if_index)))
        {
          LCP_ITF_PAIR_ERR (
            "pair_create: can't find LCP for outer vlan %d proto %s on %U",
            outer_vlan, outer_proto == ETH_P_8021AD ? "dot1ad" : "dot1q",
            format_vnet_sw_if_index_name, vnm, hw->sw_if_index);
          err = clib_error_return (0, "parent pair not found");
          goto socket_close;
        }
          LCP_ITF_PAIR_DBG ("pair_create: linux parent %U", format_lcp_itf_pair, llip);
          parent_vif_index = llip->lip_vif_index;
        }

对于VPP的vlan接口只有一层vlan的情况父接口为物理接口的linux映射接口。函数lcp_netlink_add_link_vlan为linux接口parent_vif_index创建vlan子接口host_if_name。

      else
        {
          vlan = outer_vlan;
          proto = outer_proto;
        }
      err = lcp_netlink_add_link_vlan (parent_vif_index, vlan, proto,
                       (const char *) host_if_name);
      if (err != 0)
        {
          LCP_ITF_PAIR_ERR ("pair_create: cannot create link "
                "outer(proto:0x%04x,vlan:%u).inner(proto:0x%"
                "04x,vlan:%u) name:'%s'",
                outer_proto, outer_vlan, inner_proto,
                inner_vlan, host_if_name);
        }
      if (!err)
        vif_index = if_nametoindex ((char *) host_if_name);
    }

在VPP中为父接口对应的tap接口创建vlan接口。

      /* create a sub-interface on the tap
       */
      if (!err &&
      vnet_create_sub_interface (lip->lip_host_sw_if_index, sw->sub.id,
                     sw->sub.eth.raw_flags, inner_vlan,
                     outer_vlan, &host_sw_if_index))
    {
      LCP_ITF_PAIR_ERR (
        "pair_create: failed to create tap subint: %d.%d on %U",
        outer_vlan, inner_vlan, format_vnet_sw_if_index_name, vnm,
        lip->lip_host_sw_if_index);
      err = clib_error_return (
        0, "failed to create tap subint: %d.%d. on %U", outer_vlan,
        inner_vlan, format_vnet_sw_if_index_name, vnm,
        lip->lip_host_sw_if_index);
    }

切换为原有的命名空间关闭套接口。

    socket_close:
      if (orig_ns_fd != -1)
    {
      clib_setns (orig_ns_fd);
      close (orig_ns_fd);
    }
      if (ns_fd != -1)
    close (ns_fd);

      if (err)
    return VNET_API_ERROR_INVALID_ARGUMENT;
    }

物理接口

对于物理接口初始化创建tap接口所需参数接收队列设置为工作线程worker的数量发送队列设置为1。linux tap/tun接口名称设置为命令行参数host_if_name。其物理地址设置为与VPP接口的物理地址相同。

  else
    {
      tap_create_if_args_t args = {
    .num_rx_queues = clib_max (1, vlib_num_workers ()),
    .num_tx_queues = 1,
    .id = hw->hw_if_index,
    .host_if_name = host_if_name,
      };
      ethernet_interface_t *ei;
      if (host_if_type == LCP_ITF_HOST_TUN)
    args.tap_flags |= TAP_FLAG_TUN;
      else
    {
      ei = pool_elt_at_index (ethernet_main.interfaces, hw->hw_instance);
      mac_address_copy (&args.host_mac_addr, &ei->address.mac);
    }

linux映射tap接口的mtu设置为vpp相应接口的L3层MTU值在其为零的情况下设置为ETHERNET_MAX_PACKET_BYTES9216。

      /* The TAP interface does copy forward the host MTU based on the VPP
       * interface's L3 MTU, but it should also ensure that the VPP tap
       * interface has an MTU that is greater-or-equal to those. Considering
       * users can set the interfaces at runtime (set interface mtu packet ...)
       * ensure that the tap MTU is large enough, taking the VPP interface L3
       * if it's set, and otherwise a sensible default.
       */
      host_sw_mtu_size = sw->mtu[VNET_MTU_L3];
      if (host_sw_mtu_size)
    {
      args.host_mtu_set = 1;
      args.host_mtu_size = host_sw_mtu_size;
    }
      else
    host_sw_mtu_size = ETHERNET_MAX_PACKET_BYTES;

通过函数tap_create_if创建指定的linux tap/tun接口和VPP中对应的virtio tap/tun接口。

      if (ns && ns[0] != 0)
    args.host_namespace = ns;

      vm = vlib_get_main ();
      tap_create_if (vm, &args);
      if (args.rv < 0)
    {
      LCP_ITF_PAIR_ERR ("pair_create: could not create tap, retval:%d", args.rv);
      return args.rv;
    } 
      vnet_sw_interface_set_mtu (vnm, args.sw_if_index, host_sw_mtu_size);

根据virtio接口索引得到其父接口继而得到virtio_if_t结构。对于tap类型设置ETHERNET_INTERFACE_FLAG_STATUS_L3层标志vif_index为virtio接口对应的linux中的接口索引host_sw_if_index为vpp中virtio接口的索引。

      /* get the hw and ethernet of the tap
       */
      hw = vnet_get_sup_hw_interface (vnm, args.sw_if_index);
      virtio_main_t *mm = &virtio_main;
      virtio_if_t *vif = pool_elt_at_index (mm->interfaces, hw->dev_instance);

      /* Leave the TAP permanently up on the VPP side.
       * This TAP will be shared by many sub-interface.
       * Therefore we can't use it to manage admin state.
       * force the tap in promiscuous mode.
       */
      if (host_if_type == LCP_ITF_HOST_TAP)
    {
      ei = pool_elt_at_index (ethernet_main.interfaces, hw->hw_instance);
      ei->flags |= ETHERNET_INTERFACE_FLAG_STATUS_L3;
    }
      vif_index = vif->ifindex;
      host_sw_if_index = args.sw_if_index;
    }

将vpp物理接口的链路状态设置到linux中映射tap接口的链路状态。

  /* Copy the link state from VPP into the host side.
   * The TAP is shared by many interfaces, always keep it up.
   * This controls whether the host can RX/TX.
   */
  sw = vnet_get_sw_interface (vnm, phy_sw_if_index);
  lip = lcp_itf_pair_get (lcp_itf_pair_find_by_vif (vif_index));
  LCP_ITF_PAIR_INFO ("pair create: %U sw-flags %u hw-flags %u",
             format_lcp_itf_pair, lip, sw->flags, hw->flags);
  vnet_sw_interface_admin_up (vnm, host_sw_if_index);
  lcp_itf_set_link_state (lip, sw->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP);

根据物理接口的链路状态设置virtio tap/tun接口的链路状态和速率。

  /* Reflect current link state and link speed of the hardware interface on the
   * TAP interface.
   */
  if (host_if_type == LCP_ITF_HOST_TAP &&
      !vnet_sw_interface_is_sub (vnm, phy_sw_if_index))
    {
      hw = vnet_get_sup_hw_interface (vnm, phy_sw_if_index);
      lcp_itf_pair_link_up_down (vnm, hw->hw_if_index, hw->flags);
    }

  if (host_sw_if_indexp)
    *host_sw_if_indexp = host_sw_if_index;

  return 0;
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: linux