SWITCH.P4的l2.p4代码分析
L2.p4的源代码主要用来实现fdb转发核mac学习等功能。在switch中采用bridge-domain这个概念来统一描述二层广播域。vlan(常规来说一个子网其实也是一个vlan域),bridge,vxlan都会被映射到一个bd。
基本元数据
/*
* Layer-2 processing
*/
header_type l2_metadata_t {
fields {
//带lkp前缀的元数据都是用来做匹配域的,从报文头中提取的字段
lkp_mac_sa : 48;
lkp_mac_da : 48;
lkp_pkt_type : 3;
lkp_mac_type : 16;
lkp_pcp: 3;
//对应于重定向功能的相关信息,用于实现高级功能
l2_nexthop : 16; /* next hop from l2 */
l2_nexthop_type : 2; /* ecmp or nexthop */
l2_redirect : 1; /* l2 redirect action
** 重定向功能,主要用于实现一些高级功能
** 比如evpn overlay fdb转发
*/
l2_src_miss : 1; /* l2 source miss
源mac匹配缺失,说明需要进行mac学习 */
l2_src_move : IFINDEX_BIT_WIDTH; /* l2 source interface mis-match
源mac学习时,源mac表项存在,但是接口发
生了变化,对应于一个设备从一个物理接口迁
移到另外一个物理口上 */
stp_group: 10; /* spanning tree group id */
stp_state : 3; /* spanning tree port state */
bd_stats_idx : 16; /* ingress BD stats index 该bd的统计索引 */
learning_enabled : 1; /* is learning enabled 是否是能了fdb学习 */
port_vlan_mapping_miss : 1; /* port vlan mapping miss
vlan端口映射失败,vlan+port映射决定了bd */
same_if_check : IFINDEX_BIT_WIDTH; /* same interface check
报文输入输出端口是否一致检查,开启hairpin
功能的交换机时允许的 */
}
}
metadata l2_metadata_t l2_metadata;
输入流水线
报文校验
根据报文的源目的mac,三层类型以及一些其它的信息对报文进行分类和校验,比如smac地址为非单播地址,那么这个报文就是非法的报文。
/*****************************************************************************/
/* Validate packet */
/*****************************************************************************/
action set_unicast() {//合法单播
modify_field(l2_metadata.lkp_pkt_type, L2_UNICAST);
}
action set_unicast_and_ipv6_src_is_link_local() {//源ip地址是ipv6 link local的合法单播报文
modify_field(l2_metadata.lkp_pkt_type, L2_UNICAST);
modify_field(ipv6_metadata.ipv6_src_is_link_local, TRUE);
}
action set_multicast() {//组播报文,并且设置统计索引为l2_metadata.bd_stats_idx + 1
modify_field(l2_metadata.lkp_pkt_type, L2_MULTICAST);
add_to_field(l2_metadata.bd_stats_idx, 1);
}
action set_multicast_and_ipv6_src_is_link_local() {
//源ip地址是ipv6 link local的组播报文,并且设置统计索引为l2_metadata.bd_stats_idx + 1
modify_field(l2_metadata.lkp_pkt_type, L2_MULTICAST);
modify_field(ipv6_metadata.ipv6_src_is_link_local, TRUE);
add_to_field(l2_metadata.bd_stats_idx, 1);
}
action set_broadcast() {//广播报文,并且设置统计索引为l2_metadata.bd_stats_idx + 2
modify_field(l2_metadata.lkp_pkt_type, L2_BROADCAST);
add_to_field(l2_metadata.bd_stats_idx, 2);
}
//l2_metadata.bd_stats_idx + 1统计的是本bd的组播报文字节数和个数
//l2_metadata.bd_stats_idx + 2统计的是本bd的广播报文字节数和个数
//异常报文丢弃
action set_malformed_packet(drop_reason) {
modify_field(ingress_metadata.drop_flag, TRUE);
modify_field(ingress_metadata.drop_reason, drop_reason);
}
//报文校验
table validate_packet {
reads {
l2_metadata.lkp_mac_sa : ternary; //源目的mac
l2_metadata.lkp_mac_da : ternary;
l3_metadata.lkp_ip_type : ternary;
l3_metadata.lkp_ip_ttl : ternary;//ttl值,小于1则非法
l3_metadata.lkp_ip_version : ternary;//ip版本,不是4和6则非法
ipv4_metadata.lkp_ipv4_sa mask 0xFF000000 : ternary;
#ifndef IPV6_DISABLE
ipv6_metadata.lkp_ipv6_sa mask 0xFFFF0000000000000000000000000000 : ternary;
#endif /* IPV6_DISABLE */
}
actions {
nop;
set_unicast;
set_unicast_and_ipv6_src_is_link_local;
set_multicast;
set_multicast_and_ipv6_src_is_link_local;
set_broadcast;
set_malformed_packet;
}
size : VALIDATE_PACKET_TABLE_SIZE;
}
control process_validate_packet {
if (DO_LOOKUP(PKT_VALIDATION) and//进行检测并且没有设置丢弃标志
(ingress_metadata.drop_flag == FALSE)) {
apply(validate_packet);
}
}
该表在初始化的时候会设置许多默认表项,不需要动态添加,详细可以参看函数:
p4_pd_status_t switch_pd_validate_packet_table_add_default_entry(switch_device_t device)
源mac检查
源mac检查主要是用于mac学习。通过对源mac表的查找,可以判断报文的源mac是否在转发表中,以及是否发生该表。通过设置相关标志,后续产生mac学习摘要通知CPU进行mac学习。采用的软件mac学习(软件mac学习是否满足线上要求是一个值得思考的问题)。
/*****************************************************************************/
/* Source MAC lookup */
/*****************************************************************************/
action smac_miss() {
modify_field(l2_metadata.l2_src_miss, TRUE);
}
action smac_hit(ifindex) {
//计算异或,判断两个接口索引是否相同
bit_xor(l2_metadata.l2_src_move, ingress_metadata.ifindex, ifindex);
}
//源mac检查码,该表的主要功能是进行源mac学习的先期检测。
//通过设置l2_metadata.l2_src_miss和l2_metadata.l2_src_move
//决定是否产生摘要通知CPU,fdb学习事件。
table smac {
reads {
ingress_metadata.bd : exact;//bd域,广播域隔离
l2_metadata.lkp_mac_sa : exact;//源mac
}
actions {
nop;
smac_miss;
smac_hit;
}
size : MAC_TABLE_SIZE;
}
FDB转发
dmac表用于fdb转发,可以用于underlay转发(BUM报文),也可以用于overlay转发。
/*****************************************************************************/
/* Destination MAC lookup */
/*****************************************************************************/
//这个是常规的动作,命中fdb表项后直接设置出接口索引,同时校验输入输出接口是否相同。
action dmac_hit(ifindex) {
modify_field(ingress_metadata.egress_ifindex, ifindex);
bit_xor(l2_metadata.same_if_check, l2_metadata.same_if_check, ifindex);
}
//如果报文是一个组播地址,这映射到组播组,由组播组负责报文的复制分发。
action dmac_multicast_hit(mc_index) {
modify_field(intrinsic_metadata.mcast_grp, mc_index);
#ifdef FABRIC_ENABLE
modify_field(fabric_metadata.dst_device, FABRIC_DEVICE_MULTICAST);
#endif /* FABRIC_ENABLE */
}
//对于未知单播报文,则进行泛洪,设置ingress_metadata.egress_ifindex = IFINDEX_FLOOD。
//后续流程会进行广播组设置,进行泛洪。
action dmac_miss() {
modify_field(ingress_metadata.egress_ifindex, IFINDEX_FLOOD);
#ifdef FABRIC_ENABLE
modify_field(fabric_metadata.dst_device, FABRIC_DEVICE_MULTICAST);
#endif /* FABRIC_ENABLE */
}
//上面三个动作就是最基本的二层转发的动作,BUM泛洪处理。
//下面三个动作是一些特殊的处理,比如对于overlay报文,同子网报文进过fdb处理后。
//需要指定外层封装的相关参数,这个时候通过设置下一跳索引,由下一跳索引继续处理
//后续事宜。
action dmac_redirect_nexthop(nexthop_index) {
modify_field(l2_metadata.l2_redirect, TRUE);
modify_field(l2_metadata.l2_nexthop, nexthop_index);
modify_field(l2_metadata.l2_nexthop_type, NEXTHOP_TYPE_SIMPLE);
}
action dmac_redirect_ecmp(ecmp_index) {
modify_field(l2_metadata.l2_redirect, TRUE);
modify_field(l2_metadata.l2_nexthop, ecmp_index);
modify_field(l2_metadata.l2_nexthop_type, NEXTHOP_TYPE_ECMP);
}
//主要用于特殊的保护措施。指定的mac地址丢弃等作用。
action dmac_drop() {
drop();
}
//fdb转发
table dmac {
reads {
ingress_metadata.bd : exact;
l2_metadata.lkp_mac_da : exact;
}
actions {
#ifdef OPENFLOW_ENABLE
openflow_apply;
openflow_miss;
#endif /* OPENFLOW_ENABLE */
nop;
dmac_hit;
dmac_multicast_hit;
dmac_miss;
dmac_redirect_nexthop;
dmac_redirect_ecmp;
dmac_drop;
}
size : MAC_TABLE_SIZE;
support_timeout: true;//支持老化,不设置的话,默认是false。可以使用接口设置老化时间,默认是10秒,老化后会有回调函数通知上层应用。
}
#endif /* L2_DISABLE */
control process_mac {
#ifndef L2_DISABLE
if (DO_LOOKUP(SMAC_CHK) and //源mac检查,用于mac学习
(ingress_metadata.port_type == PORT_TYPE_NORMAL)) {
apply(smac);
}
if (DO_LOOKUP(L2)) {//fdb转发
apply(dmac);
}
#endif /* L2_DISABLE */
}
mac地址学习
对smac-miss以及l2_metadata.l2_src_move的报文,产生摘要信息,通知CPU进行mac地址学习相关事件处理。
#ifndef L2_DISABLE
/*****************************************************************************/
/* MAC learn notification */
/*****************************************************************************/
//摘要包含的信息
field_list mac_learn_digest {
ingress_metadata.bd;//bd
l2_metadata.lkp_mac_sa;//源mac地址
ingress_metadata.ifindex;//输入接口
}
//产生摘要,MAC_LEARN_RECEIVER是驱动在初始化的时候注册的接收摘要的通道。
action generate_learn_notify() {
generate_digest(MAC_LEARN_RECEIVER, mac_learn_digest);
}
table learn_notify {
reads {
l2_metadata.l2_src_miss : ternary;
l2_metadata.l2_src_move : ternary;
l2_metadata.stp_state : ternary;//mac学习需要考虑端口的stp状态
}
actions {
nop;
generate_learn_notify;
}
size : LEARN_NOTIFY_TABLE_SIZE;
}
#endif /* L2_DISABLE */
control process_mac_learning {
#ifndef L2_DISABLE
if (l2_metadata.learning_enabled == TRUE) {
apply(learn_notify);
}
#endif /* L2_DISABLE */
}
出流水线
出接口报文类型信息统计
/*****************************************************************************/
/* Egress BD lookup */
/*****************************************************************************/
#ifndef STATS_DISABLE
counter egress_bd_stats {
type : packets_and_bytes;//统计字节数和报文数
direct : egress_bd_stats;//direct模式,直接由egress_bd_stats进行刷新,表项大小为
//EGRESS_BD_STATS_TABLE_SIZE
min_width : 32;
}
table egress_bd_stats {
reads {
egress_metadata.bd : exact;//bd隔离
l2_metadata.lkp_pkt_type: exact;//对输出报文类型进行统计
}
actions {//没有动作,只进行统计
nop;
}
size : EGRESS_BD_STATS_TABLE_SIZE;
}
#endif /* STATS_DISABLE */
control process_egress_bd_stats {
#ifndef STATS_DISABLE
apply(egress_bd_stats);
#endif /* STATS_DISABLE */
}
出BD属性设置
action set_egress_bd_properties(smac_idx, nat_mode, bd_label) {
modify_field(egress_metadata.smac_idx, smac_idx);
//设置源mac索引,这个是用于三层转发时才有用,用于修改报文的源mac地址,这里使用索引是为了节约资源
//因为本地的源mac有限,可以使用一个单独的表存放,这里使用索引的话将大大减小egress_bd_map占用的资源。
modify_field(nat_metadata.egress_nat_mode, nat_mode);//nat相关
modify_field(acl_metadata.egress_bd_label, bd_label);//用于acl
}
table egress_bd_map {
reads {
egress_metadata.bd : exact;
}
actions {
nop;
set_egress_bd_properties;
}
size : EGRESS_BD_MAPPING_TABLE_SIZE;
}
control process_egress_bd {
apply(egress_bd_map);
}
VLAN解封装
/*****************************************************************************/
/* Egress VLAN decap */
/*****************************************************************************/
action remove_vlan_single_tagged() {
modify_field(ethernet.etherType, vlan_tag_[0].etherType);
remove_header(vlan_tag_[0]);
}
//qinq or double vlan
action remove_vlan_double_tagged() {
modify_field(ethernet.etherType, vlan_tag_[1].etherType);
remove_header(vlan_tag_[0]);
remove_header(vlan_tag_[1]);
}
table vlan_decap {
reads {
vlan_tag_[0] : valid;
vlan_tag_[1] : valid;
}
actions {
nop;
remove_vlan_single_tagged;
remove_vlan_double_tagged;
}
size: VLAN_DECAP_TABLE_SIZE;
}
control process_vlan_decap {
apply(vlan_decap);
}