Coverage Report

Created: 2026-11-06 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}