blob: 22f68db3f91fe15c44fe18efb3856586dab630e0 [file] [log] [blame]
Carmelo Casconeb81f4be2018-01-16 23:24:01 -08001/*
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
21control spgw_ingress(
22 inout ipv4_t gtpu_ipv4,
23 inout udp_t gtpu_udp,
24 inout gtpu_t gtpu,
25 inout spgw_meta_t spgw_meta,
26 in ipv4_t ipv4
27 ) {
28
29 direct_counter(CounterType.packets_and_bytes) ue_counter;
30
31 action gtpu_decap() {
32 gtpu_ipv4.setInvalid();
33 gtpu_udp.setInvalid();
34 gtpu.setInvalid();
35 }
36
37 action set_sdf_rule_id(sdf_rule_id_t id) {
38 spgw_meta.sdf_rule_id = id;
39 }
40
41 action set_pcc_rule_id(pcc_rule_id_t id) {
42 spgw_meta.pcc_rule_id = id;
43 }
44
45 action set_pcc_info(pcc_gate_status_t gate_status) {
46 spgw_meta.pcc_gate_status = gate_status;
47 }
48
49 action set_dl_sess_info(bit<32> dl_sess_teid,
50 bit<32> dl_sess_enb_addr,
51 bit<32> dl_sess_s1u_addr) {
52 spgw_meta.dl_sess_teid = dl_sess_teid;
53 spgw_meta.dl_sess_enb_addr = dl_sess_enb_addr;
54 spgw_meta.dl_sess_s1u_addr = dl_sess_s1u_addr;
55 }
56
57 action update_ue_cdr() {
58 ue_counter.count();
59 }
60
61 table ue_filter_table {
62 key = {
63 // IP prefixes of the UEs managed by this switch.
64 ipv4.dst_addr : lpm;
65 }
66 actions = {
67 NoAction();
68 }
69 }
70
71 table s1u_filter_table {
72 key = {
73 // IP addresses of the S1U interfaces embodied by this switch.
74 gtpu_ipv4.dst_addr : exact;
75 }
76 actions = {
77 NoAction();
78 }
79 }
80
81 table sdf_rule_lookup {
82 key = {
83 spgw_meta.direction : exact;
84 ipv4.src_addr : ternary;
85 ipv4.dst_addr : ternary;
86 ipv4.protocol : ternary;
87 spgw_meta.l4_src_port : ternary;
88 spgw_meta.l4_dst_port : ternary;
89 }
90 actions = {
91 set_sdf_rule_id();
92 }
93 const default_action = set_sdf_rule_id(DEFAULT_SDF_RULE_ID);
94 }
95
96 table pcc_rule_lookup {
97 key = {
98 spgw_meta.sdf_rule_id : exact;
99 }
100 actions = {
101 set_pcc_rule_id();
102 }
103 const default_action = set_pcc_rule_id(DEFAULT_PCC_RULE_ID);
104 }
105
106 table pcc_info_lookup {
107 key = {
108 spgw_meta.pcc_rule_id : exact;
109 }
110 actions = {
111 set_pcc_info();
112 }
113 const default_action = set_pcc_info(PCC_GATE_OPEN);
114 }
115
116 table dl_sess_lookup {
117 key = {
118 // UE addr for downlink
119 ipv4.dst_addr : exact;
120 }
121 actions = {
122 set_dl_sess_info();
123 }
124 }
125
126 table ue_cdr_table {
127 key = {
128 // UE addr for downlink
129 ipv4.dst_addr : exact;
130 }
131 actions = {
132 update_ue_cdr();
133 }
134 counters = ue_counter;
135 }
136
137 apply {
138 // Admit only packets to known UE/S1U addresses, if so sets direction,
139 // otherwise skip SPGW processing.
140 spgw_meta.do_spgw = false;
141 if (gtpu.isValid() && gtpu.msgtype == GTP_GPDU) {
142 spgw_meta.direction = DIR_UPLINK;
143 if (s1u_filter_table.apply().hit) {
144 spgw_meta.do_spgw = true;
145 }
146 } else {
Carmelo Cascone14cde402018-01-25 01:57:18 -0800147 spgw_meta.direction = DIR_DOWNLINK;
Carmelo Casconeb81f4be2018-01-16 23:24:01 -0800148 if (ue_filter_table.apply().hit) {
149 spgw_meta.do_spgw = true;
150 }
151 }
152
153 if (!spgw_meta.do_spgw) {
154 // Exit this control block.
155 return;
156 }
157
158 if (spgw_meta.direction == DIR_UPLINK) {
159 gtpu_decap();
160 }
161
162 // Allow all traffic by default.
163 spgw_meta.pcc_gate_status = PCC_GATE_OPEN;
164
165 sdf_rule_lookup.apply();
166 pcc_rule_lookup.apply();
167 pcc_info_lookup.apply();
168
169 if (spgw_meta.pcc_gate_status == PCC_GATE_CLOSED) {
170 mark_to_drop();
171 exit;
172 }
173
174 if (spgw_meta.direction == DIR_DOWNLINK) {
175 if (!dl_sess_lookup.apply().hit) {
176 // We have no other choice than drop, as we miss the session
177 // info necessary to properly GTPU encap the packet.
178 mark_to_drop();
179 exit;
180 }
181 ue_cdr_table.apply();
182 }
183 }
184}
185
186
187control spgw_egress(
188 out ipv4_t gtpu_ipv4,
189 out udp_t gtpu_udp,
190 out gtpu_t gtpu,
191 in spgw_meta_t spgw_meta,
192 in standard_metadata_t std_meta
193 ) {
194
195 action gtpu_encap() {
Carmelo Cascone14cde402018-01-25 01:57:18 -0800196 gtpu_ipv4.setValid();
197 gtpu_ipv4.version = IP_VERSION_4;
198 gtpu_ipv4.ihl = IPV4_MIN_IHL;
199 gtpu_ipv4.diffserv = 0;
200 gtpu_ipv4.total_len = (bit<16>) (std_meta.packet_length
201 - ETH_HDR_SIZE + IPV4_HDR_SIZE + UDP_HDR_SIZE + GTP_HDR_SIZE);
202 gtpu_ipv4.identification = 0x1513; /* From NGIC */
203 gtpu_ipv4.flags = 0;
204 gtpu_ipv4.frag_offset = 0;
205 gtpu_ipv4.ttl = DEFAULT_IPV4_TTL;
206 gtpu_ipv4.protocol = PROTO_UDP;
207 gtpu_ipv4.dst_addr = spgw_meta.dl_sess_enb_addr;
208 gtpu_ipv4.src_addr = spgw_meta.dl_sess_s1u_addr;
209 gtpu_ipv4.hdr_checksum = 0; // Updated later
210
211 gtpu_udp.setValid();
212 gtpu_udp.src_port = UDP_PORT_GTPU;
213 gtpu_udp.dst_port = UDP_PORT_GTPU;
214 gtpu_udp.len = (bit<16>) (std_meta.packet_length
215 - ETH_HDR_SIZE + UDP_HDR_SIZE + GTP_HDR_SIZE);
216 gtpu_udp.checksum = 0; // Updated later
217
Carmelo Casconeb81f4be2018-01-16 23:24:01 -0800218 gtpu.setValid();
219 gtpu.version = GTPU_VERSION;
220 gtpu.pt = GTP_PROTOCOL_TYPE_GTP;
221 gtpu.spare = 0;
222 gtpu.ex_flag = 0;
223 gtpu.seq_flag = 0;
224 gtpu.npdu_flag = 0;
225 gtpu.msgtype = GTP_GPDU;
226 gtpu.msglen = (bit<16>) (std_meta.packet_length - ETH_HDR_SIZE);
227 gtpu.teid = spgw_meta.dl_sess_teid;
Carmelo Casconeb81f4be2018-01-16 23:24:01 -0800228 }
229
230 apply {
231 if (spgw_meta.do_spgw && spgw_meta.direction == DIR_DOWNLINK) {
232 gtpu_encap();
233 }
234 }
235}
236
237
Carmelo Cascone14cde402018-01-25 01:57:18 -0800238control verify_gtpu_checksum(
239 inout ipv4_t gtpu_ipv4
240 ) {
Carmelo Casconeb81f4be2018-01-16 23:24:01 -0800241 apply {
242 verify_checksum(gtpu_ipv4.isValid(),
243 {
244 gtpu_ipv4.version,
245 gtpu_ipv4.ihl,
246 gtpu_ipv4.diffserv,
247 gtpu_ipv4.total_len,
248 gtpu_ipv4.identification,
249 gtpu_ipv4.flags,
250 gtpu_ipv4.frag_offset,
251 gtpu_ipv4.ttl,
252 gtpu_ipv4.protocol,
253 gtpu_ipv4.src_addr,
254 gtpu_ipv4.dst_addr
255 },
256 gtpu_ipv4.hdr_checksum,
257 HashAlgorithm.csum16
258 );
259 }
260}
261
262
Carmelo Cascone14cde402018-01-25 01:57:18 -0800263control update_gtpu_checksum(
264 inout ipv4_t gtpu_ipv4,
265 inout udp_t gtpu_udp,
266 in gtpu_t gtpu,
267 in ipv4_t ipv4,
268 in udp_t udp
269 ) {
Carmelo Casconeb81f4be2018-01-16 23:24:01 -0800270 apply {
271 // Compute outer IPv4 checksum.
272 update_checksum(gtpu_ipv4.isValid(),
273 {
274 gtpu_ipv4.version,
275 gtpu_ipv4.ihl,
276 gtpu_ipv4.diffserv,
277 gtpu_ipv4.total_len,
278 gtpu_ipv4.identification,
279 gtpu_ipv4.flags,
280 gtpu_ipv4.frag_offset,
281 gtpu_ipv4.ttl,
282 gtpu_ipv4.protocol,
283 gtpu_ipv4.src_addr,
284 gtpu_ipv4.dst_addr
285 },
286 gtpu_ipv4.hdr_checksum,
287 HashAlgorithm.csum16
288 );
Carmelo Cascone14cde402018-01-25 01:57:18 -0800289
290 // Compute outer UDP checksum.
291 update_checksum_with_payload(gtpu_udp.isValid(),
292 {
293 gtpu_ipv4.src_addr,
294 gtpu_ipv4.dst_addr,
295 8w0,
296 gtpu_ipv4.protocol,
297 gtpu_udp.len,
298 gtpu_udp.src_port,
299 gtpu_udp.dst_port,
300 gtpu_udp.len,
301 gtpu,
302 ipv4,
303 // FIXME: we are assuming only UDP for downlink packets
304 // How to conditionally switch between UDP/TCP/ICMP?
305 udp
306 },
307 gtpu_udp.checksum,
308 HashAlgorithm.csum16
309 );
Carmelo Casconeb81f4be2018-01-16 23:24:01 -0800310 }
311}
312
313#endif