Coverage Report

Created: 2026-08-17 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open5gs/lib/pfcp/path.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2019-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
ogs_sock_t *ogs_pfcp_server(ogs_socknode_t *node)
23
0
{
24
0
    char buf[OGS_ADDRSTRLEN];
25
0
    ogs_sock_t *pfcp;
26
0
    ogs_assert(node);
27
28
0
    pfcp = ogs_udp_server(node->addr, node->option);
29
0
    if (pfcp) {
30
0
        ogs_info("pfcp_server() [%s]:%d",
31
0
                OGS_ADDR(node->addr, buf), OGS_PORT(node->addr));
32
33
0
        node->sock = pfcp;
34
0
    }
35
36
0
    return pfcp;
37
0
}
38
39
/* Minimum PFCP header length (e.g., 12 bytes) */
40
0
#define MIN_PFCP_HEADER_LENGTH 12
41
42
/*
43
 * ogs_pfcp_recvfrom
44
 *
45
 * Receives a PFCP message from the socket 'fd'. It allocates a pkbuf,
46
 * receives the message, trims the pkbuf, and verifies the header.
47
 * If any error occurs (e.g., too short message, unsupported version, or
48
 * incomplete message), the function frees the pkbuf and returns NULL.
49
 *
50
 * The sender's address is stored in 'from'.
51
 *
52
 * Returns a pointer to ogs_pkbuf_t on success, or NULL on failure.
53
 */
54
ogs_pkbuf_t *ogs_pfcp_recvfrom(ogs_socket_t fd, ogs_sockaddr_t *from)
55
0
{
56
0
    ogs_pkbuf_t *pkbuf;
57
0
    ssize_t size;
58
0
    ogs_pfcp_header_t *h;
59
0
    uint16_t pfcp_body_length;
60
0
    size_t expected_total_length;
61
62
0
    ogs_assert(fd != INVALID_SOCKET);
63
0
    ogs_assert(from);
64
65
    /* Allocate buffer for maximum SDU length */
66
0
    pkbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN);
67
0
    if (pkbuf == NULL) {
68
0
        ogs_error("ogs_pkbuf_alloc() failed");
69
0
        return NULL;
70
0
    }
71
0
    ogs_pkbuf_put(pkbuf, OGS_MAX_SDU_LEN);
72
73
0
    size = ogs_recvfrom(fd, pkbuf->data, pkbuf->len, 0, from);
74
0
    if (size <= 0) {
75
0
        ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
76
0
            "ogs_recvfrom() failed");
77
0
        ogs_pkbuf_free(pkbuf);
78
0
        return NULL;
79
0
    }
80
0
    ogs_pkbuf_trim(pkbuf, size);
81
82
    /* Check that the data is at least as long as the header */
83
0
    if (size < MIN_PFCP_HEADER_LENGTH) {
84
0
        ogs_error("Received PFCP message too short: %ld bytes (min %d)",
85
0
            (long)size, MIN_PFCP_HEADER_LENGTH);
86
0
        ogs_pkbuf_free(pkbuf);
87
0
        return NULL;
88
0
    }
89
90
0
    h = (ogs_pfcp_header_t *)pkbuf->data;
91
92
    /* Verify PFCP version */
93
0
    if (h->version != OGS_PFCP_VERSION) {
94
0
        ogs_pfcp_header_t rsp;
95
0
        memset(&rsp, 0, sizeof(rsp));
96
0
        ogs_error("Not supported version[%d]", h->version);
97
0
        rsp.flags = (OGS_PFCP_VERSION << 5);
98
0
        rsp.type = OGS_PFCP_VERSION_NOT_SUPPORTED_RESPONSE_TYPE;
99
0
        rsp.length = htobe16(4);
100
0
        rsp.sqn_only = h->sqn_only;
101
0
        if (ogs_sendto(fd, &rsp, 8, 0, from) < 0) {
102
0
            ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
103
0
                "ogs_sendto() failed");
104
0
        }
105
0
        ogs_pkbuf_free(pkbuf);
106
0
        return NULL;
107
0
    }
108
109
    /* Check total PFCP message length.
110
       Assume the header's length field indicates the body length,
111
       excluding the first 4 bytes. */
