VPP以太网接口模式

以太网接口结构ethernet_interface_t如下,其成员flags为32bit,高16bit为运行状态标志,低16bit为操作标志。最低bit位表示接口的L2/L3模式设置。第16bit为1表示接口运行在L3模式。

/* Ethernet interface instance. */
typedef struct ethernet_interface
{ 
  u32 flags;
  
  /* Top 16 bits for status and bottom 16 bits for set operation */
#define ETHERNET_INTERFACE_FLAGS_STATUS_MASK  (0xffff0000)
#define ETHERNET_INTERFACE_FLAGS_SET_OPN_MASK (0x0000ffff)
  
  /* Interface driver/hw is in L3/non-promiscuous mode so packet DMAC would already be filtered */
#define ETHERNET_INTERFACE_FLAG_STATUS_L3 (1 << 16)
  
  /* Set interface to default L3 mode */
#define ETHERNET_INTERFACE_FLAG_DEFAULT_L3 0
  
  /* Set interface to accept all packets (promiscuous mode). */
#define ETHERNET_INTERFACE_FLAG_ACCEPT_ALL 1

物理接口在注册时,例如在VPP插件DPDK中,以太网接口结构成员flags没有进行赋值。函数ethernet_set_flags设置flags的值。

vnet_eth_register_interface (vnet_main_t *vnm,
                 vnet_eth_interface_registration_t *r)
{     
  ethernet_main_t *em = ðernet_main;
  ethernet_interface_t *ei; 
  vnet_hw_interface_t *hi;
  u32 hw_if_index;
    
  pool_get (em->interfaces, ei);
  clib_memcpy (&ei->cb, &r->cb, sizeof (vnet_eth_if_callbacks_t));

修改接口标志

设置以太网接口的操作标志,高16位的状态标志保持不变。

ethernet_set_flags (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
{
  ethernet_main_t *em = ðernet_main;
  vnet_hw_interface_t *hi;
  ethernet_interface_t *ei;
  u32 opn_flags = flags & ETHERNET_INTERFACE_FLAGS_SET_OPN_MASK;

  hi = vnet_get_hw_interface (vnm, hw_if_index);

  ASSERT (hi->hw_class_index == ethernet_hw_interface_class.index);

  ei = pool_elt_at_index (em->interfaces, hi->hw_instance);

  /* preserve status bits and update last set operation bits */
  ei->flags = (ei->flags & ETHERNET_INTERFACE_FLAGS_STATUS_MASK) | opn_flags;

对于DPDK接口,注册的flag_change函数为dpdk_flag_change。如果操作模式设置为L3,检查接口硬件是否支持MAC地址过滤功能,如果不支持,状态不能设置STATUS_L3标志。

硬件如果支持MAC地址过滤,设置STATUS_L3标志(高16bit)。

if (ei->cb.flag_change) {
  switch (opn_flags) {	
    case ETHERNET_INTERFACE_FLAG_DEFAULT_L3:
      if (hi->caps & VNET_HW_IF_CAP_MAC_FILTER) {
        if (ei->cb.flag_change (vnm, hi, opn_flags) != ~0) {
          ei->flags |= ETHERNET_INTERFACE_FLAG_STATUS_L3;
          return 0;
        }
        ei->flags &= ~ETHERNET_INTERFACE_FLAG_STATUS_L3;
        return ~0;
      }
      /* fall through */
    case ETHERNET_INTERFACE_FLAG_ACCEPT_ALL:
      ei->flags &= ~ETHERNET_INTERFACE_FLAG_STATUS_L3;
      return ei->cb.flag_change (vnm, hi, opn_flags);
    default:   return ~0;
  }
}
return ~0;

如果操作模式设置为L3,关闭网卡混杂模式。否则,开启混杂模式。返回值为混杂模式之前的状态。

dpdk_flag_change (vnet_main_t * vnm, vnet_hw_interface_t * hi, u32 flags)
{
  dpdk_main_t *dm = &dpdk_main;
  dpdk_device_t *xd = vec_elt_at_index (dm->devices, hi->dev_instance);
  u32 old = (xd->flags & DPDK_DEVICE_FLAG_PROMISC) != 0;

  switch (flags) {
    case ETHERNET_INTERFACE_FLAG_DEFAULT_L3:           /* set to L3/non-promisc mode */
      dpdk_device_flag_set (xd, DPDK_DEVICE_FLAG_PROMISC, 0);
      break;
    case ETHERNET_INTERFACE_FLAG_ACCEPT_ALL:
      dpdk_device_flag_set (xd, DPDK_DEVICE_FLAG_PROMISC, 1);
      break;
    default:  return ~0; 
  }
  if (xd->flags & DPDK_DEVICE_FLAG_ADMIN_UP) {
      if (xd->flags & DPDK_DEVICE_FLAG_PROMISC)
        rte_eth_promiscuous_enable (xd->port_id);
      else
        rte_eth_promiscuous_disable (xd->port_id);
  }
  return old;

接口默认模式

以DPDK接口为例,DPDK接口默认设置为L3操作模式。

static clib_error_t *
dpdk_lib_init (dpdk_main_t * dm)
{ 
   /* create interface */
   xd->hw_if_index = vnet_eth_register_interface (vnm, &eir);
   sw = vnet_get_hw_sw_interface (vnm, xd->hw_if_index);
   xd->sw_if_index = sw->sw_if_index;

   ethernet_set_flags (vnm, xd->hw_if_index,
           ETHERNET_INTERFACE_FLAG_DEFAULT_L3);

L2/L3模式

如下命令,设置接口GigabitEthernet0/8/0为L2二层模式,以及设置接口GigabitEthernet0/a/0为L3三层模式。

vpp# set interface l2 bridge GigabitEthernet0/8/0 200
vpp#
vpp# set interface l3 GigabitEthernet0/a/0

函数set_int_l2_mode负责接口模式处理。如果硬件接口的第一个子接口,设置为L2模式,调用ethernet_set_flags,将硬件接口设置为混杂模式(ACCEPT_ALL)。如果硬件接口的最后一个子接口设置为L3模式,将硬件接口的混杂模式关闭(DEFAULT_L3)。

u32
set_int_l2_mode (vlib_main_t * vm, vnet_main_t * vnet_main, )
{
  /* Adjust count of L2 interfaces */
  hi->l2_if_count += l2_if_adjust;

  if (hi->hw_class_index == ethernet_hw_interface_class.index)
  { 
    if ((hi->l2_if_count == 1) && (l2_if_adjust == 1)) { 
      /* Just added first L2 interface on this port, Set promiscuous mode on the l2 interface */
      ethernet_set_flags (vnet_main, hi->hw_if_index,
                  ETHERNET_INTERFACE_FLAG_ACCEPT_ALL);
    } 
    else if ((hi->l2_if_count == 0) && (l2_if_adjust == -1)) { 
      /* Just removed only L2 subinterface on this port, Disable promiscuous mode on the l2 interface */
      ethernet_set_flags (vnet_main, hi->hw_if_index,
                  /*ETHERNET_INTERFACE_FLAG_DEFAULT_L3 */ 0);

你可能感兴趣的:(VPP,linux,ethernet,dpdk)