P4 Tutorial 快速上手 (2) Basic IPv4 Forwarding

P4 Tutorial 快速上手 (2) Basic IPv4 Forwarding
提示:本系列仅适用于软件交换机BMv2
P4 Tutorial 快速上手系列 (1)



前言

代码库链接:https://github.com/p4lang/tutorials/tree/master/exercises/basic
在对P4 Tutorial库的粗略介绍后,便正式开始上手之旅。
本文将对最基础的练习进行讲解:basic练习的目标是编写一个具备基于ipv4地址的转发程序。因此,待编写的P4程序需要完成与传统SDN架构中交换机类似的转发操作,即控制平面将路由信息下发至交换机,交换机基于接收的信息填充路由表。具体的规则通常是指示目的 IP 地址映射到下一跃点的 MAC 地址和输出端口。在本练习中,控制平面操作已经被预先定义,仅需基于 P4 实现数据平面逻辑。
在本练习中,将使用以下拓扑。它可以视作Fat-tree的两个 pod,因此可以称为 pod-topo:
在这里插入图片描述


转发原理

在传统交换机中实现对数据包转发,实际上仅需明确数据包携带的目的MAC地址,并根据ARP表直接进行转发。但在本练习中,是需要由BMv2充当路由器的功能,即根据数据包的目的IP地址查找路由表,具体过程如下:
1.由于充当三层交换机,在接收的数据包后,需要对IP包头中的首部校验和Header Checksum进行校验,若出错将直接丢弃掉数据包。
2.根据IP Header中目的IP地址字段,通过最长前缀匹配(LPM)的方法获取到数据包的下一跳MAC地址与转发端口。
3.根据IPv4协议,在进行转发前,需要对数据包的MAC帧头中源MAC、目的MAC字段,IP包头的生存时间TTL按协议规定进行更新。
4.重新计算Header Checksum,将数据包转发至查询到的交换机端口。
为基于P4实现上述流程,本练习将在P4.org的BMv2软件交换机中实现的V1Model架构下编写,因此程序中还需要实现包头定义、解析、封装、入/出口定义等固定流程。V1Model 的架构文件可以在以下位置找到:https://github.com/p4lang/p4c/blob/main/p4include/v1model.p4。此文件描述了架构中 P4 可编程元素的接口、支持的外部以及架构的标准元数据字段,建议阅读。

#代码解析
源代码过长,因此不全部复制粘贴到此处。练习中首先提供了一份非完整的代码basic.p4,结构如下:
01-43行----头文件引用、包头部定义,值得注意的是header是一种独特的数据结构定义,多个header最终需要被封装到一个结构体headers中(40-43行)。
45-58行----包头部解析器定义
61-67行----校验和计算
70-104行----入口动作
106-114行----出口动作
116-138行----校验和更新
141-149行----包头填充(逆解析)
151-162行----V1model架构下程序入口以及各函数执行顺序
该不完整的代码还缺少4个部分,分别在包头部解析器定义、校验和验证、查表转发以及包头填充部分。(相较于教程中对校验和验证的忽略,本文进行了补全,因此与Solution中代码有微小不同)

1.包头部解析

parser MyParser(packet_in packet,
                out headers hdr,
                inout metadata meta,
                inout standard_metadata_t standard_metadata) {

    state start {
        /* TODO: add parser logic */
        transition accept;
    }
}

该部分代码的指示BMv2如何提取数据包的头部字段,本质上是一个有限状态机。程序的入口在“state start”。由于本练习旨在以太网的基础上转发IPv4数据包,涉及到以太网帧头以及IPv4包头的修改,因此需要提取这两个部分的字段。包头字段在15-34行已经定义,与以太网帧和IPv4包头格式完全对应。
首先需要提取以太网帧头,并判断上层协议是否为IPv4协议,因此定义新的状态“parse_ethernet”。由于不考虑其他二层协议,因此直接在start状态将直接转换到parse_ethernet状态,即,将上述代码中“transition accept;”转换为改为“transition parse_ethernet;”。
parse_ethernet状态需要完成的操作包括提取帧头部,判断上层协议是否为IPv4协议,若是,则转换到parse_ipv4状态,否则将直接接收。parse_ethernet的定义如下:

state parse_ethernet {
        packet.extract(hdr.ethernet);  // packet.extract将依据header定义按位提取比特并存在结构体中
        transition select(hdr.ethernet.etherType) { // select将根据选取的字段匹配下一状态
            TYPE_IPV4: parse_ipv4; // 当hdr.ethernet.etherType与TYPE_IPV4相同,即等于0x800跳转状态
            default: accept; // 当无法识别网络层协议号时,默认动作为接收
        }
    }

由于无需再解析传输层协议,因此parse_ipv4状态下仅需提取IPv4包头,具体定义如下:

state parse_ipv4 {
        packet.extract(hdr.ipv4);  // 依据header ipv4_t的定义提取各字段
        transition accept;
    }

以上为basic.p4中包头部解析部分的程序段,需要重点掌握的是几个关键字的用法,包括状态的声明state,状态转移transiton,按header定义提取包头packet.extract()。


2.校验与验证

原教程中未要求此部分内容,但在其问题与思考中提到basic.p4的solution版本能否取代路由器,对比实际的路由器功能,basic.p4缺少了对IP校验和的验证,同时显然并不具备路由功能(因为路由功能由控制器完成)。为了使程序更加完整,应当在MyVerifyChecksum中增加校验和验证,v1model提供了两种校验函数,分别是verify_checksum与verify_checksum_with_payload,在此采用前者完成对IP包头的完整性进行验证。其使用方法与update_checksum类似,具体如下:

control MyVerifyChecksum(inout headers hdr, inout metadata meta) {   
    apply {
    	verify_checksum(hdr.ipv4.isValid(), // 仅当IPv4包头存在时启用,{}中为用于hash的数据
            { hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv, hdr.ipv4.totalLen, dr.ipv4.identification,
              hdr.ipv4.flags, hdr.ipv4.fragOffset, hdr.ipv4.ttl, hdr.ipv4.protocol, hdr.ipv4.srcAddr,
              hdr.ipv4.dstAddr }, hdr.ipv4.hdrChecksum, HashAlgorithm.csum16); // 用于对比的校验和和采用的hash算法
    }
}

当verify_checksum计算所得的结果与hdr.ipv4.hdrChecksum不匹配时,交换机会将standard_metadata中的checksum_error置1,以便在Ingress中处理。因此在Ingress中需增加相应代码进行判断:

if (hdr.ipv4.isValid() && standard_metadata.checksum_error == 0) {
			…… // 其他应用
}

3.查表转发

该部分代码位于control MyIngress中,与转发相关的操作均应当位于此阶段。提供的初始代码如下:

action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {
        /* TODO: fill out code in action body */
    }

    table ipv4_lpm {
        key = {
            hdr.ipv4.dstAddr: lpm;  // 使用IPv4目的地址字段进行匹配
        }							// 匹配方法为最长前缀匹配(Longest prefix match)
        actions = {
            ipv4_forward;			// 采取上面定义的ipv4_forward
            drop;
            NoAction;               // 无操作,不采取任何action
        }
        size = 1024;				// 匹配表大小(条数)
        default_action = NoAction();
    }

    apply {
        /* TODO: fix ingress control logic
         *  - ipv4_lpm should be applied only when IPv4 header is valid
         */
        ipv4_lpm.apply();  // ipv4_lpm为定义的匹配表,apply方法将进行查表操作
    }

该段程序在执行时由apply{}开始,目前缺少了对数据包是否为IPv4数据包的判断。在v1model中,在数据包解析阶段(PARSER),交换机根据状态转移结果确定数据包携带了哪些包头,在本练习中共提取了hdr.ethernet和hdr.ipv4两种头部字段。在程序中可以用hdr.xx.isValid()确定数据包是否具有xx的封装,当xx存在与数据包且被解析时,hdr.xx.isValid()会返回布尔型的1用于判断。因此,为判断数据包是否为IPv4数据包,可以使用hdr.ipv4.isValid(),对应代码应修改为:

if (hdr.ipv4.isValid()) {
            ipv4_lpm.apply();
        }

在应用ipv4_lpm后,交换机根据数据包的目的地址匹配适宜的动作。在练习中,正常数据包都会被执行ipv4_forward动作,该动作会引入两个形参dstAddr和port,分别代表下一跳MAC地址和需要发往的端口号,两个形参的具体数值由控制层面下发的JSON文件决定,各交换机对应的转发规则在pod-topo/sX-runtime.json中,X为交换机的id号。以交换机1为例,处理发往10.0.1.1的数据包时,依据的表项为:

"table": "MyIngress.ipv4_lpm",             // 对应的表的位置(MyIngress)与名称(ipv4_lpm)
      "match": {
        "hdr.ipv4.dstAddr": ["10.0.1.1", 32]     // 匹配ipv4的目的地址,值为10.0.1.1,子网掩码32位
      },
      "action_name": "MyIngress.ipv4_forward",   // 对应的动作的位置(MyIngress)与名称(ipv4_forward)
      "action_params": {						 // 相关参数
        "dstAddr": "08:00:00:00:01:11",          // 下一跳MAC地址:"08:00:00:00:01:11"
        "port": 1								 // 转发端口:1
      }

对于发往10.0.1.1的数据包,交换机1将转发到1端口。在ipv4_forward中确定数据包发往的egress_port,完成对数据包头部的更改,包括替源/目的换MAC地址、生存时间TTL减1。对应代码更改如下:

