子接口类型如下三类:VLAN子接口、QinQ子接口,untagged子接口。
VLAN子接口
创建VLAN子接口,子接口ID和VLAN ID相同,都为1000。
//create sub-interfaces <interface> <subId>
vpp# create sub-interfaces host-vpp1out 1000
host-vpp1out.1000
等价于命令:
//create sub-interfaces <interface> <subId> dot1q|dot1ad <vlanId>|any [exact-match]
vpp# create sub-interfaces host-vpp1out 1001 dot1q 1001 exact-match
以下创建默认的VLAN子接口,指定了default命令字,表明报文携带的VLAN ID不匹配其它任何的子接口时,发送至此默认子接口。
//create sub-interfaces <interface> <subId> default
vpp# create sub-interfaces host-vpp1out 1002 default
host-vpp1out.1002
创建dot1ad子接口,接口ID和VLAN ID都是1003:
vpp# create sub-interfaces host-vpp1out 1003 dot1ad 1003
host-vpp1out.1003
QinQ子接口
创建q-in-q子接口,外层VLAN为1005,内层VLAN为1006。内层VLAN也可设置为任意值。
vpp# create sub-interfaces host-vpp1out 1005 dot1q 1005 inner-dot1q 1006
host-vpp1out.1005
vpp#
vpp#
vpp# create sub-interfaces host-vpp1out 1007 dot1q 10007 inner-dot1q any
host-vpp1out.1007
外层VLAN为dot1ad协议,ID值为1008;内层VLAN为dot1q协议,ID值为1009。内层VLAN可设置为任意值ANY。
vpp# create sub-interfaces host-vpp1out 1008 dot1ad 1008 inner-dot1q 1009
host-vpp1out.1008
vpp#
vpp#
vpp# create sub-interfaces host-vpp1out 1010 dot1ad 1010 inner-dot1q any
host-vpp1out.1010
untagged子接口
创建untagged VLAN子接口,没有携带任何VLAN ID的报文发送至此子接口。注意这条命令是执行不成功的。将此功能赋予了父接口port8,没有携带VLAN ID的报文发送至port8。
//create sub-interfaces <interface> <subId> untagged
vpp# create sub-interfaces port8 20 untagged
create sub-interfaces: vlan is already in use
在ethernet-input节点中,其维护一个物理接口所对应的主接口列表,物理接口自身对应的为untagged_subint,在物理接口注册时,以下函数将此子接口设置有效标志SUBINT_CONFIG_VALID。所以后续创建untagged类型的子接口将会报错(vlan is already in use)。
static clib_error_t *
ethernet_sw_interface_add_del (vnet_main_t * vnm, u32 sw_if_index, u32 is_create)
{
subint_config_t *subint;
subint = ethernet_sw_interface_get_config (vnm, sw_if_index, &match_flags, &unsupported);
if (subint->flags & SUBINT_CONFIG_VALID) {
error = clib_error_return (0, "vlan is already in use");
} else {
subint->flags = SUBINT_CONFIG_VALID | match_flags;
subint->sw_if_index = ~0; // because interfaces are initially down
}
}
VNET_SW_INTERFACE_ADD_DEL_FUNCTION (ethernet_sw_interface_add_del);
ethernet-input节点
ethernet-input节点中维护的接口结构如下main_intf_t。参见上节函数ethernet_sw_interface_add_del,在接口添加/删除时,对main_intf_t列表执行相应的操作。
typedef struct
{
subint_config_t untagged_subint;
subint_config_t default_subint;
u16 dot1q_vlans; // pool id for vlan table
u16 dot1ad_vlans; // pool id for vlan table
} main_intf_t;
typedef struct ethernet_main_t_
{
// The root of the vlan parsing tables. A vector with one element
// for each main interface, indexed by hw_if_index.
main_intf_t *main_intfs;
} ethernet_main_t;
报文处理路径中使用的子接口配置结构。
// Structs used when parsing packet to find sw_if_index
typedef struct
{
u32 sw_if_index;
u32 flags;
// config entry is-valid flag
// exact match flags (valid if packet has 0/1/2/3 tags)
// L2 vs L3 forwarding mode
#define SUBINT_CONFIG_MATCH_0_TAG (1<<0)
#define SUBINT_CONFIG_MATCH_1_TAG (1<<1)
#define SUBINT_CONFIG_MATCH_2_TAG (1<<2)
#define SUBINT_CONFIG_MATCH_3_TAG (1<<3)
#define SUBINT_CONFIG_VALID (1<<4)
#define SUBINT_CONFIG_L2 (1<<5)
#define SUBINT_CONFIG_P2P (1<<6)
} subint_config_t;
控制平面使用的子接口结构。
typedef struct
{
/* Subinterface ID. A number 0-N to uniquely identify
* this subinterface under the main (parent?) interface
*/
u32 id;
/* Classification data. Used to associate packet header with subinterface. */
struct
{
u16 outer_vlan_id;
u16 inner_vlan_id;
union
{
u16 raw_flags;
struct
{
u16 no_tags:1;
u16 one_tag:1;
u16 two_tags:1;
u16 dot1ad:1; /* 0 = dot1q, 1=dot1ad */
u16 exact_match:1;
u16 default_sub:1;
u16 outer_vlan_id_any:1;
u16 inner_vlan_id_any:1;
} flags;
};
} eth;
} vnet_sub_interface_t;
ethernet-input节点初始化
在ethernet-input节点初始化时,预先分配10个main_intf_t结构。
void
ethernet_input_init (vlib_main_t * vm, ethernet_main_t * em)
{
__attribute__ ((unused)) vlan_table_t *invalid_vlan_table;
__attribute__ ((unused)) qinq_table_t *invalid_qinq_table;
// Initialize pools and vector for vlan parsing
vec_validate (em->main_intfs, 10); // 10 main interfaces
pool_alloc (em->vlan_pool, 10);
pool_alloc (em->qinq_pool, 1);
// The first vlan pool will always be reserved for an invalid table
pool_get (em->vlan_pool, invalid_vlan_table); // first id = 0
// The first qinq pool will always be reserved for an invalid table
pool_get (em->qinq_pool, invalid_qinq_table); // first id = 0
接口配置
如下函数ethernet_sw_interface_get_config获取(/分配)所指定接口的子接口配置,其保存在子接口结构subint_config_t中,做为返回值。如果接口不属于ethernet_hw_interface_class以太网接口类型,返回值为空。
参数flags返回子接口类型,vlan标签的个数;参数unsupported返回失败时候的原因,包括不支持或者非以太网接口。此函数涉及的子接口类型有:P2P,PIPE,802.1q和802.1ad。
在接口创建和删除的回调函数ethernet_sw_interface_add_del中,调用此函数。
static subint_config_t *
ethernet_sw_interface_get_config (vnet_main_t * vnm, u32 sw_if_index, u32 * flags, u32 * unsupported)
{
ethernet_main_t *em = ðernet_main;
vnet_hw_interface_t *hi;
vnet_sw_interface_t *si;
main_intf_t *main_intf;
vlan_table_t *vlan_table;
qinq_table_t *qinq_table;
subint_config_t *subint = 0;
首先,对于非以太网类接口,unsupported设置为0,返回空,结束处理。
hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
if (!hi || (hi->hw_class_index != ethernet_hw_interface_class.index)) {
*unsupported = 0;
goto done; // non-ethernet interface
}
在获取子接口结构之前,先取得主接口结构main_intf_t,如果不存在,vec_validate函数进行分配。
// ensure there's an entry for the main intf (shouldn't really be necessary)
vec_validate (em->main_intfs, hi->hw_if_index);
main_intf = vec_elt_at_index (em->main_intfs, hi->hw_if_index);
// Locate the subint for the given ethernet config
si = vnet_get_sw_interface (vnm, sw_if_index);
对于P2P子接口类型,如果没有子接口subint_config_t结构,由p2p_main模块的p2p_subif_pool池中分配。参数flags设置为SUBINT_CONFIG_P2P。
if (si->type == VNET_SW_INTERFACE_TYPE_P2P) {
p2p_ethernet_main_t *p2pm = &p2p_main;
u32 p2pe_sw_if_index = p2p_ethernet_lookup (hi->hw_if_index, si->p2p.client_mac);
if (p2pe_sw_if_index == ~0) {
pool_get (p2pm->p2p_subif_pool, subint);
si->p2p.pool_index = subint - p2pm->p2p_subif_pool;
}
else
subint = vec_elt_at_index (p2pm->p2p_subif_pool, si->p2p.pool_index);
*flags = SUBINT_CONFIG_P2P;
}
对于PIPE子接口类型,pipe_main模块中取得成员subint,即为要找的subint_config_t结构。参数flags设置为SUBINT_CONFIG_P2P。
else if (si->type == VNET_SW_INTERFACE_TYPE_PIPE) {
pipe_t *pipe;
pipe = pipe_get (sw_if_index);
subint = &pipe->subint;
*flags = SUBINT_CONFIG_P2P;
}
对于默认子接口,主结构main_intf中成员default_subint,即为subint_config_t结构。flags表明接口可匹配3层VLAN标签,没有找到匹配子接口的VLAN报文,发送到此默认接口。flags设置了三个TAG标志,都可匹配。
else if (si->sub.eth.flags.default_sub) {
subint = &main_intf->default_subint;
*flags = SUBINT_CONFIG_MATCH_1_TAG | SUBINT_CONFIG_MATCH_2_TAG | SUBINT_CONFIG_MATCH_3_TAG;
}
没有任何tag标签,说明为主接口,返回主结构main_intf中成员untagged_subint,其为subint_config_t结构。flags表明不匹配任何标签。
else if ((si->sub.eth.flags.no_tags) || (si->sub.eth.raw_flags == 0)) {
// if no flags are set then this is a main interface so treat as untagged
subint = &main_intf->untagged_subint;
*flags = SUBINT_CONFIG_MATCH_0_TAG;
} else {
VLAN子接口结构
一个或者两个tag的情况。对于dot1ad协议或者dot1q协议,如果主接口结构main_intf中成员dot1ad_vlans/dot1q_vlans等于0,表明vlan表还未分配,由ethernet_main的成员vlan_pool中进行分配。
否则,根据dot1ad_vlans/dot1q_vlans的值,在vlan_pool中获得vlan_table结构。
// one or two tags. first get the vlan table
if (si->sub.eth.flags.dot1ad) {
if (main_intf->dot1ad_vlans == 0) {
pool_get (em->vlan_pool, vlan_table);
main_intf->dot1ad_vlans = vlan_table - em->vlan_pool;
} else
vlan_table = vec_elt_at_index (em->vlan_pool, main_intf->dot1ad_vlans);
} else { // dot1q
if (main_intf->dot1q_vlans == 0) {
pool_get (em->vlan_pool, vlan_table);
main_intf->dot1q_vlans = vlan_table - em->vlan_pool;
} else
vlan_table = vec_elt_at_index (em->vlan_pool, main_intf->dot1q_vlans);
}
单层vlan的情况,需要指定vlan id,暂不支持指定任意vlan。由vlan表中,根据vlan id取出对应的subint_config_t结构。如果指定了精确匹配exact_match,仅匹配一层VLAN标签;否则,匹配三层标签。
if (si->sub.eth.flags.one_tag) {
*flags = si->sub.eth.flags.exact_match ? SUBINT_CONFIG_MATCH_1_TAG :
(SUBINT_CONFIG_MATCH_1_TAG | SUBINT_CONFIG_MATCH_2_TAG | SUBINT_CONFIG_MATCH_3_TAG);
if (si->sub.eth.flags.outer_vlan_id_any) {
*unsupported = 1;
goto done;
} else {
// a single vlan, a common case
subint = &vlan_table->vlans[si->sub.eth.outer_vlan_id].single_tag_subint;
}
双层vlan的情况,不支持双层VLAN都设置为any的情况。允许内层vlan设置为any,此时根据外层vlan id在vlan_table表中找到子接口结构inner_any_subint。如果指定了exact_match,需匹配二层VLAN标签;否则,可匹配二层或者三层VLAN标签。
} else { // Two tags
*flags = si->sub.eth.flags.exact_match ? SUBINT_CONFIG_MATCH_2_TAG :
(SUBINT_CONFIG_MATCH_2_TAG | SUBINT_CONFIG_MATCH_3_TAG);
if (si->sub.eth.flags.outer_vlan_id_any && si->sub.eth.flags.inner_vlan_id_any) {
*unsupported = 1; // not implemented yet
goto done;
}
if (si->sub.eth.flags.inner_vlan_id_any) {
// a specific outer and "any" inner. don't need a qinq table for this
subint = &vlan_table->vlans[si->sub.eth.outer_vlan_id].inner_any_subint;
if (si->sub.eth.flags.exact_match)
*flags = SUBINT_CONFIG_MATCH_2_TAG;
else
*flags = SUBINT_CONFIG_MATCH_2_TAG | SUBINT_CONFIG_MATCH_3_TAG;
对于双层vlan都有确定的VLAN ID的情况,需要使用qinq_table表。首先,根据外层vlan id值在vlan_table中取得qinq表的索引,如果不存在,由qinq_pool池中新分配。子接口结构使用内层vlan id为索引,数组qinq_table->vlans的成员subint。
} else { // a specific outer + specific innner vlan id, a common case
if (vlan_table->vlans[si->sub.eth.outer_vlan_id].qinqs == 0) {
pool_get (em->qinq_pool, qinq_table);
vlan_table->vlans[si->sub.eth.outer_vlan_id].qinqs = qinq_table - em->qinq_pool;
} else {
qinq_table = vec_elt_at_index (em->qinq_pool,
vlan_table->vlans[si->sub.eth.outer_vlan_id].qinqs);
}
subint = &qinq_table->vlans[si->sub.eth.inner_vlan_id].subint;
}
}
}
done:
return subint;
接口添加删除
注册接口添加/删除回调函数,在添加接口时,为子接口结构设置flags相关标志;在删除接口时,清除flags标志。
static clib_error_t *
ethernet_sw_interface_add_del (vnet_main_t * vnm, u32 sw_if_index, u32 is_create)
{
subint_config_t *subint;
subint = ethernet_sw_interface_get_config (vnm, sw_if_index, &match_flags, &unsupported);
if (subint == 0) {
if (unsupported) error = clib_error_return (0, "not implemented yet");
goto done;
}
if (!is_create) {
subint->flags = 0;
return error;
}
if (subint->flags & SUBINT_CONFIG_VALID)
error = clib_error_return (0, "vlan is already in use");
else {
subint->flags = SUBINT_CONFIG_VALID | match_flags;
subint->sw_if_index = ~0; // because interfaces are initially down
}
done:
return error;
}
VNET_SW_INTERFACE_ADD_DEL_FUNCTION (ethernet_sw_interface_add_del);
注册接口的UP/DOWN回调函数,在接口UP/DOWN时,设置子接口的索引值sw_if_index。这样,在处理接收到的报文时,flags匹配,可定位到子接口索引。
static clib_error_t *
ethernet_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
{
subint_config_t *subint;
subint = ethernet_sw_interface_get_config (vnm, sw_if_index, &placeholder_flags, &placeholder_unsup);
if (subint == 0) goto done;
subint->sw_if_index = ((flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ? sw_if_index : ~0);
done:
return error;
}
VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ethernet_sw_interface_up_down);
VLAN查找
报文在进入ethernet-input节点之后,由函数eth_input_process_frame处理。在慢速处理路径中,对于外层协议为ETHERNET_TYPE_VLAN或者DOT1AD,使用不同的参数调用函数eth_input_tag_lookup进行处理。
eth_input_process_frame (vlib_main_t * vm, vlib_node_runtime_t * node,
vnet_hw_interface_t * hi, u32 * buffer_indices, u32 n_packets, int main_is_l3, )
{
u16 et_vlan = clib_host_to_net_u16 (ETHERNET_TYPE_VLAN);
u16 et_dot1ad = clib_host_to_net_u16 (ETHERNET_TYPE_DOT1AD);
n_left = n_slowpath;
u16 *si = slowpath_indices;
eth_input_tag_lookup_t dot1ad_lookup, dot1q_lookup = {
.mask = -1LL,
.tag = tags[si[0]] ^ -1LL,
.sw_if_index = ~0
};
clib_memcpy_fast (&dot1ad_lookup, &dot1q_lookup, sizeof (dot1q_lookup));
while (n_left) {
i = si[0];
u16 etype = etypes[i];
if (etype == et_vlan) {
vlib_buffer_t *b = vlib_get_buffer (vm, buffer_indices[i]);
eth_input_tag_lookup (vm, vnm, node, hi, tags[i], nexts + i, b,
&dot1q_lookup, dmacs_bad[i], 0, main_is_l3, dmac_check);
}
else if (etype == et_dot1ad) {
vlib_buffer_t *b = vlib_get_buffer (vm, buffer_indices[i]);
eth_input_tag_lookup (vm, vnm, node, hi, tags[i], nexts + i, b,
&dot1ad_lookup, dmacs_bad[i], 1, main_is_l3, dmac_check);
}
else
首先,根据物理接口索引找到主接口结构main_intf_t,由于tag变量长度为8字节,包含了报文的两个VLAN头部,取出外层和内层VLAN ID值(vlan1和vlan2)。如果外层协议为dot1ad,根据主结构成员dot1ad_vlans中保存的索引,取得VLAN表;否则,根据dot1q_vlans中保存的索引,取得vlan_table表。
其次,根据外层VLAN值在vlan表中取得vlan接口信息,再根据vlan接口结构成员qinqs索引,在qinq_pool中取得qinq_table表,再由内层VLAN值(vlan2)在qinq表中取得qif接口结构。
如果接下来的报文携带的VLAN字段与之前的报文相同,不再进行接口查找,直接使用之前保存的接口索引值。
eth_input_tag_lookup (vlib_main_t * vm, vnet_main_t * vnm, ...)
{
ethernet_main_t *em = ðernet_main;
if ((tag ^ l->tag) & l->mask)
{
main_intf_t *mif = vec_elt_at_index (em->main_intfs, hi->hw_if_index);
vlan_intf_t *vif;
qinq_intf_t *qif;
vlan_table_t *vlan_table;
qinq_table_t *qinq_table;
u16 *t = (u16 *) & tag;
u16 vlan1 = clib_net_to_host_u16 (t[0]) & 0xFFF;
u16 vlan2 = clib_net_to_host_u16 (t[2]) & 0xFFF;
u32 matched, is_l2, new_sw_if_index;
vlan_table = vec_elt_at_index (em->vlan_pool, is_dot1ad ? mif->dot1ad_vlans : mif->dot1q_vlans);
vif = &vlan_table->vlans[vlan1];
qinq_table = vec_elt_at_index (em->qinq_pool, vif->qinqs);
qif = &qinq_table->vlans[vlan2];
l->err = ETHERNET_ERROR_NONE;
如果外层VLAN携带的类型值为ETHERNET_TYPE_VLAN,表明报文携带了双层VLAN。否则,报文中仅有一层VLAN,如果其值为0,接口索引保持原值。否则,由函数eth_identify_subint来确定VLAN子接口索引new_sw_if_index和二层接口标识is_l2。
l->type = clib_net_to_host_u16 (t[1]);
if (l->type == ETHERNET_TYPE_VLAN) {
l->type = clib_net_to_host_u16 (t[3]);
l->n_tags = 2;
matched = eth_identify_subint (hi, SUBINT_CONFIG_VALID |
SUBINT_CONFIG_MATCH_2_TAG, mif, vif, qif, &new_sw_if_index, &l->err, &is_l2);
} else {
l->n_tags = 1;
if (vlan1 == 0) {
new_sw_if_index = hi->sw_if_index;
l->err = ETHERNET_ERROR_NONE;
matched = 1;
is_l2 = main_is_l3 == 0;
} else
matched = eth_identify_subint (hi, SUBINT_CONFIG_VALID |
SUBINT_CONFIG_MATCH_1_TAG, mif, vif, qif, &new_sw_if_index, &l->err, &is_l2);
}
如果新的接口索引与之前接口索引不相同,首先将报文和字节统计值更新到之前的接口统计中;随后,清空统计值,更新接口索引,之后统计此新接口的报文和字节。
if (l->sw_if_index != new_sw_if_index) {
eth_input_update_if_counters (vm, vnm, l);
l->n_packets = 0;
l->n_bytes = 0;
l->sw_if_index = new_sw_if_index;
}
计算以太网头部长度(ethernet_header_t),以及VLAN头部长度(ethernet_vlan_header_t),将两者之和记录在len中,表示二层头部的长度。如果主接口为L3三层接口,子接口为L2层接口,adv的值跳回到以太网头部开始处;否则,子接口为L3层接口的情况,adv向后跳过所有的VLAN头部。
如果主接口为L2二层接口,子接口也为L2层接口,adv没有变化;否则,子接口为L3层接口,adv长度跳过以太网头部和所有VLAN头部。
l->tag = tag;
l->mask = (l->n_tags == 2) ? clib_net_to_host_u64 (0xffffffffffffffff) :
clib_net_to_host_u64 (0xffffffff00000000);
if (matched && l->sw_if_index == ~0)
l->err = ETHERNET_ERROR_DOWN;
l->len = sizeof (ethernet_header_t) + l->n_tags * sizeof (ethernet_vlan_header_t);
if (main_is_l3)
l->adv = is_l2 ? -(int) sizeof (ethernet_header_t) :
l->n_tags * sizeof (ethernet_vlan_header_t);
else
l->adv = is_l2 ? 0 : l->len;
根据二层接口标识is_l2,以及报文类型,来确定下一个执行节点node。
if (PREDICT_FALSE (l->err != ETHERNET_ERROR_NONE))
l->next = ETHERNET_INPUT_NEXT_DROP;
else if (is_l2)
l->next = em->l2_next;
else if (l->type == ETHERNET_TYPE_IP4)
l->next = em->l3_next.input_next_ip4;
else if (l->type == ETHERNET_TYPE_IP6)
l->next = em->l3_next.input_next_ip6;
else if (l->type == ETHERNET_TYPE_MPLS)
l->next = em->l3_next.input_next_mpls;
else if (em->redirect_l3)
l->next = em->redirect_l3_next;
else {
l->next = eth_input_next_by_type (l->type);
if (l->next == ETHERNET_INPUT_NEXT_PUNT)
l->err = ETHERNET_ERROR_UNKNOWN_TYPE;
}
}
移动vlib_buffer_t指针指向三层头部(移动l->adv字节),设置vnet_buffer (b)->l2.l2_len和vnet_buffer (b)->l3_hdr_offset的值。更新sw_if_index[VLIB_RX]为子接口索引值。
if (check_dmac && l->adv > 0 && dmac_bad) {
l->err = ETHERNET_ERROR_L3_MAC_MISMATCH;
next[0] = ETHERNET_INPUT_NEXT_PUNT;
} else
next[0] = l->next;
vlib_buffer_advance (b, l->adv);
vnet_buffer (b)->l2.l2_len = l->len;
vnet_buffer (b)->l3_hdr_offset = vnet_buffer (b)->l2_hdr_offset + l->len;
if (l->err == ETHERNET_ERROR_NONE) {
vnet_buffer (b)->sw_if_index[VLIB_RX] = l->sw_if_index;
ethernet_buffer_set_vlan_count (b, l->n_tags);
} else
b->error = node->errors[l->err];
/* update counters */
l->n_packets += 1;
l->n_bytes += vlib_buffer_length_in_chain (vm, b);
子接口查找
按照优先级,首先比较双层VLAN的情况,qinq子接口,以及inner_any_subint接口(内层VLAN任意)。其次,比较单层VLAN的情况,single_tag_subint。最后,是默认子接口default_subint,和无标签接口untagged_subint。以上都没有匹配,返回匹配失败(返回值0)。
发生匹配时,由参数new_sw_if_index和is_l2返回新的子接口索引和是否为二层接口的标志。
eth_identify_subint (vnet_hw_interface_t * hi, u32 match_flags,
main_intf_t * main_intf, vlan_intf_t * vlan_intf,
qinq_intf_t * qinq_intf, u32 * new_sw_if_index, u8 * error0, u32 * is_l2)
{
subint_config_t *subint;
// Each comparison is checking both the valid flag and the number of tags
// (incorporating exact-match/non-exact-match).
// check for specific double tag
subint = &qinq_intf->subint;
if ((subint->flags & match_flags) == match_flags)
goto matched;
// check for specific outer and 'any' inner
subint = &vlan_intf->inner_any_subint;
if ((subint->flags & match_flags) == match_flags)
goto matched;
// check for specific single tag
subint = &vlan_intf->single_tag_subint;
if ((subint->flags & match_flags) == match_flags)
goto matched;
// check for default interface
subint = &main_intf->default_subint;
if ((subint->flags & match_flags) == match_flags)
goto matched;
// check for untagged interface
subint = &main_intf->untagged_subint;
if ((subint->flags & match_flags) == match_flags)
goto matched;
// No matching subinterface
*new_sw_if_index = ~0;
*error0 = ETHERNET_ERROR_UNKNOWN_VLAN;
*is_l2 = 0;
return 0;
matched:
*new_sw_if_index = subint->sw_if_index;
*is_l2 = subint->flags & SUBINT_CONFIG_L2;
return 1;
报文处理
如果物理接口为L3三层模式,状态也是三层模式(STATUS_L3),跳过对报文目的MAC地址的检测,因为硬件网卡已经完成。或者物理接口存在L2二层子接口,并且没有任何L3层子接口,表明只需对报文进行转发,也不需要检测目的MAC地址。
static_always_inline void
eth_input_single_int (vlib_main_t * vm, vnet_hw_interface_t * hi, ...)
{
ethernet_interface_t *ei;
ei = pool_elt_at_index (em->interfaces, hi->hw_instance);
main_intf_t *intf0 = vec_elt_at_index (em->main_intfs, hi->hw_if_index);
subint_config_t *subint0 = &intf0->untagged_subint;
int main_is_l3 = (subint0->flags & SUBINT_CONFIG_L2) == 0;
int int_is_l3 = ei->flags & ETHERNET_INTERFACE_FLAG_STATUS_L3;
if (main_is_l3) {
if (int_is_l3 || /* DMAC filter already done by NIC */
((hi->l2_if_count != 0) && (hi->l3_if_count == 0)))
{ /* All L2 usage - DMAC check not needed */
eth_input_process_frame (vm, node, hi, from, n_pkts,
/*is_l3 */ 1, ip4_cksum_ok, 0);
} else
{ /* DMAC check needed for L3 */
eth_input_process_frame (vm, node, hi, from, n_pkts,
/*is_l3 */ 1, ip4_cksum_ok, 1);
}
return;
}
否则,如果物理接口为L2模式,并且不存在L3层的子接口,也不需要检测报文的目的MAC地址。否则,对DMAC进行检查,函数eth_input_process_frame最后一个参数设置为1。
else {
if (hi->l3_if_count == 0)
{ /* All L2 usage - DMAC check not needed */
eth_input_process_frame (vm, node, hi, from, n_pkts,
/*is_l3 */ 0, ip4_cksum_ok, 0);
} else
{ /* DMAC check needed for L3 */
eth_input_process_frame (vm, node, hi, from, n_pkts,
/*is_l3 */ 0, ip4_cksum_ok, 1);
}