blob: b1bbcaf348c8b4ce74cc56a20ca3df0befcd9c16 [file] [log] [blame]
Robert MacDavidde12b982020-07-15 18:38:59 -07001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef __SPGW__
18#define __SPGW__
19
20#define DEFAULT_PDR_CTR_ID 0
21#define DEFAULT_FAR_ID 0
22
23#ifndef MAX_UES
24#define MAX_UES 1024
25#endif // MAX_UES
26#define MAX_INTERFACES 128
27
28#define MAX_UPLINK_PDRS MAX_UES
29#define MAX_DOWNLINK_PDRS MAX_UES
30#define MAX_PDR_COUNTERS 2 * MAX_UES
31#define MAX_FARS 2 * MAX_UES
32
33
34
35control SpgwIngress(inout parsed_headers_t hdr,
36 inout fabric_metadata_t fabric_md,
37 inout standard_metadata_t standard_metadata) {
38
39
40
41
42 //=============================//
43 //===== Interface Tables ======//
44 //=============================//
45
46 action set_source_iface(spgw_interface_t src_iface, direction_t direction,
47 bit<1> skip_spgw) {
48 // Interface type can be access, core, n6_lan, etc (see InterfaceType enum)
49 // If interface is from the control plane, direction can be either up or down
50 fabric_md.spgw.src_iface = src_iface;
51 fabric_md.spgw.direction = direction;
52 fabric_md.spgw.skip_spgw = (_BOOL)skip_spgw;
53 }
54 // TODO: check also that gtpu.msgtype == GTP_GPDU... somewhere
55 table interface_lookup {
56 key = {
57 hdr.ipv4.dst_addr : lpm @name("ipv4_dst_addr"); // outermost header
58 hdr.gtpu.isValid() : exact @name("gtpu_is_valid");
59 }
60 actions = {
61 set_source_iface;
62 }
63 const default_action = set_source_iface(SPGW_IFACE_UNKNOWN, SPGW_DIR_UNKNOWN, 1);
64 size = MAX_INTERFACES;
65 }
66
67
68 //=============================//
69 //===== PDR Tables ======//
70 //=============================//
71
72 action set_pdr_attributes(pdr_ctr_id_t ctr_id,
73 far_id_t far_id,
74 bit<1> needs_gtpu_decap) {
75 fabric_md.spgw.pdr_hit = _TRUE;
76 fabric_md.spgw.ctr_id = ctr_id;
77 fabric_md.spgw.far_id = far_id;
78 fabric_md.spgw.needs_gtpu_decap = (_BOOL)needs_gtpu_decap;
79 }
80
81 // These two tables scale well and cover the average case PDR
82 table downlink_pdr_lookup {
83 key = {
84 // only available ipv4 header
85 hdr.ipv4.dst_addr : exact @name("ue_addr");
86 }
87 actions = {
88 set_pdr_attributes;
89 }
90 const default_action = set_pdr_attributes(DEFAULT_PDR_CTR_ID, DEFAULT_FAR_ID, 0);
91 size = MAX_DOWNLINK_PDRS;
92 }
93 table uplink_pdr_lookup {
94 key = {
95 // tunnel_dst_addr will be static for Q2 target. Can remove if need more scaling
96 hdr.ipv4.dst_addr : exact @name("tunnel_ipv4_dst");
97 hdr.gtpu.teid : exact @name("teid");
98 hdr.inner_ipv4.src_addr : exact @name("ue_addr");
99 }
100 actions = {
101 set_pdr_attributes;
102 }
103 const default_action = set_pdr_attributes(DEFAULT_PDR_CTR_ID, DEFAULT_FAR_ID, 0);
104 size = MAX_UPLINK_PDRS;
105 }
106 // This table scales poorly and covers uncommon PDRs
107 table flexible_pdr_lookup {
108 key = {
109 fabric_md.spgw.src_iface : ternary @name("src_iface");
110 // GTPU
111 hdr.gtpu.isValid() : ternary @name("gtpu_is_valid");
112 hdr.gtpu.teid : ternary @name("teid");
113 // SDF
114 // outer 5-tuple
115 hdr.ipv4.src_addr : ternary @name("ipv4_src");
116 hdr.ipv4.dst_addr : ternary @name("ipv4_dst");
117 hdr.ipv4.protocol : ternary @name("ip_proto");
118 fabric_md.l4_sport : ternary @name("l4_sport");
119 fabric_md.l4_dport : ternary @name("l4_dport");
120 // inner 5-tuple
121 hdr.inner_ipv4.src_addr : ternary @name("inner_ipv4_src");
122 hdr.inner_ipv4.dst_addr : ternary @name("inner_ipv4_dst");
123 hdr.inner_ipv4.protocol : ternary @name("inner_ip_proto");
124 fabric_md.inner_l4_sport : ternary @name("inner_l4_sport");
125 fabric_md.inner_l4_dport : ternary @name("inner_l4_dport");
126 }
127 actions = {
128 set_pdr_attributes;
129 }
130 const default_action = set_pdr_attributes(DEFAULT_PDR_CTR_ID, DEFAULT_FAR_ID, 0);
131 }
132
133 //=============================//
134 //===== FAR Tables ======//
135 //=============================//
136
137 action load_normal_far_attributes(bit<1> drop,
138 bit<1> notify_cp) {
139 // general far attributes
140 fabric_md.spgw.far_dropped = (_BOOL)drop;
141 fabric_md.spgw.notify_spgwc = (_BOOL)notify_cp;
142 }
143 action load_tunnel_far_attributes(bit<1> drop,
144 bit<1> notify_cp,
145 bit<16> tunnel_src_port,
146 bit<32> tunnel_src_addr,
147 bit<32> tunnel_dst_addr,
148 teid_t teid) {
149 // general far attributes
150 fabric_md.spgw.far_dropped = (_BOOL)drop;
151 fabric_md.spgw.notify_spgwc = (_BOOL)notify_cp;
152 // GTP tunnel attributes
153 fabric_md.spgw.needs_gtpu_encap = _TRUE;
154 fabric_md.spgw.teid = teid;
155 fabric_md.spgw.tunnel_src_port = tunnel_src_port;
156 fabric_md.spgw.tunnel_src_addr = tunnel_src_addr;
157 fabric_md.spgw.tunnel_dst_addr = tunnel_dst_addr;
158 // update metadata for correct routing/hashing
159 fabric_md.ipv4_src_addr = tunnel_src_addr;
160 fabric_md.ipv4_dst_addr = tunnel_dst_addr;
161 fabric_md.l4_sport = tunnel_src_port;
162 fabric_md.l4_dport = UDP_PORT_GTPU;
163 }
164
165 table far_lookup {
166 key = {
167 fabric_md.spgw.far_id : exact @name("far_id");
168 }
169 actions = {
170 load_normal_far_attributes;
171 load_tunnel_far_attributes;
172 }
173 // default is drop and don't notify CP
174 const default_action = load_normal_far_attributes(1, 1);
175 size = MAX_FARS;
176 }
177
178 //=============================//
179 //===== Misc Things ======//
180 //=============================//
181
182 counter(MAX_PDR_COUNTERS, CounterType.packets_and_bytes) pdr_counter;
183
184
185 @hidden
186 action decap_inner_common() {
187 // Correct parser-set metadata to use the inner header values
188 fabric_md.ip_eth_type = ETHERTYPE_IPV4;
189 fabric_md.ip_proto = hdr.inner_ipv4.protocol;
190 fabric_md.ipv4_src_addr = hdr.inner_ipv4.src_addr;
191 fabric_md.ipv4_dst_addr = hdr.inner_ipv4.dst_addr;
192 fabric_md.l4_sport = fabric_md.inner_l4_sport;
193 fabric_md.l4_dport = fabric_md.inner_l4_dport;
194 // Move GTPU and inner L3 headers out
195 hdr.ipv4 = hdr.inner_ipv4;
196 hdr.inner_ipv4.setInvalid();
197 hdr.gtpu.setInvalid();
198 }
199 action decap_inner_tcp() {
200 decap_inner_common();
201 hdr.udp.setInvalid();
202 hdr.tcp = hdr.inner_tcp;
203 hdr.inner_tcp.setInvalid();
204 }
205 action decap_inner_udp() {
206 decap_inner_common();
207 hdr.udp = hdr.inner_udp;
208 hdr.inner_udp.setInvalid();
209 }
210 action decap_inner_icmp() {
211 decap_inner_common();
212 hdr.udp.setInvalid();
213 hdr.icmp = hdr.inner_icmp;
214 hdr.inner_icmp.setInvalid();
215 }
216 action decap_inner_unknown() {
217 decap_inner_common();
218 hdr.udp.setInvalid();
219 }
220 @hidden
221 table decap_gtpu {
222 key = {
223 hdr.inner_tcp.isValid() : exact;
224 hdr.inner_udp.isValid() : exact;
225 hdr.inner_icmp.isValid() : exact;
226 }
227 actions = {
228 decap_inner_tcp;
229 decap_inner_udp;
230 decap_inner_icmp;
231 decap_inner_unknown;
232 }
233 const default_action = decap_inner_unknown;
234 const entries = {
235 (true, false, false) : decap_inner_tcp();
236 (false, true, false) : decap_inner_udp();
237 (false, false, true) : decap_inner_icmp();
238 }
239 }
240
241
242 //=============================//
243 //===== Apply Block ======//
244 //=============================//
245 apply {
246
247 // Interfaces
248 interface_lookup.apply();
249
250 // If interface table missed, or the interface skips PDRs/FARs (TODO: is that a thing?)
251 if (fabric_md.spgw.skip_spgw == _TRUE) return;
252
253 // PDRs
254 // Currently only best-case PDR tables to make v1model-to-tofino compiler happy
255 if (hdr.gtpu.isValid()) {
256 uplink_pdr_lookup.apply();
257 } else {
258 downlink_pdr_lookup.apply();
259 }
260 // Inefficient PDR table if efficient tables missed
261 // Removed to make v1model-to-tofino compiler happy. Not removed in TNA port
262 //if (fabric_md.spgw.pdr_hit == _FALSE) {
263 // flexible_pdr_lookup.apply();
264 //}
265 pdr_counter.count(fabric_md.spgw.ctr_id);
266
267 // GTPU Decapsulate
268 if (fabric_md.spgw.needs_gtpu_decap == _TRUE) {
269 decap_gtpu.apply();
270 }
271
272 // FARs
273 // Load FAR info
274 far_lookup.apply();
275
276 if (fabric_md.spgw.notify_spgwc == _TRUE) {
277 // TODO: cpu clone action here
278 }
279 if (fabric_md.spgw.far_dropped == _TRUE) {
280 // Do dropping in the same way as fabric's filtering.p4, so we can traverse
281 // the ACL table, which is good for cases like DHCP.
282 fabric_md.skip_forwarding = _TRUE;
283 fabric_md.skip_next = _TRUE;
284 }
285
286 // Nothing to be done immediately for forwarding or encapsulation.
287 // Forwarding is done by other parts of fabric.p4, and
288 // encapsulation is done in the egress
289
290 // Needed for correct GTPU encapsulation in egress
291 fabric_md.spgw.ipv4_len = hdr.ipv4.total_len;
292 }
293}
294
295
296//====================================//
297//============== Egress ==============//
298//====================================//
299control SpgwEgress(
300 inout parsed_headers_t hdr,
301 inout fabric_metadata_t fabric_md) {
302
303 counter(MAX_PDR_COUNTERS, CounterType.packets_and_bytes) pdr_counter;
304
305
306 @hidden
307 action gtpu_encap() {
308 hdr.gtpu_ipv4.setValid();
309 hdr.gtpu_ipv4.version = IP_VERSION_4;
310 hdr.gtpu_ipv4.ihl = IPV4_MIN_IHL;
311 hdr.gtpu_ipv4.dscp = 0;
312 hdr.gtpu_ipv4.ecn = 0;
313 hdr.gtpu_ipv4.total_len = hdr.ipv4.total_len
314 + (IPV4_HDR_SIZE + UDP_HDR_SIZE + GTP_HDR_SIZE);
315 hdr.gtpu_ipv4.identification = 0x1513; /* From NGIC. TODO: Needs to be dynamic */
316 hdr.gtpu_ipv4.flags = 0;
317 hdr.gtpu_ipv4.frag_offset = 0;
318 hdr.gtpu_ipv4.ttl = DEFAULT_IPV4_TTL;
319 hdr.gtpu_ipv4.protocol = PROTO_UDP;
320 hdr.gtpu_ipv4.src_addr = fabric_md.spgw.tunnel_src_addr;
321 hdr.gtpu_ipv4.dst_addr = fabric_md.spgw.tunnel_dst_addr;
322 hdr.gtpu_ipv4.hdr_checksum = 0; // Updated later
323
324 hdr.gtpu_udp.setValid();
325 hdr.gtpu_udp.sport = fabric_md.spgw.tunnel_src_port;
326 hdr.gtpu_udp.dport = UDP_PORT_GTPU;
327 hdr.gtpu_udp.len = fabric_md.spgw.ipv4_len
328 + (UDP_HDR_SIZE + GTP_HDR_SIZE);
329 hdr.gtpu_udp.checksum = 0; // Updated later, if WITH_SPGW_UDP_CSUM_UPDATE
330
331
332 hdr.outer_gtpu.setValid();
333 hdr.outer_gtpu.version = GTPU_VERSION;
334 hdr.outer_gtpu.pt = GTP_PROTOCOL_TYPE_GTP;
335 hdr.outer_gtpu.spare = 0;
336 hdr.outer_gtpu.ex_flag = 0;
337 hdr.outer_gtpu.seq_flag = 0;
338 hdr.outer_gtpu.npdu_flag = 0;
339 hdr.outer_gtpu.msgtype = GTP_GPDU;
340 hdr.outer_gtpu.msglen = fabric_md.spgw.ipv4_len;
341 hdr.outer_gtpu.teid = fabric_md.spgw.teid;
342 }
343
344 apply {
345 if (fabric_md.spgw.skip_spgw == _TRUE) return;
346 pdr_counter.count(fabric_md.spgw.ctr_id);
347
348 if (fabric_md.spgw.needs_gtpu_encap == _TRUE) {
349 gtpu_encap();
350 }
351 }
352}
353
354
355control update_gtpu_checksum(
356 inout ipv4_t gtpu_ipv4,
357 inout udp_t gtpu_udp,
358 in gtpu_t gtpu,
359 in ipv4_t ipv4,
360 in udp_t udp
361 ) {
362 apply {
363 // Compute outer IPv4 checksum.
364 update_checksum(gtpu_ipv4.isValid(),
365 {
366 gtpu_ipv4.version,
367 gtpu_ipv4.ihl,
368 gtpu_ipv4.dscp,
369 gtpu_ipv4.ecn,
370 gtpu_ipv4.total_len,
371 gtpu_ipv4.identification,
372 gtpu_ipv4.flags,
373 gtpu_ipv4.frag_offset,
374 gtpu_ipv4.ttl,
375 gtpu_ipv4.protocol,
376 gtpu_ipv4.src_addr,
377 gtpu_ipv4.dst_addr
378 },
379 gtpu_ipv4.hdr_checksum,
380 HashAlgorithm.csum16
381 );
382
383#ifdef WITH_SPGW_UDP_CSUM_UPDATE
384 // Compute outer UDP checksum.
385 update_checksum_with_payload(gtpu_udp.isValid(),
386 {
387 gtpu_ipv4.src_addr,
388 gtpu_ipv4.dst_addr,
389 8w0,
390 gtpu_ipv4.protocol,
391 gtpu_udp.len,
392 gtpu_udp.sport,
393 gtpu_udp.dport,
394 gtpu_udp.len,
395 gtpu,
396 ipv4,
397 // FIXME: we are assuming only UDP for downlink packets
398 // How to conditionally switch between UDP/TCP/ICMP?
399 udp
400 },
401 gtpu_udp.checksum,
402 HashAlgorithm.csum16
403 );
404#endif // WITH_SPGW_UDP_CSUM_UPDATE
405 }
406}
407
408#endif