112
0
    pfcp_body_length = be16toh(h->length);
113
0
    expected_total_length = pfcp_body_length + 4;
114
0
    if ((size_t)size != expected_total_length) {
115
0
        ogs_error("Invalid PFCP Header Length: expected %zu bytes, "
116
0
            "received %ld bytes", expected_total_length, (long)size);
117
0
        ogs_pkbuf_free(pkbuf);
118
0
        return NULL;
119
0
    }
120
121
0
    return pkbuf;
122
0
}
123
124
int ogs_pfcp_sendto(ogs_pfcp_node_t *node, ogs_pkbuf_t *pkbuf)
125
0
{
126
0
    ssize_t sent;
127
0
    ogs_sock_t *sock = NULL;
128
0
    ogs_sockaddr_t *addr = NULL;
129
130
0
    ogs_assert(node);
131
0
    ogs_assert(pkbuf);
132
0
    ogs_assert(node->addr_list);
133
134
    /* Initialize round-robin iterator if needed */
135
0
    if (node->current_addr == NULL) {
136
0
        node->current_addr = node->addr_list;
137
0
    }
138
0
    addr = node->current_addr;
139
0
    ogs_assert(addr);
140
141
0
    if (addr->ogs_sa_family == AF_INET) {
142
0
        sock = ogs_pfcp_self()->pfcp_sock;
143
0
        if (!sock) {
144
0
            ogs_error("IPv4 socket (pfcp_sock) is not available. "
145
0
                    "Ensure that 'pfcp.server.address: 127.0.0.1' "
146
0
                    "is set in the YAML configuration file.");
147
0
            return OGS_ERROR;
148
0
        }
149
0
    } else if (addr->ogs_sa_family == AF_INET6) {
150
0
        sock = ogs_pfcp_self()->pfcp_sock6;
151
0
        if (!sock) {
152
0
            ogs_error("IPv6 socket (pfcp_sock) is not available. "
153
0
                    "Ensure that 'pfcp.server.address: [::1]' "
154
0
                    "is set in the YAML configuration file.");
155
0
            return OGS_ERROR;
156
0
        }
157
0
    } else
158
0
        ogs_assert_if_reached();
159
160
0
    sent = ogs_sendto(sock->fd, pkbuf->data, pkbuf->len, 0, addr);
161
0
    if (sent < 0 || sent != pkbuf->len) {
162
0
        if (ogs_socket_errno != OGS_EAGAIN) {
163
0
            char buf[OGS_ADDRSTRLEN];
164
0
            int err = ogs_socket_errno;
165
0
            ogs_log_message(OGS_LOG_ERROR, err,
166
0
                    "ogs_sendto(%u, %p, %u, 0, %s:%u) failed",
167
0
                    sock->fd, pkbuf->data, pkbuf->len,
168
0
                    OGS_ADDR(addr, buf), OGS_PORT(addr));
169
0
        }
170
0
        return OGS_ERROR;
171
0
    }
172
173
    /* Move to next address in round-robin sequence */
174
0
    if (node->current_addr->next)
175
0
        node->current_addr = node->current_addr->next;
176
0
    else
177
        /* If end of list reached, wrap around to the start */
178
0
        node->current_addr = node->addr_list;
179
180
0
    return OGS_OK;
181
0
}
182
183
int ogs_pfcp_send_heartbeat_request(ogs_pfcp_node_t *node,
184
        void (*cb)(ogs_pfcp_xact_t *xact, void *data))
