在tofino数据平面上实现表的模拟
在tofino数据平面上实现表的模拟
实验目的
当需要在数据平面实现较为复杂的信息存储和更新时,经常产生在数据平面存放一张表的需求,例如对于多台感兴趣的交换机,希望记录并更新交换机的各项网络状态信息。从数据抽象上来说,以表的形式来记录是直观的,从使用速率来说,将信息存储在数据平面可以避免与控制平面交互所需的较大时延,更适用于时延敏感的任务。
实验环境
sde版本:bf-sde-9.10.0
实验过程与分析
在数据平面存储数据的载体主要包括寄存器(register)、计数器(counter)、用户定义的元数据(user-defined metadata)。由于用元数据难以直观地建立映射关系,而计数器在数据平面只能进行简单的计数操作,所以我们考虑用寄存器来实现表的模拟。
由于寄存器本身,是一个index对应一个value的,并没有直接满足表中一个key对应多个value的特征,所以考虑不同的使用方法。
- 第一种尝试思路
一个直白的想法是通过对寄存器进行分段读写,在不同段存储不同的信息,例如设置长度64bit的寄存器,15:0位(即低16位)用于存放交换机ID。
但寄存器并不支持分段写入,当写入的位数长度不是32位,例如register_data[15:0] = 16w5;
时,则会报错error: can only output entire 32-bit ALU output
,若改为32位,例如register_data[31:0] = 32w5;
,则会出现以下报错:
error: Wide operations not supported in stateful alu, will only operate on bottom 32 bits
- 第二种尝试思路
由于寄存器虽然可以分段读取但不支持分段写入,所以无法将多组值存入单组寄存器中。因此,我们考虑用多组寄存器来模拟表。一个直白的想法是,为每个表项都设置一组寄存器,包括key和value,即表中的每一列对应一组寄存器,且相同行的表项对应同一个index。但是这需要在key和index之间建立联系,因为寄存器中存放的是index与value之间的对应关系
例如,下表是我们希望存储在数据平面的表:
switch id (key) | info1 | info2 | info3 |
---|---|---|---|
105 | 1 | 11 | 111 |
106 | 2 | 22 | 222 |
107 | 3 | 33 | 333 |
如果为每个表项设置一组寄存器,就是如下效果:
index | switch id (key) |
---|---|
1 | 105 |
2 | 106 |
3 | 107 |
index | info2 |
---|---|
1 | 11 |
2 | 22 |
3 | 33 |
其他info的寄存器情况类似info2,这样似乎能够满足我们的需求。但分析之后发现,当一个来自某交换机的新数据包到达时,数据平面需要对key所在的那组寄存器进行遍历搜索,才能得到对应的index。具体地说,以交换机id为107的那行表项为例,当一个来自107交换机的数据包到达时,我们希望更新107交换机的对应info,就需要这行所对应的index,因此需要对key所在的寄存器组进行搜索,以得到107对应的index=3
但是,目前还没有一种可以在数据平面遍历寄存器的方法,当表中行数较多时,这个问题就更加明显。
- 第三种尝试思路
由于寻找index是有挑战的,所以我们考虑直接将key化为index来使用。例如,我们感兴趣的交换机是相邻交换机,那么可以发现,交换机id与数据包的入端口也是一一对应的,因为每个端口只能有一个直接相连的实体。因此,我们可以直接将数据包的入端口作为index来使用,考虑到交换机中实际使用的是端口的D_P编号,所以需要扩大寄存器组的元素数量以涵盖D_P编号范围:
Register<bit<32>,bit<16>>(200, 0) info1;
Register<bit<32>,bit<16>>(200, 0) info2;
Register<bit<32>,bit<16>>(200, 0) info3;
使用时,直接将D_P编号作为index进行索引,即可更新对应行的表项。这个方案的缺点在于,为了使得index直接发挥key的功能而无需搜索,可能引入较多的未使用空间。
总结
从目前讨论的几个方案来看,在tofino数据平面实现表的模拟,需要能够在数据包中直接获取index的值,这可以通过某些转化来达成,虽然可能引入额外的空间开销。