练习4:IPv6 Segment Routing(SRv6)
说明:本文翻译自onos-p4-tutorial的Exercise 4: Segment Routing v6 (SRv6),用于理解纪录SRv6的实现
在本练习中,您将实现segment routing的简化版本,这是一种引导流量通过一组指定的节点的源路由方法。
此练习基于名为SRv6的IETF规范草案,该规范使用IPv6数据包来构架遵循SRv6策略的流量。SRv6数据包使用IPv6路由标头,它们可以完全封装IPv6(或IPv4)数据包,也可以仅将IPv6路由标头注入现有的IPv6数据包中。
IPv6路由header如下所示:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Next Header | Hdr Ext Len | Routing Type | Segments Left |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Last Entry | Flags | Tag |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Segment List[0] (128 bits IPv6 address) |
| |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| |
...
| |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Segment List[n] (128 bits IPv6 address) |
| |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Next Header字段是下一个IPv6报头或有效载荷的类型。
对于SRv6,Routing Type为4。
Segments Left指向Segment列表中当前段的索引。在格式正确的SRv6数据包中,IPv6目标地址等于Segment List[Segments Left]
。在我们的练习中原始IPv6地址应该Segment List[0]
,以便最终将流量路由到正确的目的地。
Last Entry是段列表中最后一个条目的索引。
注意:这意味着它的索引应该比列表的长度小一。(在上面的示例中,列表是n+1
条目,最后一个条目应该是n
。)
最后,Segment List是要在特定SRv6策略中遍历的IPv6地址的反向排序列表。列表中的最后一个条目是SRv6策略中的第一个Segment。该列表通常不会中途改变;整个标头作为一个整体插入或删除。
为简单起见,由于我们已经在使用IPv6,因此您的解决方案将只是将路由header添加到现有的IPv6数据包中。(尽管规范允许并且我们有有效的用例,但我们不会使用SRv6将整个数据包嵌入新的IPv6数据包内的策略。)
您可能已经注意到,SRv6使用IPv6地址来标识policy中的Segment。地址的格式与IPv6相同,但是地址空间通常不同于交换机内部IPv6地址所使用的空间。地址的格式也不同。典型的IPv6单播地址分为网络前缀和主机标识符,子网掩码用于划分两者之间的边界。典型的SRv6段标识符(SID)为定位符、函数标识符和可选的函数参数。定位器必须是可路由的,这使启用SRv6的节点和不知道节点的节点都可以参与转发。
提示:由于具有可选参数,因此,与精确匹配相比,首选128位SID上的最长前缀匹配。
网段路由网络中有三种感兴趣的节点类型:
- Source Node-注入SRv6策略的节点(主机或交换机)。
- Transit Node-转发SRv6数据包但不是流量目标的节点
- Endpoint Node-SRv6策略中的参与航点,它将修改SRv6标头并执行指定的功能
在我们的实现中,我们将这些类型简化为两个角色:
-
Endpoint Node — 对于到交换机SID的流量,请更新SRv6标头(Left Segment减少),将IPv6目标地址设置为下一个段,然后转发数据包(“End”行为)。为简单起见,我们将始终删除策略中倒数第二个分段上的SRv6 header(在规范中称为倒数第二个分段弹出或PSP)。
-
Transit Node — 默认情况下,如果流量的目的地不是交换机的IP地址或其SID,则正常转发流量(“T”行为)。允许控制平面添加规则,以针对目的地为特定IPv6地址的流量注入SRv6策略(“T.Insert”行为)。
有关更多详细信息,您可以在此处阅读规范草案:https://tools.ietf.org/id/draft-filsfils-spring-srv6-network-programming-06.html
练习步骤
步骤1.为SRv6添加表
我们已经定义了SRv6 header,并在header.p4
和中parser.p4
分别包含了解析标头的逻辑。
下一步是为main.p4
上面指定的两个角色的每一个添加两个表。除了这些表之外,您还需要为endpoint节点表(否则称为“My SID”表)编写操作。我们提供了t_insert
长度为2和3的策略操作,这些操作足以使您入门。
完成此操作后,您将需要在本apply
节底部的块中应用这些表EgressPipeImpl
。在检查L2目标地址是否与交换机的地址匹配之后,并且在应用L3表之前,您将希望应用这些表(因为在应用SRv6策略之后,您将希望使用相同的路由条目来转发流量)。您还可以将PSP行为作为apply
逻辑的一部分应用,因为我们是倒数第二个SID,我们将始终应用它。
步骤2. 使用Packet Test Framework(PTF)测试pipeline
在本练习中,您将修改srv6.py中的测试以验证管道的SRv6行为。
在以下四个测试中srv6.py
:
-
Srv6InsertTest:测试SRv6插入行为,其中交换机接收IPv6数据包并插入SRv6标头。
-
Srv6TransitTest:测试SRv6传输行为,其中交换机将忽略SRv6标头并正常路由数据包,而不应用任何与SRv6相关的修改。
-
Srv6EndTest:测试SRv6结束行为(不弹出),其中交换机将数据包转发到SRv6标头中找到的下一个SID。
-
Srv6EndPspTest:测试SRv6末端是否出现倒数第二段弹出(PSP)行为,其中交换机SID是SID列表中的倒数第二个,并且交换机在将数据包路由到其最终目的地(列表中的最后一个SID)之前先删除SRv6标头。
您应该能够在srv6.py中找到一些TODO EXERCISE 4
的提示。
完成所有TODO之后,我们应该可以运行测试并查看以下消息:
sdn@onos-p4-tutorial:~/tutorial/ptf$ make srv6
... Skip ...
srv6.Srv6EndTest ... tcpv6 3 SIDs ... udpv6 3 SIDs ... icmpv6 3 SIDs ... tcpv6 4 SIDs ... udpv6 4 SIDs ... icmpv6 4 SIDs ... ok
----------------------------------------------------------------------
Ran 1 test in 0.076s
OK
srv6.Srv6TransitTest ... tcpv6 3 SIDs ... udpv6 3 SIDs ... icmpv6 3 SIDs ... tcpv6 2 SIDs ... udpv6 2 SIDs ... icmpv6 2 SIDs ... ok
----------------------------------------------------------------------
Ran 1 test in 0.067s
OK
srv6.Srv6EndPspTest ... tcpv6 2 SIDs ... udpv6 2 SIDs ... icmpv6 2 SIDs ... ok
----------------------------------------------------------------------
Ran 1 test in 0.042s
OK
srv6.Srv6InsertTest ... tcpv6 3 SIDs ... udpv6 3 SIDs ... icmpv6 3 SIDs ... tcpv6 2 SIDs ... udpv6 2 SIDs ... icmpv6 2 SIDs ... ok
----------------------------------------------------------------------
Ran 1 test in 0.067s
OK
回溯检查
至此,我们的P4程序应该已经完成。我们可以通过运行ptf
目录中的所有测试来确保我们没有破坏之前的练习中的任何内容:
$ make test
现在已经展示了我们可以安装基本规则并使用BMv2传递SRv6报文。
步骤3:构建ONOS应用
对于ONOS应用程序,您将需要在Srv6Component.java
中通过以下方式进行更新:
-
完成
setUpMySidTable
方法,该方法将在My SID表中插入与指定设备的SID匹配的条目并执行end
操作。每当连接新设备时,都会调用此函数。 -
完成
insertSrv6InsertRule
函数,该函数将为提供的SRv6策略创建一个t_insert
规则。srv6-insert
CLI命令调用此函数。 -
完成由
srv6-clear
CLI命令调用的clearSrv6InsertRules
。
完成后,您应该重建并重新加载您的应用程序。这还将重建并重新发布对P4代码和ONOS pipeconf的所有更改。不要忘记在文件顶部启用Srv6Component。与之前的练习一样,您可以使用以下命令来生成并重新加载应用程序:
$ make app-build app-reload
步骤4:插入SRv6策略
下一步是显示可以使用SRv6策略控制流量。
您应该在h2
和h4
之间执行ping操作:
mininet> h2 ping h4
使用ONOS UI,您可以观察到ping数据包正在使用哪些路径。
- 按
a
直到看到“端口统计(数据包/秒)” - 按下
l
以显示设备标签

