1.实验p4runtime目的
最初看到这个实验,感觉到兴奋并且跃跃欲试。因为在basic和basic_tunnel实验中,交换机运行的具体流表都由实验者在sX-runtime.json 中提供,我对于如何编写具体的流表规则仍然是完全陌生的,所以很期待这个实验可以帮助我理解控制平面和具体流表生成。
通读作者的介绍之后,大致了解p4runtime实验需要完成一个简单的控制平面,具体的工作包括:
- 与交换机建立gRPC连接
- 将P4程序(交换机配置)推送给交换机
- 将交换机规则推送给交换机
- 从交换机读取计数器
2.实验准备
系统是ubuntu16.04,前期已经正常安装所需的依赖项。
3. 原始代码运行
本次的实验有两个核心的文件,mycontroller.py 和 advanced_tunnel.p4,后者已经由作者编写完成。我们的主要任务是在mycontroller.py添加相应的代码段。
- 我们首先运行原始代码,在终端输入
make
此命令编译advanced_tunnel.p4 ,生成了交换机所需的配置文件;启动mininet,构建实验拓扑
新建一个终端,连接到交换机s1
simple_switch_CLI --thrift-port 9090
查看已有的table和action
show_tables
show_actions
结果如下,可以看到交换机已经table和action逻辑

查看表 myTunnel_exact,结果如下图,并没有具体的流规则

- 打开wireshark,监听s1-eth1端口。在mininet的终端中输入ping命令
h1 ping h2
由于没有给交换机添加配置,所以数据包无法传输

- 新建一个终端,运行mycontroller.py
./mycontroller.py
可以看到此终端会不断打印交换机中的计数器。如果在mininet终端中,执行
h1 ping h2
可以看到打印结果如下,因为具体的规则还未生成,所以只有交换机s1的入口会有计数值的变化。

4. 实验步骤
打开mycontroller.py文件,因为是第一次修改,所以需要跟着作者的提示去理解整体框架。
1. writeTunnelRules
在这个类中,按照提示需要写入三个规则
- 隧道入口规则, 为数据包添加特殊的隧道ID。
- 传输规则。交换机按照隧道ID传给下一个交换机
- 隧道出口规则,将数据包从最后一个交换机传输到主机
note:可以看到,这个和我们在basic_tunnel实验中的逻辑是不一样的。basic_tunnel实验会易于ethert的类型选择ip转发或者隧道转发,而这里的数据包在进入交换机时,并不存在隧道ID,是由交换机添加的?那么数据包进入和离开时是否不一样?下文拓展验证
tunnel ingress rule
P4文件已经将配置实现,可以理解为接口。我们需要给它提供参数,并且不同参数会调用接口内部不同的实现。具体代码如下(无需修改):
table_entry = p4info_helper.buildTableEntry(
table_name="MyIngress.ipv4_lpm",
match_fields={
"hdr.ipv4.dstAddr": (dst_ip_addr, 32)
},
action_name="MyIngress.myTunnel_ingress",
action_params={
"dst_id": tunnel_id,
})
ingress_sw.WriteTableEntry(table_entry)
print "Installed ingress tunnel rule on %s" % ingress_sw.name
#### transit rule
table_entry = p4info_helper.buildTableEntry(
table_name = "MyIngress.myTunnel_exact",
match_fields={
"hdr.myTunnel.dst_id": tunnel_id
},
action_name = "MyIngress.myTunnel_forward",
action_params={
"port":SWITCH_TO_SWITCH_PORT
})
ingress_sw.WriteTableEntry(table_entry)
print "Installed transit tunnel rule on %s" %ingress_sw.name
engress rule
table_entry = p4info_helper.buildTableEntry(
table_name="MyIngress.myTunnel_exact",
match_fields={
"hdr.myTunnel.dst_id": tunnel_id
},
action_name="MyIngress.myTunnel_egress",
action_params={
"dstAddr": dst_eth_addr,
"port": SWITCH_TO_HOST_PORT
})
egress_sw.WriteTableEntry(table_entry)
print "Installed egress tunnel rule on %s" % egress_sw.name
2.readTableRules
这个类主要是为了读取控制平面安装到交换机的流规则,作者已实现基本版,可以不用修改。修改主要是为了更便于阅读
3.main
主函数这块很简单,将readTableRules前面的注释符号去除即可。
5.实验结果
修改完mycontroller.py之后,运行该程序。
查看交换机流表
会向交换机s1和s2下发相关的流表规则,在这里我们可以将s1的流表和s3的流表做对比
s1流表如下:

s3流表如下:

可以很清楚的看到,s1已经有相关的流表规则,而s3并没有。
ping实验
在mininet的终端再次输入执行h1 ping h2的命令,可以看到计数器的变化如下:

拓展
验证交换机添加隧道头。wireshark监听交换机s1的eth1和eth2,可以看到下面结果

数据包从eth1进入时是98字节,离开时是102字节,多了四个字节。我们打开advanced_tunnel.p4,查看动作myTunnel_ingress,其命令使隧道头myTunnel可用。

我们查看了myTunnel头格式,可以看到正好是四个字节:

列出eth2和eth1报文的不同,如下图,结合p4的逻辑,0x1212表示协议,00 64 表示十进制的100,也就是我们设定的ID!
eth1: 08 00
eth2: 12 12 08 00 00 64