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);
}
posted @ 2020-03-31 11:19  ouyangxibao  阅读(353)  评论(0)    收藏  举报