/src/open5gs/lib/pfcp/util.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2025 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 | | /* |
23 | | * Requirements of Node ID: |
24 | | * NONE : Node ID must not be present |
25 | | * OPTIONAL : Node ID may or may not be present |
26 | | * MANDATORY : Node ID must be present |
27 | | */ |
28 | 0 | #define OGS_PFCP_NODE_ID_NONE 0 |
29 | 0 | #define OGS_PFCP_NODE_ID_OPTIONAL 1 |
30 | 0 | #define OGS_PFCP_NODE_ID_MANDATORY 2 |
31 | | |
32 | | /* |
33 | | * This function extracts the PFCP Node ID from the given PFCP message. |
34 | | * It determines the Node ID field location and requirement based on |
35 | | * the message type. Then it validates presence and copies data into |
36 | | * 'node_id'. If Node ID is not consistent with the requirement, an |
37 | | * error status is returned. |
38 | | */ |
39 | | ogs_pfcp_status_e |
40 | | ogs_pfcp_extract_node_id(ogs_pfcp_message_t *message, |
41 | | ogs_pfcp_node_id_t *node_id) |
42 | 0 | { |
43 | | |
44 | | /* For C89 compliance, all variables are declared upfront. */ |
45 | 0 | ogs_pfcp_tlv_node_id_t *tlv_node_id = NULL; |
46 | 0 | int requirement = OGS_PFCP_NODE_ID_NONE; |
47 | 0 | ogs_pfcp_status_e status = OGS_PFCP_STATUS_SUCCESS; |
48 | | |
49 | | /* Validate input pointers */ |
50 | 0 | ogs_assert(message); |
51 | 0 | ogs_assert(node_id); |
52 | | |
53 | | /* Initialize the output structure */ |
54 | 0 | memset(node_id, 0, sizeof(*node_id)); |
55 | | |
56 | | /* Determine the location of node_id TLV and requirement */ |
57 | 0 | switch (message->h.type) { |
58 | 0 | case OGS_PFCP_PFD_MANAGEMENT_REQUEST_TYPE: |
59 | 0 | tlv_node_id = &message->pfcp_pfd_management_request.node_id; |
60 | 0 | requirement = OGS_PFCP_NODE_ID_OPTIONAL; |
61 | 0 | break; |
62 | | |
63 | 0 | case OGS_PFCP_PFD_MANAGEMENT_RESPONSE_TYPE: |
64 | 0 | tlv_node_id = &message->pfcp_pfd_management_response.node_id; |
65 | 0 | requirement = OGS_PFCP_NODE_ID_OPTIONAL; |
66 | 0 | break; |
67 | | |
68 | 0 | case OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE: |
69 | 0 | tlv_node_id = &message->pfcp_association_setup_request.node_id; |
70 | 0 | requirement = OGS_PFCP_NODE_ID_MANDATORY; |
71 | 0 | break; |
72 | | |
73 | 0 | case OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE: |
74 | 0 | tlv_node_id = &message->pfcp_association_setup_response.node_id; |
75 | 0 | requirement = OGS_PFCP_NODE_ID_MANDATORY; |
76 | 0 | break; |
77 | | |
78 | 0 | case OGS_PFCP_ASSOCIATION_UPDATE_REQUEST_TYPE: |
79 | 0 | tlv_node_id = &message->pfcp_association_update_request.node_id; |
80 | 0 | requirement = OGS_PFCP_NODE_ID_MANDATORY; |
81 | 0 | break; |
82 | | |
83 | 0 | case OGS_PFCP_ASSOCIATION_UPDATE_RESPONSE_TYPE: |
84 | 0 | tlv_node_id = &message->pfcp_association_update_response.node_id; |
85 | 0 | requirement = OGS_PFCP_NODE_ID_MANDATORY; |
86 | 0 | break; |
87 | | |
88 | 0 | case OGS_PFCP_ASSOCIATION_RELEASE_REQUEST_TYPE: |
89 | 0 | tlv_node_id = &message->pfcp_association_release_request.node_id; |
90 | 0 | requirement = OGS_PFCP_NODE_ID_MANDATORY; |
91 | 0 | break; |
92 | | |
93 | 0 | case OGS_PFCP_ASSOCIATION_RELEASE_RESPONSE_TYPE: |
94 | 0 | tlv_node_id = &message->pfcp_association_release_response.node_id; |
95 | 0 | requirement = OGS_PFCP_NODE_ID_MANDATORY; |
96 | 0 | break; |
97 | | |
98 | 0 | case OGS_PFCP_NODE_REPORT_REQUEST_TYPE: |
99 | 0 | tlv_node_id = &message->pfcp_node_report_request.node_id; |
100 | 0 | requirement = OGS_PFCP_NODE_ID_MANDATORY; |
101 | 0 | break; |
102 | | |
103 | 0 | case OGS_PFCP_NODE_REPORT_RESPONSE_TYPE: |
104 | 0 | tlv_node_id = &message->pfcp_node_report_response.node_id; |
105 | 0 | requirement = OGS_PFCP_NODE_ID_MANDATORY; |
106 | 0 | break; |
107 | | |
108 | 0 | case OGS_PFCP_SESSION_SET_DELETION_REQUEST_TYPE: |
109 | 0 | tlv_node_id = &message->pfcp_session_set_deletion_request.node_id; |
110 | 0 | requirement = OGS_PFCP_NODE_ID_MANDATORY; |
111 | 0 | break; |
112 | | |
113 | 0 | case OGS_PFCP_SESSION_SET_DELETION_RESPONSE_TYPE: |
114 | 0 | tlv_node_id = &message->pfcp_session_set_deletion_response.node_id; |
115 | 0 | requirement = OGS_PFCP_NODE_ID_MANDATORY; |
116 | 0 | break; |
117 | | |
118 | 0 | case OGS_PFCP_SESSION_SET_MODIFICATION_REQUEST_TYPE: |
119 | 0 | tlv_node_id = &message->pfcp_session_set_modification_request.node_id; |
120 | 0 | requirement = OGS_PFCP_NODE_ID_MANDATORY; |
121 | 0 | break; |
122 | | |
123 | 0 | case OGS_PFCP_SESSION_SET_MODIFICATION_RESPONSE_TYPE: |
124 | 0 | tlv_node_id = &message->pfcp_session_set_modification_response.node_id; |
125 | 0 | requirement = OGS_PFCP_NODE_ID_MANDATORY; |
126 | 0 | break; |
127 | | |
128 | 0 | case OGS_PFCP_SESSION_ESTABLISHMENT_REQUEST_TYPE: |
129 | 0 | tlv_node_id = &message->pfcp_session_establishment_request.node_id; |
130 | 0 | requirement = OGS_PFCP_NODE_ID_MANDATORY; |
131 | 0 | break; |
132 | | |
133 | 0 | case OGS_PFCP_SESSION_ESTABLISHMENT_RESPONSE_TYPE: |
134 | 0 | tlv_node_id = &message->pfcp_session_establishment_response.node_id; |
135 | 0 | requirement = OGS_PFCP_NODE_ID_MANDATORY; |
136 | 0 | break; |
137 | | |
138 | 0 | case OGS_PFCP_SESSION_MODIFICATION_REQUEST_TYPE: |
139 | 0 | tlv_node_id = &message->pfcp_session_modification_request.node_id; |
140 | 0 | requirement = OGS_PFCP_NODE_ID_OPTIONAL; |
141 | 0 | break; |
142 | | |
143 | | /* Add other message types with node_id here as needed */ |
144 | | |
145 | 0 | case OGS_PFCP_HEARTBEAT_REQUEST_TYPE: |
146 | 0 | case OGS_PFCP_HEARTBEAT_RESPONSE_TYPE: |
147 | 0 | case OGS_PFCP_VERSION_NOT_SUPPORTED_RESPONSE_TYPE: |
148 | 0 | case OGS_PFCP_SESSION_MODIFICATION_RESPONSE_TYPE: |
149 | 0 | case OGS_PFCP_SESSION_DELETION_REQUEST_TYPE: |
150 | 0 | case OGS_PFCP_SESSION_DELETION_RESPONSE_TYPE: |
151 | 0 | case OGS_PFCP_SESSION_REPORT_REQUEST_TYPE: |
152 | 0 | case OGS_PFCP_SESSION_REPORT_RESPONSE_TYPE: |
153 | | /* Node ID must not be present for these messages */ |
154 | 0 | requirement = OGS_PFCP_NODE_ID_NONE; |
155 | 0 | break; |
156 | | |
157 | 0 | default: |
158 | | /* Unknown message type */ |
159 | 0 | ogs_error("Unknown message type %d", message->h.type); |
160 | 0 | return OGS_PFCP_ERROR_UNKNOWN_MESSAGE; |
161 | 0 | } |
162 | | |
163 | | /* Check requirement vs. tlv_node_id existence */ |
164 | 0 | switch (requirement) { |
165 | 0 | case OGS_PFCP_NODE_ID_MANDATORY: |
166 | | /* Must have tlv_node_id. presence must be 1. */ |
167 | 0 | ogs_assert(tlv_node_id); |
168 | 0 | if (!tlv_node_id->presence) { |
169 | 0 | status = OGS_PFCP_ERROR_NODE_ID_NOT_PRESENT; |
170 | 0 | goto done; |
171 | 0 | } |
172 | 0 | break; |
173 | | |
174 | 0 | case OGS_PFCP_NODE_ID_OPTIONAL: |
175 | | /* |
176 | | * Must have tlv_node_id. presence=1 => real Node ID |
177 | | * presence=0 => no Node ID |
178 | | */ |
179 | 0 | ogs_assert(tlv_node_id); |
180 | 0 | if (!tlv_node_id->presence) { |
181 | 0 | status = OGS_PFCP_STATUS_NODE_ID_OPTIONAL_ABSENT; |
182 | 0 | goto done; |
183 | 0 | } |
184 | 0 | break; |
185 | | |
186 | 0 | case OGS_PFCP_NODE_ID_NONE: |
187 | | /* Must be NULL => no Node ID field */ |
188 | 0 | ogs_assert(tlv_node_id == NULL); |
189 | 0 | status = OGS_PFCP_STATUS_NODE_ID_NONE; |
190 | 0 | goto done; |
191 | | |
192 | 0 | default: |
193 | 0 | status = OGS_PFCP_ERROR_UNKNOWN_MESSAGE; |
194 | 0 | goto done; |
195 | 0 | } |
196 | | |
197 | 0 | memcpy(node_id, tlv_node_id->data, ogs_min(tlv_node_id->len, sizeof(ogs_pfcp_node_id_t))); |
198 | 0 | node_id->fqdn[OGS_MAX_FQDN_LEN - 1] = '\0'; |
199 | |
|
200 | 0 | if (node_id->type != OGS_PFCP_NODE_ID_IPV4 && |
201 | 0 | node_id->type != OGS_PFCP_NODE_ID_IPV6 && |
202 | 0 | node_id->type != OGS_PFCP_NODE_ID_FQDN) { |
203 | 0 | ogs_error("Semantic incorrect message[%d] type[%d]", |
204 | 0 | message->h.type, node_id->type); |
205 | 0 | return OGS_PFCP_ERROR_SEMANTIC_INCORRECT_MESSAGE; |
206 | 0 | } |
207 | | |
208 | | /* Node ID is valid */ |
209 | 0 | status = OGS_PFCP_STATUS_SUCCESS; |
210 | |
|
211 | 0 | done: |
212 | 0 | return status; |
213 | 0 | } |
214 | | |
215 | | ogs_sockaddr_t *ogs_pfcp_node_id_to_addrinfo(const ogs_pfcp_node_id_t *node_id) |
216 | 0 | { |
217 | 0 | ogs_sockaddr_t *p; |
218 | 0 | int ret; |
219 | 0 | uint16_t port = ogs_pfcp_self()->pfcp_port; |
220 | 0 | char fqdn[OGS_MAX_FQDN_LEN+1]; |
221 | |
|
222 | 0 | ogs_assert(node_id); |
223 | 0 | switch (node_id->type) { |
224 | | |
225 | | /*------------------------------------------------ |
226 | | * 1) IPv4 |
227 | | *-----------------------------------------------*/ |
228 | 0 | case OGS_PFCP_NODE_ID_IPV4: |
229 | 0 | p = (ogs_sockaddr_t *)ogs_calloc(1, sizeof(*p)); |
230 | 0 | if (!p) { |
231 | 0 | ogs_error("ogs_calloc() failed"); |
232 | 0 | return NULL; |
233 | 0 | } |
234 | 0 | p->sa.sa_family = AF_INET; |
235 | 0 | p->sin.sin_port = htobe16(port); |
236 | 0 | p->sin.sin_addr.s_addr = node_id->addr; |
237 | 0 | p->next = NULL; |
238 | 0 | return p; |
239 | | |
240 | | /*------------------------------------------------ |
241 | | * 2) IPv6 |
242 | | *-----------------------------------------------*/ |
243 | 0 | case OGS_PFCP_NODE_ID_IPV6: |
244 | 0 | p = (ogs_sockaddr_t *)ogs_calloc(1, sizeof(*p)); |
245 | 0 | if (!p) { |
246 | 0 | ogs_error("ogs_calloc() failed"); |
247 | 0 | return NULL; |
248 | 0 | } |
249 | 0 | p->sa.sa_family = AF_INET6; |
250 | 0 | p->sin6.sin6_port = htobe16(port); |
251 | | /* Copy 16 bytes of IPv6 address */ |
252 | 0 | memcpy(&p->sin6.sin6_addr, node_id->addr6, 16); |
253 | 0 | p->next = NULL; |
254 | 0 | return p; |
255 | | |
256 | | /*------------------------------------------------ |
257 | | * 3) FQDN |
258 | | *-----------------------------------------------*/ |
259 | 0 | case OGS_PFCP_NODE_ID_FQDN: |
260 | | /* If the FQDN is not empty, we attempt DNS resolution. |
261 | | * ogs_addaddrinfo() is a placeholder for your actual |
262 | | * DNS -> ogs_sockaddr_t function (often wraps getaddrinfo). |
263 | | */ |
264 | | /* Port=0 or set as needed, family=AF_UNSPEC, flags=0. */ |
265 | 0 | if (ogs_fqdn_parse(fqdn, node_id->fqdn, strlen(node_id->fqdn)) <= 0) { |
266 | 0 | ogs_error("ogs_fqdn_parse() error [%s]", node_id->fqdn); |
267 | 0 | return NULL; |
268 | 0 | } |
269 | 0 | ret = ogs_getaddrinfo(&p, AF_UNSPEC, fqdn, port, 0); |
270 | 0 | if (ret != 0) { |
271 | | /* DNS resolution failed => *out remains NULL */ |
272 | 0 | ogs_error("ogs_addaddrinfo() failed"); |
273 | 0 | return NULL; |
274 | 0 | } |
275 | | /* If FQDN is empty, just return with no addresses. */ |
276 | 0 | return p; |
277 | | |
278 | | /*------------------------------------------------ |
279 | | * 4) Unsupported type or default |
280 | | *-----------------------------------------------*/ |
281 | 0 | default: |
282 | | /* Optionally handle an error or just return success |
283 | | * with no addresses. |
284 | | */ |
285 | 0 | ogs_error("Unknown type [%d]", node_id->type); |
286 | 0 | return NULL; |
287 | 0 | } |
288 | 0 | } |
289 | | |
290 | | /* Utility function to convert node_id to string for logging */ |
291 | | const char *ogs_pfcp_node_id_to_string_static( |
292 | | const ogs_pfcp_node_id_t *node_id) |
293 | 0 | { |
294 | 0 | static char buffer[OGS_MAX_FQDN_LEN+1] = { 0, }; |
295 | |
|
296 | 0 | if (node_id) { |
297 | 0 | switch (node_id->type) { |
298 | 0 | case OGS_PFCP_NODE_ID_IPV4: |
299 | 0 | inet_ntop(AF_INET, &node_id->addr, buffer, sizeof(buffer)); |
300 | 0 | break; |
301 | 0 | case OGS_PFCP_NODE_ID_IPV6: |
302 | 0 | inet_ntop(AF_INET6, node_id->addr6, buffer, sizeof(buffer)); |
303 | 0 | break; |
304 | 0 | case OGS_PFCP_NODE_ID_FQDN: |
305 | 0 | if (ogs_fqdn_parse(buffer, |
306 | 0 | node_id->fqdn, |
307 | 0 | strlen(node_id->fqdn)) <= 0) |
308 | 0 | snprintf(buffer, sizeof(buffer), "%s", node_id->fqdn); |
309 | 0 | break; |
310 | 0 | default: |
311 | 0 | snprintf(buffer, sizeof(buffer), "Unknown"); |
312 | 0 | break; |
313 | 0 | } |
314 | 0 | } |
315 | | |
316 | 0 | return buffer; |
317 | 0 | } |