action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {
        standard_metadata.egress_spec = port;        // 令交换机识别数据包需要发往的端口
        hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; // MAC地址调整(不懂的温习计算机网络)
        hdr.ethernet.dstAddr = dstAddr;
        hdr.ipv4.ttl = hdr.ipv4.ttl - 1;			 // TTL调整(不懂的温习计算机网络)
    }

##包头填充
在包头解析阶段将数据包包头对应的比特提取后,需要在逆解析(封装阶段)进行重新填充,代码如下:

control MyDeparser(packet_out packet, in headers hdr) {
    apply {
        packet.emit(hdr.ethernet);  // 与packet.extract相反,填充以太帧头
        packet.emit(hdr.ipv4);      // 填充IPv4包头
    }
}

在经过以上步骤后,一个完整的basic.p4程序就改好啦,可以与solution中文件进行对照以发现自己的代码是否存在错误,接下来就可以编译运行测试一条龙了。


运行结果

连通性实验

在对应文件夹下运行终端,输入make run进行程序编译、拓扑构建以及策略下发:
在这里插入图片描述
命令执行结果如下所示:
在这里插入图片描述
接下来就可以测试转发程序是否工作啦,使用mininet中常用命令pingall进行测试:
在这里插入图片描述
Tutorial库中所有实验都会贴心的进行自动抓包,实验中产生的数据包将以PCAP的文件格式存储在pcaps文件夹下。利用wireshark打开s1-eth1-in.pcap文件可以看到如下所示的数据包。(pcap文件的明明规则为 交换机ID-端口号-入/出方向)
在这里插入图片描述

校验和

为了测试校验和检验程序是否生效,可以发送携带错误checksum的数据包进行判断。首先需要确定Checksum数据包能正常转发。利用xterm命令打开 h1 和 h2 终端,先在h2运行python receive.py, 再由h1运行python send.py 10.0.2.2 Test:
在这里插入图片描述
对send.py和receive.py的具体说明暂且略过,上述程序将一个携带“Test”内容的TCP报文由h1发送至h2,通过验算可知在发送阶段chksum字段没有出错,因此转发正常进行。值得注意的是,在转发过程中,由于TTL发生了改变,chksum也会逐跳变化。下面需要修改发送程序以插入错误的chksum。打开send.py,找到:

pkt = pkt /IP(dst=addr) / TCP(dport=1234, sport=random.randint(49152,65535)) / sys.argv[2]

将其改为

pkt = pkt /IP(dst=addr, chksum=0) / TCP(dport=1234, sport=random.randint(49152,65535)) / sys.argv[2]

保存后在h1终端执行python send.py 10.0.2.2 Hello? :
在这里插入图片描述
可以发现数据包在h1发出时,IP Header中chksum字段被置0,h2未能收到该数据包,说明交换机在检测到校验和出错时直接丢弃了数据包。


总结

以上就是本系列(2)的主要内容,基于basic.p4对P4中常用的函数进行解释说明,对于新手必须熟练掌握常用的关键字,才能高效的进行后续学习和开发。
下一步,将对basic_tunnel进行讲解。

附录

完整basic.p4程序
P4 tutorial教程PPT

  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
As an AI language model, I can provide an overview of the basics of STA (Static Timing Analysis). Static Timing Analysis (STA) is a method used to verify the timing performance of an integrated circuit (IC) design. It is a critical step in the design and manufacturing process of semiconductor devices. STA helps to ensure that the design meets the timing requirements and operates correctly under various conditions. The STA process involves analyzing the delays of a circuit design, including the delays of all the paths, from inputs to outputs. Timing analysis tools are used to determine the worst-case delay and to ensure that the circuit meets the required timing constraints. Timing constraints are typically specified in the form of timing paths, which define the maximum delay allowed for each path in the circuit. STA is performed at various stages of the design process, from the initial design to the final implementation. In the early stages of the design process, STA is used to estimate the performance of the design and to identify potential timing issues. As the design progresses, STA is used to verify the timing performance of the implemented design. STA involves several key steps, including: 1. Timing modeling: The timing behavior of each component in the circuit is modeled using delay models, which describe the delay of the component as a function of its input and output signals. 2. Timing analysis: The timing analysis tool analyzes the circuit to determine the worst-case delay and to identify any timing violations. 3. Timing optimization: Timing optimization techniques are used to improve the timing performance of the design, such as changing the placement of components or adjusting the clock frequency. 4. Timing verification: The final step is to verify that the design meets the timing requirements, including the timing paths and the overall timing budget. In conclusion, STA is a critical step in semiconductor design and manufacturing. It helps to ensure that the design meets the timing requirements and operates correctly under various conditions. STA involves timing modeling, analysis, optimization, and verification to ensure that the design meets the required timing constraints.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值