-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathpna-example-tcp-connection-state-tracking.p4
300 lines (265 loc) · 10.4 KB
/
pna-example-tcp-connection-state-tracking.p4
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
/*
Copyright 2022 AMD
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <core.p4>
#include "../pna.p4"
// Sample PNA program demonstrating the use of write back table
// entries. Although a possible syntax is proposed to indicate
// action data that is written back to a table, the purpose of
// this example is not to support that specific syntax, but
// rather the usefulness and advantages of being able to write
// action data back to a table to store state information
// NOTE: this example uses a simplified version of the code in
// pna-example-tcp-connection-tracking.p4 to add entries
// in the TCP connection table (ct_tcp_table).
// Although the same naming is purposedly used for the connection
// table, the removal of entries (due to time out or explicit connection
// tear-down is explicitly not supported for the sake of keeping the code
// simpler and more legible.
// A complete implementation of TCP connection and status tracking
// would require to include such functionalities that can be found in
// pna-example-tcp-connection-tracking.p4
// NOTE: the code is currently written for a compiler supporting
// if statements within actions. The same functionality could be rewritten
// for a compiler not supporting if statements within actions by using if
// if statements in the control block and a table to set up proper context.
header ethernet_t {
bit<48> dstAddr;
bit<48> srcAddr;
bit<16> etherType;
}
header ipv4_t {
bit<4> version;
bit<4> ihl;
bit<8> diffserv;
bit<16> totalLength;
bit<16> identification;
bit<3> flags;
bit<13> fragOffset;
bit<8> ttl;
bit<8> protocol;
bit<16> hdrChecksum;
bit<32> srcAddr;
bit<32> dstAddr;
}
header tcp_t {
bit<16> srcPort;
bit<16> dstPort;
bit<32> seqNo;
bit<32> ackNo;
bit<4> dataOffset;
bit<4> res;
bit<8> flags;
bit<16> window;
bit<16> checksum;
bit<16> urgentPtr;
}
// Masks of the bit positions of some bit flags within the TCP flags
// field.
const bit<8> TCP_ACK_MASK = 0x10;
const bit<8> TCP_SYN_MASK = 0x02;
// Define names for different expire time profile id values.
const ExpireTimeProfileId_t EXPIRE_TIME_PROFILE_TCP_NOW = (ExpireTimeProfileId_t) 0;
const ExpireTimeProfileId_t EXPIRE_TIME_PROFILE_TCP_NEW = (ExpireTimeProfileId_t) 1;
const ExpireTimeProfileId_t EXPIRE_TIME_PROFILE_TCP_ESTABLISHED = (ExpireTimeProfileId_t) 2;
const ExpireTimeProfileId_t EXPIRE_TIME_PROFILE_TCP_NEVER = (ExpireTimeProfileId_t) 3;
//////////////////////////////////////////////////////////////////////
// Struct types for holding user-defined collections of headers and
// metadata in the P4 developer's program.
//////////////////////////////////////////////////////////////////////
struct metadata_t {
}
struct headers_t {
ethernet_t eth;
ipv4_t ipv4;
tcp_t tcp;
}
parser MainParserImpl(
packet_in pkt,
out headers_t hdr,
inout metadata_t meta,
in pna_main_parser_input_metadata_t istd)
{
state start {
pkt.extract(hdr.eth);
transition select (hdr.eth.etherType) {
0x0800: parse_ipv4;
default: accept;
}
}
state parse_ipv4 {
pkt.extract(hdr.ipv4);
transition select (hdr.ipv4.protocol) {
6: parse_tcp;
default: accept;
}
}
state parse_tcp {
pkt.extract(hdr.tcp);
transition accept;
}
}
struct ct_tcp_table_hit_params_t {
bit<32> n2h_seqNo;
bit<32> h2n_seqNo;
bit<32> n2h_ackNo;
bit<32> h2n_ackNo;
// other connection state being tracked can be added here
}
control MainControlImpl(
inout headers_t hdr,
inout metadata_t meta,
in pna_main_input_metadata_t istd,
inout pna_main_output_metadata_t ostd)
{
action drop () {
drop_packet();
}
// Inputs from previous tables (or actions, or in general other P4
// code) that can modify the behavior of actions of ct_tcp_table.
bool do_add_on_miss;
bool update_aging_info;
bool update_expire_time;
ExpireTimeProfileId_t new_expire_time_profile_id;
// Outputs from actions of ct_tcp_table
AddEntryErrorStatus_t add_status;
action ct_tcp_table_hit (
bit<32> n2h_seqNo, // NEW: support for write back entry
bit<32> h2n_seqNo, // NEW: support for write back entry
bit<32> n2h_ackNo, // NEW: support for write back entry
bit<32> h2n_ackNo // NEW: support for write back entry
// More action data that is not written back can be here as well
) {
// some table types, e.g., T-CAM-based ones, may not support re-writable
// entries.
if ((hdr.tcp.flags & TCP_SYN_MASK) != 0) {
if ((hdr.tcp.flags & TCP_ACK_MASK) == 0 ) {
if (SelectByDirection(is_net_port(istd.input_port),n2h_seqNo,h2n_seqNo) != hdr.tcp.seqNo) {
set_entry_expire_time(EXPIRE_TIME_PROFILE_TCP_NOW);
drop_packet();
// This is protecting from certain misbehavior, but a
// complete robust and secure solution against denial
// of service attacks should include additional checks
}
} else {
if (hdr.tcp.ackNo ==
(SelectByDirection(is_net_port(istd.input_port),h2n_seqNo,n2h_seqNo)+1)) {
if (is_net_port(istd.input_port)) {
// As of 2013-Mar-20, open source p4c gives a
// compile-time error if you attempt to assign
// a value to an action parameter. As long as
// you do not #define
// ALLOW_ASSIGNMENTS_TO_ACTION_PARAMS, the
// rest of this program compiles without
// error.
#ifdef ALLOW_ASSIGNMENTS_TO_ACTION_PARAMS
n2h_seqNo = hdr.tcp.seqNo; // NEW: support for write back entry
n2h_ackNo = hdr.tcp.ackNo; // NEW: support for write back entry
h2n_ackNo = hdr.tcp.seqNo; // NEW: support for write back entry
#endif // ALLOW_ASSIGNMENTS_TO_ACTION_PARAMS
} else {
#ifdef ALLOW_ASSIGNMENTS_TO_ACTION_PARAMS
h2n_seqNo = hdr.tcp.seqNo; // NEW: support for write back entry
h2n_ackNo = hdr.tcp.ackNo; // NEW: support for write back entry
n2h_ackNo = hdr.tcp.seqNo; // NEW: support for write back entry
#endif // ALLOW_ASSIGNMENTS_TO_ACTION_PARAMS
}
restart_expire_timer();
} else {
set_entry_expire_time(EXPIRE_TIME_PROFILE_TCP_NOW);
drop_packet();
}
}
} else {
if (hdr.tcp.ackNo <= SelectByDirection(is_net_port(istd.input_port),h2n_seqNo,n2h_seqNo) &&
hdr.tcp.ackNo >= SelectByDirection(is_net_port(istd.input_port),n2h_ackNo,h2n_ackNo)) {
if (is_net_port(istd.input_port)) {
#ifdef ALLOW_ASSIGNMENTS_TO_ACTION_PARAMS
n2h_seqNo = hdr.tcp.seqNo; // NEW: support for write back entry
n2h_ackNo = hdr.tcp.ackNo; // NEW: support for write back entry
#endif // ALLOW_ASSIGNMENTS_TO_ACTION_PARAMS
} else {
#ifdef ALLOW_ASSIGNMENTS_TO_ACTION_PARAMS
h2n_seqNo = hdr.tcp.seqNo; // NEW: support for write back entry
h2n_ackNo = hdr.tcp.ackNo; // NEW: support for write back entry
#endif // ALLOW_ASSIGNMENTS_TO_ACTION_PARAMS
}
set_entry_expire_time(EXPIRE_TIME_PROFILE_TCP_ESTABLISHED);
} else {
drop_packet();
}
}
}
action ct_tcp_table_miss() {
ct_tcp_table_hit_params_t tcp_params;
if ((hdr.tcp.flags & TCP_SYN_MASK) != 0) {
if ((hdr.tcp.flags & TCP_ACK_MASK) == 0) {
if (is_net_port(istd.input_port))
tcp_params.n2h_seqNo = hdr.tcp.seqNo;
else
tcp_params.h2n_seqNo = hdr.tcp.seqNo;
add_status =
add_entry(action_name = "ct_tcp_table_hit", // name of action
action_params = tcp_params,
expire_time_profile_id = EXPIRE_TIME_PROFILE_TCP_NEW);
} else {
drop_packet();
}
} else {
drop_packet();
}
}
table ct_tcp_table {
/* add_on_miss table is restricted to have all exact match fields */
key = {
// other key fields also possible, e.g. VRF
SelectByDirection(is_net_port(istd.input_port), hdr.ipv4.srcAddr, hdr.ipv4.dstAddr):
exact @name("ipv4_addr_0");
SelectByDirection(is_net_port(istd.input_port), hdr.ipv4.dstAddr, hdr.ipv4.srcAddr):
exact @name("ipv4_addr_1");
hdr.ipv4.protocol : exact;
SelectByDirection(is_net_port(istd.input_port), hdr.tcp.srcPort, hdr.tcp.dstPort):
exact @name("tcp_port_0");
SelectByDirection(is_net_port(istd.input_port), hdr.tcp.dstPort, hdr.tcp.srcPort):
exact @name("tcp_port_1");
}
actions = {
@tableonly ct_tcp_table_hit;
@defaultonly ct_tcp_table_miss;
}
add_on_miss = true;
default_idle_timeout_for_data_plane_added_entries = 1;
idle_timeout_with_auto_delete = true;
const default_action = ct_tcp_table_miss;
}
apply {
// ct_tcp_table is a bidirectional table
if (hdr.ipv4.isValid() && hdr.tcp.isValid()) {
ct_tcp_table.apply();
}
}
}
control MainDeparserImpl(
packet_out pkt,
inout headers_t hdr,
in metadata_t meta,
in pna_main_output_metadata_t ostd)
{
apply {
pkt.emit(hdr.eth);
}
}
PNA_NIC(
MainParserImpl(),
MainControlImpl(),
MainDeparserImpl()
) main;