P4 Tutorial 快速上手 (3) Basic_tunnel
提示:本系列仅适用于软件交换机BMv2
P4 Tutorial 快速上手 (2)
简介
代码库链接:https://github.com/p4lang/tutorials/tree/master/exercises/basic_tunnel
Basic_tunnel教程的目标是在basic.p4的基础上增加对隧道协议的支持。请注意在本练习中,拓扑改为了如下所示的三角形拓扑。当然,这对数据平面运行的P4代码几乎没有影响,只需修改控制平面的规则。关于流表下发、P4runtime协议等控制平面与数据平面交互的内容,将在系列(4)中进行介绍。

一、转发原理
同样,为了便于理解代码,首先对隧道协议的基本概念进行介绍。其主要思想是使用隧道头将其他用于转发的协议的头部封装起来,路由器在检测到检测到隧道头时便根据隧道头当中的地址信息转发数据包,而不再解析其他协议。常见的应用包括MPLS/IPsec/VPN等,通过将IP包头封装的方式到达易于实现流量工程或安全防护的目的。“隧道”是一个对此类协议形象的描述,数据包仅需知道隧道入口和出口的位置,而无需关心沿途需要如何转发,就像老司机在隧道中无需知道应该往哪开,因为修隧道的工人已经为你考虑好了~~。
在之前的basic_forwarding中已经实现了基于IPv4的转发功能,本练习考虑增加一个简单的隧道协议,即转发设备在解析到该数据包被隧道协议封装后,根据隧道头中的目的主机ID的值进行转发。因此需要增加的P4程序包括:
1.在接收的数据包后,需在对隧道协议的解析。
2.若数据包被封装在隧道头中,根据目的主机ID,通过精准匹配(exact)的方法获取到数据包的转发端口。
本练习同样在P4.org的BMv2软件交换机中实现的V1Model架构下编写。
二、代码解析
Basic_tunnel需要完成的代码十分简单,有了basic.p4的经验,直接在之前的程序基础上进行修改即可。
1.包头部解析
// TODO: Update the parser to parse the myTunnel header as well
与解析IPv4协议类似,在提取以太网帧头后首先需要判断上层协议类型,注意教程提供的不完整代码已经定义了隧道协议号:
TYPE_MYTUNNEL = 0x1212
因此在parse_ethernet状态中增加对隧道协议的判断,若是,则转换到parse_tunnel状态:
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跳转状态
TYPE_MYTUNNEL: parse_tunnel; // 当hdr.ethernet.etherType与TYPE_MYTUNNEL相同,即等于0x1212则说明是经隧道封装的数据包
default: accept; // 当无法识别网络层协议号时,默认动作为接收
}
}
与parse_ipv4类似,parse_tunnel具体定义如下:
state parse_ipv4 {
packet.extract(hdr.tunnel); // 依据header myTunnel_t的定义提取各字段
transition accept;
}
以上为basic_tunnel.p4中包头部解析部分需要补充的程序段,注意与solution中给出的方法不同,此处不再对被封装协议进行解析,更符合某些隧道协议的实际情况。在此可以对之前学习的状态的声明state,状态转移transiton,按header定义提取包头packet.extract()等关键方法进行回顾掌握。
2.按目的主机ID转发
与basic.p4相同,实现查表转发的核心代码仍在ingress中.首先需要确定在流程中如何应用转发表。该段程序在执行时由apply{}开始,需增加对数据包是否为隧道封装的数据包的判断,即通过hdr.tunnel.isValid()判断是否需要应用名为myTunnel_exact的Table:
apply {
if (hdr.ipv4.isValid()) {
// Process IPv4 packets
ipv4_lpm.apply();
} else if (hdr.myTunnel.isValid()) { // 在没有解析到IPv4包头时进一步判断隧道包头是否存在
// process tunneled packets
myTunnel_exact.apply();
}
}
与ipv4_lpm类似,交换机根据控制平面下发的转发规则判断应当如何处理数据包。各交换机对应的转发规则在pod-topo/sX-runtime.json中,X为交换机的id号。以交换机1为例,发往h3的隧道封装数据包对应表项为:
"table": "MyIngress.myTunnel_exact", // 对应的表的位置(MyIngress)与名称(myTunnel_exact)
"match": {
"hdr.myTunnel.dst_id": [3] // 匹配的目的主机ID为3
},
"action_name": "MyIngress.myTunnel_forward", // 对应的动作的位置(MyIngress)与名称(ipv4_forward)
"action_params": {
"port": 3 // 转发端口:1
}
对于发往h3的数据包,交换机1将转发到3端口。相应的,参考ipv4_lpm的定义,myTunnel_exact的定义如下:
table myTunnel_exact {
key = {
hdr.myTunnel.dst_id: exact; // 唯一区别在于使用exact进行精确匹配
}
actions = {
myTunnel_forward; // 需要采取的转发动作,使用action关键字定义
drop;
}
size = 1024;
default_action = drop();
}
出于练习的目的,在动作myTunnel_forward中简单地确定数据包发往的egress_port即可。对应代码更改如下:
action myTunnel_forward(egressSpec_t port) {
standard_metadata.egress_spec = port;
}
3.包头填充
在包头解析阶段将数据包包头对应的比特提取后,需要在逆解析(封装阶段)进行重新填充,代码如下:
control MyDeparser(packet_out packet, in headers hdr) {
apply {
packet.emit(hdr.ethernet); // 与packet.extract相反,填充以太帧头
packet.emit(hdr.ipv4); // 填充IPv4包头
packet.emit(hdr.ipv4); // 填充隧道包头
}
}
以上修改完成后,下一步将结合代码库提供的send.py和receive.py进行测试,。
三、运行结果
1.连通实验
同样的mak run一下,首先测试下基本的IPv4转发是否正常,使用pingall进行测试:

利用xterm命令打开 h1, h2, 和 h3 终端,在 h2 和 h3 的终端运行:
python receive.py
然后,由h1尝试用隧道协议向h3发送"hello"消息,在终端键入:
python send.py 10.0.2.2 “Hello!” --dst_id=3

可以发现,h1 发送了一个目的IP为 h2 的IP地址的数据包,但是隧道封装的目的主机ID为3。在h2和h3可以观察到:

即仅有h3收到了数据包(通过PCAP文件夹下的抓包记录同样可验证),说明BMv2能够解析隧道协议并优先以隧道协议为转发凭据进行数据转发,。
总结
以上就是Basic_tunnel练习的主要内容,由于是对基础转发流程的回顾,因此几乎没有难点,但也为实现其他转发方案提供了基本思路。
下一步,将对实现控制平面与数据平面的交互的P4runtime进行说明。
本文档介绍了P4教程中的Basic_tunnel练习,该练习旨在在基本的P4转发程序上添加对隧道协议的支持。主要内容包括隧道协议转发原理、代码解析,涉及包头部解析、目的主机ID转发及包头填充。通过修改基本转发流程,实现根据隧道头中的目的主机ID进行数据包转发。实验结果显示,BMv2软件交换机能够正确解析隧道协议并转发数据包。
2043

被折叠的 条评论
为什么被折叠?



