练习4-修改代码以启用IPv6路由
说明:本教程翻译自Next-Gen SDN Tutorial的Exercise 4 - Modify code to enable IPv6 routing,自用学习Docker-P4-ONOS
在本练习中,系统将要求您将更改应用于P4代码以及ONOS应用,以为不同子网中的主机启用IPv6路由。
实际上,我们的结构控制平面(即ONOS应用程序)假定给定子网的主机都连接到相同的叶子,并且两个不同叶子的两个接口不能配置相同的IPv6子网。换句话说,仅对连接到同一叶的主机允许L2桥接。为了能够ping通连接到不同叶子的不同主机,我们需要在数据平面中支持IPv6路由。
本教程中使用的Mininet脚本topo.py定义了4个子网:
-
2001:1:1::/64
用3台主机连接到leaf1
(h1a
,h1b
,和h1c
) -
2001:1:2::/64
1台主机连接到leaf1
(h2
) -
2001:2:3::/64
1台主机连接到leaf2
(h3
) -
2001:2:4::/64
1台主机连接到leaf2
(h4
)
相同的IPv6前缀在netcfg.json文件中定义,并用于向交换机接口提供IPv6地址,即从主机角度来看的网关地址。我们的ONOS应用程序的目标是对交换机进行编程,使其表现得像传统的IPv6路由器。
不幸的是,我们提供给您的入门代码对IPv6路由的实现有误。您的任务是修复它。
为什么路由不起作用?
要找出原因,请使用Mininet CLI(make mn-cli
)。ONOS和Mininet应该从上一个练习开始运行。如果不是这种情况,请返回练习3的步骤2。
让我们从ping通同一子网中的两个主机开始,例如h1a
和h1b
:
mininet> h1a ping h1b
PING 2001:1:1::b(2001:1:1::b) 56 data bytes
64 bytes from 2001:1:1::b: icmp_seq=1 ttl=64 time=1068 ms
64 bytes from 2001:1:1::b: icmp_seq=2 ttl=64 time=5.38 ms
64 bytes from 2001:1:1::b: icmp_seq=3 ttl=64 time=1.75 ms
...
对同一子网中的主机执行Ping操作。让我们尝试ping通不同子网中的主机,例如h2
(IPv6地址为2001:1:2::1/64
,位于2001:1:2::
网络)和h1a
(2001:1:1::a/64
,位于2001:1:1::
网络):
mininet> h2 ping h1a
PING 2001:1:1::a(2001:1:1::a) 56 data bytes
From 2001:1:2::1 icmp_seq=1 Destination unreachable: Address unreachable
From 2001:1:2::1 icmp_seq=2 Destination unreachable: Address unreachable
From 2001:1:2::1 icmp_seq=3 Destination unreachable: Address unreachable
...
Ping不起作用。要找出原因并调试问题,让我们暂时让ping运行。
检查流规则和组
首先要做的是检查leaf1
(两个主机都连接在哪里)具有所有必需的流规则和组,以在h2
和h1a
之间路由数据包。我们应该在中看到相关条目routing_v6_table
。
打开ONOS CLI(make onos-cli
)并转储以下条目routing_v6_table
:
onos> flows -s any device:leaf1 | grep routing_v6_table
验证是否存在以下条目:
ADDED, bytes=0, packets=0, table=IngressPipeImpl.routing_v6_table, priority=10, selector=[hdr.ipv6.dst_addr=0x2001000100010000000000000000000a/128], treatment=[immediate=[GROUP:0x1a]]
...
ADDED, bytes=0, packets=0, table=IngressPipeImpl.routing_v6_table, priority=10, selector=[hdr.ipv6.dst_addr=0x20010001000200000000000000000001/128], treatment=[immediate=[GROUP:0x20]]
需要使用这些条目来路由具有目标IPv6地址h2
(第一个条目)和h1a
(第二个条目之一)的数据包。两个条目都有字节和数据包计数器,其值为0,这是可疑的,因为routing_v6_table
P4程序中将其定义为具有计数器,并且ONOS定期读取这些计数器。似乎没有数据包与这些条目匹配。可能是什么问题呢?
在h2的接口上dump数据包
虽然之间的中国平安h2
和h1a
运行,让我们用tcpdump
来dump发送和接收的所有数据包h2
。
在新的终端窗口上,键入以下命令:
util/mn-cmd h2 tcpdump -i h2-eth0 -n
mn-cmd
实用程序可用于在Mininet主机的网络名称空间内运行任意命令。在这里,我们使用它在h2
上启动tcpdump
,指定将数据包转储出接口h2-eth0
(h2
中的唯一接口)。-n
用于避免在数据包地址上进行DNS查找。
屏幕上应该显示路由不起作用的原因。
...
21:05:13.653869 IP6 2001:1:2::1 > ff02::1:ff00:ff: ICMP6, neighbor solicitation, who has 2001:1:2::ff, length 32
21:05:14.678054 IP6 2001:1:2::1 > ff02::1:ff00:ff: ICMP6, neighbor solicitation, who has 2001:1:2::ff, length 32
21:05:15.701769 IP6 2001:1:2::1 > ff02::1:ff00:ff: ICMP6, neighbor solicitation, who has 2001:1:2::ff, length 32
...
2001:1:2::ff
是h2
中配置的IPv6网关。为了对h1a
进行ping操作,在这种情况下,h2
需要将数据包发送到其网关2001:1:2::ff
,但它无法获得相应网关的MAC地址。
如果你看一下netcfg.json文件,你会发现,2001:1:2::ff
关联到leaf1
的6
端口的IPv6地址。
之前我们说过,我们的结构应该像传统的IPv6路由器一样工作,因此它应该能够回复连接的主机发送的邻居请求(NS)数据包,并生成带有交换机MAC地址(myStationMac
)的邻居广告(NA) 。不幸的是,对于交换机接口IPv6地址的NDP处理的实现是不完整的。您将需要修改P4程序以及ONOS应用才能使其正常运行。
为交换机接口IPv6地址实现NDP处理
我们已经提供了处理由连接到同一子网的主机交换的NDP NS和NA的方法(请参阅参考资料l2_ternary_table
)。对于主机,Linux网络堆栈负责生成NDP NA答复。但是对于我们架构中的交换机,实在没有能用的Linux网络堆栈。
有多种解决此问题的方法:
- 我们可以为主机配置静态NDP条目,而无需交换机回复NDP NS数据包;
- 我们可以通过输入包拦截NDP NS,在ONOS中生成相应的NDP NA答复,然后通过输出包将其发送回去。还可以
- 我们可以指示交换机使用P4生成NDP NA答复。也就是说,我们可以编写P4代码来处理对NDP请求的答复,而无需控制平面的任何干预。
我们选择实现最后一个选项,以显示一个有趣的P4用例。
这个想法很简单,NDP NA数据包具有与NDP NS数据包相同的标头结构。它们都是具有不同报头字段值的ICMPv6数据包,例如不同的ICMPv6类型,不同的以太网地址等。知道NDP NS请求中找到的给定IPv6目标地址的MAC地址的交换机可以将同一数据包转换为NDP NA通过修改其某些字段来回复。
练习步骤
在下面,您将被要求:
- 修改P4程序以启用交换机内NDP NA生成;
- 运行PTF测试,以确保您的更改能够按预期进行,并避免出现退化;
- 修改ONOS应用程序以控制P4程序的NDP生成功能;
- 在Mininet上测试连接性。
1.修改P4程序
在p4src/main.p4的入口管道实现中,我们已经提供了一个名为ndp_ns_to_na
的将NDP NS数据包转换为NDP NA数据包的动作。您的任务是实现使用此类操作的表。
该表应定义netcfg.json中提供接口的IPv6地址和myStationMac
与每个交换机关联的接口IPv6地址(也在netcfg.json中定义)之间的映射。当收到一个NDP NS数据包,要求解析此类IPv6地址之一时,ndp_ns_to_na
应使用给定的myStationMac
作为参数调用该操作。ONOS应用程序将负责根据netcfg.json的内容在此表中插入条目。
- 开放
p4src/main.p4
; - 寻找入口管道(
control IngressPipeImpl
)的实现; - 在要求的地方修改代码(查找
TODO EXERCISE 4
); - 使用该
make p4-build
命令编译修改后的P4程序。在继续操作之前,请确保解决所有编译器错误。
2.运行PTF测试
在继续之前,让我们通过运行一些PTF测试来确保P4更改能够按预期进行。我们提供了一个用于NDP生成的测试用例,以及其他一些基本功能(例如,包输入/输出,桥接,路由等)的测试用例。运行PTF测试将为您的更改按预期工作提供一定的保证,而不会破坏其他部分。
在运行测试之前,您需要对测试用例实现进行一些简单的更改。
打开文件ptf/tests/ndp.py
并在需要的地方进行修改(查找TODO EXERCISE 4
)。该测试文件仅定义一个执行以下操作的测试用例:
- 在刚创建的表中插入一个条目;
- 向交换机发送NDP NS报文;
- 验证交换机是否发回正确填充的NDP NA。
您将需要通过填写刚刚创建的表的名称来修改测试用例。
要仅运行NDP测试用例:
make p4-test TEST=ndp
为确保新更改不会破坏其他功能,应使用以下命令运行所有测试:
make p4-test
如果所有测试成功,那么恭喜!您可以转到下一步。除此以外...
如何调试失败的测试?
运行PTF测试时,会产生多个文件,可用于发现错误:
-
ptf/bmv2.log
:具有跟踪级别的BMv2日志(显示匹配的表以及每个数据包的其他信息) -
ptf/ptf.pcap
:包含测试期间发送和接收的所有数据包的PCAP文件(您可能需要安装Wireshark以便更直观地查看) -
ptf/ptf.log
:所有包操作(发送和接收)的PTF日志
3.修改ONOS应用
应用程序启动程序代码已经提供了一个组件,该组件负责控制P4程序中的NDP生成功能。该组件通过侦听设备事件来工作。当ONOS连接到新交换机时,它将查看网络配置(netcfg.json)以获取所有接口IPv6地址myStationMac
,然后使用,该信息填充您刚刚创建的表。
在使用此组件之前,您需要进行一些修改才能使其与P4程序一起使用。例如,您需要提供刚创建的P4表的名称。
打开文件:
app/src/main/java/org/onosproject/ngsdn/tutorial/NdpReplyComponent.java
根据需要修改(查找
TODO EXERCISE 4
)使用命令构建ONOS应用(包括pipeconf)
make app-build
。
4.生成并重新加载应用
在ONOS运行时,使用以下命令重新加载您的应用程序:
$ make app-reload
第一次后重新加载应用
在构建应用程序时,修改后的P4编译器输出(bmv2.json
和 p4info.txt
)将与Java类一起打包在一起。如果同一应用程序的另一个实例正在运行,该命令make app-reload
将首先停用正在运行的实例并加载新实例。
要将更改应用于应用实现,请随意使用任意make app-build app-reload
多次。该应用程序已包含在每次重新加载时从ONOS清除任何表条目和其他转发状态的逻辑。
验证ONOS日志
重新加载应用程序后,您应该看到消息,表明已设置了新的管道配置并NdpReplyComponent
已将其激活:
INFO [PiPipeconfManager] Unregistered pipeconf: org.onosproject.ngsdn-tutorial (fingerprint=...)
INFO [PipeconfLoader] Found 1 outdated drivers for pipeconf 'org.onosproject.ngsdn-tutorial', removing...
INFO [PiPipeconfManager] New pipeconf registered: org.onosproject.ngsdn-tutorial (fingerprint=...)
INFO [PipelineConfigClientImpl] Setting pipeline config for device:leaf1 to org.onosproject.ngsdn-tutorial...
...
INFO [MainComponent] Waiting to remove flows and groups from previous execution of org.onosproject.ngsdn-tutorial..
...
INFO [MainComponent] Started
INFO [NdpReplyComponent] Started
...
INFO [NdpReplyComponent] *** NDP REPLY - Starting Initial set up for device:leaf1...
INFO [NdpReplyComponent] Adding rules to device:leaf1 to generate NDP NA for 4 IPv6 interfaces...
INFO [NdpReplyComponent] *** NDP REPLY - Starting Initial set up for device:spine1...
INFO [NdpReplyComponent] device:spine1 does not have any IPv6 interface configured
INFO [NdpReplyComponent] *** NDP REPLY - Starting Initial set up for device:spine2...
INFO [NdpReplyComponent] device:spine2 does not have any IPv6 interface configured
INFO [NdpReplyComponent] *** NDP REPLY - Starting Initial set up for device:leaf2...
INFO [NdpReplyComponent] Adding rules to device:leaf2 to generate NDP NA for 2 IPv6 interfaces...
...
了解ONOS错误日志
在Mininet中尝试解决方案之前,值得查看一下ONOS日志中可能的错误。重新加载应用程序时可能会看到两种主要类型的错误:
-
写入错误,例如删除不存在的实体或插入已经存在的实体:
WARN [WriteResponseImpl] Unable to DELETE PRE entry on device...: NOT_FOUND Multicast group does not exist ... WARN [WriteResponseImpl] Unable to INSERT table entry on device...: ALREADY_EXIST Match entry exists, use MODIFY if you wish to change action ...
这些通常是暂时性错误,您不必担心它们。它们描述了ONOS内部设备状态的暂时不一致,应通过定期对帐机制尽快恢复这种状态。ONOS核心会定期轮询设备状态,以确保其内部表示准确无误,同时将任何未决的修改写入设备,以解决这些错误。
否则,如果您看到它们定期出现(每3-4秒一次),则表示对帐过程不起作用,并且其他地方出了问题。尝试重新加载应用程序(
make app-reload
);如果仍不能解决警告,请与教员联系。 -
转换错误,表示ONOS无法将应用程序生成的流规则(或组)转换为与P4Info兼容的表示形式。例如:
WARN [P4RuntimeFlowRuleProgrammable] Unable to translate flow rule for pipeconf 'org.onosproject.ngsdn-tutorial':...
请仔细阅读错误消息并根据需要对应用进行更改。可能是您使用的表,匹配字段或操作名称不存在于P4Info中。检查您的P4Info文件,修改并重新加载应用程序(
make app-build app-reload
)。
5.在Mininet上测试IPv6路由
验证ping
如果您在运行h2
和h1a
之间留下了ping命令,请检查该终端窗口是否可以正常工作!
为了验证ping也适用于连接到不同叶子的主机,让我们依次使用以下命令在h2
和h3
之间开始新的ping:
mininet> h2 ping h3
mininet> h3 ping h2
PING 2001:1:2::1(2001:1:2::1) 56 data bytes
64 bytes from 2001:1:2::1: icmp_seq=2 ttl=61 time=2.39 ms
64 bytes from 2001:1:2::1: icmp_seq=3 ttl=61 time=2.29 ms
64 bytes from 2001:1:2::1: icmp_seq=4 ttl=61 time=2.71 ms
...
在h3
和h2
之间执行ping操作有效。
注意:我们需要先从h2
开始ping,然后再从h3
开始ping,以便ONOS在转发ping数据包之前发现两个主机的位置。这是因为当前的实现要求主机生成ONDP会发现的NDP NS数据包。为了避免必须手动生成NDP NS消息,可能的解决方案是:
在Mininet中配置IPv6主机,以定期自动生成另一种NDP消息,称为路由器请求(RS)。
在ACL表中插入流规则,将NDP RS数据包克隆到CPU。这将需要匹配NDP NA和NS以外的ICMPv6代码的其他值。
修改
hostprovider
内置的应用程序实现,以从NDP RS消息中了解主机位置(当前仅使用NDP NA和NS)。
验证基于P4的NDP NA生成
要验证交换机基于P4的NDP NA生成是否正常工作,可以检查h2
或的邻居表h3
,它应该显示类似以下内容:
mininet> h2 ip -6 n
2001:2:3::ff dev h2-eth0 lladdr 00:aa:00:00:00:02 router REACHABLE
凡2001:2:3::ff
在规定的IPv6网关地址netcfg.json
和topo.py
,和00:aa:00:00:00:02
是myStationMac
用于定义leaf2
中netcfg.json
。
6.使用ONOS Web UI可视化ECMP
为了验证ECMP是否正常工作,让我们开始从h2
到h3
使用iperf的多个并行流量。在Mininet命令提示符下,键入:
mininet> h2 iperf -c h3 -u -V -P5 -b1M -t600 -i1
此命令将在h2
上启动一个iperf
客户端,通过IPv6(-V
)将UDP数据包(-u
)发送到h3(-c
)。为此,我们生成5个不同的流(-P5
),每个流的上限为1Mbit/s(-b1M
),运行10分钟(-t600
),并每1秒报告一次统计信息(-i1
)。
由于我们正在生成UDP流量,因此无需在上启动iperf服务器h3
。
要可视化流量,请从教程VM(例如Firefox)中打开浏览器到http://127.0.0.1:8181/onos/ui。询问时,请使用用户名onos
和密码rocks
。在显示ONOS拓扑视图的同一页面上:
- 按
H
键盘上的显示主机; - 按下
L
以显示设备标签; - 多次按
A
,直到看到端口/链接统计信息,以包/秒(pps)或位/秒为单位。
ECMP应该能按预期工作,因此您应该看到流量转发到两个通道,如下面的屏幕截图所示:
恭喜你!
您已经完成了这个练习!现在,您的结构可以在任何主机之间转发IPv6流量。