blob: 34a88b8bec4d4d36486faa4a2f393359d145bcbb [file] [log] [blame]
Robert MacDavidc9fce632020-07-28 22:17:07 -04001/*
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 = {
Robert MacDavidc9fce632020-07-28 22:17:07 -040095 hdr.ipv4.dst_addr : exact @name("tunnel_ipv4_dst");
96 hdr.gtpu.teid : exact @name("teid");
Robert MacDavidc9fce632020-07-28 22:17:07 -040097 }
98 actions = {
99 set_pdr_attributes;
100 }
101 const default_action = set_pdr_attributes(DEFAULT_PDR_CTR_ID, DEFAULT_FAR_ID, 0);
102 size = MAX_UPLINK_PDRS;
103 }
104 // This table scales poorly and covers uncommon PDRs
105 table flexible_pdr_lookup {
106 key = {
107 fabric_md.spgw.src_iface : ternary @name("src_iface");
108 // GTPU
109 hdr.gtpu.isValid() : ternary @name("gtpu_is_valid");
110 hdr.gtpu.teid : ternary @name("teid");
111 // SDF
112 // outer 5-tuple
113 hdr.ipv4.src_addr : ternary @name("ipv4_src");
114 hdr.ipv4.dst_addr : ternary @name("ipv4_dst");
115 hdr.ipv4.protocol : ternary @name("ip_proto");
116 fabric_md.l4_sport : ternary @name("l4_sport");
117 fabric_md.l4_dport : ternary @name("l4_dport");
118 // inner 5-tuple
119 hdr.inner_ipv4.src_addr : ternary @name("inner_ipv4_src");
120 hdr.inner_ipv4.dst_addr : ternary @name("inner_ipv4_dst");
121 hdr.inner_ipv4.protocol : ternary @name("inner_ip_proto");
122 fabric_md.inner_l4_sport : ternary @name("inner_l4_sport");
123 fabric_md.inner_l4_dport : ternary @name("inner_l4_dport");
124 }
125 actions = {
126 set_pdr_attributes;
127 }
128 const default_action = set_pdr_attributes(DEFAULT_PDR_CTR_ID, DEFAULT_FAR_ID, 0);
129 }
130
131 //=============================//
132 //===== FAR Tables ======//
133 //=============================//
134
135 action load_normal_far_attributes(bit<1> drop,
136 bit<1> notify_cp) {
137 // general far attributes
138 fabric_md.spgw.far_dropped = (_BOOL)drop;
139 fabric_md.spgw.notify_spgwc = (_BOOL)notify_cp;
140 }
141 action load_tunnel_far_attributes(bit<1> drop,
142 bit<1> notify_cp,
143 bit<16> tunnel_src_port,
144 bit<32> tunnel_src_addr,
145 bit<32> tunnel_dst_addr,
146 teid_t teid) {
147 // general far attributes
148 fabric_md.spgw.far_dropped = (_BOOL)drop;
149 fabric_md.spgw.notify_spgwc = (_BOOL)notify_cp;
150 // GTP tunnel attributes
151 fabric_md.spgw.needs_gtpu_encap = _TRUE;
152 fabric_md.spgw.teid = teid;
153 fabric_md.spgw.tunnel_src_port = tunnel_src_port;
154 fabric_md.spgw.tunnel_src_addr = tunnel_src_addr;
155 fabric_md.spgw.tunnel_dst_addr = tunnel_dst_addr;
156 // update metadata for correct routing/hashing
157 fabric_md.ipv4_src_addr = tunnel_src_addr;
158 fabric_md.ipv4_dst_addr = tunnel_dst_addr;
159 fabric_md.l4_sport = tunnel_src_port;
160 fabric_md.l4_dport = UDP_PORT_GTPU;
161 }
162
163 table far_lookup {
164 key = {
165 fabric_md.spgw.far_id : exact @name("far_id");
166 }
167 actions = {
168 load_normal_far_attributes;
169 load_tunnel_far_attributes;
170 }
171 // default is drop and don't notify CP
172 const default_action = load_normal_far_attributes(1, 1);
173 size = MAX_FARS;
174 }
175
176 //=============================//
177 //===== Misc Things ======//
178 //=============================//
179
180 counter(MAX_PDR_COUNTERS, CounterType.packets_and_bytes) pdr_counter;
181
182
183 @hidden
184 action decap_inner_common() {
185 // Correct parser-set metadata to use the inner header values
186 fabric_md.ip_eth_type = ETHERTYPE_IPV4;
187 fabric_md.ip_proto = hdr.inner_ipv4.protocol;
188 fabric_md.ipv4_src_addr = hdr.inner_ipv4.src_addr;
189 fabric_md.ipv4_dst_addr = hdr.inner_ipv4.dst_addr;
190 fabric_md.l4_sport = fabric_md.inner_l4_sport;
191 fabric_md.l4_dport = fabric_md.inner_l4_dport;
192 // Move GTPU and inner L3 headers out
193 hdr.ipv4 = hdr.inner_ipv4;
194 hdr.inner_ipv4.setInvalid();
195 hdr.gtpu.setInvalid();
196 }
197 action decap_inner_tcp() {
198 decap_inner_common();
199 hdr.udp.setInvalid();
200 hdr.tcp = hdr.inner_tcp;
201 hdr.inner_tcp.setInvalid();
202 }
203 action decap_inner_udp() {
204 decap_inner_common();
205 hdr.udp = hdr.inner_udp;
206 hdr.inner_udp.setInvalid();
207 }
208 action decap_inner_icmp() {
209 decap_inner_common();
210 hdr.udp.setInvalid();
211 hdr.icmp = hdr.inner_icmp;
212 hdr.inner_icmp.setInvalid();
213 }
214 action decap_inner_unknown() {
215 decap_inner_common();
216 hdr.udp.setInvalid();
217 }
218 @hidden
219 table decap_gtpu {
220 key = {
221 hdr.inner_tcp.isValid() : exact;
222 hdr.inner_udp.isValid() : exact;
223 hdr.inner_icmp.isValid() : exact;
224 }
225 actions = {
226 decap_inner_tcp;
227 decap_inner_udp;
228 decap_inner_icmp;
229 decap_inner_unknown;
230 }
231 const default_action = decap_inner_unknown;
232 const entries = {
233 (true, false, false) : decap_inner_tcp();
234 (false, true, false) : decap_inner_udp();
235 (false, false, true) : decap_inner_icmp();
236 }
237 }
238
239
240 //=============================//
241 //===== Apply Block ======//
242 //=============================//
243 apply {
244
245 // Interfaces
246 interface_lookup.apply();
247
248 // If interface table missed, or the interface skips PDRs/FARs (TODO: is that a thing?)
249 if (fabric_md.spgw.skip_spgw == _TRUE) return;
250
251 // PDRs
252 // Currently only best-case PDR tables to make v1model-to-tofino compiler happy
253 if (hdr.gtpu.isValid()) {
254 uplink_pdr_lookup.apply();
255 } else {
256 downlink_pdr_lookup.apply();
257 }
258 // Inefficient PDR table if efficient tables missed
259 // Removed to make v1model-to-tofino compiler happy. Not removed in TNA port
260 //if (fabric_md.spgw.pdr_hit == _FALSE) {
261 // flexible_pdr_lookup.apply();
262 //}
263 pdr_counter.count(fabric_md.spgw.ctr_id);
264
265 // GTPU Decapsulate
266 if (fabric_md.spgw.needs_gtpu_decap == _TRUE) {
267 decap_gtpu.apply();
268 }
269
270 // FARs
271 // Load FAR info
272 far_lookup.apply();
273
274 if (fabric_md.spgw.notify_spgwc == _TRUE) {
275 // TODO: cpu clone action here
276 }
277 if (fabric_md.spgw.far_dropped == _TRUE) {
278 // Do dropping in the same way as fabric's filtering.p4, so we can traverse
279 // the ACL table, which is good for cases like DHCP.
280 fabric_md.skip_forwarding = _TRUE;
281 fabric_md.skip_next = _TRUE;
282 }
283
284 // Nothing to be done immediately for forwarding or encapsulation.
285 // Forwarding is done by other parts of fabric.p4, and
286 // encapsulation is done in the egress
287
288 // Needed for correct GTPU encapsulation in egress
289 fabric_md.spgw.ipv4_len = hdr.ipv4.total_len;
290 }
291}
292
293
294//====================================//
295//============== Egress ==============//
296//====================================//
297control SpgwEgress(
298 inout parsed_headers_t hdr,
299 inout fabric_metadata_t fabric_md) {
300
301 counter(MAX_PDR_COUNTERS, CounterType.packets_and_bytes) pdr_counter;
302
303
304 @hidden
305 action gtpu_encap() {
306 hdr.gtpu_ipv4.setValid();
307 hdr.gtpu_ipv4.version = IP_VERSION_4;
308 hdr.gtpu_ipv4.ihl = IPV4_MIN_IHL;
309 hdr.gtpu_ipv4.dscp = 0;
310 hdr.gtpu_ipv4.ecn = 0;
311 hdr.gtpu_ipv4.total_len = hdr.ipv4.total_len
312 + (IPV4_HDR_SIZE + UDP_HDR_SIZE + GTP_HDR_SIZE);
313 hdr.gtpu_ipv4.identification = 0x1513; /* From NGIC. TODO: Needs to be dynamic */
314 hdr.gtpu_ipv4.flags = 0;
315 hdr.gtpu_ipv4.frag_offset = 0;
316 hdr.gtpu_ipv4.ttl = DEFAULT_IPV4_TTL;
317 hdr.gtpu_ipv4.protocol = PROTO_UDP;
318 hdr.gtpu_ipv4.src_addr = fabric_md.spgw.tunnel_src_addr;
319 hdr.gtpu_ipv4.dst_addr = fabric_md.spgw.tunnel_dst_addr;
320 hdr.gtpu_ipv4.hdr_checksum = 0; // Updated later
321
322 hdr.gtpu_udp.setValid();
323 hdr.gtpu_udp.sport = fabric_md.spgw.tunnel_src_port;
324 hdr.gtpu_udp.dport = UDP_PORT_GTPU;
325 hdr.gtpu_udp.len = fabric_md.spgw.ipv4_len
326 + (UDP_HDR_SIZE + GTP_HDR_SIZE);
327 hdr.gtpu_udp.checksum = 0; // Updated later, if WITH_SPGW_UDP_CSUM_UPDATE
328
329
330 hdr.outer_gtpu.setValid();
331 hdr.outer_gtpu.version = GTPU_VERSION;
332 hdr.outer_gtpu.pt = GTP_PROTOCOL_TYPE_GTP;
333 hdr.outer_gtpu.spare = 0;
334 hdr.outer_gtpu.ex_flag = 0;
335 hdr.outer_gtpu.seq_flag = 0;
336 hdr.outer_gtpu.npdu_flag = 0;
337 hdr.outer_gtpu.msgtype = GTP_GPDU;
338 hdr.outer_gtpu.msglen = fabric_md.spgw.ipv4_len;
339 hdr.outer_gtpu.teid = fabric_md.spgw.teid;
340 }
341
342 apply {
343 if (fabric_md.spgw.skip_spgw == _TRUE) return;
344 pdr_counter.count(fabric_md.spgw.ctr_id);
345
346 if (fabric_md.spgw.needs_gtpu_encap == _TRUE) {
347 gtpu_encap();
348 }
349 }
350}
351
352
353control update_gtpu_checksum(
354 inout ipv4_t gtpu_ipv4,
355 inout udp_t gtpu_udp,
356 in gtpu_t gtpu,
357 in ipv4_t ipv4,
358 in udp_t udp
359 ) {
360 apply {
361 // Compute outer IPv4 checksum.
362 update_checksum(gtpu_ipv4.isValid(),
363 {
364 gtpu_ipv4.version,
365 gtpu_ipv4.ihl,
366 gtpu_ipv4.dscp,
367 gtpu_ipv4.ecn,
368 gtpu_ipv4.total_len,
369 gtpu_ipv4.identification,
370 gtpu_ipv4.flags,
371 gtpu_ipv4.frag_offset,
372 gtpu_ipv4.ttl,
373 gtpu_ipv4.protocol,
374 gtpu_ipv4.src_addr,
375 gtpu_ipv4.dst_addr
376 },
377 gtpu_ipv4.hdr_checksum,
378 HashAlgorithm.csum16
379 );
380
381#ifdef WITH_SPGW_UDP_CSUM_UPDATE
382 // Compute outer UDP checksum.
383 update_checksum_with_payload(gtpu_udp.isValid(),
384 {
385 gtpu_ipv4.src_addr,
386 gtpu_ipv4.dst_addr,
387 8w0,
388 gtpu_ipv4.protocol,
389 gtpu_udp.len,
390 gtpu_udp.sport,
391 gtpu_udp.dport,
392 gtpu_udp.len,
393 gtpu,
394 ipv4,
395 // FIXME: we are assuming only UDP for downlink packets
396 // How to conditionally switch between UDP/TCP/ICMP?
397 udp
398 },
399 gtpu_udp.checksum,
400 HashAlgorithm.csum16
401 );
402#endif // WITH_SPGW_UDP_CSUM_UPDATE
403 }
404}
405
406#endif