p4runtime 实现

P4官方教程(三)

Posted by BY beta on October 26, 2020

1.实验p4runtime目的

最初看到这个实验,感觉到兴奋并且跃跃欲试。因为在basic和basic_tunnel实验中,交换机运行的具体流表都由实验者在sX-runtime.json 中提供,我对于如何编写具体的流表规则仍然是完全陌生的,所以很期待这个实验可以帮助我理解控制平面和具体流表生成。

通读作者的介绍之后,大致了解p4runtime实验需要完成一个简单的控制平面,具体的工作包括:

  • 与交换机建立gRPC连接
  • 将P4程序(交换机配置)推送给交换机
  • 将交换机规则推送给交换机
  • 从交换机读取计数器

2.实验准备

系统是ubuntu16.04,前期已经正常安装所需的依赖项。

3. 原始代码运行

本次的实验有两个核心的文件,mycontroller.py 和 advanced_tunnel.p4,后者已经由作者编写完成。我们的主要任务是在mycontroller.py添加相应的代码段。

  1. 我们首先运行原始代码,在终端输入
make

此命令编译advanced_tunnel.p4 ,生成了交换机所需的配置文件;启动mininet,构建实验拓扑

新建一个终端,连接到交换机s1

simple_switch_CLI --thrift-port 9090

查看已有的table和action

show_tables
show_actions

结果如下,可以看到交换机已经table和action逻辑

image-20201027191341879

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

image-20201027191610976

  1. 打开wireshark,监听s1-eth1端口。在mininet的终端中输入ping命令
h1 ping h2

由于没有给交换机添加配置,所以数据包无法传输

image-20201027190330046

  1. 新建一个终端,运行mycontroller.py
./mycontroller.py

可以看到此终端会不断打印交换机中的计数器。如果在mininet终端中,执行

h1 ping h2

可以看到打印结果如下,因为具体的规则还未生成,所以只有交换机s1的入口会有计数值的变化。

image-20201027192030157

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流表如下:

image-20201027195619404

s3流表如下:

image-20201027195652066

可以很清楚的看到,s1已经有相关的流表规则,而s3并没有。

ping实验

在mininet的终端再次输入执行h1 ping h2的命令,可以看到计数器的变化如下:

image-20201027200107548

拓展

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

image-20201027203713722

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

image-20201027204136759

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

image-20201027204231975

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

eth1: 08 00
eth2: 12 12 08 00 00 64