一旦确定了数据包被散列到哪些spine(请求和回复都采用不同的路径),就应该插入一组SRv6策略,该策略通过另一个spine(或你选择的spine)发送ping数据包。
要添加新的SRv6策略,应使用以下srv6-insert
命令。
onos> srv6-insert <device ID> <segment list>
注意:在我们的拓扑中,spine1的SID为3:201:2::
,spine2的SID为3:202:2::
。
例如,要添加通过spine1和leaf2在h2和h4之间转发流量的策略,可以使用以下命令:
- 将SRv6策略从h2插入到leaf1上的h4中(通过spine1和leaf2)
onos> srv6-insert device:leaf1 3:201:2:: 3:102:2:: 2001:1:4::1
Installing path on device device:leaf1: 3:201:2::, 3:102:2::, 2001:1:4::1
- 将SRv6策略从h4插入到leaf2上的h2中(通过spine1和leaf1)
onos> srv6-insert device:leaf2 3:201:2:: 3:101:2:: 2001:1:2::1
Installing path on device device:leaf2: 3:201:2::, 3:101:2::, 2001:1:2::1
这些命令将在流量上匹配到指定设备上的最后一个网段(例如,在leaf1
上匹配2001:1:4::1
)。您可以更新命令以允许使用更具体的匹配条件作为额外工作。
您可以使用以下变体确认您的规则已添加:
(提示:请确保更新tableId以使其与P4程序中的表ID匹配。)
onos> flows any device:leaf1 | grep tableId=IngressPipeImpl.srv6_transit
id=c000006d73f05e, state=ADDED, bytes=0, packets=0, duration=871, liveType=UNKNOWN, priority=10,
tableId=IngressPipeImpl.srv6_transit,
appId=org.p4.srv6-tutorial,
selector=[hdr.ipv6.dst_addr=0x20010001000400000000000000000001/128],
treatment=DefaultTrafficTreatment{immediate=[
IngressPipeImpl.srv6_t_insert_3(
s3=0x20010001000400000000000000000001,
s1=0x30201000200000000000000000000,
s2=0x30102000200000000000000000000)],
deferred=[], transition=None, meter=[], cleared=false, StatTrigger=null, metadata=null}
现在,您应该返回到ONOS UI,以确认流量正在通过指定的主干。

调试和清理
如果需要删除SRv6策略,则可以使用srv6-clear
命令从特定设备清除所有SRv6策略。例如,要从leaf1
清除流,请使用以下命令:
onos> srv6-clear device:leaf1
要验证设备是否插入了正确的SRv6标头,可以使用Wireshark从每个设备端口捕获数据包。
例如,如果要从spine1的端口1捕获数据包,请从接口spine1-eth1
捕获数据包。
注意:spine1-eth1
连接到leaf1,spine1-eth2
连接到leaf2;两个spine遵循相同的模式。
网友评论