P4 tutorials实验 - basic
P4 tutorials实验 - basic
基础知识
- P4,是网络设备的域特定语言,用于指定数据平面设备(交换机、网卡、路由器等)如何处理数据包
- Bmv2 simple_switch是一种软件交换机
- V1Model架构,是在simple_switch上实现的,用于测试P4程序功能
其中,parser,deparser都是可编程的,parser可以用某种P4定义的方式提取数据包头,deparser用来按照P4程序的规定,重构数据包
- bit< n >:无符号整数,可使用typedef增加别名,提高可读性
实验预览
实验链接:https://github.com/p4lang/tutorials/tree/master/exercises/basic
本实验的目标是写一个P4程序,实现基本的转发,为简单起见,只实现ipv4的转发
在basic目录下提供的基本的p4程序basic.p4会丢弃所有数据包,我们的工作是扩展这个程序(完成代码中的TODO部分),使其能够正确转发ipv4数据包
需要实现TODO的部分有:
parser部分
parser MyParser(packet_in packet,
out headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
//inout: 同时作为输入和输出值,类似 c++ 里面的引用
state start {
/* TODO: add parser logic */
//可以填充ethernet_t和ipv4_t字段
transition accept;
}
}//MyParser的作用是解析数据包,提取包头
ingress processing部分
control MyIngress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
action drop() {
mark_to_drop(standard_metadata);
}
action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {
/* TODO: fill out code in action body */
}
//要实现一个ipv4_forward操作,可以设置下一跳的出端口、用交换机的MAC地址更新数据包的源MAC地址、用下一个跃点的地址更新数据包的目的MAC地址,并减少ttl字段
table ipv4_lpm {
key = {
hdr.ipv4.dstAddr: lpm;
}
actions = {
ipv4_forward;
drop;
NoAction;
}
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();
}
}//MyIngress的作用是输入处理
deparser部分
control MyDeparser(packet_out packet, in headers hdr) {
apply {
/* TODO: add deparser logic */
}
}//MyDeparser是逆解析器
具体实验
step 1:修改basic.p4之前
在basic目录下,用make run
命令编译运行basic.p4实验,发现无法ping通
step 2:修改basic.p4
参考basic/solution目录下的basic.p4
- parser部分
parser MyParser(packet_in packet,
out headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
state start {
transition parse_ethernet;
//单独的transition可以理解为跳转
}
state parse_ethernet {
packet.extract(hdr.ethernet);
//提取ethernet部分,也就是根据ethernet_t中定义的格式来提取报头内容
transition select(hdr.ethernet.etherType) {
TYPE_IPV4: parse_ipv4;
//如果etherType==Type_IPV4,则转到ipv4_parser状态
default: accept;
//如果是其他值,默认接受
}
}
state parse_ipv4 {//ipv4解析部分
packet.extract(hdr.ipv4);//取出ipv4报头
transition accept;
}
}
Parser可以看作一个状态机,Parser始终以start状态开始,执行零或多个语句,转换到另一个状态,直到遇到accept或reject状态。所以代码从state start
处开始执行。当解析器开始对首部实例进行提取操作时,它根据作为extract函数参数的首部实例的格式进行提取,将数据包的数据更新到该首部实例中,同时更新该数据包的解析表示。transition select
可以理解为C语言中的switch-case语句,对参数进行值的匹配。
在此基础上,改写成:
state start{
packet.extract(hdr.ethernet);
transition select(hdr.ethernet.etherType){
TYPE_IPV4: ipv4_parser;
default: accept;
}
state ipv4_parser{
packet.extract(hdr.ipv4);
transition accept;
}
}
也可以实现相同的功能
- ingress processing部分
//actions可以看成C语言的函数(但没有循环)
action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {
standard_metadata.egress_spec = port;
//设置下一跳的出端口
hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
//用交换机的MAC地址更新数据包的源MAC地址
hdr.ethernet.dstAddr = dstAddr;
//用下一个跃点的地址更新数据包的目的MAC地址
//egress_spec是standard_metadata_t中的一个字段
hdr.ipv4.ttl = hdr.ipv4.ttl - 1;//减少ttl字段
}
apply {
//仅当ipv4报头是有效的,才应用ipv4_lpm
if (hdr.ipv4.isValid()) {
ipv4_lpm.apply();
}
}
- deparser部分
control MyDeparser(packet_out packet, in headers hdr) {
apply {
//使用emit函数重构数据包
packet.emit(hdr.ethernet);
packet.emit(hdr.ipv4);
//顺序不能对调,对调之后无法ping通
//因为要按照解析的顺序装回去,前面先拆的是ethernet,就先装ethernet
}
}
step 3:修改basic.p4后
修改代码之后重新make run运行: