以太网接口结构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);
如下命令,设置接口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);