本文讲解在二层桥接域下水平分割组的应用。当接口加入到桥接域中时可以指定shg(水平分割组标识符)。可与任何承载2层数据的接口(例如硬件接口、L2 GRE 隧道等)一起使用,但主要与 VXLAN 接口一起使用。
SHG功能主要应用于桥接或VLAN环境,以防止广播、多播或者某些单播流量在网络中形成环路。具体来说,L2 SHG的作用如下:
下面是接口加入BD域命令行配置:
set interface l2 bridge <interface> <bridge-domain-id> [bvi|uu-fwd] [shg]
当在桥接域的成员上配置非零 SHG 时,VPP不会将到达该接口的数据包转发到配置有相同 SHG 标识符的桥接域的任何其他成员。这对于防止数据包在对等点之间互连的成员接口之间环回很有用。默认情况下shg数值为0,表示禁用SHG检查。
接下来搭建环境来验证SHG功能,创建3个tap接口,tap1和tap2接口分别加入命名空间pc1和pc2中,tap3默认在内核,其中tap2和tap3指定设置水平分割1。 具体组网如下:
首先需要在内核创建2个命名空间pc1和pc1,然后再vpp的命令行视图下上配置如下命令。
create bridge-domain 1
creat tap id 1 host-ns pc1 host-ip4-addr 192.168.1.1/24 host-if-name tap1
creat tap id 2 host-ns pc2 host-ip4-addr 192.168.1.2/24 host-if-name tap2
creat tap id 3 host-ip4-addr 192.168.1.3/24 host-if-name tap3
set interface state tap1 up
set interface state tap2 up
set interface state tap3 up
set interface l2 bridge tap1 1
set interface l2 bridge tap2 1 1
set interface l2 bridge tap3 1 1
可以通过命令行show bridge-domain 1 detail查询BD域1下面接口配置信息,其中可以看到tap1 SHG=0,tap2和tap3 SHG=1.
dpdk-vpp源码分析: show bridge-domain 1 detail
BD-ID Index BSN Age(min) Learning U-Forwrd UU-Flood Flooding ARP-Term arp-ufwd Learn-co Learn-li BVI-Intf
1 1 0 off on on flood on off off 3 16777216 N/A
span-l2-input l2-input-classify l2-input-feat-arc l2-policer-classify l2-input-acl vpath-input-l2 l2-ip-qos-record l2-input-vtr l2-learn l2-rw l2-fwd l2-flood l2-flood l2-output
Interface If-idx ISN SHG BVI TxFlood VLAN-Tag-Rewrite
tap1 1 1 0 - * none
tap2 2 1 1 - * none
tap3 3 1 1 - * none
#l2fib表学习情况
dpdk-vpp源码分析: show l2fib bd_id 1
Mac-Address BD-Idx If-Idx BSN-ISN Age(min) static filter bvi Interface-Name
02:fe:a6:ce:4a:d4 1 1 0/1 - - - - tap1
02:fe:2e:4d:7d:cc 1 3 0/1 - - - - tap3
02:fe:18:13:4f:a2 1 2 0/1 - - - - tap2
L2FIB total/learned entries: 3/3 Last scan time: 0.0000e0sec Learn limit: 500
接下来我们在内核上分别ping tap1和tap2接口ip地址,预期tap1接口192.168.1.1可以ping通,但无法ping通tap2接口ip地址。
接下来在命名空间PC1上分别ping tap2和tap3接口ip地址,预期结果都可以ping通。
下面通过l2_flood 节点代码来走读一下泛洪处理逻辑。在l2_input节点时会通过RX接口查询到接口的配置信息BD_index和shg数值记录到报文元数据中。在l2_flood节点从报文元数据中获取到BD_index,查询BD域的配置信息。具体结构体如下:
其中members记录当前BD域下成员口信息;flood_count记录当前BD成员口需要泛洪的数量。遍历待泛洪成员口信息,判断是否需要泛洪。下面两种情况不在泛洪成员中,1、泛洪成员口接口索引等于入接口索引;2、设置了SHG,且泛洪成员口SHG数值等于入接口SHG数值。下面是具体代码逻辑
VLIB_NODE_FN (l2flood_node) (vlib_main_t * vm,
vlib_node_runtime_t * node, vlib_frame_t * frame)
{
.....
b0 = vlib_get_buffer (vm, bi0);
/*获取BD的配置,bd_index和shg是在l2 input 节点赋值的*/
bd_config = vec_elt_at_index (l2input_main.bd_configs,
vnet_buffer (b0)->l2.bd_index);
/*记录入接口shg数值*/
in_shg = vnet_buffer (b0)->l2.shg;
sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
vec_validate (msm->members[thread_index],vec_len (bd_config->members));
vec_reset_length (msm->members[thread_index]);
/*查询当前BD域下,需要泛洪的成员口,加入到当前线程临时缓存区*/
for (mi = bd_config->flood_count - 1; mi >= 0; mi--)
{
member = &bd_config->members[mi];
/*泛洪口需要排除下面几中情况,1、泛洪接口索引等于入接口索引
2、设置水平分割组,切与入接口水平分割相同时*/
if ((member->sw_if_index != sw_if_index0) &&
(!in_shg || (member->shg != in_shg)))
{
vec_add1 (msm->members[thread_index], member);
}
}
/*查询当前报文需要泛洪的数量*/
n_clones = vec_len (msm->members[thread_index]);
if (0 == n_clones)
{
/*需泛洪成员口数量为0时,报文丢弃处理*/
}
else if (n_clones > 1)
{
/*大于1时,报文进行泛洪处理*/
}
else {
/*只有一个待泛洪接口*/
ci0 = bi0;
member = msm->members[thread_index][0];
}
}
在遍历BD域成员口是否需要泛洪时,采用的倒序的处理方式,这里在接口加入BD域时会更具接口泛洪类型按照顺序来插入到成员口中。具体顺序如下:[bvi, normal/tun_masters..., tun_normals... no_flood]。下面是接口flood_class类型。
当泛洪时,bvi接口(如果存在)必须是最后处理的成员,因为bvi处理可以改变数据包。为了实现这个顺序,我们将bvi接口设置为向量中的第一个接口,并使泛洪以相反的方式遍历向量。计数flood_count决定从成员列表的哪个位置开始泛洪。flood_count数据也是根据接口flood类型来计算的。具体代码如下:
static void
update_flood_count (l2_bridge_domain_t * bd_config)
{
bd_config->flood_count = (vec_len (bd_config->members) -
(bd_config->tun_master_count ?
bd_config->tun_normal_count : 0));
bd_config->flood_count -= bd_config->no_flood_count;
}
void
bd_add_member (l2_bridge_domain_t * bd_config, l2_flood_member_t * member)
{
u32 ix = 0;
vnet_sw_interface_t *sw_if = vnet_get_sw_interface
(vnet_get_main (), member->sw_if_index);
switch (sw_if->flood_class)
{
case VNET_FLOOD_CLASS_NO_FLOOD:
bd_config->no_flood_count++;
ix = vec_len (bd_config->members);
break;
case VNET_FLOOD_CLASS_BVI:
ix = 0;
break;
case VNET_FLOOD_CLASS_TUNNEL_MASTER:
bd_config->tun_master_count++;
/* Fall through */
case VNET_FLOOD_CLASS_NORMAL:
ix = (vec_len (bd_config->members) -
bd_config->tun_normal_count - bd_config->no_flood_count);
break;
case VNET_FLOOD_CLASS_TUNNEL_NORMAL:
ix = (vec_len (bd_config->members) - bd_config->no_flood_count);
bd_config->tun_normal_count++;
break;
}
vec_insert_elts (bd_config->members, member, 1, ix);
update_flood_count (bd_config);
}
由此可见,不是加入到BD接口所有接口都会泛洪,还存在一些处理逻辑。
总结,在Bridge-Domain并添加接口时,可以通过设置不同的SHG来控制哪些接口间的流量应该被阻止回传,以此达到管理和优化数据平面流量的目的。这在虚拟化环境、数据中心网络、以及需要高性能数据包处理的场景中尤为重要。
本文分享自 DPDK VPP源码分析 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!