185
0
{
186
0
    int rv;
187
0
    ogs_pkbuf_t *pkbuf = NULL;
188
0
    ogs_pfcp_header_t h;
189
0
    ogs_pfcp_xact_t *xact = NULL;
190
191
0
    ogs_assert(node);
192
193
0
    memset(&h, 0, sizeof(ogs_pfcp_header_t));
194
0
    h.type = OGS_PFCP_HEARTBEAT_REQUEST_TYPE;
195
0
    h.seid = 0;
196
197
0
    xact = ogs_pfcp_xact_local_create(node, cb, node);
198
0
    if (!xact) {
199
0
        ogs_error("ogs_pfcp_xact_local_create() failed");
200
0
        return OGS_ERROR;
201
0
    }
202
203
0
    pkbuf = ogs_pfcp_build_heartbeat_request(h.type);
204
0
    if (!pkbuf) {
205
0
        ogs_error("ogs_pfcp_build_heartbeat_request() failed");
206
0
        return OGS_ERROR;
207
0
    }
208
209
0
    rv = ogs_pfcp_xact_update_tx(xact, &h, pkbuf);
210
0
    if (rv != OGS_OK) {
211
0
        ogs_error("ogs_pfcp_xact_update_tx() failed");
212
0
        return OGS_ERROR;
213
0
    }
214
215
0
    rv = ogs_pfcp_xact_commit(xact);
216
0
    ogs_expect(rv == OGS_OK);
217
218
0
    return rv;
219
0
}
220
221
int ogs_pfcp_send_heartbeat_response(ogs_pfcp_xact_t *xact)
222
0
{
223
0
    int rv;
224
0
    ogs_pkbuf_t *pkbuf = NULL;
225
0
    ogs_pfcp_header_t h;
226
227
0
    ogs_assert(xact);
228
229
0
    memset(&h, 0, sizeof(ogs_pfcp_header_t));
230
0
    h.type = OGS_PFCP_HEARTBEAT_RESPONSE_TYPE;
231
0
    h.seid = 0;
232
233
0
    pkbuf = ogs_pfcp_build_heartbeat_response(h.type);
234
0
    if (!pkbuf) {
235
0
        ogs_error("ogs_pfcp_build_heartbeat_response() failed");
236
0
        return OGS_ERROR;
237
0
    }
238
239
0
    rv = ogs_pfcp_xact_update_tx(xact, &h, pkbuf);
240
0
    if (rv != OGS_OK) {
241
0
        ogs_error("ogs_pfcp_xact_update_tx() failed");
242
0
        return OGS_ERROR;
243
0
    }
244
245
0
    rv = ogs_pfcp_xact_commit(xact);
246
0
    ogs_expect(rv == OGS_OK);
247
248
    /*
249
     * Force delete the PFCP transaction to check the PFCP recovery timestamp.
250
     *
251
     * Otherwise, duplicated request (lib/pfcp/xact.c:384) prevents the message
252
     * from being passed to the state machine, so the PFCP recovery timestamp
253
     * cannot be delivered in the handler routine.
254
     */
255
0
    ogs_pfcp_xact_delete(xact);
256
257
0
    return rv;
258
0
}
259
260
int ogs_pfcp_cp_send_association_setup_request(ogs_pfcp_node_t *node,
261
        void (*cb)(ogs_pfcp_xact_t *xact, void *data))
262
0
{
263
0
    int rv;
264
0
    ogs_pkbuf_t *pkbuf = NULL;
265
0
    ogs_pfcp_header_t h;
266
0
    ogs_pfcp_xact_t *xact = NULL;
267
268
0
    ogs_assert(node);
269
270
0
    memset(&h, 0, sizeof(ogs_pfcp_header_t));
271
0
    h.type = OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE;
272
0
    h.seid = 0;
273
274
0
    xact = ogs_pfcp_xact_local_create(node, cb, node);
275
0
    if (!xact) {
276
0
        ogs_error("ogs_pfcp_xact_local_create() failed");
277
0
        return OGS_ERROR;
278
0
    }
279
280
0
    pkbuf = ogs_pfcp_cp_build_association_setup_request(h.type);
281
0
    if (!pkbuf) {
282
0
        ogs_error("ogs_pfcp_cp_build_association_setup_request() failed");
283
0
        return OGS_ERROR;
284
0
    }
285
286
0
    rv = ogs_pfcp_xact_update_tx(xact, &h, pkbuf);
287
0
    if (rv != OGS_OK) {
288
0
        ogs_error("ogs_pfcp_xact_update_tx() failed");
289
0
        return OGS_ERROR;
290
0
    }
291
292
0
    rv = ogs_pfcp_xact_commit(xact);
293
0
    ogs_expect(rv == OGS_OK);
294
295
0
    return rv;
296
0
}
297
298
int ogs_pfcp_cp_send_association_setup_response(ogs_pfcp_xact_t *xact,
299
        uint8_t cause)
