ONOS-P4-Tutorial-Ⅰ
练习1: 软件工具基础知识和数据包I/O
说明:本文翻译自onos-p4-tutorial的Exercise 1: Software tools basics and packet I/O,由于对ACL不甚理解而做此纪录
本练习提供了本教程在其余部分中使用的软件工具介绍。
首先,您将在本练习中学习如何:
- 使用
stratum_bmv2
软件交换机的2x2叶子-主干拓扑启动Mininet - 启动ONOS以及一组用于基础服务(例如拓扑发现)的内置应用程序
- 将网络配置文件推送到ONOS以
stratum_bmv2
使用P4Runtime发现和控制交换机 - 通过使用P4Runtime数据包入/出验证ONOS是否能够自动发现所有链接
为此,将要求您对原始P4代码进行简单的更改以增加对数据包输入的支持,使用基于PTF的数据平面单元测试来验证P4更改,最后将更改应用于pipeconf Java实现以启用ONOS。内置应用程序使用输入/输出数据包执行拓扑发现。
要进行此练习,您将需要多个终端窗口(或标签页)以使用Mininet,ONOS和BMv2的CLI进行操作。我们使用以下约定来区分不同CLI的命令:
- 开头的命令
$
旨在在Ubuntu终端提示符下执行; -
onos>
用于ONOS CLI中的命令; -
mininet>
用于Mininet CLI; -
RuntimeCmd:
用于BMv2 CLI。
带有P4Runtime的控制器数据包I/O
下面的P4程序p4src/
支持在P4RuntimePacketIn
和PacketOut
消息中携带任意元数据。定义了两个特殊的标头,并使用标准P4注释进行了注释@controller_header
:
@controller_header("packet_in")
header packet_in_header_t {
port_num_t ingress_port;
bit<7> _pad;
}
@controller_header("packet_out")
header packet_out_header_t {
port_num_t egress_port;
bit<7> _pad;
}
这些标头用于携带输入包的原始交换机入口端口,并指定输出包的预期输出端口。
当Stratum中的P4Runtime代理从CPU端口接收到一个数据包时,它希望找到packet_in_header_t
标头作为帧中的第一个报头。实际上,它会查看P4Info文件的ControllerPacketMetadata
,以确定从帧开始处要剥离的位数,并填充相应的PacketIn.metadata
字段(包括本例中的入端口)。
同样,当Stratum收到P4RuntimePacketOut
消息时,它将把在PacketOut.metadata
字段中找到的值进行序列化并在PacketOut.payload
之前加上一个packet_out_header_t
,然后将其提供给管道解析器。
练习步骤
1. 修改P4程序
P4入门代码已经提供了对以下功能的支持:
- 解析
packet_out
报头(如果入端口是CPU端口) - 将
packet_in
标头发送为Deparser中的第一个报头 - 跳过处理数据包输出的入口流水线处理以,并将出口端口设置为
packet-out
标头中指定的端口 - 提供具有三元匹配字段的类ACL表,以及将数据包克隆到CPU端口的操作(用于生成数据包输入)
为了提供完整的打包支持,缺少了一个组件,您必须修改P4程序才能实现它:
- 打开
p4src/main.p4
; - 找到出口pipeline的实现(
control EgressPipeImpl
); - 在提示的地方修改代码(查找
TODO EXERCISE 1
); - 使用
make p4
命令编译修改后的P4程序。在继续操作之前,请确保解决所有编译器错误。
最后一条命令将在以下位置生成两个输出文件p4src/build
:
-
bmv2.json
:BMv2 JSON管道配置 -
p4info.txt
:protobuf文本格式的P4Info文件
此时,我们的P4 pipeline已经准备好进行测试。
2. 运行PTF测试
在启动ONOS之前,让我们通过运行一些PTF测试来确保P4的更改符合预期。但是首先,您需要对测试用例进行一些简单的修改。
打开ptf/tests/packetio.py
文件并在提示的地方进行修改(查找TODO EXERCISE 1
)。该测试文件提供了两个测试用例:一个用于输入数据包,另一个用于输出数据包。在这两个测试用例中,您都必须修改实现以对P4Runtime实体使用与在编译P4程序后获得的P4Info文件(p4src/build/p4info.txt
)中指定的名称相同的名称。
要运行此练习的所有测试:
cd ptf
make packetio
此命令将运行packetio
组中的所有测试(即ptf/tests/packetio.py
的内容)。要运行特定的测试用例,可以使用:
make <PYTHON MODULE>.<TEST CASE NAME>
例如:
make packetio.PacketOutTest
如果所有测试成功,那么恭喜!您可以转到下一步。
如何调试失败的测试?
运行PTF测试时,会产生多个文件,可用于发现错误:
-
ptf/bmv2.log
:具有跟踪级别的BMv2日志(显示匹配的表以及每个数据包的其他信息) -
ptf/ptf.pcap
:PCAP文件,其中包含在测试期间发送和接收的所有数据包(教程VM随Wireshark一起提供,以便于可视化) -
ptf/ptf.log
:所有包操作(发送和接收)的PTF日志
3. 修改ONOS管道解释器
PipelineInterpreter
是ONOS驱动程序行为,用于将数据包入/出的ONOS表示映射到与P4管道一致的映射(以及其他类似的映射)。
具体来说,要使用基于LLDP的链接发现之类的服务,ONOS内置应用程序需要能够设置数据包输出的输出端口并访问数据包输入的原始入口端口。
在下面,将提示您对PipelineInterpreter
实现进行一些简单的更改:
-
打开文件:
app/src/main/java/org/p4/p4d2/tutorial/pipeconf/InterpreterImpl.java
-
根据提示修改(查找
TODO EXERCISE 1
),详细来说是:-
查找一个名为的方法
buildPacketOut
,修改实现以使用与 P4Info文件中指定的标头相同的出口端口元数据字段名称packet_out
。 -
查找
mapInboundPacket
方法,修改实现以对P4Info文件中指定的packet_in
标头使用与入口端口元数据字段相同的名称。
-
- 使用命令构建ONOS应用(包括pipeconf)
make app-build
。
如果需要,最后一条命令将触发P4程序的构建。P4编译器的输出(bmv2.json
和p4info.txt
)在应用程序资源文件夹(app/src/main/resources
)中进行符号链接,并将收入在ONOS应用程序二进制文件中。启动连接后,ONOS应用程序中包含的副本将是ONOS部署到设备上的副本。
4. 启动ONOS
在终端窗口中,键入:
$ make onos-run
此命令将重新安装ONOS(即从以前的执行中删除所有状态)。在启动过程中,环境变量$ONOS_APPS
的值用于定义要在启动期间加载的内置应用程序。
在教程VM中,此变量为以下几个组件:
$ echo $ONOS_APPS
gui,drivers.bmv2,lldpprovider,hostprovider
请求ONOS预加载以下内置应用程序:
-
gui
:ONOS Web用户界面(即http:/localhost:181/onos/ui) -
drivers.bmv2
:基于P4Runtime、gNMI和gNOI的BMv2/Stratum驱动程序 -
lldpprovider
:基于LLDP的链路发现应用程序 -
hostprovider
:主机发现应用程序
ONOS启动后,您应该会在屏幕上看到正在打印的日志。在打印以下日志消息时,ONOS已准备就绪(应该是最后一条):
INFO [AtomixClusterStore] Updated node 127.0.0.1 state to READY
要验证是否已激活所有必需的应用程序,请在新的终端窗口中运行以下命令以访问ONOS CLI:
$ make onos-cli
在ONOS CLI中键入以下命令以显示正在运行的应用程序的列表:
onos> apps -a -s
确保您看到以下显示的应用程序列表:
* ... org.onosproject.lldpprovider ... LLDP Link Provider
* ... org.onosproject.drivers ... Default Drivers
* ... org.onosproject.protocols.grpc ... gRPC Protocol Subsystem
* ... org.onosproject.protocols.gnmi ... gNMI Protocol Subsystem
* ... org.onosproject.generaldeviceprovider ... General Device Provider
* ... org.onosproject.protocols.p4runtime ... P4Runtime Protocol Subsystem
* ... org.onosproject.p4runtime ... P4Runtime Provider
* ... org.onosproject.drivers.p4runtime ... P4Runtime Drivers
* ... org.onosproject.pipelines.basic ... Basic Pipelines
* ... org.onosproject.drivers.gnmi ... gNMI Drivers
* ... org.onosproject.protocols.gnoi ... gNOI Protocol Subsystem
* ... org.onosproject.drivers.gnoi ... gNOI Drivers
* ... org.onosproject.drivers.stratum ... Stratum Drivers
* ... org.onosproject.hostprovider ... Host Location Provider
* ... org.onosproject.gui ... ONOS Legacy GUI
* ... org.onosproject.drivers.bmv2 ... BMv2 Drivers
绝对比$ONOS_APPS
定义的应用更多。这是因为ONOS中的每个应用程序都可以将其他应用程序定义为依赖项。加载应用程序时,ONOS会自动解决依赖关系并加载所有其他必需的应用程序。
要退出ONOS CLI,请使用Ctrl-D
。这只会结束CLI进程,而不会停止ONOS进程。
发生错误时重新启动ONOS
如果出现任何问题,您需要终止/重新启动ONOS,请在启动ONOS的同一终端窗口中按Ctrl+C
(并打印日志)。另外,您也可以使用命令make reset
。要重新启动ONOS,请执行make onos-run`。
5. 加载应用程序并注册pipeconf
在第二个终端窗口中,键入:
$ make app-reload
此命令将上传到ONOS并激活先前构建的应用程序二进制文件(位于app/target/srv6-tutorial-1.0-SNAPSHOT.oar
)。
激活应用程序后,您应该在日志中看到以下消息,表明pipeconf已注册并且不同的应用程序组件已启动(在第一个运行ONOS的窗口中):
INFO [PiPipeconfManager] New pipeconf registered: org.p4.srv6-tutorial (fingerprint=...)
INFO [MainComponent] Started
另外,您可以使用ONOS CLI命令显示已注册pipeconf的列表:
onos> pipeconfs
提示:make onos-cli
用于启动ONOS CLI。
第一次启动ONOS后重新加载应用
如果同一应用程序的另一个实例正在运行,该命令make app-reload
将首先停用正在运行的实例并加载新实例。
要将新更改应用于P4程序或应用实现,请随意使用 make app-build app-reload
任意多次。该应用程序已包含在每次重新加载时从ONOS清除任何表条目和其他转发状态的逻辑。
6. 启动Mininet拓扑
在第三个终端窗口上,键入:
$ make topo
该命令将启动基于Mininet的拓扑脚本(mininet/topo.py
)。该脚本创建了前面描述的2x2结构拓扑,并启动了Mininet CLI。
作为此过程的一部分,在/tmp
中生成了一组文件。例如,可以在/tmp/bmv2-log-<switch-name>
中找到每个stratum_bmv2
实例的日志,其中交换机名称可以是leaf1
,leaf2
,spine1
,spine2
。该日志文件结合了Stratum进程(例如P4Runtime操作)和BMv2的simple_switch
消息。
如果要stratum_bmv2
实时跟踪日志更新,可以使用以下命令在屏幕上打印所有新消息:
$ bm-log leaf1
7. 将netcfg推送到ONOS
现在,ONOS和Mininet正在运行,现在该让ONOS知道如何到达这四个交换机并对其进行控制了。
在第四个终端窗口(如果要保留shell,则在第二个窗口)上,键入:
$ make netcfg
此命令将推送netcfg的JSON文件(netcfg.json
)到ONOS,以触发4个交换机的发现和配置。
您应该在日志消息中看到以下内容:
INFO [GrpcChannelControllerImpl] Creating new gRPC channel grpc://127.0.0.1:50001?device_id=1...
...
INFO [StreamClientImpl] Setting mastership on device:leaf1...
...
INFO [PipelineConfigClientImpl] Setting pipeline config for device:leaf1 to org.p4.srv6-tutorial...
...
INFO [GnmiDeviceStateSubscriber] Started gNMI subscription for 6 ports on device:leaf1
...
INFO [DeviceManager] Device device:leaf1 port [leaf1-eth1](1) status changed (enabled=true)
INFO [DeviceManager] Device device:leaf1 port [leaf1-eth2](2) status changed (enabled=true)
INFO [DeviceManager] Device device:leaf1 port [leaf1-eth3](3) status changed (enabled=true)
INFO [DeviceManager] Device device:leaf1 port [leaf1-eth4](4) status changed (enabled=true)
INFO [DeviceManager] Device device:leaf1 port [leaf1-eth5](5) status changed (enabled=true)
INFO [DeviceManager] Device device:leaf1 port [leaf1-eth6](6) status changed (enabled=true)
8. 使用ONOS CLI验证网络配置
使用make onos-cli
访问ONOS CLI。输入以下命令以验证之前推送的网络配置:
onos> netcfg
设备
验证是否已找到并连接了所有4个设备:
onos> devices -s
id=device:leaf1, available=true, role=MASTER, type=SWITCH, driver=stratum-bmv2:org.p4.srv6-tutorial
id=device:leaf2, available=true, role=MASTER, type=SWITCH, driver=stratum-bmv2:org.p4.srv6-tutorial
id=device:spine1, available=true, role=MASTER, type=SWITCH, driver=stratum-bmv2:org.p4.srv6-tutorial
id=device:spine2, available=true, role=MASTER, type=SWITCH, driver=stratum-bmv2:org.p4.srv6-tutorial
确保所有设备的available=true
。
接口
验证是否像netcfg文件中一样在ONOS中配置了6个接口,为leaf1
配置4个接口,为leaf2
配置2个接口,每个接口都分配了一个IPv6地址:
onos> interfaces
leaf1-3: port=device:leaf1/3 ips=[2001:1:1::ff/64]
leaf1-4: port=device:leaf1/4 ips=[2001:1:1::ff/64]
leaf1-5: port=device:leaf1/5 ips=[2001:1:1::ff/64]
leaf1-6: port=device:leaf1/6 ips=[2001:1:2::ff/64]
leaf2-3: port=device:leaf2/3 ips=[2001:2:3::ff/64]
leaf2-4: port=device:leaf2/4 ips=[2001:2:4::ff/64]
此IPv6地址配置将在以后用于提供路由功能。
链接
验证是否已找到所有链接。您应该总共看到8个链接:
onos> links
src=device:leaf1/1, dst=device:spine1/1, type=DIRECT, state=ACTIVE, expected=false
src=device:leaf1/2, dst=device:spine2/1, type=DIRECT, state=ACTIVE, expected=false
src=device:leaf2/1, dst=device:spine1/2, type=DIRECT, state=ACTIVE, expected=false
src=device:leaf2/2, dst=device:spine2/2, type=DIRECT, state=ACTIVE, expected=false
src=device:spine1/1, dst=device:leaf1/1, type=DIRECT, state=ACTIVE, expected=false
src=device:spine1/2, dst=device:leaf2/1, type=DIRECT, state=ACTIVE, expected=false
src=device:spine2/1, dst=device:leaf1/2, type=DIRECT, state=ACTIVE, expected=false
src=device:spine2/2, dst=device:leaf2/2, type=DIRECT, state=ACTIVE, expected=false
如果您没有看到任何链接,请检查ONOS日志中检查是否有进出报文处理错误。如果发生错误,则可能是您未将InterpreterImpl.java
正确修改。在这种情况下,请关闭ONOS,然后重新执行步骤3。
注意:从理论上讲,无需关闭并重新启动ONOS。但尽管ONOS支持使用修改后的版本(例如,更新的bmv2.json
和p4info.txt
)重新加载pipeconf,但本教程中使用的ONOS版本(2.1.0,是撰写本文时的最新版本)不支持重新加载pipeconf行为类,在这种情况下,仍将使用旧类。因此,要重新加载InterpreterImpl.java
的修改版本,您需要先关闭ONOS。
流规则(Flow rules)和组(groups)
验证流规则,您应该看到每个设备的5条流规则。例如,显示到目前为止在设备leaf1
上安装的所有流规则:
onos> flows -s any device:leaf1
deviceId=device:leaf1, flowRuleCount=5
ADDED, ..., table=IngressPipeImpl.acl, priority=40000, selector=[ETH_TYPE:ipv6, IP_PROTO:58, ICMPV6_TYPE:136], treatment=[immediate=[IngressPipeImpl.clone_to_cpu()]]
ADDED, ..., table=IngressPipeImpl.acl, priority=40000, selector=[ETH_TYPE:arp], treatment=[immediate=[IngressPipeImpl.clone_to_cpu()]]
ADDED, ..., table=IngressPipeImpl.acl, priority=40000, selector=[ETH_TYPE:ipv6, IP_PROTO:58, ICMPV6_TYPE:135], treatment=[immediate=[IngressPipeImpl.clone_to_cpu()]]
ADDED, ..., table=IngressPipeImpl.acl, priority=40000, selector=[ETH_TYPE:lldp], treatment=[immediate=[IngressPipeImpl.clone_to_cpu()]]
ADDED, ..., table=IngressPipeImpl.acl, priority=40000, selector=[ETH_TYPE:bddp], treatment=[immediate=[IngressPipeImpl.clone_to_cpu()]]
这些流规则(flow rules)是hostprovider
和lldpprovider
应用为每个设备自动生成的流目标(flow objectives)转换的结果。
hostprovider
应用程序通过嗅探克隆到控制器的ARP(selector=[ETH_TYPE:arp]
)和NDP数据包(selector=[ETH_TYPE:ipv6, IP_PROTO:58, ICMPV6_TYPE:...]
)来提供主机发现功能treatment=[immediate=[IngressPipeImpl.clone_to_cpu()]]
。类似地,lldpprovider
产生流目标(flow objectives),以嗅探在所有设备端口上定期作为P4Runtime数据包输出发出的LLDP和BBDP数据包(selector=[ETH_TYPE:lldp]
和selector=[ETH_TYPE:bbdp]
),从而实现自动链路发现。
流目标(Flow objectives)由pipeconf转换为流规则(flow rules)和组(groups),pipeconf在(PipelinerImpl.java)提供了Pipeliner
的行为实现。此外,这些流规则(flow rules)使用ETH_TYPE
,ICMPV6_TYPE
等ONOS标准/已知报头字段(或使用ONOS术语的“Criteria”)指定匹配键。这些类型通过在InterpreterImpl.java之前修改的同一管道interpreter映射到P4程序/ P4Info进行特定字段的匹配(查找方法mapCriterionType
)
流目标由pipeconf转换为流规则和组,pipeconf提供了Pipeliner行为实现(PipelinerImpl.java)。此外,这些流规则使用ETH_TYPE,ICMPV6_TYPE等ONOS标准/已知标头字段(或使用ONOS术语的“准则”)指定匹配键。
要显示到目前为止已安装的所有组,可以使用groups
命令。例如,要显示leaf1
的组:
sdn@root > groups any device:leaf1
deviceId=device:leaf1, groupCount=1
id=0x63, state=ADDED, type=CLONE, ..., appId=org.onosproject.core, referenceCount=0
id=0x63, bucket=1, ..., weight=-1, actions=[OUTPUT:CONTROLLER]
在这种情况下,您应该只会看到一组类型为CLONE
的组,该组用于将数据包克隆到控制器(或使用数据平面术语到CPU的数据包)。 CLONE
组是ONOS北向抽象,等同于P4Runtime的数据包复制引擎(PRE)CloneSessionEntry
。
9. 用ONOS Web UI进行拓扑可视化
从教程VM中打开浏览器(例如Firefox),前往http://127.0.0.1:8181/onos/ui。访问时,请使用用户名onos
和密码rocks
。
在这里,您可以随时与之互动并发现ONOS UI。有关如何使用ONOS网络用户界面的更多信息,请参阅以下指南:
https://wiki.onosproject.org/x/OYMg
要显示或隐藏开关标签,请按L
键。
恭喜你!
您已完成第一个练习。您可以移至下一个,或查看以下bonus任务。
Bonus:检查BMv2内部状态
您可以使用BMv2 CLI转储ONOS当前在交换机上安装的所有表条目。在单独的终端窗口上键入:
$ bm-cli leaf1
此命令(在教程VM中定义为bash别名)将启动Mininet中名称为“ leaf1”的BMv2交换机的CLI。
在BMv2 CLI提示符下,键入以下命令:
RuntimeCmd: table_dump IngressPipeImpl.acl
您应该看到恰好5个条目,每个条目对应于ONOS中的流规则(flow rule)。例如,在BMv2 CLI中,与ARP数据包匹配的流规则(flow rule)应如下所示:
Dumping entry 0x4
Match key:
* standard_metadata.ingress_port : TERNARY 0000 &&& 0000
* ethernet.dst_addr : TERNARY 000000000000 &&& 000000000000
* ethernet.src_addr : TERNARY 000000000000 &&& 000000000000
* ethernet.ether_type : TERNARY 0806 &&& ffff
* scalars.local_metadata_t.ip_proto : TERNARY 00 &&& 00
* scalars.local_metadata_t.icmp_type : TERNARY 00 &&& 00
* scalars.local_metadata_t.l4_src_port: TERNARY 0000 &&& 0000
* scalars.local_metadata_t.l4_dst_port: TERNARY 0000 &&& 0000
Priority: 2147443646
Action entry: IngressPipeImpl.clone_to_cpu -
注意这里是如何将ONOS选择器[ETH_TYPE:arp]
转换为与BMv2特定的头字段ethernet.ether_type
(P4程序和P4Info中的ethernet.ether_type
)匹配的条目,同时设置所有其他字段的位如“无关”(掩码全为零)。
同样,您可以使用mc_dump
命令显示BMv2多播引擎的状态,该引擎用于实现数据包复制功能(例如克隆会话)。在这种情况下,对于CPU克隆会话,您应该只看到一个条目:
RuntimeCmd: mc_dump
==========
MC ENTRIES
**********
mgrp(32867)
-> (L1h=0, rid=1) -> (ports=[255], lags=[])
==========
...
stratum_bmv2
在Mininet(mininet / topo.py)中运行时,以及在ONOS(AppConstants.java)中写入克隆会话时,设置的CPU端口为255。
BMv2 CLI是调试BMv2交换机状态的强大工具。键入help
以显示可使用的命令列表。按下tab
键时,此CLI还提供自动完成功能。
警告:BMv2 CLI使用simple_switch
公开的Thrift服务器。因为Thrift服务器的功能与Stratum提供的gRPC/P4Runtime的功能重叠(例如,两者都公开了表管理API),所以在使用两者将状态写入交换机时可能会出现不一致的问题。因此,我们建议仅使用此CLI读取状态。