/src/open5gs/lib/pfcp/build.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2019-2024 by Sukchan Lee <acetcom@gmail.com> |
3 | | * |
4 | | * This file is part of Open5GS. |
5 | | * |
6 | | * This program is free software: you can redistribute it and/or modify |
7 | | * it under the terms of the GNU Affero General Public License as published by |
8 | | * the Free Software Foundation, either version 3 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * This program is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU General Public License |
17 | | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
18 | | */ |
19 | | |
20 | | #include "ogs-pfcp.h" |
21 | | |
22 | | ogs_pkbuf_t *ogs_pfcp_build_heartbeat_request(uint8_t type) |
23 | 0 | { |
24 | 0 | ogs_pfcp_message_t *pfcp_message = NULL; |
25 | 0 | ogs_pfcp_heartbeat_request_t *req = NULL; |
26 | 0 | ogs_pkbuf_t *pkbuf = NULL; |
27 | |
|
28 | 0 | ogs_debug("Heartbeat Request"); |
29 | |
|
30 | 0 | pfcp_message = ogs_calloc(1, sizeof(*pfcp_message)); |
31 | 0 | if (!pfcp_message) { |
32 | 0 | ogs_error("ogs_calloc() failed"); |
33 | 0 | return NULL; |
34 | 0 | } |
35 | | |
36 | 0 | req = &pfcp_message->pfcp_heartbeat_request; |
37 | |
|
38 | 0 | req->recovery_time_stamp.presence = 1; |
39 | 0 | req->recovery_time_stamp.u32 = ogs_pfcp_self()->local_recovery; |
40 | |
|
41 | 0 | pfcp_message->h.type = type; |
42 | 0 | pkbuf = ogs_pfcp_build_msg(pfcp_message); |
43 | 0 | ogs_expect(pkbuf); |
44 | |
|
45 | 0 | ogs_free(pfcp_message); |
46 | |
|
47 | 0 | return pkbuf; |
48 | 0 | } |
49 | | |
50 | | ogs_pkbuf_t *ogs_pfcp_build_heartbeat_response(uint8_t type) |
51 | 0 | { |
52 | 0 | ogs_pfcp_message_t *pfcp_message = NULL; |
53 | 0 | ogs_pfcp_heartbeat_response_t *rsp = NULL; |
54 | 0 | ogs_pkbuf_t *pkbuf = NULL; |
55 | |
|
56 | 0 | ogs_debug("Heartbeat Response"); |
57 | |
|
58 | 0 | pfcp_message = ogs_calloc(1, sizeof(*pfcp_message)); |
59 | 0 | if (!pfcp_message) { |
60 | 0 | ogs_error("ogs_calloc() failed"); |
61 | 0 | return NULL; |
62 | 0 | } |
63 | | |
64 | 0 | rsp = &pfcp_message->pfcp_heartbeat_response; |
65 | |
|
66 | 0 | rsp->recovery_time_stamp.presence = 1; |
67 | 0 | rsp->recovery_time_stamp.u32 = ogs_pfcp_self()->local_recovery; |
68 | |
|
69 | 0 | pfcp_message->h.type = type; |
70 | 0 | pkbuf = ogs_pfcp_build_msg(pfcp_message); |
71 | 0 | ogs_expect(pkbuf); |
72 | |
|
73 | 0 | ogs_free(pfcp_message); |
74 | |
|
75 | 0 | return pkbuf; |
76 | 0 | } |
77 | | |
78 | | ogs_pkbuf_t *ogs_pfcp_cp_build_association_setup_request(uint8_t type) |
79 | 0 | { |
80 | 0 | ogs_pfcp_message_t *pfcp_message = NULL; |
81 | 0 | ogs_pfcp_association_setup_request_t *req = NULL; |
82 | 0 | ogs_pkbuf_t *pkbuf = NULL; |
83 | |
|
84 | 0 | ogs_pfcp_node_id_t node_id; |
85 | 0 | int node_id_len = 0, rv; |
86 | |
|
87 | 0 | ogs_debug("Association Setup Request"); |
88 | |
|
89 | 0 | pfcp_message = ogs_calloc(1, sizeof(*pfcp_message)); |
90 | 0 | if (!pfcp_message) { |
91 | 0 | ogs_error("ogs_calloc() failed"); |
92 | 0 | return NULL; |
93 | 0 | } |
94 | | |
95 | 0 | req = &pfcp_message->pfcp_association_setup_request; |
96 | |
|
97 | 0 | rv = ogs_pfcp_sockaddr_to_node_id(&node_id, &node_id_len); |
98 | 0 | if (rv != OGS_OK) { |
99 | 0 | ogs_error("ogs_pfcp_sockaddr_to_node_id() failed"); |
100 | 0 | ogs_free(pfcp_message); |
101 | 0 | return NULL; |
102 | 0 | } |
103 | 0 | req->node_id.presence = 1; |
104 | 0 | req->node_id.data = &node_id; |
105 | 0 | req->node_id.len = node_id_len; |
106 | |
|
107 | 0 | req->recovery_time_stamp.presence = 1; |
108 | 0 | req->recovery_time_stamp.u32 = ogs_pfcp_self()->local_recovery; |
109 | |
|
110 | 0 | req->cp_function_features.presence = 1; |
111 | 0 | req->cp_function_features.u8 = ogs_pfcp_self()->cp_function_features.octet5; |
112 | |
|
113 | 0 | pfcp_message->h.type = type; |
114 | 0 | pkbuf = ogs_pfcp_build_msg(pfcp_message); |
115 | 0 | ogs_expect(pkbuf); |
116 | |
|
117 | 0 | ogs_free(pfcp_message); |
118 | |
|
119 | 0 | return pkbuf; |
120 | 0 | } |
121 | | |
122 | | ogs_pkbuf_t *ogs_pfcp_cp_build_association_setup_response(uint8_t type, |
123 | | uint8_t cause) |
124 | 0 | { |
125 | 0 | ogs_pfcp_message_t *pfcp_message = NULL; |
126 | 0 | ogs_pfcp_association_setup_response_t *rsp = NULL; |
127 | 0 | ogs_pkbuf_t *pkbuf = NULL; |
128 | |
|
129 | 0 | ogs_pfcp_node_id_t node_id; |
130 | 0 | int node_id_len = 0, rv; |
131 | |
|
132 | 0 | ogs_debug("Association Setup Response"); |
133 | |
|
134 | 0 | pfcp_message = ogs_calloc(1, sizeof(*pfcp_message)); |
135 | 0 | if (!pfcp_message) { |
136 | 0 | ogs_error("ogs_calloc() failed"); |
137 | 0 | return NULL; |
138 | 0 | } |
139 | | |
140 | 0 | rsp = &pfcp_message->pfcp_association_setup_response; |
141 | |
|
142 | 0 | rv = ogs_pfcp_sockaddr_to_node_id(&node_id, &node_id_len); |
143 | 0 | if (rv != OGS_OK) { |
144 | 0 | ogs_error("ogs_pfcp_sockaddr_to_node_id() failed"); |
145 | 0 | ogs_free(pfcp_message); |
146 | 0 | return NULL; |
147 | 0 | } |
148 | 0 | rsp->node_id.presence = 1; |
149 | 0 | rsp->node_id.data = &node_id; |
150 | 0 | rsp->node_id.len = node_id_len; |
151 | |
|
152 | 0 | rsp->cause.presence = 1; |
153 | 0 | rsp->cause.u8 = cause; |
154 | |
|
155 | 0 | rsp->recovery_time_stamp.presence = 1; |
156 | 0 | rsp->recovery_time_stamp.u32 = ogs_pfcp_self()->local_recovery; |
157 | |
|
158 | 0 | rsp->cp_function_features.presence = 1; |
159 | 0 | rsp->cp_function_features.u8 = ogs_pfcp_self()->cp_function_features.octet5; |
160 | |
|
161 | 0 | pfcp_message->h.type = type; |
162 | 0 | pkbuf = ogs_pfcp_build_msg(pfcp_message); |
163 | 0 | ogs_expect(pkbuf); |
164 | |
|
165 | 0 | ogs_free(pfcp_message); |
166 | |
|
167 | 0 | return pkbuf; |
168 | 0 | } |
169 | | |
170 | | ogs_pkbuf_t *ogs_pfcp_up_build_association_setup_request(uint8_t type) |
171 | 0 | { |
172 | 0 | ogs_pfcp_message_t *pfcp_message = NULL; |
173 | 0 | ogs_pfcp_association_setup_request_t *req = NULL; |
174 | 0 | ogs_pkbuf_t *pkbuf = NULL; |
175 | |
|
176 | 0 | ogs_pfcp_node_id_t node_id; |
177 | 0 | int node_id_len = 0, rv; |
178 | |
|
179 | 0 | ogs_debug("Association Setup Request"); |
180 | |
|
181 | 0 | pfcp_message = ogs_calloc(1, sizeof(*pfcp_message)); |
182 | 0 | if (!pfcp_message) { |
183 | 0 | ogs_error("ogs_calloc() failed"); |
184 | 0 | return NULL; |
185 | 0 | } |
186 | | |
187 | 0 | req = &pfcp_message->pfcp_association_setup_request; |
188 | |
|
189 | 0 | rv = ogs_pfcp_sockaddr_to_node_id(&node_id, &node_id_len); |
190 | 0 | if (rv != OGS_OK) { |
191 | 0 | ogs_error("ogs_pfcp_sockaddr_to_node_id() failed"); |
192 | 0 | ogs_free(pfcp_message); |
193 | 0 | return NULL; |
194 | 0 | } |
195 | 0 | req->node_id.presence = 1; |
196 | 0 | req->node_id.data = &node_id; |
197 | 0 | req->node_id.len = node_id_len; |
198 | |
|
199 | 0 | req->recovery_time_stamp.presence = 1; |
200 | 0 | req->recovery_time_stamp.u32 = ogs_pfcp_self()->local_recovery; |
201 | |
|
202 | 0 | ogs_assert(ogs_pfcp_self()->up_function_features_len); |
203 | 0 | req->up_function_features.presence = 1; |
204 | 0 | req->up_function_features.data = &ogs_pfcp_self()->up_function_features; |
205 | 0 | req->up_function_features.len = ogs_pfcp_self()->up_function_features_len; |
206 | |
|
207 | 0 | pfcp_message->h.type = type; |
208 | 0 | pkbuf = ogs_pfcp_build_msg(pfcp_message); |
209 | 0 | ogs_expect(pkbuf); |
210 | |
|
211 | 0 | ogs_free(pfcp_message); |
212 | |
|
213 | 0 | return pkbuf; |
214 | 0 | } |
215 | | |
216 | | ogs_pkbuf_t *ogs_pfcp_up_build_association_setup_response(uint8_t type, |
217 | | uint8_t cause) |
218 | 0 | { |
219 | 0 | ogs_pfcp_message_t *pfcp_message = NULL; |
220 | 0 | ogs_pfcp_association_setup_response_t *rsp = NULL; |
221 | 0 | ogs_pkbuf_t *pkbuf = NULL; |
222 | |
|
223 | 0 | ogs_pfcp_node_id_t node_id; |
224 | 0 | int node_id_len = 0, rv; |
225 | |
|
226 | 0 | ogs_debug("Association Setup Response"); |
227 | |
|
228 | 0 | pfcp_message = ogs_calloc(1, sizeof(*pfcp_message)); |
229 | 0 | if (!pfcp_message) { |
230 | 0 | ogs_error("ogs_calloc() failed"); |
231 | 0 | return NULL; |
232 | 0 | } |
233 | | |
234 | 0 | rsp = &pfcp_message->pfcp_association_setup_response; |
235 | |
|
236 | 0 | rv = ogs_pfcp_sockaddr_to_node_id(&node_id, &node_id_len); |
237 | 0 | if (rv != OGS_OK) { |
238 | 0 | ogs_error("ogs_pfcp_sockaddr_to_node_id() failed"); |
239 | 0 | ogs_free(pfcp_message); |
240 | 0 | return NULL; |
241 | 0 | } |
242 | 0 | rsp->node_id.presence = 1; |
243 | 0 | rsp->node_id.data = &node_id; |
244 | 0 | rsp->node_id.len = node_id_len; |
245 | |
|
246 | 0 | rsp->cause.presence = 1; |
247 | 0 | rsp->cause.u8 = cause; |
248 | |
|
249 | 0 | rsp->recovery_time_stamp.presence = 1; |
250 | 0 | rsp->recovery_time_stamp.u32 = ogs_pfcp_self()->local_recovery; |
251 | |
|
252 | 0 | ogs_assert(ogs_pfcp_self()->up_function_features_len); |
253 | 0 | rsp->up_function_features.presence = 1; |
254 | 0 | rsp->up_function_features.data = &ogs_pfcp_self()->up_function_features; |
255 | 0 | rsp->up_function_features.len = ogs_pfcp_self()->up_function_features_len; |
256 | |
|
257 | 0 | pfcp_message->h.type = type; |
258 | 0 | pkbuf = ogs_pfcp_build_msg(pfcp_message); |
259 | 0 | ogs_expect(pkbuf); |
260 | |
|
261 | 0 | ogs_free(pfcp_message); |
262 | |
|
263 | 0 | return pkbuf; |
264 | 0 | } |
265 | | |
266 | | static struct { |
267 | | ogs_pfcp_f_teid_t f_teid; |
268 | | char dnn[OGS_MAX_DNN_LEN+1]; |
269 | | char *sdf_filter[OGS_MAX_NUM_OF_FLOW_IN_PDR]; |
270 | | } pdrbuf[OGS_MAX_NUM_OF_PDR]; |
271 | | |
272 | | void ogs_pfcp_pdrbuf_init(void) |
273 | 0 | { |
274 | 0 | memset(pdrbuf, 0, sizeof(pdrbuf)); |
275 | 0 | } |
276 | | |
277 | | void ogs_pfcp_pdrbuf_clear(void) |
278 | 0 | { |
279 | 0 | int i, j; |
280 | 0 | for (i = 0; i < OGS_MAX_NUM_OF_PDR; i++) { |
281 | 0 | for (j = 0; j < OGS_MAX_NUM_OF_FLOW_IN_PDR; j++) { |
282 | 0 | if (pdrbuf[i].sdf_filter[j]) |
283 | 0 | ogs_free(pdrbuf[i].sdf_filter[j]); |
284 | 0 | } |
285 | 0 | } |
286 | 0 | } |
287 | | |
288 | | void ogs_pfcp_build_create_pdr( |
289 | | ogs_pfcp_tlv_create_pdr_t *message, int i, ogs_pfcp_pdr_t *pdr) |
290 | 0 | { |
291 | 0 | ogs_pfcp_far_t *far = NULL; |
292 | 0 | ogs_pfcp_sdf_filter_t pfcp_sdf_filter[OGS_MAX_NUM_OF_FLOW_IN_PDR]; |
293 | 0 | int j = 0; |
294 | 0 | int len = 0; |
295 | |
|
296 | 0 | ogs_assert(message); |
297 | | |
298 | 0 | ogs_assert(pdr); |
299 | | |
300 | 0 | far = pdr->far; |
301 | 0 | ogs_assert(far); |
302 | | |
303 | 0 | message->presence = 1; |
304 | 0 | message->pdr_id.presence = 1; |
305 | 0 | message->pdr_id.u16 = pdr->id; |
306 | |
|
307 | 0 | if (pdr->precedence) { /* No precedence in Sxa */ |
308 | 0 | message->precedence.presence = 1; |
309 | 0 | message->precedence.u32 = pdr->precedence; |
310 | 0 | } |
311 | |
|
312 | 0 | message->pdi.presence = 1; |
313 | 0 | message->pdi.source_interface.presence = 1; |
314 | 0 | message->pdi.source_interface.u8 = pdr->src_if; |
315 | |
|
316 | 0 | if (pdr->src_if_type_presence) { |
317 | 0 | message->pdi.source_interface_type.presence = 1; |
318 | 0 | message->pdi.source_interface_type.u8 = pdr->src_if_type; |
319 | 0 | } |
320 | |
|
321 | 0 | if (pdr->dnn) { |
322 | 0 | message->pdi.network_instance.presence = 1; |
323 | 0 | message->pdi.network_instance.len = ogs_fqdn_build( |
324 | 0 | pdrbuf[i].dnn, pdr->dnn, strlen(pdr->dnn)); |
325 | 0 | message->pdi.network_instance.data = pdrbuf[i].dnn; |
326 | 0 | } |
327 | |
|
328 | 0 | memset(pfcp_sdf_filter, 0, sizeof(pfcp_sdf_filter)); |
329 | 0 | for (j = 0; j < pdr->num_of_flow && j < OGS_MAX_NUM_OF_FLOW_IN_PDR; j++) { |
330 | 0 | ogs_assert(pdr->flow[j].fd || pdr->flow[j].bid); |
331 | | |
332 | 0 | if (pdr->flow[j].fd) { |
333 | 0 | pfcp_sdf_filter[j].fd = 1; |
334 | 0 | pfcp_sdf_filter[j].flow_description_len = |
335 | 0 | strlen(pdr->flow[j].description); |
336 | 0 | pfcp_sdf_filter[j].flow_description = pdr->flow[j].description; |
337 | 0 | } |
338 | 0 | if (pdr->flow[j].bid) { |
339 | 0 | pfcp_sdf_filter[j].bid = 1; |
340 | 0 | pfcp_sdf_filter[j].sdf_filter_id = pdr->flow[j].sdf_filter_id; |
341 | 0 | } |
342 | |
|
343 | 0 | len = sizeof(ogs_pfcp_sdf_filter_t) + |
344 | 0 | pfcp_sdf_filter[j].flow_description_len; |
345 | |
|
346 | 0 | message->pdi.sdf_filter[j].presence = 1; |
347 | 0 | pdrbuf[i].sdf_filter[j] = ogs_calloc(1, len); |
348 | 0 | ogs_assert(pdrbuf[i].sdf_filter[j]); |
349 | 0 | ogs_pfcp_build_sdf_filter(&message->pdi.sdf_filter[j], |
350 | 0 | &pfcp_sdf_filter[j], pdrbuf[i].sdf_filter[j], len); |
351 | 0 | } |
352 | | |
353 | 0 | if (pdr->ue_ip_addr_len) { |
354 | 0 | message->pdi.ue_ip_address.presence = 1; |
355 | 0 | message->pdi.ue_ip_address.data = &pdr->ue_ip_addr; |
356 | 0 | message->pdi.ue_ip_address.len = pdr->ue_ip_addr_len; |
357 | 0 | } |
358 | |
|
359 | 0 | for (j = 0; j < OGS_MAX_NUM_OF_FRAMED_ROUTES_IN_PDI; j++) { |
360 | 0 | if (!pdr->ipv4_framed_routes || !pdr->ipv4_framed_routes[j]) |
361 | 0 | break; |
362 | 0 | message->pdi.framed_route[j].presence = 1; |
363 | 0 | message->pdi.framed_route[j].data = pdr->ipv4_framed_routes[j]; |
364 | 0 | message->pdi.framed_route[j].len = strlen(pdr->ipv4_framed_routes[j]); |
365 | 0 | ogs_info("PFCP encode Framed-Route in PDR[%u]: %s", |
366 | 0 | pdr->id, pdr->ipv4_framed_routes[j]); |
367 | 0 | } |
368 | |
|
369 | 0 | for (j = 0; j < OGS_MAX_NUM_OF_FRAMED_ROUTES_IN_PDI; j++) { |
370 | 0 | if (!pdr->ipv6_framed_routes || !pdr->ipv6_framed_routes[j]) |
371 | 0 | break; |
372 | 0 | message->pdi.framed_ipv6_route[j].presence = 1; |
373 | 0 | message->pdi.framed_ipv6_route[j].data = pdr->ipv6_framed_routes[j]; |
374 | 0 | message->pdi.framed_ipv6_route[j].len = strlen(pdr->ipv6_framed_routes[j]); |
375 | 0 | ogs_info("PFCP encode Framed-IPv6-Route in PDR[%u]: %s", |
376 | 0 | pdr->id, pdr->ipv6_framed_routes[j]); |
377 | 0 | } |
378 | |
|
379 | 0 | if (pdr->f_teid_len) { |
380 | 0 | int f_teid_len = |
381 | 0 | ogs_min(pdr->f_teid_len, sizeof(pdrbuf[i].f_teid)); |
382 | |
|
383 | 0 | memcpy(&pdrbuf[i].f_teid, &pdr->f_teid, f_teid_len); |
384 | 0 | pdrbuf[i].f_teid.teid = htobe32(pdr->f_teid.teid); |
385 | |
|
386 | 0 | message->pdi.local_f_teid.presence = 1; |
387 | 0 | message->pdi.local_f_teid.data = &pdrbuf[i].f_teid; |
388 | 0 | message->pdi.local_f_teid.len = f_teid_len; |
389 | 0 | } |
390 | |
|
391 | 0 | if (pdr->qfi) { |
392 | 0 | message->pdi.qfi.presence = 1; |
393 | 0 | message->pdi.qfi.u8 = pdr->qfi; |
394 | 0 | } |
395 | |
|
396 | 0 | if (pdr->outer_header_removal_len) { |
397 | 0 | message->outer_header_removal.presence = 1; |
398 | 0 | message->outer_header_removal.data = &pdr->outer_header_removal; |
399 | 0 | message->outer_header_removal.len = pdr->outer_header_removal_len; |
400 | 0 | } |
401 | |
|
402 | 0 | if (pdr->far) { |
403 | 0 | message->far_id.presence = 1; |
404 | 0 | message->far_id.u32 = pdr->far->id; |
405 | 0 | } |
406 | |
|
407 | 0 | ogs_assert(pdr->num_of_urr <= OGS_ARRAY_SIZE(message->urr_id)); |
408 | 0 | for (i = 0; i < pdr->num_of_urr; i++) { |
409 | 0 | message->urr_id[i].presence = 1; |
410 | 0 | message->urr_id[i].u32 = pdr->urr[i]->id; |
411 | 0 | } |
412 | |
|
413 | 0 | if (pdr->qer) { |
414 | 0 | message->qer_id.presence = 1; |
415 | 0 | message->qer_id.u32 = pdr->qer->id; |
416 | 0 | } |
417 | 0 | } |
418 | | |
419 | | bool ogs_pfcp_build_created_pdr( |
420 | | ogs_pfcp_tlv_created_pdr_t *message, int i, ogs_pfcp_pdr_t *pdr) |
421 | 0 | { |
422 | 0 | bool pdr_presence = false; |
423 | |
|
424 | 0 | ogs_assert(message); |
425 | | |
426 | 0 | ogs_assert(pdr); |
427 | | |
428 | 0 | if (pdr->f_teid_len) { |
429 | 0 | int f_teid_len = |
430 | 0 | ogs_min(pdr->f_teid_len, sizeof(pdrbuf[i].f_teid)); |
431 | |
|
432 | 0 | memcpy(&pdrbuf[i].f_teid, &pdr->f_teid, f_teid_len); |
433 | 0 | pdrbuf[i].f_teid.teid = htobe32(pdr->f_teid.teid); |
434 | |
|
435 | 0 | message->local_f_teid.presence = 1; |
436 | 0 | message->local_f_teid.data = &pdrbuf[i].f_teid; |
437 | 0 | message->local_f_teid.len = f_teid_len; |
438 | |
|
439 | 0 | pdr_presence = true; |
440 | 0 | } |
441 | |
|
442 | 0 | if (pdr_presence == true) { |
443 | 0 | message->presence = 1; |
444 | 0 | message->pdr_id.presence = 1; |
445 | 0 | message->pdr_id.u16 = pdr->id; |
446 | 0 | } |
447 | |
|
448 | 0 | return pdr_presence; |
449 | 0 | } |
450 | | |
451 | | void ogs_pfcp_build_update_pdr( |
452 | | ogs_pfcp_tlv_update_pdr_t *message, int i, |
453 | | ogs_pfcp_pdr_t *pdr, uint64_t modify_flags) |
454 | 0 | { |
455 | 0 | ogs_pfcp_sdf_filter_t pfcp_sdf_filter[OGS_MAX_NUM_OF_FLOW_IN_PDR]; |
456 | 0 | int j = 0; |
457 | 0 | int len = 0; |
458 | |
|
459 | 0 | ogs_assert(message); |
460 | 0 | ogs_assert(pdr); |
461 | | |
462 | 0 | ogs_assert(modify_flags & |
463 | 0 | (OGS_PFCP_MODIFY_TFT_NEW|OGS_PFCP_MODIFY_TFT_ADD| |
464 | 0 | OGS_PFCP_MODIFY_TFT_REPLACE|OGS_PFCP_MODIFY_TFT_DELETE| |
465 | 0 | OGS_PFCP_MODIFY_EPC_TFT_UPDATE| |
466 | 0 | OGS_PFCP_MODIFY_OUTER_HEADER_REMOVAL)); |
467 | | |
468 | 0 | message->presence = 1; |
469 | 0 | message->pdr_id.presence = 1; |
470 | 0 | message->pdr_id.u16 = pdr->id; |
471 | |
|
472 | 0 | if (modify_flags & |
473 | 0 | (OGS_PFCP_MODIFY_TFT_NEW|OGS_PFCP_MODIFY_TFT_ADD| |
474 | 0 | OGS_PFCP_MODIFY_TFT_REPLACE|OGS_PFCP_MODIFY_TFT_DELETE| |
475 | 0 | OGS_PFCP_MODIFY_EPC_TFT_UPDATE)) { |
476 | 0 | message->pdi.presence = 1; |
477 | 0 | message->pdi.source_interface.presence = 1; |
478 | 0 | message->pdi.source_interface.u8 = pdr->src_if; |
479 | |
|
480 | 0 | if (pdr->src_if_type_presence) { |
481 | 0 | message->pdi.source_interface_type.presence = 1; |
482 | 0 | message->pdi.source_interface_type.u8 = pdr->src_if_type; |
483 | 0 | } |
484 | |
|
485 | 0 | memset(pfcp_sdf_filter, 0, sizeof(pfcp_sdf_filter)); |
486 | 0 | for (j = 0; j < pdr->num_of_flow && j < OGS_MAX_NUM_OF_FLOW_IN_PDR; j++) { |
487 | 0 | ogs_assert(pdr->flow[j].fd || pdr->flow[j].bid); |
488 | | |
489 | 0 | if (pdr->flow[j].fd) { |
490 | 0 | pfcp_sdf_filter[j].fd = 1; |
491 | 0 | pfcp_sdf_filter[j].flow_description_len = |
492 | 0 | strlen(pdr->flow[j].description); |
493 | 0 | pfcp_sdf_filter[j].flow_description = pdr->flow[j].description; |
494 | 0 | } |
495 | 0 | if (pdr->flow[j].bid) { |
496 | 0 | pfcp_sdf_filter[j].bid = 1; |
497 | 0 | pfcp_sdf_filter[j].sdf_filter_id = pdr->flow[j].sdf_filter_id; |
498 | 0 | } |
499 | |
|
500 | 0 | len = sizeof(ogs_pfcp_sdf_filter_t) + |
501 | 0 | pfcp_sdf_filter[j].flow_description_len; |
502 | |
|
503 | 0 | message->pdi.sdf_filter[j].presence = 1; |
504 | 0 | pdrbuf[i].sdf_filter[j] = ogs_calloc(1, len); |
505 | 0 | ogs_assert(pdrbuf[i].sdf_filter[j]); |
506 | 0 | ogs_pfcp_build_sdf_filter(&message->pdi.sdf_filter[j], |
507 | 0 | &pfcp_sdf_filter[j], pdrbuf[i].sdf_filter[j], len); |
508 | 0 | } |
509 | 0 | } |
510 | 0 | if (modify_flags & OGS_PFCP_MODIFY_OUTER_HEADER_REMOVAL) { |
511 | 0 | if (pdr->outer_header_removal_len) { |
512 | 0 | message->outer_header_removal.presence = 1; |
513 | 0 | message->outer_header_removal.data = &pdr->outer_header_removal; |
514 | 0 | message->outer_header_removal.len = pdr->outer_header_removal_len; |
515 | 0 | } |
516 | 0 | } |
517 | 0 | } |
518 | | |
519 | | static struct { |
520 | | ogs_pfcp_outer_header_creation_t outer_header_creation; |
521 | | char dnn[OGS_MAX_DNN_LEN+1]; |
522 | | } farbuf[OGS_MAX_NUM_OF_FAR]; |
523 | | |
524 | | void ogs_pfcp_build_create_far( |
525 | | ogs_pfcp_tlv_create_far_t *message, int i, ogs_pfcp_far_t *far) |
526 | 0 | { |
527 | 0 | ogs_pfcp_sess_t *sess = NULL; |
528 | |
|
529 | 0 | ogs_assert(message); |
530 | 0 | ogs_assert(far); |
531 | 0 | sess = far->sess; |
532 | 0 | ogs_assert(sess); |
533 | | |
534 | 0 | message->presence = 1; |
535 | 0 | message->far_id.presence = 1; |
536 | 0 | message->far_id.u32 = far->id; |
537 | |
|
538 | 0 | message->apply_action.presence = 1; |
539 | 0 | message->apply_action.u16 = far->apply_action; |
540 | |
|
541 | 0 | if (far->apply_action & OGS_PFCP_APPLY_ACTION_FORW) { |
542 | 0 | message->forwarding_parameters.presence = 1; |
543 | 0 | message->forwarding_parameters.destination_interface.presence = 1; |
544 | 0 | message->forwarding_parameters.destination_interface.u8 = |
545 | 0 | far->dst_if; |
546 | |
|
547 | 0 | if (far->dst_if_type_presence) { |
548 | 0 | message->forwarding_parameters.destination_interface_type. |
549 | 0 | presence = 1; |
550 | 0 | message->forwarding_parameters.destination_interface_type. |
551 | 0 | u8 = far->dst_if_type; |
552 | 0 | } |
553 | |
|
554 | 0 | if (far->dnn) { |
555 | 0 | message->forwarding_parameters.network_instance.presence = 1; |
556 | 0 | message->forwarding_parameters.network_instance.len = |
557 | 0 | ogs_fqdn_build(farbuf[i].dnn, far->dnn, strlen(far->dnn)); |
558 | 0 | message->forwarding_parameters.network_instance.data = |
559 | 0 | farbuf[i].dnn; |
560 | 0 | } |
561 | |
|
562 | 0 | if (far->outer_header_creation_len) { |
563 | 0 | memcpy(&farbuf[i].outer_header_creation, |
564 | 0 | &far->outer_header_creation, far->outer_header_creation_len); |
565 | 0 | farbuf[i].outer_header_creation.teid = |
566 | 0 | htobe32(far->outer_header_creation.teid); |
567 | |
|
568 | 0 | message->forwarding_parameters.outer_header_creation.presence = 1; |
569 | 0 | message->forwarding_parameters.outer_header_creation.data = |
570 | 0 | &farbuf[i].outer_header_creation; |
571 | 0 | message->forwarding_parameters.outer_header_creation.len = |
572 | 0 | far->outer_header_creation_len; |
573 | 0 | } |
574 | 0 | } else if (far->apply_action & OGS_PFCP_APPLY_ACTION_BUFF) { |
575 | 0 | ogs_assert(sess->bar); |
576 | 0 | message->bar_id.presence = 1; |
577 | 0 | message->bar_id.u8 = sess->bar->id; |
578 | 0 | } |
579 | 0 | } |
580 | | |
581 | | void ogs_pfcp_build_update_far_deactivate( |
582 | | ogs_pfcp_tlv_update_far_t *message, int i, ogs_pfcp_far_t *far) |
583 | 0 | { |
584 | 0 | ogs_pfcp_sess_t *sess = NULL; |
585 | |
|
586 | 0 | ogs_assert(message); |
587 | 0 | ogs_assert(far); |
588 | 0 | sess = far->sess; |
589 | 0 | ogs_assert(sess); |
590 | | |
591 | 0 | message->presence = 1; |
592 | 0 | message->far_id.presence = 1; |
593 | 0 | message->far_id.u32 = far->id; |
594 | |
|
595 | 0 | far->apply_action = |
596 | 0 | OGS_PFCP_APPLY_ACTION_BUFF | OGS_PFCP_APPLY_ACTION_NOCP; |
597 | 0 | message->apply_action.presence = 1; |
598 | 0 | message->apply_action.u16 = far->apply_action; |
599 | |
|
600 | 0 | ogs_assert(sess->bar); |
601 | 0 | message->bar_id.presence = 1; |
602 | 0 | message->bar_id.u8 = sess->bar->id; |
603 | 0 | } |
604 | | |
605 | | void ogs_pfcp_build_update_far_activate( |
606 | | ogs_pfcp_tlv_update_far_t *message, int i, ogs_pfcp_far_t *far) |
607 | 0 | { |
608 | 0 | ogs_assert(message); |
609 | 0 | ogs_assert(far); |
610 | | |
611 | 0 | message->presence = 1; |
612 | 0 | message->far_id.presence = 1; |
613 | 0 | message->far_id.u32 = far->id; |
614 | |
|
615 | 0 | ogs_assert(far->apply_action & OGS_PFCP_APPLY_ACTION_FORW); |
616 | | |
617 | 0 | message->apply_action.presence = 1; |
618 | 0 | message->apply_action.u16 = far->apply_action; |
619 | |
|
620 | 0 | message->update_forwarding_parameters.presence = 1; |
621 | 0 | message->update_forwarding_parameters.destination_interface.presence = 1; |
622 | 0 | message->update_forwarding_parameters. |
623 | 0 | destination_interface.u8 = far->dst_if; |
624 | |
|
625 | 0 | if (far->dst_if_type_presence) { |
626 | 0 | message->update_forwarding_parameters.destination_interface_type. |
627 | 0 | presence = 1; |
628 | 0 | message->update_forwarding_parameters.destination_interface_type. |
629 | 0 | u8 = far->dst_if_type; |
630 | 0 | } |
631 | |
|
632 | 0 | if (far->dnn) { |
633 | 0 | message->update_forwarding_parameters.network_instance.presence = 1; |
634 | 0 | message->update_forwarding_parameters.network_instance.len = |
635 | 0 | ogs_fqdn_build(farbuf[i].dnn, far->dnn, strlen(far->dnn)); |
636 | 0 | message->update_forwarding_parameters.network_instance.data = |
637 | 0 | farbuf[i].dnn; |
638 | 0 | } |
639 | |
|
640 | 0 | if (far->outer_header_creation_len || far->smreq_flags.value) { |
641 | |
|
642 | 0 | if (far->outer_header_creation_len) { |
643 | 0 | memcpy(&farbuf[i].outer_header_creation, |
644 | 0 | &far->outer_header_creation, far->outer_header_creation_len); |
645 | 0 | farbuf[i].outer_header_creation.teid = |
646 | 0 | htobe32(far->outer_header_creation.teid); |
647 | |
|
648 | 0 | message->update_forwarding_parameters. |
649 | 0 | outer_header_creation.presence = 1; |
650 | 0 | message->update_forwarding_parameters. |
651 | 0 | outer_header_creation.data = &farbuf[i].outer_header_creation; |
652 | 0 | message->update_forwarding_parameters. |
653 | 0 | outer_header_creation.len = far->outer_header_creation_len; |
654 | |
|
655 | 0 | } |
656 | |
|
657 | 0 | if (far->smreq_flags.value) { |
658 | 0 | message->update_forwarding_parameters.pfcpsmreq_flags.presence = 1; |
659 | 0 | message->update_forwarding_parameters.pfcpsmreq_flags.u8 = |
660 | 0 | far->smreq_flags.value; |
661 | 0 | } |
662 | 0 | } |
663 | 0 | } |
664 | | |
665 | | static struct { |
666 | | ogs_pfcp_volume_threshold_t vol_threshold; |
667 | | ogs_pfcp_volume_quota_t vol_quota; |
668 | | ogs_pfcp_dropped_dl_traffic_threshold_t dropped_dl_traffic_threshold; |
669 | | } urrbuf[OGS_MAX_NUM_OF_URR]; |
670 | | |
671 | | void ogs_pfcp_build_create_urr( |
672 | | ogs_pfcp_tlv_create_urr_t *message, int i, ogs_pfcp_urr_t *urr) |
673 | 0 | { |
674 | 0 | ogs_assert(message); |
675 | 0 | ogs_assert(urr); |
676 | | |
677 | 0 | message->presence = 1; |
678 | 0 | message->urr_id.presence = 1; |
679 | 0 | message->urr_id.u32 = urr->id; |
680 | 0 | message->measurement_method.presence = 1; |
681 | 0 | message->measurement_method.u8 = urr->meas_method; |
682 | 0 | message->reporting_triggers.presence = 1; |
683 | 0 | message->reporting_triggers.u24 = (urr->rep_triggers.reptri_5 << 16) |
684 | 0 | | (urr->rep_triggers.reptri_6 << 8) |
685 | 0 | | (urr->rep_triggers.reptri_7); |
686 | 0 | if (urr->meas_period) { |
687 | 0 | message->measurement_period.presence = 1; |
688 | 0 | message->measurement_period.u32 = urr->meas_period; |
689 | 0 | } |
690 | |
|
691 | 0 | if (urr->vol_threshold.flags) { |
692 | 0 | message->volume_threshold.presence = 1; |
693 | 0 | ogs_pfcp_build_volume( |
694 | 0 | &message->volume_threshold, &urr->vol_threshold, |
695 | 0 | &urrbuf[i].vol_threshold, sizeof(urrbuf[i].vol_threshold)); |
696 | 0 | } |
697 | |
|
698 | 0 | if (urr->vol_quota.flags) { |
699 | 0 | message->volume_quota.presence = 1; |
700 | 0 | ogs_pfcp_build_volume( |
701 | 0 | &message->volume_quota, &urr->vol_quota, |
702 | 0 | &urrbuf[i].vol_quota, sizeof(urrbuf[i].vol_quota)); |
703 | 0 | } |
704 | |
|
705 | 0 | if (urr->event_threshold) { |
706 | 0 | message->event_threshold.presence = 1; |
707 | 0 | message->event_threshold.u32 = urr->event_threshold; |
708 | 0 | } |
709 | |
|
710 | 0 | if (urr->event_quota) { |
711 | 0 | message->event_quota.presence = 1; |
712 | 0 | message->event_quota.u32 = urr->event_quota; |
713 | 0 | } |
714 | |
|
715 | 0 | if (urr->time_threshold) { |
716 | 0 | message->time_threshold.presence = 1; |
717 | 0 | message->time_threshold.u32 = urr->time_threshold; |
718 | 0 | } |
719 | |
|
720 | 0 | if (urr->time_quota) { |
721 | 0 | message->time_quota.presence = 1; |
722 | 0 | message->time_quota.u32 = urr->time_quota; |
723 | 0 | } |
724 | |
|
725 | 0 | if (urr->quota_holding_time) { |
726 | 0 | message->quota_holding_time.presence = 1; |
727 | 0 | message->quota_holding_time.u32 = urr->quota_holding_time; |
728 | 0 | } |
729 | |
|
730 | 0 | if (urr->dropped_dl_traffic_threshold.flags) { |
731 | 0 | message->dropped_dl_traffic_threshold.presence = 1; |
732 | 0 | ogs_pfcp_build_dropped_dl_traffic_threshold( |
733 | 0 | &message->dropped_dl_traffic_threshold, |
734 | 0 | &urr->dropped_dl_traffic_threshold, |
735 | 0 | &urrbuf[i].dropped_dl_traffic_threshold, |
736 | 0 | sizeof(urrbuf[i].dropped_dl_traffic_threshold)); |
737 | 0 | } |
738 | |
|
739 | 0 | if (urr->quota_validity_time) { |
740 | 0 | message->quota_validity_time.presence = 1; |
741 | 0 | message->quota_validity_time.u32 = urr->quota_validity_time; |
742 | 0 | } |
743 | |
|
744 | 0 | if (urr->meas_info.octet5) { |
745 | 0 | message->measurement_information.presence = 1; |
746 | 0 | message->measurement_information.data = &urr->meas_info.octet5; |
747 | 0 | message->measurement_information.len = 1; |
748 | 0 | } |
749 | 0 | } |
750 | | |
751 | | void ogs_pfcp_build_update_urr( |
752 | | ogs_pfcp_tlv_update_urr_t *message, int i, |
753 | | ogs_pfcp_urr_t *urr, uint64_t modify_flags) |
754 | 0 | { |
755 | 0 | ogs_assert(message); |
756 | 0 | ogs_assert(urr); |
757 | | |
758 | 0 | ogs_assert(modify_flags & |
759 | 0 | (OGS_PFCP_MODIFY_URR_MEAS_METHOD| |
760 | 0 | OGS_PFCP_MODIFY_URR_REPORT_TRIGGER| |
761 | 0 | OGS_PFCP_MODIFY_URR_VOLUME_THRESH| |
762 | 0 | OGS_PFCP_MODIFY_URR_VOLUME_QUOTA| |
763 | 0 | OGS_PFCP_MODIFY_URR_TIME_THRESH| |
764 | 0 | OGS_PFCP_MODIFY_URR_TIME_QUOTA| |
765 | 0 | OGS_PFCP_MODIFY_URR_QUOTA_VALIDITY_TIME)); |
766 | | |
767 | | /* Change request: Send only changed IEs */ |
768 | 0 | message->presence = 1; |
769 | 0 | message->urr_id.presence = 1; |
770 | 0 | message->urr_id.u32 = urr->id; |
771 | |
|
772 | 0 | if (modify_flags & OGS_PFCP_MODIFY_URR_MEAS_METHOD) { |
773 | 0 | message->measurement_method.presence = 1; |
774 | 0 | message->measurement_method.u8 = urr->meas_method; |
775 | 0 | } |
776 | |
|
777 | 0 | if (modify_flags & OGS_PFCP_MODIFY_URR_REPORT_TRIGGER) { |
778 | 0 | message->reporting_triggers.presence = 1; |
779 | 0 | message->reporting_triggers.u24 = (urr->rep_triggers.reptri_5 << 16) |
780 | 0 | | (urr->rep_triggers.reptri_6 << 8) |
781 | 0 | | (urr->rep_triggers.reptri_7); |
782 | 0 | } |
783 | |
|
784 | 0 | if (modify_flags & OGS_PFCP_MODIFY_URR_VOLUME_THRESH) { |
785 | 0 | if (urr->vol_threshold.flags) { |
786 | 0 | message->volume_threshold.presence = 1; |
787 | 0 | ogs_pfcp_build_volume( |
788 | 0 | &message->volume_threshold, &urr->vol_threshold, |
789 | 0 | &urrbuf[i].vol_threshold, sizeof(urrbuf[i].vol_threshold)); |
790 | 0 | } |
791 | 0 | } |
792 | |
|
793 | 0 | if (modify_flags & OGS_PFCP_MODIFY_URR_VOLUME_QUOTA) { |
794 | 0 | if (urr->vol_quota.flags) { |
795 | 0 | message->volume_quota.presence = 1; |
796 | 0 | ogs_pfcp_build_volume( |
797 | 0 | &message->volume_quota, &urr->vol_quota, |
798 | 0 | &urrbuf[i].vol_quota, sizeof(urrbuf[i].vol_quota)); |
799 | 0 | } |
800 | 0 | } |
801 | |
|
802 | 0 | if (modify_flags & OGS_PFCP_MODIFY_URR_TIME_THRESH) { |
803 | 0 | if (urr->time_threshold) { |
804 | 0 | message->time_threshold.presence = 1; |
805 | 0 | message->time_threshold.u32 = urr->time_threshold; |
806 | 0 | } |
807 | 0 | } |
808 | |
|
809 | 0 | if (modify_flags & OGS_PFCP_MODIFY_URR_TIME_QUOTA) { |
810 | 0 | if (urr->time_quota) { |
811 | 0 | message->time_quota.presence = 1; |
812 | 0 | message->time_quota.u32 = urr->time_quota; |
813 | 0 | } |
814 | 0 | } |
815 | |
|
816 | 0 | if (modify_flags & OGS_PFCP_MODIFY_URR_QUOTA_VALIDITY_TIME) { |
817 | 0 | if (urr->quota_validity_time) { |
818 | 0 | message->quota_validity_time.presence = 1; |
819 | 0 | message->quota_validity_time.u32 = urr->quota_validity_time; |
820 | 0 | } |
821 | 0 | } |
822 | 0 | } |
823 | | |
824 | | static struct { |
825 | | char mbr[OGS_PFCP_BITRATE_LEN]; |
826 | | char gbr[OGS_PFCP_BITRATE_LEN]; |
827 | | } create_qer_buf[OGS_MAX_NUM_OF_QER], update_qer_buf[OGS_MAX_NUM_OF_QER]; |
828 | | |
829 | | void ogs_pfcp_build_create_qer( |
830 | | ogs_pfcp_tlv_create_qer_t *message, int i, ogs_pfcp_qer_t *qer) |
831 | 0 | { |
832 | 0 | ogs_assert(message); |
833 | 0 | ogs_assert(qer); |
834 | | |
835 | 0 | message->presence = 1; |
836 | 0 | message->qer_id.presence = 1; |
837 | 0 | message->qer_id.u32 = qer->id; |
838 | |
|
839 | 0 | message->gate_status.presence = 1; |
840 | 0 | message->gate_status.u8 = qer->gate_status.value; |
841 | |
|
842 | 0 | if (qer->mbr.uplink || qer->mbr.downlink) { |
843 | 0 | message->maximum_bitrate.presence = 1; |
844 | 0 | ogs_pfcp_build_bitrate( |
845 | 0 | &message->maximum_bitrate, |
846 | 0 | &qer->mbr, create_qer_buf[i].mbr, OGS_PFCP_BITRATE_LEN); |
847 | 0 | } |
848 | 0 | if (qer->gbr.uplink || qer->gbr.downlink) { |
849 | 0 | message->guaranteed_bitrate.presence = 1; |
850 | 0 | ogs_pfcp_build_bitrate( |
851 | 0 | &message->guaranteed_bitrate, |
852 | 0 | &qer->gbr, create_qer_buf[i].gbr, OGS_PFCP_BITRATE_LEN); |
853 | 0 | } |
854 | |
|
855 | 0 | if (qer->qfi) { |
856 | 0 | message->qos_flow_identifier.presence = 1; |
857 | 0 | message->qos_flow_identifier.u8 = qer->qfi; |
858 | 0 | } |
859 | 0 | } |
860 | | |
861 | | void ogs_pfcp_build_update_qer( |
862 | | ogs_pfcp_tlv_update_qer_t *message, int i, |
863 | | ogs_pfcp_qer_t *qer, uint64_t modify_flags) |
864 | 0 | { |
865 | 0 | ogs_assert(message); |
866 | 0 | ogs_assert(qer); |
867 | | |
868 | 0 | ogs_assert(modify_flags & |
869 | 0 | (OGS_PFCP_MODIFY_QOS_MODIFY| |
870 | 0 | OGS_PFCP_MODIFY_EPC_QOS_UPDATE)); |
871 | | |
872 | 0 | message->presence = 1; |
873 | 0 | message->qer_id.presence = 1; |
874 | 0 | message->qer_id.u32 = qer->id; |
875 | |
|
876 | 0 | if (modify_flags & |
877 | 0 | (OGS_PFCP_MODIFY_QOS_MODIFY| |
878 | 0 | OGS_PFCP_MODIFY_EPC_QOS_UPDATE)) { |
879 | 0 | if (qer->mbr.uplink || qer->mbr.downlink) { |
880 | 0 | message->maximum_bitrate.presence = 1; |
881 | 0 | ogs_pfcp_build_bitrate( |
882 | 0 | &message->maximum_bitrate, |
883 | 0 | &qer->mbr, update_qer_buf[i].mbr, OGS_PFCP_BITRATE_LEN); |
884 | 0 | } |
885 | 0 | if (qer->gbr.uplink || qer->gbr.downlink) { |
886 | 0 | message->guaranteed_bitrate.presence = 1; |
887 | 0 | ogs_pfcp_build_bitrate( |
888 | 0 | &message->guaranteed_bitrate, |
889 | 0 | &qer->gbr, update_qer_buf[i].gbr, OGS_PFCP_BITRATE_LEN); |
890 | 0 | } |
891 | 0 | } |
892 | 0 | } |
893 | | |
894 | | void ogs_pfcp_build_create_bar( |
895 | | ogs_pfcp_tlv_create_bar_t *message, ogs_pfcp_bar_t *bar) |
896 | 0 | { |
897 | 0 | ogs_assert(message); |
898 | 0 | ogs_assert(bar); |
899 | | |
900 | 0 | message->presence = 1; |
901 | 0 | message->bar_id.presence = 1; |
902 | 0 | message->bar_id.u8 = bar->id; |
903 | 0 | } |
904 | | |
905 | | static struct { |
906 | | ogs_pfcp_volume_measurement_t vol_meas; |
907 | | } usage_report_buf; |
908 | | |
909 | | ogs_pkbuf_t *ogs_pfcp_build_session_report_request( |
910 | | uint8_t type, ogs_pfcp_user_plane_report_t *report) |
911 | 0 | { |
912 | 0 | ogs_pfcp_message_t *pfcp_message = NULL; |
913 | 0 | ogs_pfcp_session_report_request_t *req = NULL; |
914 | 0 | ogs_pkbuf_t *pkbuf = NULL; |
915 | 0 | ogs_pfcp_downlink_data_service_information_t info; |
916 | 0 | unsigned int i; |
917 | |
|
918 | 0 | ogs_assert(report); |
919 | | |
920 | 0 | ogs_debug("PFCP session report request"); |
921 | |
|
922 | 0 | pfcp_message = ogs_calloc(1, sizeof(*pfcp_message)); |
923 | 0 | if (!pfcp_message) { |
924 | 0 | ogs_error("ogs_calloc() failed"); |
925 | 0 | return NULL; |
926 | 0 | } |
927 | | |
928 | 0 | req = &pfcp_message->pfcp_session_report_request; |
929 | |
|
930 | 0 | req->report_type.presence = 1; |
931 | 0 | req->report_type.u8 = report->type.value; |
932 | |
|
933 | 0 | if (report->type.downlink_data_report) { |
934 | 0 | int info_len = 0; |
935 | |
|
936 | 0 | req->downlink_data_report.presence = 1; |
937 | 0 | req->downlink_data_report.pdr_id.presence = 1; |
938 | 0 | req->downlink_data_report.pdr_id.u16 = report->downlink_data.pdr_id; |
939 | |
|
940 | 0 | memset(&info, 0, sizeof(info)); |
941 | 0 | if (report->downlink_data.qfi && |
942 | 0 | report->downlink_data.paging_policy_indication_value) { |
943 | |
|
944 | 0 | info_len = 3; |
945 | |
|
946 | 0 | info.qfii = 1; |
947 | 0 | info.qfi = report->downlink_data.qfi; |
948 | 0 | info.ppi = 1; |
949 | 0 | info.paging_policy_indication_value = |
950 | 0 | report->downlink_data.paging_policy_indication_value; |
951 | |
|
952 | 0 | } else if (report->downlink_data.qfi) { |
953 | |
|
954 | 0 | info_len = 2; |
955 | |
|
956 | 0 | info.qfii = 1; |
957 | 0 | info.qfi = report->downlink_data.qfi; |
958 | 0 | } else if (report->downlink_data.paging_policy_indication_value) { |
959 | |
|
960 | 0 | info_len = 2; |
961 | |
|
962 | 0 | info.ppi = 1; |
963 | 0 | info.paging_policy_indication_value = |
964 | 0 | report->downlink_data.paging_policy_indication_value; |
965 | 0 | } |
966 | |
|
967 | 0 | if (info_len) { |
968 | 0 | req->downlink_data_report. |
969 | 0 | downlink_data_service_information.presence = 1; |
970 | 0 | req->downlink_data_report. |
971 | 0 | downlink_data_service_information.data = &info; |
972 | 0 | req->downlink_data_report. |
973 | 0 | downlink_data_service_information.len = info_len; |
974 | 0 | } |
975 | 0 | } |
976 | |
|
977 | 0 | if (report->type.usage_report) { |
978 | 0 | ogs_assert(report->num_of_usage_report > 0); |
979 | 0 | for (i = 0; i < report->num_of_usage_report; i++) { |
980 | 0 | req->usage_report[i].presence = 1; |
981 | 0 | req->usage_report[i].urr_id.presence = 1; |
982 | 0 | req->usage_report[i].urr_id.u32 = report->usage_report[i].id; |
983 | 0 | req->usage_report[i].ur_seqn.presence = 1; |
984 | 0 | req->usage_report[i].ur_seqn.u32 = report->usage_report[i].seqn; |
985 | 0 | req->usage_report[i].usage_report_trigger.presence = 1; |
986 | 0 | req->usage_report[i].usage_report_trigger.u24 = |
987 | 0 | (report->usage_report[i].rep_trigger.reptri_5 << 16) |
988 | 0 | | (report->usage_report[i].rep_trigger.reptri_6 << 8) |
989 | 0 | | (report->usage_report[i].rep_trigger.reptri_7); |
990 | |
|
991 | 0 | if (report->usage_report[i].start_time) { |
992 | 0 | req->usage_report[i].start_time.presence = 1; |
993 | 0 | req->usage_report[i].start_time.u32 = report->usage_report[i].start_time; |
994 | 0 | } |
995 | |
|
996 | 0 | if (report->usage_report[i].end_time) { |
997 | 0 | req->usage_report[i].end_time.presence = 1; |
998 | 0 | req->usage_report[i].end_time.u32 = report->usage_report[i].end_time; |
999 | 0 | } |
1000 | |
|
1001 | 0 | if (report->usage_report[i].vol_measurement.flags) { |
1002 | 0 | req->usage_report[i].volume_measurement.presence = 1; |
1003 | 0 | ogs_pfcp_build_volume_measurement( |
1004 | 0 | &req->usage_report[i].volume_measurement, |
1005 | 0 | &report->usage_report[i].vol_measurement, |
1006 | 0 | &usage_report_buf.vol_meas, |
1007 | 0 | sizeof(usage_report_buf.vol_meas)); |
1008 | 0 | } |
1009 | |
|
1010 | 0 | if (report->usage_report[i].dur_measurement) { |
1011 | 0 | req->usage_report[i].duration_measurement.presence = 1; |
1012 | 0 | req->usage_report[i].duration_measurement.u32 = |
1013 | 0 | report->usage_report[i].dur_measurement; |
1014 | 0 | } |
1015 | |
|
1016 | 0 | if (report->usage_report[i].time_of_first_packet) { |
1017 | 0 | req->usage_report[i].time_of_first_packet.presence = 1; |
1018 | 0 | req->usage_report[i].time_of_first_packet.u32 = |
1019 | 0 | report->usage_report[i].time_of_first_packet; |
1020 | 0 | } |
1021 | |
|
1022 | 0 | if (report->usage_report[i].time_of_last_packet) { |
1023 | 0 | req->usage_report[i].time_of_last_packet.presence = 1; |
1024 | 0 | req->usage_report[i].time_of_last_packet.u32 = |
1025 | 0 | report->usage_report[i].time_of_last_packet; |
1026 | 0 | } |
1027 | 0 | } |
1028 | 0 | } |
1029 | | |
1030 | 0 | if (report->error_indication.remote_f_teid_len) { |
1031 | 0 | req->error_indication_report.presence = 1; |
1032 | 0 | req->error_indication_report.remote_f_teid.presence = 1; |
1033 | 0 | req->error_indication_report.remote_f_teid.data = |
1034 | 0 | &report->error_indication.remote_f_teid; |
1035 | 0 | req->error_indication_report.remote_f_teid.len = |
1036 | 0 | report->error_indication.remote_f_teid_len; |
1037 | 0 | } |
1038 | |
|
1039 | 0 | pfcp_message->h.type = type; |
1040 | 0 | pkbuf = ogs_pfcp_build_msg(pfcp_message); |
1041 | 0 | ogs_expect(pkbuf); |
1042 | |
|
1043 | 0 | ogs_free(pfcp_message); |
1044 | |
|
1045 | 0 | return pkbuf; |
1046 | 0 | } |
1047 | | |
1048 | | ogs_pkbuf_t *ogs_pfcp_build_session_report_response( |
1049 | | uint8_t type, uint8_t cause) |
1050 | 0 | { |
1051 | 0 | ogs_pfcp_message_t *pfcp_message = NULL; |
1052 | 0 | ogs_pfcp_session_report_response_t *rsp = NULL; |
1053 | 0 | ogs_pkbuf_t *pkbuf = NULL; |
1054 | |
|
1055 | 0 | ogs_debug("PFCP session report response"); |
1056 | |
|
1057 | 0 | pfcp_message = ogs_calloc(1, sizeof(*pfcp_message)); |
1058 | 0 | if (!pfcp_message) { |
1059 | 0 | ogs_error("ogs_calloc() failed"); |
1060 | 0 | return NULL; |
1061 | 0 | } |
1062 | | |
1063 | 0 | rsp = &pfcp_message->pfcp_session_report_response; |
1064 | |
|
1065 | 0 | rsp->cause.presence = 1; |
1066 | 0 | rsp->cause.u8 = cause; |
1067 | |
|
1068 | 0 | pfcp_message->h.type = type; |
1069 | 0 | pkbuf = ogs_pfcp_build_msg(pfcp_message); |
1070 | 0 | ogs_expect(pkbuf); |
1071 | |
|
1072 | 0 | ogs_free(pfcp_message); |
1073 | |
|
1074 | 0 | return pkbuf; |
1075 | 0 | } |
1076 | | |
1077 | | ogs_pkbuf_t *ogs_pfcp_build_session_deletion_response( uint8_t type, uint8_t cause, |
1078 | | ogs_pfcp_user_plane_report_t *report) |
1079 | 0 | { |
1080 | 0 | ogs_pfcp_message_t *pfcp_message = NULL; |
1081 | 0 | ogs_pfcp_session_deletion_response_t *rsp = NULL; |
1082 | 0 | ogs_pkbuf_t *pkbuf = NULL; |
1083 | 0 | unsigned int i; |
1084 | |
|
1085 | 0 | ogs_debug("PFCP session deletion response"); |
1086 | |
|
1087 | 0 | pfcp_message = ogs_calloc(1, sizeof(*pfcp_message)); |
1088 | 0 | if (!pfcp_message) { |
1089 | 0 | ogs_error("ogs_calloc() failed"); |
1090 | 0 | return NULL; |
1091 | 0 | } |
1092 | | |
1093 | 0 | rsp = &pfcp_message->pfcp_session_deletion_response; |
1094 | |
|
1095 | 0 | rsp->cause.presence = 1; |
1096 | 0 | rsp->cause.u8 = cause; |
1097 | |
|
1098 | 0 | if (report->type.usage_report) { |
1099 | 0 | ogs_assert(report->num_of_usage_report > 0); |
1100 | 0 | for (i = 0; i < report->num_of_usage_report; i++) { |
1101 | 0 | rsp->usage_report[i].presence = 1; |
1102 | 0 | rsp->usage_report[i].urr_id.presence = 1; |
1103 | 0 | rsp->usage_report[i].urr_id.u32 = report->usage_report[i].id; |
1104 | 0 | rsp->usage_report[i].ur_seqn.presence = 1; |
1105 | 0 | rsp->usage_report[i].ur_seqn.u32 = report->usage_report[i].seqn; |
1106 | 0 | rsp->usage_report[i].usage_report_trigger.presence = 1; |
1107 | 0 | rsp->usage_report[i].usage_report_trigger.u24 = |
1108 | 0 | (report->usage_report[i].rep_trigger.reptri_5 << 16) |
1109 | 0 | | (report->usage_report[i].rep_trigger.reptri_6 << 8) |
1110 | 0 | | (report->usage_report[i].rep_trigger.reptri_7); |
1111 | |
|
1112 | 0 | if (report->usage_report[i].start_time) { |
1113 | 0 | rsp->usage_report[i].start_time.presence = 1; |
1114 | 0 | rsp->usage_report[i].start_time.u32 = report->usage_report[i].start_time; |
1115 | 0 | } |
1116 | |
|
1117 | 0 | if (report->usage_report[i].end_time) { |
1118 | 0 | rsp->usage_report[i].end_time.presence = 1; |
1119 | 0 | rsp->usage_report[i].end_time.u32 = report->usage_report[i].end_time; |
1120 | 0 | } |
1121 | |
|
1122 | 0 | if (report->usage_report[i].vol_measurement.flags) { |
1123 | 0 | rsp->usage_report[i].volume_measurement.presence = 1; |
1124 | 0 | ogs_pfcp_build_volume_measurement( |
1125 | 0 | &rsp->usage_report[i].volume_measurement, |
1126 | 0 | &report->usage_report[i].vol_measurement, |
1127 | 0 | &usage_report_buf.vol_meas, |
1128 | 0 | sizeof(usage_report_buf.vol_meas)); |
1129 | 0 | } |
1130 | |
|
1131 | 0 | rsp->usage_report[i].duration_measurement.presence = 1; |
1132 | 0 | rsp->usage_report[i].duration_measurement.u32 = |
1133 | 0 | report->usage_report[i].dur_measurement; |
1134 | |
|
1135 | 0 | if (report->usage_report[i].time_of_first_packet) { |
1136 | 0 | rsp->usage_report[i].time_of_first_packet.presence = 1; |
1137 | 0 | rsp->usage_report[i].time_of_first_packet.u32 = |
1138 | 0 | report->usage_report[i].time_of_first_packet; |
1139 | 0 | } |
1140 | |
|
1141 | 0 | if (report->usage_report[i].time_of_last_packet) { |
1142 | 0 | rsp->usage_report[i].time_of_last_packet.presence = 1; |
1143 | 0 | rsp->usage_report[i].time_of_last_packet.u32 = |
1144 | 0 | report->usage_report[i].time_of_last_packet; |
1145 | 0 | } |
1146 | 0 | } |
1147 | 0 | } |
1148 | 0 | pfcp_message->h.type = type; |
1149 | 0 | pkbuf = ogs_pfcp_build_msg(pfcp_message); |
1150 | 0 | ogs_expect(pkbuf); |
1151 | |
|
1152 | 0 | ogs_free(pfcp_message); |
1153 | |
|
1154 | 0 | return pkbuf; |
1155 | 0 | } |