300
0
{
301
0
    int rv;
302
0
    ogs_pkbuf_t *pkbuf = NULL;
303
0
    ogs_pfcp_header_t h;
304
305
0
    ogs_assert(xact);
306
307
0
    memset(&h, 0, sizeof(ogs_pfcp_header_t));
308
0
    h.type = OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE;
309
0
    h.seid = 0;
310
311
0
    pkbuf = ogs_pfcp_cp_build_association_setup_response(h.type, cause);
312
0
    if (!pkbuf) {
313
0
        ogs_error("ogs_pfcp_cp_build_association_setup_response() failed");
314
0
        return OGS_ERROR;
315
0
    }
316
317
0
    rv = ogs_pfcp_xact_update_tx(xact, &h, pkbuf);
318
0
    if (rv != OGS_OK) {
319
0
        ogs_error("ogs_pfcp_xact_update_tx() failed");
320
0
        return OGS_ERROR;
321
0
    }
322
323
0
    rv = ogs_pfcp_xact_commit(xact);
324
0
    ogs_expect(rv == OGS_OK);
325
326
0
    return rv;
327
0
}
328
329
int ogs_pfcp_up_send_association_setup_request(ogs_pfcp_node_t *node,
330
        void (*cb)(ogs_pfcp_xact_t *xact, void *data))
331
0
{
332
0
    int rv;
333
0
    ogs_pkbuf_t *pkbuf = NULL;
334
0
    ogs_pfcp_header_t h;
335
0
    ogs_pfcp_xact_t *xact = NULL;
336
337
0
    ogs_assert(node);
338
339
0
    memset(&h, 0, sizeof(ogs_pfcp_header_t));
340
0
    h.type = OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE;
341
0
    h.seid = 0;
342
343
0
    xact = ogs_pfcp_xact_local_create(node, cb, node);
344
0
    if (!xact) {
345
0
        ogs_error("ogs_pfcp_xact_local_create() failed");
346
0
        return OGS_ERROR;
347
0
    }
348
349
0
    pkbuf = ogs_pfcp_up_build_association_setup_request(h.type);
350
0
    if (!pkbuf) {
351
0
        ogs_error("ogs_pfcp_build_heartbeat_request() failed");
352
0
        return OGS_ERROR;
353
0
    }
354
355
0
    rv = ogs_pfcp_xact_update_tx(xact, &h, pkbuf);
356
0
    if (rv != OGS_OK) {
357
0
        ogs_error("ogs_pfcp_xact_update_tx() failed");
358
0
        return OGS_ERROR;
359
0
    }
360
361
0
    rv = ogs_pfcp_xact_commit(xact);
362
0
    ogs_expect(rv == OGS_OK);
363
364
0
    return rv;
365
0
}
366
367
int ogs_pfcp_up_send_association_setup_response(ogs_pfcp_xact_t *xact,
368
        uint8_t cause)
