1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
#define REGISTER_SIZE 8192
#define TIMESTAMP_WIDTH 48
#define ID_WIDTH 16
// 以下定义等效于bit<48> FLOWLET_TIMEOUT = 200000;
// 48w表示这是一个宽度为48bit的数值,200000是其数值
#define FLOWLET_TIMEOUT 48w200000
control MyIngress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
// 使用寄存器记录每个流的当前Flowlet编号
register<bit<ID_WIDTH>>(REGISTER_SIZE) flowlet_to_id;
// 使用寄存器记录每个流的上一次接收到数据包的时间
register<bit<TIMESTAMP_WIDTH>>(REGISTER_SIZE) flowlet_time_stamp;
action drop() {
mark_to_drop(standard_metadata);
}
// 读取并更新寄存器中存储的流信息
action read_flowlet_registers(){
// 通过对流的五元组进行哈希获得索引,该索引即为该流在寄存器中的索引位置
hash(meta.flowlet_register_index, HashAlgorithm.crc16,
(bit<16>)0,
{ hdr.ipv4.srcAddr, hdr.ipv4.dstAddr, hdr.tcp.srcPort, hdr.tcp.dstPort,hdr.ipv4.protocol},
(bit<14>)REGISTER_SIZE);
// 根据索引值在寄存器flowlet_last_stamp中读取上一次接收到数据包的时间
// 并存入元数据的flowlet_last_stamp中
flowlet_time_stamp.read(meta.flowlet_last_stamp, (bit<32>)meta.flowlet_register_index);
// 根据索引值在寄存器flowlet_to_id中读取流的当前Flowlet编号
// 并存入元数据的flowlet_id中
flowlet_to_id.read(meta.flowlet_id, (bit<32>)meta.flowlet_register_index);
// 更新当前时间戳为当前数据包的到达时间
// 到达时间可在标准元数据中获取,即ingress_global_timestamp
// 根据索引值在寄存器flowlet_time_stamp中写入时间戳
flowlet_time_stamp.write((bit<32>)meta.flowlet_register_index, standard_metadata.ingress_global_timestamp);
}
// 采用随机数更新Flowlet编号
action update_flowlet_id(){
// 定义一个32位的临时变量,用来存储接下来生成的随机数
bit<32> random_t;
// 调用随机数函数生成一个范围在0~65000之间的随机数
random(random_t, (bit<32>)0, (bit<32>)65000);
// 更新Flowlet编号
meta.flowlet_id = (bit<16>)random_t;
// 根据索引值在寄存器flowlet_to_id中写入新的Flowlet编号
flowlet_to_id.write((bit<32>)meta.flowlet_register_index, (bit<16>)meta.flowlet_id);
}
action ecmp_group(bit<14> ecmp_group_id, bit<16> num_nhops){
hash(meta.ecmp_hash,
HashAlgorithm.crc16,
(bit<1>)0,
{
hdr.ipv4.srcAddr,
hdr.ipv4.dstAddr,
hdr.tcp.srcPort,
hdr.tcp.dstPort,
hdr.ipv4.protocol,
meta.flowlet_id // 在哈希选路时增加Flowlet编号
},
num_nhops
);
meta.ecmp_group_id = ecmp_group_id;
}
action set_nhop(macAddr_t dstAddr, egressSpec_t port) {
hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
hdr.ethernet.dstAddr = dstAddr;
hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
standard_metadata.egress_spec = port;
}
table ecmp_group_to_nhop {
key = {
meta.ecmp_group_id: exact;
meta.ecmp_hash: exact;
}
actions = {
drop;
set_nhop;
}
size = 1024;
}
table ipv4_lpm {
key = {
hdr.ipv4.dstAddr: lpm;
}
actions = {
set_nhop;
ecmp_group;
drop;
}
size = 1024;
default_action = drop;
}
apply {
if (hdr.ipv4.isValid()){
// 每个数据包在多流水线上经过时,实际上是并行在不同阶段执行的
// 如果两个包几乎同时到达,可能同时触发读写寄存器,造成逻辑错误
// @atomic表示其中的逻辑是原子操作,不能被其它操作打断
@atomic {
// 从寄存器中获取流的相关信息
read_flowlet_registers();
// 计算当前数据包距离上一个数据包到达的时间间隔
meta.flowlet_time_diff = standard_metadata.ingress_global_timestamp - meta.flowlet_last_stamp;
// 如果当前时间间隔超过了阈值,则可以认为是一个新的Flowlet
if (meta.flowlet_time_diff > FLOWLET_TIMEOUT){
update_flowlet_id();
}
}
switch (ipv4_lpm.apply().action_run){
ecmp_group: {
ecmp_group_to_nhop.apply();
}
}
}
}
}
|