当前位置: 首页 > news >正文

Linux-cp创建接口对

为了能够将数据报文由VPP送到Linux中,Linux-cp的如下命令为VPP中的接口创建对应的linux中映射接口(host-if),host-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_BYTES(9216)。

      /* 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;

相关文章:

  • 网页制作免费的模板/大同优化推广
  • 阿里云wordpress托管/网站搭建需要多少钱?
  • 怎么把网站扒下来/百度seo流量
  • 上海建设工程造价网站/深圳百度代理
  • 没有网站可以icp备案吗/网络推广法
  • 秦皇岛做网站的公司/新闻今日头条最新消息
  • QT中矩形操作中QMarginsF[QMargins]的使用与QRectF的adjusted的对比
  • HTTP/HTTPS协议介绍
  • 并查集是什么?怎么模拟实现?如何应用?
  • 同源策略和跨域请求的实现
  • Python-import导入上级目录文件
  • 1592_AURIX_TC275_PMU_部分安全措施
  • 彻底分析Arduino库安装和开发板库安装路径和方式
  • GO 语言 Web 开发实战一
  • 区块链技术3--BTC协议
  • WAL Write AheadLog
  • 过气明星组合大衣哥、李嘉明、唐磊,谁录制祝福视频能价值100万
  • 明清专题数据库:企业匹配官办书局距离、科举考试、商帮文化变量等