369
0
{
370
0
    int rv;
371
0
    ogs_pkbuf_t *pkbuf = NULL;
372
0
    ogs_pfcp_header_t h;
373
374
0
    ogs_assert(xact);
375
376
0
    memset(&h, 0, sizeof(ogs_pfcp_header_t));
377
0
    h.type = OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE;
378
0
    h.seid = 0;
379
380
0
    pkbuf = ogs_pfcp_up_build_association_setup_response(h.type, cause);
381
0
    if (!pkbuf) {
382
0
        ogs_error("ogs_pfcp_up_build_association_setup_response() failed");
383
0
        return OGS_ERROR;
384
0
    }
385
386
0
    rv = ogs_pfcp_xact_update_tx(xact, &h, pkbuf);
387
0
    if (rv != OGS_OK) {
388
0
        ogs_error("ogs_pfcp_xact_update_tx() failed");
389
0
        return OGS_ERROR;
390
0
    }
391
392
0
    rv = ogs_pfcp_xact_commit(xact);
393
0
    ogs_expect(rv == OGS_OK);
394
395
0
    return rv;
396
0
}
397
398
void ogs_pfcp_send_gtpu(ogs_pfcp_pdr_t *pdr, ogs_pkbuf_t *sendbuf)
399
0
{
400
0
    ogs_gtp_node_t *gnode = NULL;
401
0
    ogs_pfcp_far_t *far = NULL;
402
403
0
    ogs_assert(pdr);
404
0
    ogs_assert(sendbuf);
405
406
0
    far = pdr->far;
407
0
    if (!far) {
408
0
        ogs_error("No FAR");
409
0
        ogs_pkbuf_free(sendbuf);
410
0
        return;
411
0
    }
412
413
0
    if (far->dst_if == OGS_PFCP_INTERFACE_UNKNOWN) {
414
0
        ogs_error("No Destination Interface");
415
0
        ogs_pkbuf_free(sendbuf);
416
0
        return;
417
0
    }
418
419
0
    gnode = far->gnode;
420
0
    if (!gnode) {
421
0
        ogs_error("No GTP Node Setup");
422
0
        ogs_pkbuf_free(sendbuf);
423
0
        return;
424
0
    }
425
0
    if (!gnode->sock) {
426
0
        ogs_error("No GTP Socket Setup");
427
0
        ogs_pkbuf_free(sendbuf);
428
0
        return;
429
0
    }
430
431
0
    ogs_gtp_send_with_teid(
432
0
            gnode->sock,
433
0
            sendbuf, far->outer_header_creation.teid,
434
0
            &gnode->addr);
435
436
0
    ogs_pkbuf_free(sendbuf);
437
0
}
438
439
void ogs_pfcp_send_buffered_gtpu(ogs_pfcp_pdr_t *pdr)
440
0
{
441
0
    ogs_pfcp_far_t *far = NULL;
442
0
    int i;
443
444
0
    ogs_assert(pdr);
445
0
    far = pdr->far;
446
447
0
    if (far && far->gnode) {
448
0
        if (far->apply_action & OGS_PFCP_APPLY_ACTION_FORW) {
449
0
            for (i = 0; i < far->num_of_buffered_gtpu; i++) {
450
0
                ogs_pfcp_send_gtpu(pdr, far->buffered_gtpu[i]);
451
0
            }
452
0
            far->num_of_buffered_gtpu = 0;
453
0
        }
454
0
    }
455
0
}
456
457
int ogs_pfcp_send_end_marker(ogs_pfcp_pdr_t *pdr)
458
0
{
459
0
    ogs_pkbuf_t *sendbuf = NULL;
460
0
    ogs_gtp2_header_desc_t header_desc;
461
462
0
    ogs_assert(pdr);
463
464
0
    sendbuf = ogs_pkbuf_alloc(NULL, OGS_GTPV1U_5GC_HEADER_LEN);
465
0
    if (!sendbuf) {
466
0
        ogs_error("ogs_pkbuf_alloc() failed");
467
0
        return OGS_ERROR;
468
0
    }
469
0
    ogs_pkbuf_reserve(sendbuf, OGS_GTPV1U_5GC_HEADER_LEN);
470
471
0
    memset(&header_desc, 0, sizeof(header_desc));
472
0
    header_desc.type = OGS_GTPU_MSGTYPE_END_MARKER;
473
474
0
    if (pdr->qer && pdr->qer->qfi) {
475
0
        header_desc.pdu_type =
476
0
            OGS_GTP2_EXTENSION_HEADER_PDU_TYPE_DL_PDU_SESSION_INFORMATION;
477
0
        header_desc.qos_flow_identifier = pdr->qer->qfi;
478
0
    }
479
480
0
    ogs_gtp2_encapsulate_header(&header_desc, sendbuf);
481
482
0
    ogs_pfcp_send_gtpu(pdr, sendbuf);
483
484
0
    return OGS_OK;
485
0
}
486
487
void ogs_pfcp_send_error_message(
488
    ogs_pfcp_xact_t *xact, uint64_t seid, uint8_t type,
489
    uint8_t cause_value, uint16_t offending_ie_value)
