# P4 Tutorial 快速上手 (4) P4Runtime

P4 Tutorial 快速上手 (4) P4Runtime

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



简介

代码库链接:https://github.com/p4lang/tutorials/tree/master/exercises/p4runtime
为了介绍用于向P4交换机下发策略的P4Runtime协议,官方教程中提供了基于Python的简易控制器(位于tutorials/utils/p4runtime_lib),并提供了相应的练习帮助理解。因此,本节围绕exercises中p4runtime进行说明,该练习所使用的P4程序为advanced_tunnel.p4,与之前的教程不同,该练习中P4文件已经被修改完善。其在上一篇中的basic_tunnel.p4的基础上增加了在交换机内部添加隧道头、按隧道转发与删除隧道头功能,读者需要对mycontroller.py进行修改,将缺少的流表规则下发到交换机中并远程监测交换机的计数器状态


P4Runtime协议是?

P4Runtime
P4Runtime是一组控制平面规范,是基于Protobuf以及gRPC框架的协议,用于控制由 P4 程序定义的设备或程序的数据平面实体。图 1 表示 P4 运行时参考体系结构。逻辑上,控制器位于顶部,转发设备或目标位于底部。P4Runtime 仅为每个读/写实体授予对单个主控制器的写入访问权限。P4Runtime 允许每个角色使用一个主控制器,并且基于角色的客户端仲裁方案确保只有一个控制器对每个读/写实体或管道配置本身具有访问权限。任何控制器都可以对任何实体或管道配置执行读取访问。关于P4Runtime API的介绍可以在此处找到:P4Runtime Specification

Exercises说明

该练习需关注的文件包括advanced_tunnel.p4mycontroller.py

在P4源程序中,首先,在Ingress中添加了两个计数器用于监测以网内隧道转发的数据包个数:

counter(MAX_TUNNEL_ID, CounterType.packets_and_bytes) ingressTunnelCounter;
counter(MAX_TUNNEL_ID, CounterType.packets_and_bytes) egressTunnelCounter;

其次,增添了两个action:

action myTunnel_ingress(bit<16> dst_id) {
        hdr.myTunnel.setValid();
        hdr.myTunnel.dst_id = dst_id;
        hdr.myTunnel.proto_id = hdr.ethernet.etherType;
        hdr.ethernet.etherType = TYPE_MYTUNNEL;
        ingressTunnelCounter.count((bit<32>) hdr.myTunnel.dst_id);
    }
action myTunnel_egress(macAddr_t dstAddr, egressSpec_t port) {
        standard_metadata.egress_spec = port;
        hdr.ethernet.dstAddr = dstAddr;
        hdr.ethernet.etherType = hdr.myTunnel.proto_id;
        hdr.myTunnel.setInvalid();
        egressTunnelCounter.count((bit<32>) hdr.myTunnel.dst_id);
    }

对于myTunnel_ingress,其为数据包增添了隧道包头,并希望根据流表规则插入目的地id(dst_id)。与之相反的是myTunnel_egress,在删除隧道包头的同时修改目的地mac地址。值得一提的是counter的用法,计数器将对对应索引地址计数。

在简易控制器mycontroller.py中,首先需要注意的是导入的包。

import p4runtime_lib.bmv2
import p4runtime_lib.helper
from p4runtime_lib.switch import ShutdownAllSwitchConnections

Lib文件夹位于tutorials/utils/p4runtime_lib中(后续若改用高级控制器可以略过),包括了一些用于控制数据平面的工具库,上述代码中涉及到bmv2.py/helper.py/switch.py三个文件。其中,helper.py主要负责与P4info文件的交互,switch.py主要负责对交换机的一系列操作(连接/读写流表等)。

教程重点在于使用该简易控制器体验实际的控制平面与数据平面的交互流程。

mycontroller.py

程序的主函数首先对P4info和json文件的检查,然后执行main()程序。main()程序逻辑如下:

  1. 连接交换机s1和s2
  2. 成为si和s2的主控制器
  3. 安装P4info与json文件
  4. 将隧道规则写入s1和s2流表
  5. 读取s1和s2的流表规则(记得取消注释)
  6. 每2秒统计一次各交换机ingress和egress计数器的值
  7. 当程序结束市会断开与所有交换机的连接

其中第4步中writeTunnelRules()与第5步readTableRules()是不完整的,需要对其进行修改,其中用到的方法均在tutorials/utils/p4runtime_lib/helper.py文件中。

对于方法writeTunnelRules(),需要补充安装transit tunnel表项的程序段,仿照其他tunnel表项的插入程序即可:

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)

注意advanced_tunnel.p4定义的动作myTunnel_forward只需要一个端口参数,由于是在交换机间传输,选择topology.json所设置的端口2即可(s1端口2与s2端口2相连接),mycontroller.py在程序第十九行定义了SWITCH_TO_SWITCH_PORT=2,直接使用即可(方便理解程序)。

对于方法readTableRules(),其目标是从返回的报文解析出想要的表项信息,例如表的名字,匹配域的名字和值,动作的名字和值等,同样需要用到helper.py中get开头的方法。

for response in sw.ReadTableEntries():
        for entity in response.entities:
            entry = entity.table_entry
            # TODO For extra credit, you can use the p4info_helper to translate
            #      the IDs in the entry to names
            table_name = p4info_helper.get_tables_name(entry.table_id)
            print('%s: ' % table_name, end=' ')
            for m in entry.match:
                print(p4info_helper.get_match_field_name(table_name, m.field_id), end=' ')
                print('%r' % (p4info_helper.get_match_field_value(m),), end=' ')
            action = entry.action.action
            action_name = p4info_helper.get_actions_name(action.action_id)
            print('->', action_name, end=' ')
            for p in action.params:
                print(p4info_helper.get_action_param_name(action_name, p.param_id), end=' ')
                print('%r' % p.value, end=' ')
            print()

这里需要注意程序段首先调用了switch.py的ReadTableEntries()方法以获取table_id,基于helper.py的注释可以知道通过get_tables_name获取表名,以供其他get_……方法传参,从而print表项。

测试

该部分测试参照README.md即可

总结

以上就是本系列(4)的主要内容,该教程由于涉及控制平面与数据平面交互,并且使用的是教程制作者提供的lib库,较之前的教程更为复杂,主要是感受使用P4runtime协议完成从上至下的控制,后续建议使用成熟的商业网络控制器(e.g. ONOS/ODL)。

附录

mycontroller.py完整程序

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值