/src/open5gs/lib/sbi/nnrf-handler.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2019-2023 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-sbi.h" |
21 | | |
22 | | static void handle_nf_service( |
23 | | ogs_sbi_nf_service_t *nf_service, OpenAPI_nf_service_t *NFService); |
24 | | static bool handle_smf_info( |
25 | | ogs_sbi_nf_instance_t *nf_instance, OpenAPI_smf_info_t *SmfInfo); |
26 | | static bool handle_scp_info( |
27 | | ogs_sbi_nf_instance_t *nf_instance, OpenAPI_scp_info_t *ScpInfo); |
28 | | static bool handle_sepp_info( |
29 | | ogs_sbi_nf_instance_t *nf_instance, OpenAPI_sepp_info_t *SeppInfo); |
30 | | static bool handle_amf_info( |
31 | | ogs_sbi_nf_instance_t *nf_instance, OpenAPI_amf_info_t *AmfInfo); |
32 | | |
33 | | void ogs_nnrf_nfm_handle_nf_register( |
34 | | ogs_sbi_nf_instance_t *nf_instance, ogs_sbi_message_t *recvmsg) |
35 | 0 | { |
36 | 0 | OpenAPI_nf_profile_t *NFProfile = NULL; |
37 | 0 | OpenAPI_lnode_t *node; |
38 | |
|
39 | 0 | ogs_assert(recvmsg); |
40 | 0 | ogs_assert(nf_instance); |
41 | | |
42 | 0 | NFProfile = recvmsg->NFProfile; |
43 | 0 | if (!NFProfile) { |
44 | 0 | ogs_error("No NFProfile"); |
45 | 0 | return; |
46 | 0 | } |
47 | | |
48 | | /* TIME : Update heartbeat from NRF */ |
49 | 0 | if (NFProfile->is_heart_beat_timer == true) |
50 | 0 | nf_instance->time.heartbeat_interval = NFProfile->heart_beat_timer; |
51 | |
|
52 | 0 | if (NFProfile->plmn_list) { |
53 | 0 | ogs_local_conf()->num_of_serving_plmn_id = 0; |
54 | 0 | OpenAPI_list_for_each(NFProfile->plmn_list, node) { |
55 | 0 | OpenAPI_plmn_id_t *PlmnId = node->data; |
56 | 0 | if (PlmnId) { |
57 | 0 | if (ogs_local_conf()->num_of_serving_plmn_id >= |
58 | 0 | OGS_ARRAY_SIZE(ogs_local_conf()->serving_plmn_id)) { |
59 | 0 | ogs_error("OVERFLOW NFProfile->plmn_list [%d:%d:%d]", |
60 | 0 | ogs_local_conf()->num_of_serving_plmn_id, |
61 | 0 | OGS_MAX_NUM_OF_PLMN, |
62 | 0 | (int)OGS_ARRAY_SIZE( |
63 | 0 | ogs_local_conf()->serving_plmn_id)); |
64 | 0 | break; |
65 | 0 | } |
66 | 0 | ogs_sbi_parse_plmn_id( |
67 | 0 | &ogs_local_conf()->serving_plmn_id[ |
68 | 0 | ogs_local_conf()->num_of_serving_plmn_id], PlmnId); |
69 | 0 | ogs_local_conf()->num_of_serving_plmn_id++; |
70 | 0 | } |
71 | 0 | } |
72 | 0 | } |
73 | 0 | } |
74 | | |
75 | | bool ogs_nnrf_nfm_handle_nf_profile( |
76 | | ogs_sbi_nf_instance_t *nf_instance, OpenAPI_nf_profile_t *NFProfile) |
77 | 0 | { |
78 | 0 | int rv; |
79 | 0 | OpenAPI_lnode_t *node; |
80 | |
|
81 | 0 | ogs_assert(nf_instance); |
82 | 0 | ogs_assert(NFProfile); |
83 | 0 | ogs_assert(NFProfile->nf_instance_id); |
84 | 0 | ogs_assert(NFProfile->nf_type); |
85 | 0 | ogs_assert(NFProfile->nf_status); |
86 | | |
87 | 0 | ogs_sbi_nf_instance_clear(nf_instance); |
88 | |
|
89 | 0 | nf_instance->nf_type = NFProfile->nf_type; |
90 | 0 | nf_instance->nf_status = NFProfile->nf_status; |
91 | 0 | if (NFProfile->is_heart_beat_timer == true) |
92 | 0 | nf_instance->time.heartbeat_interval = NFProfile->heart_beat_timer; |
93 | |
|
94 | 0 | if (NFProfile->fqdn) |
95 | 0 | nf_instance->fqdn = ogs_strdup(NFProfile->fqdn); |
96 | |
|
97 | 0 | if (NFProfile->is_priority == true) |
98 | 0 | nf_instance->priority = NFProfile->priority; |
99 | 0 | if (NFProfile->is_capacity == true) |
100 | 0 | nf_instance->capacity = NFProfile->capacity; |
101 | 0 | if (NFProfile->is_load == true) |
102 | 0 | nf_instance->load = NFProfile->load; |
103 | |
|
104 | 0 | nf_instance->num_of_plmn_id = 0; |
105 | 0 | OpenAPI_list_for_each(NFProfile->plmn_list, node) { |
106 | 0 | OpenAPI_plmn_id_t *PlmnId = node->data; |
107 | 0 | if (PlmnId) { |
108 | 0 | if (nf_instance->num_of_plmn_id >= |
109 | 0 | OGS_ARRAY_SIZE(nf_instance->plmn_id)) { |
110 | 0 | ogs_error("OVERFLOW NFProfile->plmn_list [%d:%d:%d]", |
111 | 0 | nf_instance->num_of_plmn_id, OGS_MAX_NUM_OF_PLMN, |
112 | 0 | (int)OGS_ARRAY_SIZE(nf_instance->plmn_id)); |
113 | 0 | break; |
114 | 0 | } |
115 | 0 | ogs_sbi_parse_plmn_id( |
116 | 0 | &nf_instance->plmn_id[nf_instance->num_of_plmn_id], PlmnId); |
117 | 0 | nf_instance->num_of_plmn_id++; |
118 | 0 | } |
119 | 0 | } |
120 | |
|
121 | 0 | OpenAPI_list_for_each(NFProfile->ipv4_addresses, node) { |
122 | 0 | ogs_sockaddr_t *addr = NULL; |
123 | |
|
124 | 0 | if (!node->data) { |
125 | 0 | ogs_error("No IPv4 Address"); |
126 | 0 | continue; |
127 | 0 | } |
128 | | |
129 | 0 | if (nf_instance->num_of_ipv4 < OGS_SBI_MAX_NUM_OF_IP_ADDRESS) { |
130 | |
|
131 | 0 | rv = ogs_getaddrinfo( |
132 | 0 | &addr, AF_UNSPEC, |
133 | 0 | node->data, |
134 | 0 | ogs_sbi_default_client_port(OpenAPI_uri_scheme_NULL), 0); |
135 | 0 | if (rv != OGS_OK) { |
136 | 0 | ogs_error("ogs_getaddrinfo[%s] failed", (char *)node->data); |
137 | 0 | continue; |
138 | 0 | } |
139 | | |
140 | 0 | nf_instance->ipv4[nf_instance->num_of_ipv4] = addr; |
141 | 0 | nf_instance->num_of_ipv4++; |
142 | 0 | } |
143 | 0 | } |
144 | 0 | OpenAPI_list_for_each(NFProfile->ipv6_addresses, node) { |
145 | 0 | ogs_sockaddr_t *addr = NULL; |
146 | |
|
147 | 0 | if (!node->data) { |
148 | 0 | ogs_error("No IPv6 Address"); |
149 | 0 | continue; |
150 | 0 | } |
151 | | |
152 | 0 | if (nf_instance->num_of_ipv6 < OGS_SBI_MAX_NUM_OF_IP_ADDRESS) { |
153 | |
|
154 | 0 | rv = ogs_getaddrinfo( |
155 | 0 | &addr, AF_UNSPEC, |
156 | 0 | node->data, |
157 | 0 | ogs_sbi_default_client_port(OpenAPI_uri_scheme_NULL), 0); |
158 | 0 | if (rv != OGS_OK) { |
159 | 0 | ogs_error("ogs_getaddrinfo[%s] failed", (char *)node->data); |
160 | 0 | continue; |
161 | 0 | } |
162 | | |
163 | 0 | nf_instance->ipv6[nf_instance->num_of_ipv6] = addr; |
164 | 0 | nf_instance->num_of_ipv6++; |
165 | 0 | } |
166 | 0 | } |
167 | |
|
168 | 0 | OpenAPI_list_for_each(NFProfile->allowed_nf_types, node) { |
169 | 0 | OpenAPI_nf_type_e AllowedNfType = (uintptr_t)node->data; |
170 | |
|
171 | 0 | if (!AllowedNfType) { |
172 | 0 | ogs_error("AllowedNfType"); |
173 | 0 | continue; |
174 | 0 | } |
175 | | |
176 | 0 | if (nf_instance->num_of_allowed_nf_type < |
177 | 0 | OGS_SBI_MAX_NUM_OF_NF_TYPE) { |
178 | 0 | nf_instance->allowed_nf_type[ |
179 | 0 | nf_instance->num_of_allowed_nf_type] = AllowedNfType; |
180 | 0 | nf_instance->num_of_allowed_nf_type++; |
181 | 0 | } |
182 | 0 | } |
183 | | |
184 | | /* |
185 | | * TS 33.518 4.2.2.2.1 - Store S-NSSAIs for slice-based discovery |
186 | | * authorization. sNssais identifies the slices this NF serves; |
187 | | * allowedNssais restricts for which slices the NF may be discovered. |
188 | | */ |
189 | 0 | nf_instance->num_of_s_nssai = 0; |
190 | 0 | OpenAPI_list_for_each(NFProfile->s_nssais, node) { |
191 | 0 | OpenAPI_ext_snssai_t *sNssai = node->data; |
192 | 0 | if (sNssai) { |
193 | 0 | if (nf_instance->num_of_s_nssai >= OGS_MAX_NUM_OF_SLICE) { |
194 | 0 | ogs_warn("Overflow: NFProfile.sNssais exceeds %d", |
195 | 0 | OGS_MAX_NUM_OF_SLICE); |
196 | 0 | break; |
197 | 0 | } |
198 | 0 | nf_instance->s_nssai[nf_instance->num_of_s_nssai].sst = |
199 | 0 | sNssai->sst; |
200 | 0 | nf_instance->s_nssai[nf_instance->num_of_s_nssai].sd = |
201 | 0 | ogs_s_nssai_sd_from_string(sNssai->sd); |
202 | 0 | nf_instance->num_of_s_nssai++; |
203 | 0 | } |
204 | 0 | } |
205 | |
|
206 | 0 | nf_instance->num_of_allowed_nssai = 0; |
207 | 0 | OpenAPI_list_for_each(NFProfile->allowed_nssais, node) { |
208 | 0 | OpenAPI_ext_snssai_t *sNssai = node->data; |
209 | 0 | if (sNssai) { |
210 | 0 | if (nf_instance->num_of_allowed_nssai >= OGS_MAX_NUM_OF_SLICE) { |
211 | 0 | ogs_warn("Overflow: NFProfile.allowedNssais exceeds %d", |
212 | 0 | OGS_MAX_NUM_OF_SLICE); |
213 | 0 | break; |
214 | 0 | } |
215 | 0 | nf_instance->allowed_nssai[nf_instance->num_of_allowed_nssai].sst = |
216 | 0 | sNssai->sst; |
217 | 0 | nf_instance->allowed_nssai[nf_instance->num_of_allowed_nssai].sd = |
218 | 0 | ogs_s_nssai_sd_from_string(sNssai->sd); |
219 | 0 | nf_instance->num_of_allowed_nssai++; |
220 | 0 | } |
221 | 0 | } |
222 | |
|
223 | 0 | OpenAPI_list_for_each(NFProfile->nf_services, node) { |
224 | 0 | ogs_sbi_nf_service_t *nf_service = NULL; |
225 | 0 | OpenAPI_nf_service_t *NFService = node->data; |
226 | |
|
227 | 0 | if (!NFService) { |
228 | 0 | ogs_error("No NFService"); |
229 | 0 | continue; |
230 | 0 | } |
231 | | |
232 | 0 | if (!NFService->service_instance_id) { |
233 | 0 | ogs_error("No NFService.service_instance_id"); |
234 | 0 | continue; |
235 | 0 | } |
236 | | |
237 | 0 | if (!NFService->service_name) { |
238 | 0 | ogs_error("No NFService.service_name"); |
239 | 0 | continue; |
240 | 0 | } |
241 | | |
242 | 0 | if (!NFService->scheme) { |
243 | 0 | ogs_error("No NFService.scheme"); |
244 | 0 | continue; |
245 | 0 | } |
246 | | |
247 | 0 | nf_service = ogs_sbi_nf_service_find_by_id( |
248 | 0 | nf_instance, NFService->service_instance_id); |
249 | 0 | if (!nf_service) { |
250 | 0 | nf_service = ogs_sbi_nf_service_add( |
251 | 0 | nf_instance, |
252 | 0 | NFService->service_instance_id, |
253 | 0 | NFService->service_name, NFService->scheme); |
254 | 0 | if (!nf_service) { |
255 | 0 | ogs_error("Failed to add NFService [%s]", |
256 | 0 | NFService->service_instance_id); |
257 | 0 | return false; |
258 | 0 | } |
259 | 0 | } |
260 | | |
261 | 0 | ogs_sbi_nf_service_clear(nf_service); |
262 | |
|
263 | 0 | handle_nf_service(nf_service, NFService); |
264 | 0 | } |
265 | | |
266 | 0 | OpenAPI_list_for_each(NFProfile->nf_service_list, node) { |
267 | 0 | ogs_sbi_nf_service_t *nf_service = NULL; |
268 | 0 | OpenAPI_map_t *NFServiceMap = NULL; |
269 | 0 | OpenAPI_nf_service_t *NFService = node->data; |
270 | |
|
271 | 0 | NFServiceMap = node->data; |
272 | 0 | if (NFServiceMap) { |
273 | 0 | NFService = NFServiceMap->value; |
274 | 0 | if (!NFService) { |
275 | 0 | ogs_error("No NFService"); |
276 | 0 | continue; |
277 | 0 | } |
278 | | |
279 | 0 | if (!NFService->service_instance_id) { |
280 | 0 | ogs_error("No NFService.service_instance_id"); |
281 | 0 | continue; |
282 | 0 | } |
283 | | |
284 | 0 | if (!NFService->service_name) { |
285 | 0 | ogs_error("No NFService.service_name"); |
286 | 0 | continue; |
287 | 0 | } |
288 | | |
289 | 0 | if (!NFService->scheme) { |
290 | 0 | ogs_error("No NFService.scheme"); |
291 | 0 | continue; |
292 | 0 | } |
293 | | |
294 | 0 | nf_service = ogs_sbi_nf_service_find_by_id( |
295 | 0 | nf_instance, NFService->service_instance_id); |
296 | 0 | if (!nf_service) { |
297 | 0 | nf_service = ogs_sbi_nf_service_add( |
298 | 0 | nf_instance, |
299 | 0 | NFService->service_instance_id, |
300 | 0 | NFService->service_name, NFService->scheme); |
301 | 0 | if (!nf_service) { |
302 | 0 | ogs_error("Failed to add NFService [%s]", |
303 | 0 | NFService->service_instance_id); |
304 | 0 | return false; |
305 | 0 | } |
306 | 0 | } |
307 | | |
308 | 0 | ogs_sbi_nf_service_clear(nf_service); |
309 | |
|
310 | 0 | handle_nf_service(nf_service, NFService); |
311 | 0 | } |
312 | 0 | } |
313 | | |
314 | 0 | ogs_sbi_nf_info_remove_all(&nf_instance->nf_info_list); |
315 | |
|
316 | 0 | if (NFProfile->smf_info && |
317 | 0 | handle_smf_info(nf_instance, NFProfile->smf_info) == false) |
318 | 0 | return false; |
319 | | |
320 | 0 | OpenAPI_list_for_each(NFProfile->smf_info_list, node) { |
321 | 0 | OpenAPI_map_t *SmfInfoMap = node->data; |
322 | 0 | if (SmfInfoMap && SmfInfoMap->value && |
323 | 0 | handle_smf_info(nf_instance, SmfInfoMap->value) == false) |
324 | 0 | return false; |
325 | 0 | } |
326 | 0 | if (NFProfile->amf_info && |
327 | 0 | handle_amf_info(nf_instance, NFProfile->amf_info) == false) |
328 | 0 | return false; |
329 | | |
330 | 0 | OpenAPI_list_for_each(NFProfile->amf_info_list, node) { |
331 | 0 | OpenAPI_map_t *AmfInfoMap = node->data; |
332 | 0 | if (AmfInfoMap && AmfInfoMap->value && |
333 | 0 | handle_amf_info(nf_instance, AmfInfoMap->value) == false) |
334 | 0 | return false; |
335 | 0 | } |
336 | 0 | if (NFProfile->scp_info && |
337 | 0 | handle_scp_info(nf_instance, NFProfile->scp_info) == false) |
338 | 0 | return false; |
339 | 0 | if (NFProfile->sepp_info && |
340 | 0 | handle_sepp_info(nf_instance, NFProfile->sepp_info) == false) |
341 | 0 | return false; |
342 | | |
343 | 0 | return true; |
344 | 0 | } |
345 | | |
346 | | static void handle_nf_service( |
347 | | ogs_sbi_nf_service_t *nf_service, OpenAPI_nf_service_t *NFService) |
348 | 0 | { |
349 | 0 | int rv; |
350 | 0 | OpenAPI_lnode_t *node = NULL; |
351 | |
|
352 | 0 | ogs_assert(nf_service); |
353 | 0 | ogs_assert(NFService); |
354 | | |
355 | 0 | OpenAPI_list_for_each(NFService->versions, node) { |
356 | 0 | OpenAPI_nf_service_version_t *NFServiceVersion = node->data; |
357 | |
|
358 | 0 | if (!NFServiceVersion) { |
359 | 0 | ogs_error("No NFServiceVersion"); |
360 | 0 | continue; |
361 | 0 | } |
362 | | |
363 | 0 | ogs_sbi_nf_service_add_version(nf_service, |
364 | 0 | NFServiceVersion->api_version_in_uri, |
365 | 0 | NFServiceVersion->api_full_version, |
366 | 0 | NFServiceVersion->expiry); |
367 | 0 | } |
368 | |
|
369 | 0 | if (NFService->fqdn) |
370 | 0 | nf_service->fqdn = ogs_strdup(NFService->fqdn); |
371 | |
|
372 | 0 | OpenAPI_list_for_each(NFService->ip_end_points, node) { |
373 | 0 | OpenAPI_ip_end_point_t *IpEndPoint = node->data; |
374 | 0 | ogs_sockaddr_t *addr = NULL, *addr6 = NULL; |
375 | 0 | int port = 0; |
376 | |
|
377 | 0 | if (!IpEndPoint) { |
378 | 0 | ogs_error("No IpEndPoint"); |
379 | 0 | continue; |
380 | 0 | } |
381 | | |
382 | 0 | if (nf_service->num_of_addr < OGS_SBI_MAX_NUM_OF_IP_ADDRESS) { |
383 | 0 | if (!IpEndPoint->is_port) |
384 | 0 | port = ogs_sbi_default_client_port(nf_service->scheme); |
385 | 0 | else |
386 | 0 | port = IpEndPoint->port; |
387 | |
|
388 | 0 | if (IpEndPoint->ipv4_address) { |
389 | 0 | rv = ogs_getaddrinfo(&addr, AF_UNSPEC, |
390 | 0 | IpEndPoint->ipv4_address, port, 0); |
391 | 0 | if (rv != OGS_OK) { |
392 | 0 | ogs_error("ogs_getaddrinfo[%s] failed", |
393 | 0 | IpEndPoint->ipv4_address); |
394 | 0 | continue; |
395 | 0 | } |
396 | 0 | } |
397 | 0 | if (IpEndPoint->ipv6_address) { |
398 | 0 | rv = ogs_getaddrinfo(&addr6, AF_UNSPEC, |
399 | 0 | IpEndPoint->ipv6_address, port, 0); |
400 | 0 | if (rv != OGS_OK) { |
401 | 0 | ogs_error("ogs_getaddrinfo[%s] failed", |
402 | 0 | IpEndPoint->ipv6_address); |
403 | 0 | continue; |
404 | 0 | } |
405 | 0 | } |
406 | | |
407 | 0 | if (addr || addr6) { |
408 | 0 | nf_service->addr[nf_service->num_of_addr]. |
409 | 0 | is_port = IpEndPoint->is_port; |
410 | 0 | nf_service->addr[nf_service->num_of_addr]. |
411 | 0 | port = port; |
412 | 0 | nf_service->addr[nf_service->num_of_addr]. |
413 | 0 | ipv4 = addr; |
414 | 0 | nf_service->addr[nf_service->num_of_addr]. |
415 | 0 | ipv6 = addr6; |
416 | 0 | nf_service->num_of_addr++; |
417 | 0 | } |
418 | 0 | } |
419 | 0 | } |
420 | |
|
421 | 0 | OpenAPI_list_for_each(NFService->allowed_nf_types, node) { |
422 | 0 | OpenAPI_nf_type_e AllowedNfType = (uintptr_t)node->data; |
423 | |
|
424 | 0 | if (!AllowedNfType) { |
425 | 0 | ogs_error("AllowedNfType"); |
426 | 0 | continue; |
427 | 0 | } |
428 | | |
429 | 0 | if (nf_service->num_of_allowed_nf_type < |
430 | 0 | OGS_SBI_MAX_NUM_OF_NF_TYPE) { |
431 | 0 | nf_service->allowed_nf_type[ |
432 | 0 | nf_service->num_of_allowed_nf_type] = AllowedNfType; |
433 | 0 | nf_service->num_of_allowed_nf_type++; |
434 | 0 | } |
435 | 0 | } |
436 | |
|
437 | 0 | if (NFService->is_priority == true) |
438 | 0 | nf_service->priority = NFService->priority; |
439 | 0 | if (NFService->is_capacity == true) |
440 | 0 | nf_service->capacity = NFService->capacity; |
441 | 0 | if (NFService->is_load == true) |
442 | 0 | nf_service->load = NFService->load; |
443 | 0 | } |
444 | | |
445 | | static bool handle_smf_info( |
446 | | ogs_sbi_nf_instance_t *nf_instance, OpenAPI_smf_info_t *SmfInfo) |
447 | 0 | { |
448 | 0 | bool rv = true; |
449 | 0 | ogs_sbi_nf_info_t *nf_info = NULL; |
450 | |
|
451 | 0 | OpenAPI_list_t *sNssaiSmfInfoList = NULL; |
452 | 0 | OpenAPI_snssai_smf_info_item_t *sNssaiSmfInfoItem = NULL; |
453 | 0 | OpenAPI_ext_snssai_t *sNssai = NULL; |
454 | 0 | OpenAPI_list_t *DnnSmfInfoList = NULL; |
455 | 0 | OpenAPI_dnn_smf_info_item_t *DnnSmfInfoItem = NULL; |
456 | |
|
457 | 0 | OpenAPI_list_t *TaiList = NULL; |
458 | 0 | OpenAPI_tai_t *TaiItem = NULL; |
459 | 0 | OpenAPI_list_t *TaiRangeList = NULL; |
460 | 0 | OpenAPI_tai_range_t *TaiRangeItem = NULL; |
461 | 0 | OpenAPI_list_t *TacRangeList = NULL; |
462 | 0 | OpenAPI_tac_range_t *TacRangeItem = NULL; |
463 | |
|
464 | 0 | OpenAPI_lnode_t *node = NULL, *node2 = NULL; |
465 | |
|
466 | 0 | ogs_assert(nf_instance); |
467 | 0 | ogs_assert(SmfInfo); |
468 | | |
469 | 0 | nf_info = ogs_sbi_nf_info_add( |
470 | 0 | &nf_instance->nf_info_list, OpenAPI_nf_type_SMF); |
471 | 0 | if (!nf_info) { |
472 | 0 | ogs_error("Failed to add SMF nfInfo"); |
473 | 0 | return false; |
474 | 0 | } |
475 | | |
476 | 0 | sNssaiSmfInfoList = SmfInfo->s_nssai_smf_info_list; |
477 | 0 | OpenAPI_list_for_each(sNssaiSmfInfoList, node) { |
478 | 0 | sNssaiSmfInfoItem = node->data; |
479 | 0 | if (sNssaiSmfInfoItem) { |
480 | 0 | if (nf_info->smf.num_of_slice >= OGS_MAX_NUM_OF_SLICE) { |
481 | 0 | ogs_error("OVERFLOW Slice [%d:%d]", |
482 | 0 | nf_info->smf.num_of_slice, OGS_MAX_NUM_OF_SLICE); |
483 | 0 | rv = false; |
484 | 0 | break; |
485 | 0 | } |
486 | | |
487 | 0 | DnnSmfInfoList = sNssaiSmfInfoItem->dnn_smf_info_list; |
488 | 0 | OpenAPI_list_for_each(DnnSmfInfoList, node2) { |
489 | 0 | DnnSmfInfoItem = node2->data; |
490 | 0 | if (DnnSmfInfoItem && DnnSmfInfoItem->dnn) { |
491 | 0 | int dnn_index = nf_info->smf.slice |
492 | 0 | [nf_info->smf.num_of_slice].num_of_dnn; |
493 | |
|
494 | 0 | if (dnn_index >= OGS_MAX_NUM_OF_DNN) { |
495 | 0 | ogs_error("OVERFLOW DNN [%d:%d]", |
496 | 0 | dnn_index, OGS_MAX_NUM_OF_DNN); |
497 | 0 | rv = false; |
498 | 0 | break; |
499 | 0 | } |
500 | 0 | nf_info->smf.slice[nf_info->smf.num_of_slice]. |
501 | 0 | dnn[dnn_index] = ogs_strdup(DnnSmfInfoItem->dnn); |
502 | 0 | if (!nf_info->smf.slice[nf_info->smf.num_of_slice]. |
503 | 0 | dnn[dnn_index]) { |
504 | 0 | ogs_error("No memory for DNN"); |
505 | 0 | rv = false; |
506 | 0 | break; |
507 | 0 | } |
508 | 0 | nf_info->smf.slice[nf_info->smf.num_of_slice]. |
509 | 0 | num_of_dnn++; |
510 | 0 | } |
511 | 0 | } |
512 | |
|
513 | 0 | if (rv == false) |
514 | 0 | break; |
515 | | |
516 | 0 | if (!nf_info->smf.slice[nf_info->smf.num_of_slice].num_of_dnn) { |
517 | 0 | ogs_error("No DNN"); |
518 | 0 | continue; |
519 | 0 | } |
520 | | |
521 | 0 | sNssai = sNssaiSmfInfoItem->s_nssai; |
522 | 0 | if (sNssai) { |
523 | 0 | ogs_s_nssai_t *s_nssai = NULL; |
524 | |
|
525 | 0 | s_nssai = &nf_info->smf. |
526 | 0 | slice[nf_info->smf.num_of_slice].s_nssai; |
527 | 0 | s_nssai->sst = sNssai->sst; |
528 | 0 | s_nssai->sd = ogs_s_nssai_sd_from_string(sNssai->sd); |
529 | 0 | nf_info->smf.num_of_slice++; |
530 | 0 | } |
531 | 0 | } |
532 | 0 | } |
533 | |
|
534 | 0 | if (rv == false) goto out; |
535 | | |
536 | 0 | if (nf_info->smf.num_of_slice == 0) { |
537 | 0 | ogs_error("No S-NSSAI(DNN) in smfInfo"); |
538 | 0 | ogs_sbi_nf_info_remove(&nf_instance->nf_info_list, nf_info); |
539 | 0 | return true; |
540 | 0 | } |
541 | | |
542 | 0 | TaiList = SmfInfo->tai_list; |
543 | 0 | OpenAPI_list_for_each(TaiList, node) { |
544 | 0 | TaiItem = node->data; |
545 | 0 | if (TaiItem && TaiItem->plmn_id && TaiItem->tac) { |
546 | 0 | ogs_5gs_tai_t *nr_tai = NULL; |
547 | 0 | if (nf_info->smf.num_of_nr_tai >= OGS_MAX_NUM_OF_TAI) { |
548 | 0 | ogs_error("OVERFLOW TAI [%d:%d]", |
549 | 0 | nf_info->smf.num_of_nr_tai, OGS_MAX_NUM_OF_TAI); |
550 | 0 | rv = false; |
551 | 0 | break; |
552 | 0 | } |
553 | | |
554 | 0 | nr_tai = &nf_info->smf.nr_tai[nf_info->smf.num_of_nr_tai]; |
555 | 0 | ogs_assert(nr_tai); |
556 | 0 | ogs_sbi_parse_plmn_id(&nr_tai->plmn_id, TaiItem->plmn_id); |
557 | 0 | nr_tai->tac = ogs_uint24_from_string_hexadecimal(TaiItem->tac); |
558 | |
|
559 | 0 | nf_info->smf.num_of_nr_tai++; |
560 | 0 | } |
561 | 0 | } |
562 | | |
563 | 0 | if (rv == false) goto out; |
564 | | |
565 | 0 | TaiRangeList = SmfInfo->tai_range_list; |
566 | 0 | OpenAPI_list_for_each(TaiRangeList, node) { |
567 | 0 | TaiRangeItem = node->data; |
568 | 0 | if (TaiRangeItem && TaiRangeItem->plmn_id && |
569 | 0 | TaiRangeItem->tac_range_list) { |
570 | |
|
571 | 0 | if (nf_info->smf.num_of_nr_tai_range >= OGS_MAX_NUM_OF_TAI) { |
572 | 0 | ogs_error("OVERFLOW TaiRangeItem [%d:%d]", |
573 | 0 | nf_info->smf.num_of_nr_tai_range, OGS_MAX_NUM_OF_TAI); |
574 | 0 | rv = false; |
575 | 0 | break; |
576 | 0 | } |
577 | | |
578 | 0 | ogs_sbi_parse_plmn_id( |
579 | 0 | &nf_info->smf.nr_tai_range |
580 | 0 | [nf_info->smf.num_of_nr_tai_range].plmn_id, |
581 | 0 | TaiRangeItem->plmn_id); |
582 | |
|
583 | 0 | TacRangeList = TaiRangeItem->tac_range_list; |
584 | 0 | OpenAPI_list_for_each(TacRangeList, node2) { |
585 | 0 | TacRangeItem = node2->data; |
586 | 0 | if (TacRangeItem && |
587 | 0 | TacRangeItem->start && TacRangeItem->end) { |
588 | 0 | int tac_index = nf_info->smf.nr_tai_range |
589 | 0 | [nf_info->smf.num_of_nr_tai_range].num_of_tac_range; |
590 | 0 | if (tac_index >= OGS_MAX_NUM_OF_TAI) { |
591 | 0 | ogs_error("OVERFLOW TAI [%d:%d]", |
592 | 0 | tac_index, OGS_MAX_NUM_OF_TAI); |
593 | 0 | rv = false; |
594 | 0 | break; |
595 | 0 | } |
596 | | |
597 | 0 | nf_info->smf.nr_tai_range |
598 | 0 | [nf_info->smf.num_of_nr_tai_range]. |
599 | 0 | start[tac_index] = |
600 | 0 | ogs_uint24_from_string_hexadecimal( |
601 | 0 | TacRangeItem->start); |
602 | 0 | nf_info->smf.nr_tai_range |
603 | 0 | [nf_info->smf.num_of_nr_tai_range]. |
604 | 0 | end[tac_index] = |
605 | 0 | ogs_uint24_from_string_hexadecimal( |
606 | 0 | TacRangeItem->end); |
607 | |
|
608 | 0 | nf_info->smf.nr_tai_range |
609 | 0 | [nf_info->smf.num_of_nr_tai_range]. |
610 | 0 | num_of_tac_range++; |
611 | 0 | } |
612 | 0 | } |
613 | |
|
614 | 0 | if (rv == false) |
615 | 0 | break; |
616 | | |
617 | 0 | nf_info->smf.num_of_nr_tai_range++; |
618 | 0 | } |
619 | 0 | } |
620 | |
|
621 | 0 | out: |
622 | 0 | if (rv == false) { |
623 | 0 | ogs_sbi_nf_info_remove(&nf_instance->nf_info_list, nf_info); |
624 | 0 | return false; |
625 | 0 | } |
626 | | |
627 | 0 | return true; |
628 | 0 | } |
629 | | |
630 | | static void scp_info_free(ogs_sbi_scp_info_t *scp_info) |
631 | 0 | { |
632 | 0 | int i; |
633 | |
|
634 | 0 | ogs_assert(scp_info); |
635 | | |
636 | 0 | for (i = 0; i < scp_info->num_of_domain; i++) { |
637 | 0 | ogs_free(scp_info->domain[i].name); |
638 | 0 | ogs_free(scp_info->domain[i].fqdn); |
639 | 0 | } |
640 | 0 | memset(scp_info, 0, sizeof(*scp_info)); |
641 | 0 | } |
642 | | |
643 | | static bool handle_scp_info( |
644 | | ogs_sbi_nf_instance_t *nf_instance, OpenAPI_scp_info_t *ScpInfo) |
645 | 0 | { |
646 | 0 | ogs_sbi_nf_info_t *nf_info = NULL; |
647 | |
|
648 | 0 | OpenAPI_list_t *PortList = NULL; |
649 | 0 | OpenAPI_map_t *PortMap = NULL; |
650 | |
|
651 | 0 | OpenAPI_map_t *DomainInfoMap = NULL; |
652 | 0 | OpenAPI_scp_domain_info_t *DomainInfo = NULL; |
653 | |
|
654 | 0 | OpenAPI_lnode_t *node = NULL, *node2 = NULL; |
655 | |
|
656 | 0 | ogs_sbi_scp_info_t scp_info; |
657 | |
|
658 | 0 | ogs_assert(nf_instance); |
659 | 0 | ogs_assert(ScpInfo); |
660 | | |
661 | 0 | memset(&scp_info, 0, sizeof(scp_info)); |
662 | |
|
663 | 0 | PortList = ScpInfo->scp_ports; |
664 | 0 | OpenAPI_list_for_each(PortList, node) { |
665 | 0 | PortMap = node->data; |
666 | 0 | if (PortMap) { |
667 | 0 | if (PortMap->key) { |
668 | 0 | double *port = PortMap->value; |
669 | 0 | if (strcmp(PortMap->key, "http") == 0) { |
670 | 0 | if (port) { |
671 | 0 | scp_info.http.presence = true; |
672 | 0 | scp_info.http.port = *port; |
673 | 0 | } else { |
674 | 0 | ogs_error("No Port Value"); |
675 | 0 | } |
676 | 0 | } else if (strcmp(PortMap->key, "https") == 0) { |
677 | 0 | if (port) { |
678 | 0 | scp_info.https.presence = true; |
679 | 0 | scp_info.https.port = *port; |
680 | 0 | } else { |
681 | 0 | ogs_error("No Port Value"); |
682 | 0 | } |
683 | 0 | } else { |
684 | 0 | ogs_error("Unknown Port Key = %s", PortMap->key); |
685 | 0 | } |
686 | 0 | } |
687 | 0 | } |
688 | 0 | } |
689 | |
|
690 | 0 | OpenAPI_list_for_each(ScpInfo->scp_domain_info_list, node) { |
691 | 0 | DomainInfoMap = node->data; |
692 | 0 | if (DomainInfoMap && DomainInfoMap->key && DomainInfoMap->value) { |
693 | 0 | if (scp_info.num_of_domain >= OGS_MAX_NUM_OF_SCP_DOMAIN) { |
694 | 0 | ogs_warn("Exceeded maximum SCP domains (%d)", |
695 | 0 | OGS_MAX_NUM_OF_SCP_DOMAIN); |
696 | 0 | break; |
697 | 0 | } |
698 | 0 | scp_info.domain[scp_info.num_of_domain].name = |
699 | 0 | ogs_strdup(DomainInfoMap->key); |
700 | 0 | DomainInfo = DomainInfoMap->value; |
701 | 0 | if (DomainInfo->scp_fqdn) |
702 | 0 | scp_info.domain[scp_info.num_of_domain].fqdn = |
703 | 0 | ogs_strdup(DomainInfo->scp_fqdn); |
704 | 0 | OpenAPI_list_for_each(DomainInfo->scp_ports, node2) { |
705 | 0 | PortMap = node2->data; |
706 | 0 | if (PortMap) { |
707 | 0 | if (PortMap->key) { |
708 | 0 | double *port = PortMap->value; |
709 | 0 | if (strcmp(PortMap->key, "http") == 0) { |
710 | 0 | if (port) { |
711 | 0 | scp_info.domain[scp_info.num_of_domain]. |
712 | 0 | http.presence = true; |
713 | 0 | scp_info.domain[scp_info.num_of_domain]. |
714 | 0 | http.port = *port; |
715 | 0 | } else { |
716 | 0 | ogs_error("No Port Value"); |
717 | 0 | } |
718 | 0 | } else if (strcmp(PortMap->key, "https") == 0) { |
719 | 0 | if (port) { |
720 | 0 | scp_info.domain[scp_info.num_of_domain]. |
721 | 0 | https.presence = true; |
722 | 0 | scp_info.domain[scp_info.num_of_domain]. |
723 | 0 | https.port = *port; |
724 | 0 | } else { |
725 | 0 | ogs_error("No Port Value"); |
726 | 0 | } |
727 | 0 | } else { |
728 | 0 | ogs_error("Unknown Port Key = %s", PortMap->key); |
729 | 0 | } |
730 | 0 | } |
731 | 0 | } |
732 | 0 | } |
733 | 0 | scp_info.num_of_domain++; |
734 | 0 | } |
735 | 0 | } |
736 | |
|
737 | 0 | if (scp_info.http.presence || scp_info.https.presence || |
738 | 0 | scp_info.num_of_domain) { |
739 | 0 | nf_info = ogs_sbi_nf_info_add( |
740 | 0 | &nf_instance->nf_info_list, OpenAPI_nf_type_SCP); |
741 | 0 | if (!nf_info) { |
742 | 0 | ogs_error("Failed to add SCP nfInfo"); |
743 | 0 | scp_info_free(&scp_info); |
744 | 0 | return false; |
745 | 0 | } |
746 | | |
747 | 0 | memcpy(&nf_info->scp, &scp_info, sizeof(scp_info)); |
748 | 0 | } |
749 | | |
750 | 0 | return true; |
751 | 0 | } |
752 | | |
753 | | static bool handle_sepp_info( |
754 | | ogs_sbi_nf_instance_t *nf_instance, OpenAPI_sepp_info_t *SeppInfo) |
755 | 0 | { |
756 | 0 | ogs_sbi_nf_info_t *nf_info = NULL; |
757 | |
|
758 | 0 | OpenAPI_list_t *PortList = NULL; |
759 | 0 | OpenAPI_map_t *PortMap = NULL; |
760 | |
|
761 | 0 | OpenAPI_lnode_t *node = NULL; |
762 | |
|
763 | 0 | ogs_port_t http, https; |
764 | |
|
765 | 0 | ogs_assert(nf_instance); |
766 | 0 | ogs_assert(SeppInfo); |
767 | | |
768 | 0 | http.port = 0; |
769 | 0 | https.port = 0; |
770 | 0 | http.presence = false; |
771 | 0 | https.presence = false; |
772 | |
|
773 | 0 | PortList = SeppInfo->sepp_ports; |
774 | 0 | OpenAPI_list_for_each(PortList, node) { |
775 | 0 | PortMap = node->data; |
776 | 0 | if (PortMap) { |
777 | 0 | if (PortMap->key) { |
778 | 0 | double *port = PortMap->value; |
779 | 0 | if (strcmp(PortMap->key, "http") == 0) { |
780 | 0 | if (port) { |
781 | 0 | http.presence = true; |
782 | 0 | http.port = *port; |
783 | 0 | } else { |
784 | 0 | ogs_error("No Port Value"); |
785 | 0 | } |
786 | 0 | } else if (strcmp(PortMap->key, "https") == 0) { |
787 | 0 | if (port) { |
788 | 0 | https.presence = true; |
789 | 0 | https.port = *port; |
790 | 0 | } else { |
791 | 0 | ogs_error("No Port Value"); |
792 | 0 | } |
793 | 0 | } else { |
794 | 0 | ogs_error("Unknown Port Key = %s", PortMap->key); |
795 | 0 | } |
796 | 0 | } |
797 | 0 | } |
798 | 0 | } |
799 | |
|
800 | 0 | if (http.presence || https.presence) { |
801 | 0 | nf_info = ogs_sbi_nf_info_add( |
802 | 0 | &nf_instance->nf_info_list, OpenAPI_nf_type_SEPP); |
803 | 0 | if (!nf_info) { |
804 | 0 | ogs_error("Failed to add SEPP nfInfo"); |
805 | 0 | return false; |
806 | 0 | } |
807 | | |
808 | 0 | nf_info->sepp.http.presence = http.presence; |
809 | 0 | nf_info->sepp.http.port = http.port; |
810 | |
|
811 | 0 | nf_info->sepp.https.presence = https.presence; |
812 | 0 | nf_info->sepp.https.port = https.port; |
813 | 0 | } |
814 | | |
815 | 0 | return true; |
816 | 0 | } |
817 | | |
818 | | static bool handle_amf_info( |
819 | | ogs_sbi_nf_instance_t *nf_instance, OpenAPI_amf_info_t *AmfInfo) |
820 | 0 | { |
821 | 0 | bool rv = true; |
822 | 0 | ogs_sbi_nf_info_t *nf_info = NULL; |
823 | 0 | OpenAPI_list_t *GuamiList = NULL; |
824 | 0 | OpenAPI_guami_t *GuamiAmfInfoItem = NULL; |
825 | 0 | OpenAPI_list_t *TaiList = NULL; |
826 | 0 | OpenAPI_tai_t *TaiItem = NULL; |
827 | 0 | OpenAPI_list_t *TaiRangeList = NULL; |
828 | 0 | OpenAPI_tai_range_t *TaiRangeItem = NULL; |
829 | 0 | OpenAPI_list_t *TacRangeList = NULL; |
830 | 0 | OpenAPI_tac_range_t *TacRangeItem = NULL; |
831 | 0 | OpenAPI_lnode_t *node = NULL, *node2 = NULL; |
832 | |
|
833 | 0 | ogs_assert(nf_instance); |
834 | 0 | ogs_assert(AmfInfo); |
835 | | |
836 | 0 | nf_info = ogs_sbi_nf_info_add( |
837 | 0 | &nf_instance->nf_info_list, OpenAPI_nf_type_AMF); |
838 | 0 | if (!nf_info) { |
839 | 0 | ogs_error("Failed to add AMF nfInfo"); |
840 | 0 | return false; |
841 | 0 | } |
842 | | |
843 | 0 | nf_info->amf.amf_set_id = ogs_uint64_from_string_hexadecimal( |
844 | 0 | AmfInfo->amf_set_id); |
845 | 0 | nf_info->amf.amf_region_id = ogs_uint64_from_string_hexadecimal( |
846 | 0 | AmfInfo->amf_region_id); |
847 | |
|
848 | 0 | GuamiList = AmfInfo->guami_list; |
849 | 0 | OpenAPI_list_for_each(GuamiList, node) { |
850 | 0 | GuamiAmfInfoItem = node->data; |
851 | 0 | if (GuamiAmfInfoItem) { |
852 | 0 | if (nf_info->amf.num_of_guami >= OGS_MAX_NUM_OF_SERVED_GUAMI) { |
853 | 0 | ogs_error("OVERFLOW Guami [%d:%d]", |
854 | 0 | nf_info->amf.num_of_guami, OGS_MAX_NUM_OF_SERVED_GUAMI); |
855 | 0 | rv = false; |
856 | 0 | break; |
857 | 0 | } |
858 | | |
859 | 0 | if (GuamiAmfInfoItem->amf_id && GuamiAmfInfoItem->plmn_id && |
860 | 0 | GuamiAmfInfoItem->plmn_id->mnc && |
861 | 0 | GuamiAmfInfoItem->plmn_id->mcc) { |
862 | |
|
863 | 0 | if (!ogs_sbi_parse_guami( |
864 | 0 | &nf_info->amf.guami[nf_info->amf.num_of_guami], |
865 | 0 | GuamiAmfInfoItem)) { |
866 | 0 | ogs_error("[%s] Invalid GUAMI in AMF NF profile", |
867 | 0 | nf_instance->id); |
868 | 0 | rv = false; |
869 | 0 | break; |
870 | 0 | } |
871 | 0 | nf_info->amf.num_of_guami++; |
872 | 0 | } |
873 | 0 | } |
874 | 0 | } |
875 | 0 | if (rv == false) goto out; |
876 | | |
877 | 0 | TaiList = AmfInfo->tai_list; |
878 | 0 | OpenAPI_list_for_each(TaiList, node) { |
879 | 0 | TaiItem = node->data; |
880 | 0 | if (TaiItem && TaiItem->plmn_id && TaiItem->tac) { |
881 | 0 | ogs_5gs_tai_t *nr_tai = NULL; |
882 | |
|
883 | 0 | if (nf_info->amf.num_of_nr_tai >= OGS_MAX_NUM_OF_TAI) { |
884 | 0 | ogs_error("OVERFLOW TaiItem [%d:%d]", |
885 | 0 | nf_info->amf.num_of_nr_tai, OGS_MAX_NUM_OF_TAI); |
886 | 0 | rv = false; |
887 | 0 | break; |
888 | 0 | } |
889 | | |
890 | 0 | nr_tai = &nf_info->amf.nr_tai[nf_info->amf.num_of_nr_tai]; |
891 | |
|
892 | 0 | ogs_sbi_parse_plmn_id(&nr_tai->plmn_id, TaiItem->plmn_id); |
893 | 0 | nr_tai->tac = ogs_uint24_from_string_hexadecimal(TaiItem->tac); |
894 | 0 | nf_info->amf.num_of_nr_tai++; |
895 | 0 | } |
896 | 0 | } |
897 | 0 | if (rv == false) goto out; |
898 | | |
899 | 0 | TaiRangeList = AmfInfo->tai_range_list; |
900 | 0 | OpenAPI_list_for_each(TaiRangeList, node) { |
901 | 0 | TaiRangeItem = node->data; |
902 | 0 | if (TaiRangeItem && TaiRangeItem->plmn_id && |
903 | 0 | TaiRangeItem->tac_range_list) { |
904 | 0 | if (nf_info->amf.num_of_nr_tai_range >= OGS_MAX_NUM_OF_TAI) { |
905 | 0 | ogs_error("OVERFLOW TaiRangeItem [%d:%d]", |
906 | 0 | nf_info->amf.num_of_nr_tai_range, OGS_MAX_NUM_OF_TAI); |
907 | 0 | rv = false; |
908 | 0 | break; |
909 | 0 | } |
910 | | |
911 | 0 | ogs_sbi_parse_plmn_id( |
912 | 0 | &nf_info->amf.nr_tai_range |
913 | 0 | [nf_info->amf.num_of_nr_tai_range].plmn_id, |
914 | 0 | TaiRangeItem->plmn_id); |
915 | |
|
916 | 0 | TacRangeList = TaiRangeItem->tac_range_list; |
917 | 0 | OpenAPI_list_for_each(TacRangeList, node2) { |
918 | 0 | TacRangeItem = node2->data; |
919 | 0 | if (TacRangeItem && |
920 | 0 | TacRangeItem->start && TacRangeItem->end) { |
921 | 0 | int tac_index = nf_info->amf.nr_tai_range |
922 | 0 | [nf_info->amf.num_of_nr_tai_range].num_of_tac_range; |
923 | 0 | if (tac_index >= OGS_MAX_NUM_OF_TAI) { |
924 | 0 | ogs_error("OVERFLOW TAI [%d:%d]", |
925 | 0 | tac_index, OGS_MAX_NUM_OF_TAI); |
926 | 0 | rv = false; |
927 | 0 | break; |
928 | 0 | } |
929 | | |
930 | 0 | nf_info->amf.nr_tai_range |
931 | 0 | [nf_info->amf.num_of_nr_tai_range].start[tac_index] = |
932 | 0 | ogs_uint24_from_string_hexadecimal( |
933 | 0 | TacRangeItem->start); |
934 | 0 | nf_info->amf.nr_tai_range |
935 | 0 | [nf_info->amf.num_of_nr_tai_range].end[tac_index] = |
936 | 0 | ogs_uint24_from_string_hexadecimal( |
937 | 0 | TacRangeItem->end); |
938 | |
|
939 | 0 | nf_info->amf.nr_tai_range |
940 | 0 | [nf_info->amf.num_of_nr_tai_range].num_of_tac_range++; |
941 | 0 | } |
942 | 0 | } |
943 | |
|
944 | 0 | if (rv == false) |
945 | 0 | break; |
946 | | |
947 | 0 | nf_info->amf.num_of_nr_tai_range++; |
948 | 0 | } |
949 | 0 | } |
950 | |
|
951 | 0 | out: |
952 | 0 | if (rv == false) { |
953 | 0 | ogs_sbi_nf_info_remove(&nf_instance->nf_info_list, nf_info); |
954 | 0 | return false; |
955 | 0 | } |
956 | | |
957 | 0 | return true; |
958 | 0 | } |
959 | | |
960 | | static void handle_validity_time( |
961 | | ogs_sbi_subscription_data_t *subscription_data, |
962 | | char *validity_time, const char *action) |
963 | 0 | { |
964 | 0 | ogs_time_t time, validity, patch; |
965 | 0 | char *validity_time_string = NULL; |
966 | |
|
967 | 0 | ogs_assert(subscription_data); |
968 | 0 | ogs_assert(action); |
969 | | |
970 | | /* |
971 | | * If there is a validity_time, then the NRF is updating |
972 | | * the validity_time by sending HTTP_STATUS to 200. |
973 | | * Therefore, change subscription_data->valdity_duration |
974 | | * according to this value. |
975 | | * |
976 | | * If validity_time is NULL, NRF sent an HTTP_STATUS of 204 (No content). |
977 | | * In this case, use the existing subscription_data->validity_duration. |
978 | | */ |
979 | 0 | if (validity_time) { |
980 | 0 | if (ogs_sbi_time_from_string(&time, validity_time) == false) { |
981 | 0 | ogs_error("[%s] Subscription %s until %s [parser error]", |
982 | 0 | subscription_data->id, action, validity_time); |
983 | 0 | return; |
984 | 0 | } |
985 | | |
986 | 0 | validity = time - ogs_time_now(); |
987 | 0 | if (validity < 0) { |
988 | 0 | ogs_error("[%s] Subscription %s until %s [validity:%d.%06d]", |
989 | 0 | subscription_data->id, action, validity_time, |
990 | 0 | (int)ogs_time_sec(validity), (int)ogs_time_usec(validity)); |
991 | 0 | return; |
992 | 0 | } |
993 | | |
994 | | /* |
995 | | * Store subscription_data->validity_duration to derive NRF validity. |
996 | | * It will be used in ogs_nnrf_nfm_build_status_update(). |
997 | | * |
998 | | * So, you should not remove the following lines. |
999 | | */ |
1000 | 0 | subscription_data->validity_duration = |
1001 | | /* Normalize seconds */ |
1002 | 0 | ogs_time_from_sec(ogs_time_to_sec(validity)); |
1003 | 0 | } |
1004 | | |
1005 | 0 | if (!subscription_data->t_validity) { |
1006 | 0 | subscription_data->t_validity = |
1007 | 0 | ogs_timer_add(ogs_app()->timer_mgr, |
1008 | 0 | ogs_timer_subscription_validity, |
1009 | 0 | OGS_UINT_TO_POINTER(subscription_data->pool_id)); |
1010 | 0 | ogs_assert(subscription_data->t_validity); |
1011 | 0 | } |
1012 | 0 | ogs_timer_start(subscription_data->t_validity, |
1013 | 0 | subscription_data->validity_duration); |
1014 | | |
1015 | | /* |
1016 | | * PATCH request will be sent before VALIDITY is expired. |
1017 | | */ |
1018 | 0 | #define PATCH_TIME_FROM_VALIDITY(x) ((x) / 2) |
1019 | 0 | patch = PATCH_TIME_FROM_VALIDITY(subscription_data->validity_duration); |
1020 | |
|
1021 | 0 | if (!subscription_data->t_patch) { |
1022 | 0 | subscription_data->t_patch = |
1023 | 0 | ogs_timer_add(ogs_app()->timer_mgr, |
1024 | 0 | ogs_timer_subscription_patch, |
1025 | 0 | OGS_UINT_TO_POINTER(subscription_data->pool_id)); |
1026 | 0 | ogs_assert(subscription_data->t_patch); |
1027 | 0 | } |
1028 | 0 | ogs_timer_start(subscription_data->t_patch, patch); |
1029 | |
|
1030 | 0 | if (validity_time) { |
1031 | 0 | validity_time_string = ogs_strdup(validity_time); |
1032 | 0 | ogs_assert(validity_time_string); |
1033 | 0 | } else { |
1034 | 0 | validity_time_string = ogs_sbi_localtime_string( |
1035 | 0 | ogs_time_now() + subscription_data->validity_duration); |
1036 | 0 | ogs_assert(validity_time_string); |
1037 | 0 | } |
1038 | | |
1039 | 0 | ogs_info("[%s] Subscription %s until %s " |
1040 | 0 | "[duration:%lld,validity:%d.%06d,patch:%d.%06d]", |
1041 | 0 | subscription_data->id, action, validity_time_string, |
1042 | 0 | (long long)subscription_data->validity_duration, |
1043 | 0 | (int)ogs_time_sec(subscription_data->validity_duration), |
1044 | 0 | (int)ogs_time_usec(subscription_data->validity_duration), |
1045 | 0 | (int)ogs_time_sec(patch), (int)ogs_time_usec(patch)); |
1046 | |
|
1047 | 0 | ogs_free(validity_time_string); |
1048 | 0 | } |
1049 | | |
1050 | | void ogs_nnrf_nfm_handle_nf_status_subscribe( |
1051 | | ogs_sbi_subscription_data_t *subscription_data, |
1052 | | ogs_sbi_message_t *recvmsg) |
1053 | 0 | { |
1054 | 0 | OpenAPI_subscription_data_t *SubscriptionData = NULL; |
1055 | |
|
1056 | 0 | int rv; |
1057 | 0 | ogs_sbi_message_t message; |
1058 | 0 | ogs_sbi_header_t header; |
1059 | |
|
1060 | 0 | bool rc; |
1061 | 0 | ogs_sbi_client_t *client = NULL; |
1062 | 0 | OpenAPI_uri_scheme_e scheme = OpenAPI_uri_scheme_NULL; |
1063 | 0 | char *fqdn = NULL; |
1064 | 0 | uint16_t fqdn_port = 0; |
1065 | 0 | ogs_sockaddr_t *addr = NULL, *addr6 = NULL; |
1066 | |
|
1067 | 0 | ogs_assert(recvmsg); |
1068 | 0 | ogs_assert(subscription_data); |
1069 | | |
1070 | 0 | SubscriptionData = recvmsg->SubscriptionData; |
1071 | 0 | if (!SubscriptionData) { |
1072 | 0 | ogs_error("No SubscriptionData"); |
1073 | 0 | return; |
1074 | 0 | } |
1075 | | |
1076 | 0 | if (!recvmsg->http.location) { |
1077 | 0 | ogs_error("No http.location"); |
1078 | 0 | return; |
1079 | 0 | } |
1080 | | |
1081 | 0 | memset(&header, 0, sizeof(header)); |
1082 | 0 | header.uri = recvmsg->http.location; |
1083 | |
|
1084 | 0 | rv = ogs_sbi_parse_header(&message, &header); |
1085 | 0 | if (rv != OGS_OK) { |
1086 | 0 | ogs_error("Cannot parse http.location [%s]", |
1087 | 0 | recvmsg->http.location); |
1088 | 0 | return; |
1089 | 0 | } |
1090 | | |
1091 | 0 | if (!message.h.resource.component[1]) { |
1092 | 0 | ogs_error("No Subscription ID [%s]", recvmsg->http.location); |
1093 | 0 | ogs_sbi_header_free(&header); |
1094 | 0 | return; |
1095 | 0 | } |
1096 | | |
1097 | 0 | rc = ogs_sbi_getaddr_from_uri( |
1098 | 0 | &scheme, &fqdn, &fqdn_port, &addr, &addr6, header.uri); |
1099 | 0 | if (rc == false || scheme == OpenAPI_uri_scheme_NULL) { |
1100 | 0 | ogs_error("Invalid URI [%s]", header.uri); |
1101 | 0 | ogs_sbi_header_free(&header); |
1102 | 0 | return; |
1103 | 0 | } |
1104 | | |
1105 | 0 | client = ogs_sbi_client_find(scheme, fqdn, fqdn_port, addr, addr6); |
1106 | 0 | if (!client) { |
1107 | 0 | ogs_debug("%s: ogs_sbi_client_add()", OGS_FUNC); |
1108 | 0 | client = ogs_sbi_client_add(scheme, fqdn, fqdn_port, addr, addr6); |
1109 | 0 | if (!client) { |
1110 | 0 | ogs_error("%s: ogs_sbi_client_add() failed", OGS_FUNC); |
1111 | |
|
1112 | 0 | ogs_sbi_header_free(&header); |
1113 | 0 | ogs_free(fqdn); |
1114 | 0 | ogs_freeaddrinfo(addr); |
1115 | 0 | ogs_freeaddrinfo(addr6); |
1116 | |
|
1117 | 0 | return; |
1118 | 0 | } |
1119 | 0 | } |
1120 | 0 | OGS_SBI_SETUP_CLIENT(subscription_data, client); |
1121 | | |
1122 | 0 | ogs_free(fqdn); |
1123 | 0 | ogs_freeaddrinfo(addr); |
1124 | 0 | ogs_freeaddrinfo(addr6); |
1125 | |
|
1126 | 0 | ogs_sbi_subscription_data_set_resource_uri( |
1127 | 0 | subscription_data, header.uri); |
1128 | 0 | ogs_sbi_subscription_data_set_id( |
1129 | 0 | subscription_data, message.h.resource.component[1]); |
1130 | |
|
1131 | 0 | ogs_sbi_header_free(&header); |
1132 | | |
1133 | | /* SBI Features */ |
1134 | 0 | if (SubscriptionData->nrf_supported_features) { |
1135 | 0 | subscription_data->nrf_supported_features = |
1136 | 0 | ogs_uint64_from_string_hexadecimal( |
1137 | 0 | SubscriptionData->nrf_supported_features); |
1138 | 0 | } else { |
1139 | 0 | subscription_data->nrf_supported_features = 0; |
1140 | 0 | } |
1141 | | |
1142 | | /* Subscription Validity Time */ |
1143 | 0 | if (SubscriptionData->validity_time) |
1144 | 0 | handle_validity_time( |
1145 | 0 | subscription_data, SubscriptionData->validity_time, "created"); |
1146 | 0 | } |
1147 | | |
1148 | | void ogs_nnrf_nfm_handle_nf_status_update( |
1149 | | ogs_sbi_subscription_data_t *subscription_data, |
1150 | | ogs_sbi_message_t *recvmsg) |
1151 | 0 | { |
1152 | 0 | OpenAPI_subscription_data_t *SubscriptionData = NULL; |
1153 | 0 | char *validity_time = NULL; |
1154 | 0 | const char *action = NULL; |
1155 | |
|
1156 | 0 | ogs_assert(recvmsg); |
1157 | 0 | ogs_assert(subscription_data); |
1158 | | |
1159 | 0 | if (recvmsg->res_status == OGS_SBI_HTTP_STATUS_OK) { |
1160 | 0 | SubscriptionData = recvmsg->SubscriptionData; |
1161 | 0 | if (!SubscriptionData) { |
1162 | 0 | ogs_error("No SubscriptionData"); |
1163 | 0 | return; |
1164 | 0 | } |
1165 | 0 | if (!SubscriptionData->validity_time) { |
1166 | 0 | ogs_error("No validityTime"); |
1167 | 0 | return; |
1168 | 0 | } |
1169 | | |
1170 | 0 | validity_time = SubscriptionData->validity_time; |
1171 | 0 | action = "updated(200 OK)"; |
1172 | 0 | } else if (recvmsg->res_status == OGS_SBI_HTTP_STATUS_NO_CONTENT) { |
1173 | | /* No valdityTime. Re-use current subscription_data->valdity_duration */ |
1174 | 0 | action = "updated(204 No Content)"; |
1175 | 0 | } else { |
1176 | 0 | ogs_fatal("[%s] HTTP response error [%d]", |
1177 | 0 | subscription_data->id ? subscription_data->id : "Unknown", |
1178 | 0 | recvmsg->res_status); |
1179 | 0 | ogs_assert_if_reached(); |
1180 | 0 | } |
1181 | | |
1182 | | /* Update Subscription Validity Time */ |
1183 | 0 | handle_validity_time(subscription_data, validity_time, action); |
1184 | 0 | } |
1185 | | |
1186 | | void ogs_nnrf_nfm_handle_nf_status_subscription_response( |
1187 | | ogs_sbi_message_t *recvmsg, void *data) |
1188 | 0 | { |
1189 | 0 | ogs_sbi_subscription_data_t *subscription_data = NULL; |
1190 | |
|
1191 | 0 | ogs_assert(recvmsg); |
1192 | | |
1193 | 0 | subscription_data = ogs_sbi_subscription_data_find_by_pool_id( |
1194 | 0 | OGS_POINTER_TO_UINT(data)); |
1195 | 0 | if (!subscription_data) { |
1196 | 0 | ogs_error("Subscription has already been removed [%d]", |
1197 | 0 | (int)OGS_POINTER_TO_UINT(data)); |
1198 | 0 | return; |
1199 | 0 | } |
1200 | | |
1201 | 0 | SWITCH(recvmsg->h.method) |
1202 | 0 | CASE(OGS_SBI_HTTP_METHOD_POST) |
1203 | 0 | if (recvmsg->res_status == OGS_SBI_HTTP_STATUS_CREATED || |
1204 | 0 | recvmsg->res_status == OGS_SBI_HTTP_STATUS_OK) { |
1205 | 0 | ogs_nnrf_nfm_handle_nf_status_subscribe( |
1206 | 0 | subscription_data, recvmsg); |
1207 | 0 | } else { |
1208 | 0 | ogs_error("HTTP response error : %d", recvmsg->res_status); |
1209 | 0 | } |
1210 | 0 | break; |
1211 | |
|
1212 | 0 | CASE(OGS_SBI_HTTP_METHOD_PATCH) |
1213 | 0 | if (recvmsg->res_status == OGS_SBI_HTTP_STATUS_OK || |
1214 | 0 | recvmsg->res_status == OGS_SBI_HTTP_STATUS_NO_CONTENT) { |
1215 | 0 | ogs_nnrf_nfm_handle_nf_status_update(subscription_data, recvmsg); |
1216 | 0 | } else { |
1217 | 0 | ogs_error("[%s] HTTP response error [%d]", |
1218 | 0 | subscription_data->id ? subscription_data->id : "Unknown", |
1219 | 0 | recvmsg->res_status); |
1220 | 0 | } |
1221 | 0 | break; |
1222 | |
|
1223 | 0 | CASE(OGS_SBI_HTTP_METHOD_DELETE) |
1224 | 0 | if (recvmsg->res_status == OGS_SBI_HTTP_STATUS_NO_CONTENT) |
1225 | 0 | ogs_info("[%s] Subscription deleted", |
1226 | 0 | subscription_data->id ? subscription_data->id : "Unknown"); |
1227 | 0 | else |
1228 | 0 | ogs_error("[%s] HTTP response error [%d]", |
1229 | 0 | subscription_data->id ? subscription_data->id : "Unknown", |
1230 | 0 | recvmsg->res_status); |
1231 | |
|
1232 | 0 | ogs_sbi_subscription_data_remove(subscription_data); |
1233 | 0 | break; |
1234 | |
|
1235 | 0 | DEFAULT |
1236 | 0 | ogs_error("[%s] Invalid HTTP method [%s]", |
1237 | 0 | subscription_data->id ? subscription_data->id : "Unknown", |
1238 | 0 | recvmsg->h.method); |
1239 | 0 | ogs_assert_if_reached(); |
1240 | 0 | END |
1241 | 0 | } |
1242 | | |
1243 | | /* |
1244 | | * Defense-in-depth (TS 33.501 clause 5.9.2.1): accept an NRF status |
1245 | | * notification only when it corresponds to a confirmed subscription this NF |
1246 | | * created with the NRF. We test live subscription state (subscription_data_list |
1247 | | * via ogs_sbi_nf_status_subscription_exists(..., confirmed_only=true)), so a |
1248 | | * notification outside our active subscription scope is discarded before any |
1249 | | * local NF cache mutation. |
1250 | | * |
1251 | | * NOTE: This is surface reduction, NOT source authentication. Transport-layer |
1252 | | * trust (TLS/mTLS via server.verify_client) remains the primary control: a |
1253 | | * forged profile that falls within our active subscription scope is still |
1254 | | * accepted. |
1255 | | */ |
1256 | | static bool nf_profile_has_status_subscription(OpenAPI_nf_profile_t *NFProfile) |
1257 | 0 | { |
1258 | 0 | OpenAPI_lnode_t *node = NULL; |
1259 | |
|
1260 | 0 | ogs_assert(NFProfile); |
1261 | 0 | ogs_assert(ogs_sbi_self()->nf_instance); |
1262 | 0 | ogs_assert(ogs_sbi_self()->nf_instance->id); |
1263 | | |
1264 | 0 | if (ogs_sbi_nf_status_subscription_exists( |
1265 | 0 | ogs_sbi_self()->nf_instance->id, |
1266 | 0 | NFProfile->nf_type, OpenAPI_service_name_NULL, true)) |
1267 | 0 | return true; |
1268 | | |
1269 | 0 | OpenAPI_list_for_each(NFProfile->nf_services, node) { |
1270 | 0 | OpenAPI_nf_service_t *NFService = node->data; |
1271 | 0 | if (NFService && ogs_sbi_nf_status_subscription_exists( |
1272 | 0 | ogs_sbi_self()->nf_instance->id, |
1273 | 0 | OpenAPI_nf_type_NULL, NFService->service_name, true)) |
1274 | 0 | return true; |
1275 | 0 | } |
1276 | | |
1277 | 0 | OpenAPI_list_for_each(NFProfile->nf_service_list, node) { |
1278 | 0 | OpenAPI_map_t *NFServiceMap = node->data; |
1279 | 0 | OpenAPI_nf_service_t *NFService = |
1280 | 0 | NFServiceMap ? NFServiceMap->value : NULL; |
1281 | 0 | if (NFService && ogs_sbi_nf_status_subscription_exists( |
1282 | 0 | ogs_sbi_self()->nf_instance->id, |
1283 | 0 | OpenAPI_nf_type_NULL, NFService->service_name, true)) |
1284 | 0 | return true; |
1285 | 0 | } |
1286 | | |
1287 | 0 | return false; |
1288 | 0 | } |
1289 | | |
1290 | | /* NF_DEREGISTERED carries no profile, so match the cached NF's type/services |
1291 | | * against a confirmed subscription before evicting it (forged-deregister DoS). */ |
1292 | | static bool nf_instance_has_status_subscription( |
1293 | | ogs_sbi_nf_instance_t *nf_instance) |
1294 | 0 | { |
1295 | 0 | ogs_sbi_nf_service_t *nf_service = NULL; |
1296 | |
|
1297 | 0 | ogs_assert(nf_instance); |
1298 | 0 | ogs_assert(ogs_sbi_self()->nf_instance); |
1299 | 0 | ogs_assert(ogs_sbi_self()->nf_instance->id); |
1300 | | |
1301 | 0 | if (ogs_sbi_nf_status_subscription_exists( |
1302 | 0 | ogs_sbi_self()->nf_instance->id, |
1303 | 0 | nf_instance->nf_type, OpenAPI_service_name_NULL, true)) |
1304 | 0 | return true; |
1305 | | |
1306 | 0 | ogs_list_for_each(&nf_instance->nf_service_list, nf_service) { |
1307 | 0 | if (ogs_sbi_nf_status_subscription_exists( |
1308 | 0 | ogs_sbi_self()->nf_instance->id, |
1309 | 0 | OpenAPI_nf_type_NULL, nf_service->name, true)) |
1310 | 0 | return true; |
1311 | 0 | } |
1312 | | |
1313 | 0 | return false; |
1314 | 0 | } |
1315 | | |
1316 | | bool ogs_nnrf_nfm_handle_nf_status_notify( |
1317 | | ogs_sbi_stream_t *stream, ogs_sbi_message_t *recvmsg) |
1318 | 0 | { |
1319 | 0 | int rv; |
1320 | |
|
1321 | 0 | ogs_sbi_response_t *response = NULL; |
1322 | 0 | OpenAPI_notification_data_t *NotificationData = NULL; |
1323 | 0 | ogs_sbi_nf_instance_t *nf_instance = NULL; |
1324 | |
|
1325 | 0 | ogs_sbi_message_t message; |
1326 | 0 | ogs_sbi_header_t header; |
1327 | |
|
1328 | 0 | ogs_assert(stream); |
1329 | 0 | ogs_assert(recvmsg); |
1330 | | |
1331 | 0 | NotificationData = recvmsg->NotificationData; |
1332 | 0 | if (!NotificationData) { |
1333 | 0 | ogs_error("No NotificationData"); |
1334 | 0 | ogs_assert(true == |
1335 | 0 | ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, |
1336 | 0 | recvmsg, "No NotificationData", NULL, NULL)); |
1337 | 0 | return false; |
1338 | 0 | } |
1339 | | |
1340 | 0 | if (!NotificationData->nf_instance_uri) { |
1341 | 0 | ogs_error("No nfInstanceUri"); |
1342 | 0 | ogs_assert(true == |
1343 | 0 | ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, |
1344 | 0 | recvmsg, "No nfInstanceUri", NULL, NULL)); |
1345 | 0 | return false; |
1346 | 0 | } |
1347 | | |
1348 | 0 | memset(&header, 0, sizeof(header)); |
1349 | 0 | header.uri = NotificationData->nf_instance_uri; |
1350 | |
|
1351 | 0 | rv = ogs_sbi_parse_header(&message, &header); |
1352 | 0 | if (rv != OGS_OK) { |
1353 | 0 | ogs_error("Cannot parse nfInstanceUri [%s]", header.uri); |
1354 | 0 | ogs_assert(true == |
1355 | 0 | ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, |
1356 | 0 | recvmsg, "Cannot parse nfInstanceUri", header.uri, NULL)); |
1357 | 0 | return false; |
1358 | 0 | } |
1359 | | |
1360 | 0 | if (!message.h.resource.component[1]) { |
1361 | 0 | ogs_error("No nfInstanceId [%s]", header.uri); |
1362 | 0 | ogs_assert(true == |
1363 | 0 | ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, |
1364 | 0 | recvmsg, "Cannot parse nfInstanceUri", header.uri, NULL)); |
1365 | 0 | ogs_sbi_header_free(&header); |
1366 | 0 | return false; |
1367 | 0 | } |
1368 | | |
1369 | 0 | if (NF_INSTANCE_ID_IS_SELF(message.h.resource.component[1])) { |
1370 | 0 | ogs_warn("[%s] The notification is not allowed", |
1371 | 0 | message.h.resource.component[1]); |
1372 | 0 | ogs_assert(true == |
1373 | 0 | ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_FORBIDDEN, |
1374 | 0 | recvmsg, "The notification is not allowed", |
1375 | 0 | message.h.resource.component[1], NULL)); |
1376 | 0 | ogs_sbi_header_free(&header); |
1377 | 0 | return false; |
1378 | 0 | } |
1379 | | |
1380 | 0 | if (NotificationData->event == |
1381 | 0 | OpenAPI_notification_event_type_NF_REGISTERED) { |
1382 | |
|
1383 | 0 | OpenAPI_nf_profile_t *NFProfile = NULL; |
1384 | 0 | bool nf_instance_created = false; |
1385 | |
|
1386 | 0 | NFProfile = NotificationData->nf_profile; |
1387 | 0 | if (!NFProfile) { |
1388 | 0 | ogs_error("No NFProfile"); |
1389 | 0 | ogs_assert(true == |
1390 | 0 | ogs_sbi_server_send_error( |
1391 | 0 | stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, |
1392 | 0 | recvmsg, "No NFProfile", NULL, NULL)); |
1393 | 0 | ogs_sbi_header_free(&header); |
1394 | 0 | return false; |
1395 | 0 | } |
1396 | | |
1397 | 0 | if (!NFProfile->nf_instance_id) { |
1398 | 0 | ogs_error("No NFProfile.NFInstanceId"); |
1399 | 0 | ogs_assert(true == |
1400 | 0 | ogs_sbi_server_send_error( |
1401 | 0 | stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, |
1402 | 0 | recvmsg, "No NFProfile.NFInstanceId", NULL, NULL)); |
1403 | 0 | ogs_sbi_header_free(&header); |
1404 | 0 | return false; |
1405 | 0 | } |
1406 | | |
1407 | 0 | if (!NFProfile->nf_type) { |
1408 | 0 | ogs_error("No NFProfile.NFType"); |
1409 | 0 | ogs_assert(true == |
1410 | 0 | ogs_sbi_server_send_error( |
1411 | 0 | stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, |
1412 | 0 | recvmsg, "No NFProfile.NFType", NULL, NULL)); |
1413 | 0 | ogs_sbi_header_free(&header); |
1414 | 0 | return false; |
1415 | 0 | } |
1416 | | |
1417 | 0 | if (!NFProfile->nf_status) { |
1418 | 0 | ogs_error("No NFProfile.NFStatus"); |
1419 | 0 | ogs_assert(true == |
1420 | 0 | ogs_sbi_server_send_error( |
1421 | 0 | stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, |
1422 | 0 | recvmsg, "No NFProfile.NFStatus", NULL, NULL)); |
1423 | 0 | ogs_sbi_header_free(&header); |
1424 | 0 | return false; |
1425 | 0 | } |
1426 | | |
1427 | 0 | if (nf_profile_has_status_subscription(NFProfile) == false) { |
1428 | 0 | ogs_warn("[%s] (NRF-notify) No active subscription for notified " |
1429 | 0 | "profile [type:%s] - discarded", |
1430 | 0 | NFProfile->nf_instance_id, |
1431 | 0 | OpenAPI_nf_type_ToString(NFProfile->nf_type)); |
1432 | 0 | ogs_assert(true == |
1433 | 0 | ogs_sbi_server_send_error( |
1434 | 0 | stream, OGS_SBI_HTTP_STATUS_NOT_FOUND, |
1435 | 0 | recvmsg, "No active subscription for notification", |
1436 | 0 | NFProfile->nf_instance_id, NULL)); |
1437 | 0 | ogs_sbi_header_free(&header); |
1438 | 0 | return false; |
1439 | 0 | } |
1440 | | |
1441 | 0 | nf_instance = ogs_sbi_nf_instance_find(message.h.resource.component[1]); |
1442 | 0 | if (!nf_instance) { |
1443 | 0 | nf_instance = ogs_sbi_nf_instance_add(); |
1444 | 0 | if (!nf_instance) { |
1445 | 0 | ogs_error("Can't add notified NF instance [%s] " |
1446 | 0 | "due to insufficient space", |
1447 | 0 | message.h.resource.component[1]); |
1448 | 0 | ogs_assert(true == ogs_sbi_server_send_error( |
1449 | 0 | stream, OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR, |
1450 | 0 | recvmsg, "Insufficient resources", |
1451 | 0 | message.h.resource.component[1], |
1452 | 0 | "INSUFFICIENT_RESOURCES")); |
1453 | 0 | ogs_sbi_header_free(&header); |
1454 | 0 | return false; |
1455 | 0 | } |
1456 | | |
1457 | 0 | ogs_sbi_nf_instance_set_id( |
1458 | 0 | nf_instance, message.h.resource.component[1]); |
1459 | 0 | ogs_sbi_nf_fsm_init(nf_instance); |
1460 | 0 | nf_instance_created = true; |
1461 | |
|
1462 | 0 | ogs_info("[%s] (NRF-notify) NF registered", nf_instance->id); |
1463 | 0 | } else { |
1464 | 0 | ogs_warn("[%s] (NRF-notify) NF has already been added [type:%s]", |
1465 | 0 | nf_instance->id, |
1466 | 0 | OpenAPI_nf_type_ToString(nf_instance->nf_type)); |
1467 | 0 | if (!OGS_FSM_CHECK(&nf_instance->sm, ogs_sbi_nf_state_registered)) { |
1468 | 0 | ogs_error("[%s] (NRF-notify) NF invalid state [type:%s]", |
1469 | 0 | nf_instance->id, |
1470 | 0 | OpenAPI_nf_type_ToString(nf_instance->nf_type)); |
1471 | 0 | } |
1472 | 0 | } |
1473 | | |
1474 | 0 | if (ogs_nnrf_nfm_handle_nf_profile(nf_instance, NFProfile) == false) { |
1475 | 0 | ogs_error("[%s] (NRF-notify) Invalid NFProfile [type:%s]", |
1476 | 0 | NFProfile->nf_instance_id, |
1477 | 0 | OpenAPI_nf_type_ToString(NFProfile->nf_type)); |
1478 | | |
1479 | | /* |
1480 | | * ogs_nnrf_nfm_handle_nf_profile() rebuilds the NF profile in |
1481 | | * place. If parsing fails for a newly created cache entry, remove |
1482 | | * it because it may contain a partial profile. If the entry |
1483 | | * already existed, keep it in the registry to avoid deleting a |
1484 | | * previously usable local cache entry because of one bad notify. |
1485 | | */ |
1486 | 0 | if (nf_instance_created == true) { |
1487 | 0 | ogs_sbi_nf_fsm_fini(nf_instance); |
1488 | 0 | ogs_sbi_nf_instance_remove(nf_instance); |
1489 | 0 | } |
1490 | |
|
1491 | 0 | ogs_assert(true == |
1492 | 0 | ogs_sbi_server_send_error( |
1493 | 0 | stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, |
1494 | 0 | recvmsg, "Invalid NFProfile", |
1495 | 0 | NFProfile->nf_instance_id, NULL)); |
1496 | 0 | ogs_sbi_header_free(&header); |
1497 | 0 | return false; |
1498 | 0 | } |
1499 | | |
1500 | 0 | ogs_info("[%s] (NRF-notify) NF Profile updated [type:%s]", |
1501 | 0 | nf_instance->id, |
1502 | 0 | OpenAPI_nf_type_ToString(nf_instance->nf_type)); |
1503 | |
|
1504 | 0 | ogs_sbi_client_associate(nf_instance); |
1505 | |
|
1506 | 0 | switch (nf_instance->nf_type) { |
1507 | 0 | case OpenAPI_nf_type_SEPP: |
1508 | 0 | ogs_sbi_self()->sepp_instance = nf_instance; |
1509 | 0 | break; |
1510 | 0 | default: |
1511 | 0 | break; |
1512 | 0 | } |
1513 | |
|
1514 | 0 | } else if (NotificationData->event == |
1515 | 0 | OpenAPI_notification_event_type_NF_DEREGISTERED) { |
1516 | 0 | nf_instance = ogs_sbi_nf_instance_find(message.h.resource.component[1]); |
1517 | 0 | if (nf_instance) { |
1518 | 0 | if (nf_instance_has_status_subscription(nf_instance) == false) { |
1519 | 0 | ogs_warn("[%s] (NRF-notify) NF_DEREGISTERED outside " |
1520 | 0 | "subscription scope [type:%s] - ignored", |
1521 | 0 | nf_instance->id, |
1522 | 0 | OpenAPI_nf_type_ToString(nf_instance->nf_type)); |
1523 | 0 | ogs_assert(true == |
1524 | 0 | ogs_sbi_server_send_error( |
1525 | 0 | stream, OGS_SBI_HTTP_STATUS_NOT_FOUND, |
1526 | 0 | recvmsg, "No active subscription for notification", |
1527 | 0 | message.h.resource.component[1], NULL)); |
1528 | 0 | ogs_sbi_header_free(&header); |
1529 | 0 | return false; |
1530 | 0 | } |
1531 | | |
1532 | 0 | ogs_info("[%s] (NRF-notify) NF_DEREGISTERED event [type:%s]", |
1533 | 0 | nf_instance->id, |
1534 | 0 | OpenAPI_nf_type_ToString(nf_instance->nf_type)); |
1535 | 0 | ogs_sbi_nf_fsm_fini(nf_instance); |
1536 | 0 | ogs_sbi_nf_instance_remove(nf_instance); |
1537 | 0 | } else { |
1538 | 0 | ogs_warn("[%s] (NRF-notify) Not found", |
1539 | 0 | message.h.resource.component[1]); |
1540 | 0 | ogs_assert(true == |
1541 | 0 | ogs_sbi_server_send_error(stream, |
1542 | 0 | OGS_SBI_HTTP_STATUS_NOT_FOUND, |
1543 | 0 | recvmsg, "Not found", message.h.resource.component[1], |
1544 | 0 | NULL)); |
1545 | 0 | ogs_sbi_header_free(&header); |
1546 | 0 | return false; |
1547 | 0 | } |
1548 | 0 | } else { |
1549 | 0 | char *eventstr = OpenAPI_notification_event_type_ToString( |
1550 | 0 | NotificationData->event); |
1551 | 0 | ogs_error("Not supported event [%d:%s]", |
1552 | 0 | NotificationData->event, eventstr ? eventstr : "Unknown"); |
1553 | 0 | ogs_assert(true == |
1554 | 0 | ogs_sbi_server_send_error(stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST, |
1555 | 0 | recvmsg, "Not supported event", |
1556 | 0 | eventstr ? eventstr : "Unknown", NULL)); |
1557 | 0 | ogs_sbi_header_free(&header); |
1558 | 0 | return false; |
1559 | 0 | } |
1560 | | |
1561 | 0 | response = ogs_sbi_build_response(recvmsg, OGS_SBI_HTTP_STATUS_NO_CONTENT); |
1562 | 0 | ogs_assert(response); |
1563 | 0 | ogs_assert(true == ogs_sbi_server_send_response(stream, response)); |
1564 | | |
1565 | 0 | ogs_sbi_header_free(&header); |
1566 | 0 | return true; |
1567 | 0 | } |
1568 | | |
1569 | | void ogs_nnrf_disc_handle_nf_discover_search_result( |
1570 | | OpenAPI_search_result_t *SearchResult) |
1571 | 0 | { |
1572 | 0 | OpenAPI_lnode_t *node = NULL; |
1573 | 0 | ogs_sbi_nf_instance_t *nf_instance = NULL; |
1574 | |
|
1575 | 0 | ogs_assert(SearchResult); |
1576 | 0 | ogs_assert(SearchResult->validity_period); |
1577 | 0 | ogs_assert(SearchResult->nf_instances); |
1578 | | |
1579 | 0 | OpenAPI_list_for_each(SearchResult->nf_instances, node) { |
1580 | 0 | OpenAPI_nf_profile_t *NFProfile = NULL; |
1581 | 0 | bool nf_instance_created = false; |
1582 | |
|
1583 | 0 | if (!node->data) continue; |
1584 | | |
1585 | 0 | NFProfile = node->data; |
1586 | |
|
1587 | 0 | if (!NFProfile) { |
1588 | 0 | ogs_error("No NFProfile"); |
1589 | 0 | continue; |
1590 | 0 | } |
1591 | | |
1592 | 0 | if (!NFProfile->nf_instance_id) { |
1593 | 0 | ogs_error("No NFProfile.NFInstanceId"); |
1594 | 0 | continue; |
1595 | 0 | } |
1596 | | |
1597 | 0 | if (!NFProfile->nf_type) { |
1598 | 0 | ogs_error("No NFProfile.NFType"); |
1599 | 0 | continue; |
1600 | 0 | } |
1601 | | |
1602 | 0 | if (!NFProfile->nf_status) { |
1603 | 0 | ogs_error("No NFProfile.NFStatus"); |
1604 | 0 | continue; |
1605 | 0 | } |
1606 | | |
1607 | 0 | nf_instance = ogs_sbi_nf_instance_find(NFProfile->nf_instance_id); |
1608 | 0 | if (!nf_instance) { |
1609 | 0 | nf_instance = ogs_sbi_nf_instance_add(); |
1610 | 0 | if (!nf_instance) { |
1611 | 0 | ogs_error("Can't add discovered NF instance [%s:%s] " |
1612 | 0 | "due to insufficient space", |
1613 | 0 | NFProfile->nf_instance_id, |
1614 | 0 | OpenAPI_nf_type_ToString(NFProfile->nf_type)); |
1615 | 0 | continue; |
1616 | 0 | } |
1617 | | |
1618 | 0 | ogs_sbi_nf_instance_set_id(nf_instance, NFProfile->nf_instance_id); |
1619 | 0 | ogs_sbi_nf_fsm_init(nf_instance); |
1620 | 0 | nf_instance_created = true; |
1621 | |
|
1622 | 0 | ogs_info("[%s] (NRF-discover) NF registered [type:%s]", |
1623 | 0 | NFProfile->nf_instance_id, |
1624 | 0 | OpenAPI_nf_type_ToString(NFProfile->nf_type)); |
1625 | 0 | } else { |
1626 | 0 | ogs_warn("[%s] (NRF-discover) NF has already been added [type:%s]", |
1627 | 0 | NFProfile->nf_instance_id, |
1628 | 0 | OpenAPI_nf_type_ToString(NFProfile->nf_type)); |
1629 | 0 | if (!OGS_FSM_CHECK(&nf_instance->sm, ogs_sbi_nf_state_registered)) { |
1630 | 0 | ogs_error("[%s] (NRF-notify) NF invalid state [type:%s]", |
1631 | 0 | NFProfile->nf_instance_id, |
1632 | 0 | OpenAPI_nf_type_ToString(NFProfile->nf_type)); |
1633 | 0 | } |
1634 | 0 | } |
1635 | | |
1636 | 0 | if (NF_INSTANCE_ID_IS_OTHERS(nf_instance->id)) { |
1637 | 0 | if (ogs_nnrf_nfm_handle_nf_profile( |
1638 | 0 | nf_instance, NFProfile) == false) { |
1639 | 0 | ogs_error("[%s] (NRF-discover) Invalid NFProfile [type:%s]", |
1640 | 0 | NFProfile->nf_instance_id, |
1641 | 0 | OpenAPI_nf_type_ToString(NFProfile->nf_type)); |
1642 | | |
1643 | | /* |
1644 | | * Do not leave a newly created partial NFProfile in the |
1645 | | * registry. If the entry already existed, keep it to avoid |
1646 | | * deleting a previously usable local cache entry because of |
1647 | | * one bad discovery result. |
1648 | | */ |
1649 | 0 | if (nf_instance_created == true) { |
1650 | 0 | ogs_sbi_nf_fsm_fini(nf_instance); |
1651 | 0 | ogs_sbi_nf_instance_remove(nf_instance); |
1652 | 0 | } |
1653 | |
|
1654 | 0 | continue; |
1655 | 0 | } |
1656 | | |
1657 | 0 | ogs_sbi_client_associate(nf_instance); |
1658 | |
|
1659 | 0 | switch (nf_instance->nf_type) { |
1660 | 0 | case OpenAPI_nf_type_SEPP: |
1661 | 0 | ogs_sbi_self()->sepp_instance = nf_instance; |
1662 | 0 | break; |
1663 | 0 | default: |
1664 | 0 | break; |
1665 | 0 | } |
1666 | | |
1667 | | /* TIME : Update validity from NRF */ |
1668 | 0 | nf_instance->time.validity_duration = SearchResult->validity_period; |
1669 | |
|
1670 | 0 | ogs_assert(nf_instance->t_validity); |
1671 | 0 | ogs_timer_start(nf_instance->t_validity, |
1672 | 0 | ogs_time_from_sec(nf_instance->time.validity_duration)); |
1673 | |
|
1674 | 0 | ogs_info("[%s] (NF-discover) NF Profile updated " |
1675 | 0 | "[type:%s validity:%ds]", |
1676 | 0 | NFProfile->nf_instance_id, |
1677 | 0 | OpenAPI_nf_type_ToString(NFProfile->nf_type), |
1678 | 0 | nf_instance->time.validity_duration); |
1679 | 0 | } |
1680 | 0 | } |
1681 | 0 | } |