490
0
{
491
0
    int rv;
492
0
    ogs_pfcp_message_t errmsg;
493
0
    ogs_pfcp_tlv_cause_t *cause = NULL;
494
0
    ogs_pfcp_tlv_offending_ie_t *offending_ie = NULL;
495
0
    ogs_pkbuf_t *pkbuf = NULL;
496
497
0
    ogs_assert(xact);
498
499
0
    memset(&errmsg, 0, sizeof(ogs_pfcp_message_t));
500
0
    errmsg.h.seid = seid;
501
0
    errmsg.h.type = type;
502
503
0
    switch (type) {
504
0
    case OGS_PFCP_PFD_MANAGEMENT_RESPONSE_TYPE:
505
0
        cause = &errmsg.pfcp_pfd_management_response.cause;
506
0
        offending_ie = &errmsg.pfcp_pfd_management_response.offending_ie;
507
0
        break;
508
0
    case OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE:
509
0
        cause = &errmsg.pfcp_association_setup_response.cause;
510
0
        break;
511
0
    case OGS_PFCP_ASSOCIATION_UPDATE_RESPONSE_TYPE:
512
0
        cause = &errmsg.pfcp_association_update_response.cause;
513
0
        break;
514
0
    case OGS_PFCP_ASSOCIATION_RELEASE_RESPONSE_TYPE:
515
0
        cause = &errmsg.pfcp_association_release_response.cause;
516
0
        break;
517
0
    case OGS_PFCP_NODE_REPORT_RESPONSE_TYPE:
518
0
        cause = &errmsg.pfcp_node_report_response.cause;
519
0
        offending_ie = &errmsg.pfcp_node_report_response.offending_ie;
520
0
        break;
521
0
    case OGS_PFCP_SESSION_SET_DELETION_RESPONSE_TYPE:
522
0
        cause = &errmsg.pfcp_session_set_deletion_response.cause;
523
0
        offending_ie = &errmsg.pfcp_session_set_deletion_response.offending_ie;
524
0
        break;
525
0
    case OGS_PFCP_SESSION_ESTABLISHMENT_RESPONSE_TYPE:
526
0
        cause = &errmsg.pfcp_session_establishment_response.cause;
527
0
        offending_ie = &errmsg.pfcp_session_establishment_response.offending_ie;
528
0
        break;
529
0
    case OGS_PFCP_SESSION_MODIFICATION_RESPONSE_TYPE:
530
0
        cause = &errmsg.pfcp_session_modification_response.cause;
531
0
        offending_ie = &errmsg.pfcp_session_modification_response.offending_ie;
532
0
        break;
533
0
    case OGS_PFCP_SESSION_DELETION_RESPONSE_TYPE:
534
0
        cause = &errmsg.pfcp_session_deletion_response.cause;
535
0
        offending_ie = &errmsg.pfcp_session_deletion_response.offending_ie;
536
0
        break;
537
0
    case OGS_PFCP_SESSION_REPORT_RESPONSE_TYPE:
538
0
        cause = &errmsg.pfcp_session_report_response.cause;
539
0
        offending_ie = &errmsg.pfcp_session_report_response.offending_ie;
540
0
        break;
541
0
    default:
542
0
        ogs_assert_if_reached();
543
0
        return;
544
0
    }
545
546
0
    ogs_assert(cause);
547
548
0
    cause->presence = 1;
549
0
    cause->u8 = cause_value;
550
551
0
    if (offending_ie && offending_ie_value) {
552
0
        offending_ie->presence = 1;
553
0
        offending_ie->u16 = offending_ie_value;
554
0
    }
555
556
0
    pkbuf = ogs_pfcp_build_msg(&errmsg);
557
0
    if (!pkbuf) {
558
0
        ogs_error("ogs_pfcp_build_msg() failed");
559
0
        return;
560
0
    }
561
562
0
    rv = ogs_pfcp_xact_update_tx(xact, &errmsg.h, pkbuf);
563
0
    if (rv != OGS_OK) {
564
0
        ogs_error("ogs_pfcp_xact_update_tx() failed");
565
0
        return;
566
0
    }
567
568
0
    rv = ogs_pfcp_xact_commit(xact);
569
0
    ogs_expect(rv == OGS_OK);
570
0
}