/* * Copyright (C) 2019 by Sukchan Lee * * This file is part of Open5GS. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "ogs-pfcp.h" ogs_sock_t *ogs_pfcp_server(ogs_socknode_t *node) { char buf[OGS_ADDRSTRLEN]; ogs_sock_t *pfcp; ogs_assert(node); pfcp = ogs_udp_server(node->addr, node->option); if (pfcp) { ogs_info("pfcp_server() [%s]:%d", OGS_ADDR(node->addr, buf), OGS_PORT(node->addr)); node->sock = pfcp; } return pfcp; } int ogs_pfcp_connect( ogs_sock_t *ipv4, ogs_sock_t *ipv6, ogs_pfcp_node_t *node) { ogs_sockaddr_t *addr; char buf[OGS_ADDRSTRLEN]; ogs_assert(ipv4 || ipv6); ogs_assert(node); ogs_assert(node->sa_list); addr = node->sa_list; while (addr) { ogs_sock_t *sock = NULL; if (addr->ogs_sa_family == AF_INET) sock = ipv4; else if (addr->ogs_sa_family == AF_INET6) sock = ipv6; else ogs_assert_if_reached(); if (sock) { ogs_info("ogs_pfcp_connect() [%s]:%d", OGS_ADDR(addr, buf), OGS_PORT(addr)); node->sock = sock; memcpy(&node->addr, addr, sizeof node->addr); break; } addr = addr->next; } if (addr == NULL) { ogs_error("ogs_pfcp_connect() [%s]:%d failed", OGS_ADDR(node->sa_list, buf), OGS_PORT(node->sa_list)); ogs_error("Please check the IP version between SMF and UPF nodes."); return OGS_ERROR; } return OGS_OK; } int ogs_pfcp_send(ogs_pfcp_node_t *node, ogs_pkbuf_t *pkbuf) { ssize_t sent; ogs_sock_t *sock = NULL; ogs_assert(node); ogs_assert(pkbuf); sock = node->sock; ogs_assert(sock); sent = ogs_send(sock->fd, pkbuf->data, pkbuf->len, 0); if (sent < 0 || sent != pkbuf->len) { ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, "ogs_pfcp_send() failed"); return OGS_ERROR; } return OGS_OK; } int ogs_pfcp_sendto(ogs_pfcp_node_t *node, ogs_pkbuf_t *pkbuf) { ssize_t sent; ogs_sock_t *sock = NULL; ogs_sockaddr_t *addr = NULL; ogs_assert(node); ogs_assert(pkbuf); sock = node->sock; ogs_assert(sock); addr = &node->addr; ogs_assert(addr); sent = ogs_sendto(sock->fd, pkbuf->data, pkbuf->len, 0, addr); if (sent < 0 || sent != pkbuf->len) { if (ogs_socket_errno != OGS_EAGAIN) { char buf[OGS_ADDRSTRLEN]; int err = ogs_socket_errno; ogs_log_message(OGS_LOG_ERROR, err, "ogs_gtp_sendto(%u, %p, %u, 0, %s:%u) failed", sock->fd, pkbuf->data, pkbuf->len, OGS_ADDR(addr, buf), OGS_PORT(addr)); } return OGS_ERROR; } return OGS_OK; } int ogs_pfcp_send_heartbeat_request(ogs_pfcp_node_t *node, void (*cb)(ogs_pfcp_xact_t *xact, void *data)) { int rv; ogs_pkbuf_t *pkbuf = NULL; ogs_pfcp_header_t h; ogs_pfcp_xact_t *xact = NULL; ogs_assert(node); memset(&h, 0, sizeof(ogs_pfcp_header_t)); h.type = OGS_PFCP_HEARTBEAT_REQUEST_TYPE; h.seid = 0; xact = ogs_pfcp_xact_local_create(node, cb, node); if (!xact) { ogs_error("ogs_pfcp_xact_local_create() failed"); return OGS_ERROR; } pkbuf = ogs_pfcp_build_heartbeat_request(h.type); if (!pkbuf) { ogs_error("ogs_pfcp_build_heartbeat_request() failed"); return OGS_ERROR; } rv = ogs_pfcp_xact_update_tx(xact, &h, pkbuf); if (rv != OGS_OK) { ogs_error("ogs_pfcp_xact_update_tx() failed"); return OGS_ERROR; } rv = ogs_pfcp_xact_commit(xact); ogs_expect(rv == OGS_OK); return rv; } int ogs_pfcp_send_heartbeat_response(ogs_pfcp_xact_t *xact) { int rv; ogs_pkbuf_t *pkbuf = NULL; ogs_pfcp_header_t h; ogs_assert(xact); memset(&h, 0, sizeof(ogs_pfcp_header_t)); h.type = OGS_PFCP_HEARTBEAT_RESPONSE_TYPE; h.seid = 0; pkbuf = ogs_pfcp_build_heartbeat_response(h.type); if (!pkbuf) { ogs_error("ogs_pfcp_build_heartbeat_response() failed"); return OGS_ERROR; } rv = ogs_pfcp_xact_update_tx(xact, &h, pkbuf); if (rv != OGS_OK) { ogs_error("ogs_pfcp_xact_update_tx() failed"); return OGS_ERROR; } rv = ogs_pfcp_xact_commit(xact); ogs_expect(rv == OGS_OK); return rv; } int ogs_pfcp_cp_send_association_setup_request(ogs_pfcp_node_t *node, void (*cb)(ogs_pfcp_xact_t *xact, void *data)) { int rv; ogs_pkbuf_t *pkbuf = NULL; ogs_pfcp_header_t h; ogs_pfcp_xact_t *xact = NULL; ogs_assert(node); memset(&h, 0, sizeof(ogs_pfcp_header_t)); h.type = OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE; h.seid = 0; xact = ogs_pfcp_xact_local_create(node, cb, node); if (!xact) { ogs_error("ogs_pfcp_xact_local_create() failed"); return OGS_ERROR; } pkbuf = ogs_pfcp_cp_build_association_setup_request(h.type); if (!pkbuf) { ogs_error("ogs_pfcp_cp_build_association_setup_request() failed"); return OGS_ERROR; } rv = ogs_pfcp_xact_update_tx(xact, &h, pkbuf); if (rv != OGS_OK) { ogs_error("ogs_pfcp_xact_update_tx() failed"); return OGS_ERROR; } rv = ogs_pfcp_xact_commit(xact); ogs_expect(rv == OGS_OK); return rv; } int ogs_pfcp_cp_send_association_setup_response(ogs_pfcp_xact_t *xact, uint8_t cause) { int rv; ogs_pkbuf_t *pkbuf = NULL; ogs_pfcp_header_t h; ogs_assert(xact); memset(&h, 0, sizeof(ogs_pfcp_header_t)); h.type = OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE; h.seid = 0; pkbuf = ogs_pfcp_cp_build_association_setup_response(h.type, cause); if (!pkbuf) { ogs_error("ogs_pfcp_cp_build_association_setup_response() failed"); return OGS_ERROR; } rv = ogs_pfcp_xact_update_tx(xact, &h, pkbuf); if (rv != OGS_OK) { ogs_error("ogs_pfcp_xact_update_tx() failed"); return OGS_ERROR; } rv = ogs_pfcp_xact_commit(xact); ogs_expect(rv == OGS_OK); return rv; } int ogs_pfcp_up_send_association_setup_request(ogs_pfcp_node_t *node, void (*cb)(ogs_pfcp_xact_t *xact, void *data)) { int rv; ogs_pkbuf_t *pkbuf = NULL; ogs_pfcp_header_t h; ogs_pfcp_xact_t *xact = NULL; ogs_assert(node); memset(&h, 0, sizeof(ogs_pfcp_header_t)); h.type = OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE; h.seid = 0; xact = ogs_pfcp_xact_local_create(node, cb, node); if (!xact) { ogs_error("ogs_pfcp_xact_local_create() failed"); return OGS_ERROR; } pkbuf = ogs_pfcp_up_build_association_setup_request(h.type); if (!pkbuf) { ogs_error("ogs_pfcp_build_heartbeat_request() failed"); return OGS_ERROR; } rv = ogs_pfcp_xact_update_tx(xact, &h, pkbuf); if (rv != OGS_OK) { ogs_error("ogs_pfcp_xact_update_tx() failed"); return OGS_ERROR; } rv = ogs_pfcp_xact_commit(xact); ogs_expect(rv == OGS_OK); return rv; } int ogs_pfcp_up_send_association_setup_response(ogs_pfcp_xact_t *xact, uint8_t cause) { int rv; ogs_pkbuf_t *pkbuf = NULL; ogs_pfcp_header_t h; ogs_assert(xact); memset(&h, 0, sizeof(ogs_pfcp_header_t)); h.type = OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE; h.seid = 0; pkbuf = ogs_pfcp_up_build_association_setup_response(h.type, cause); if (!pkbuf) { ogs_error("ogs_pfcp_up_build_association_setup_response() failed"); return OGS_ERROR; } rv = ogs_pfcp_xact_update_tx(xact, &h, pkbuf); if (rv != OGS_OK) { ogs_error("ogs_pfcp_xact_update_tx() failed"); return OGS_ERROR; } rv = ogs_pfcp_xact_commit(xact); ogs_expect(rv == OGS_OK); return rv; } void ogs_pfcp_send_g_pdu( ogs_pfcp_pdr_t *pdr, uint8_t type, ogs_pkbuf_t *sendbuf) { ogs_gtp_node_t *gnode = NULL; ogs_pfcp_far_t *far = NULL; ogs_gtp2_header_t gtp_hdesc; ogs_gtp2_extension_header_t ext_hdesc; ogs_assert(pdr); ogs_assert(type); ogs_assert(sendbuf); far = pdr->far; if (!far) { ogs_error("No FAR"); ogs_pkbuf_free(sendbuf); return; } if (far->dst_if == OGS_PFCP_INTERFACE_UNKNOWN) { ogs_error("No Destination Interface"); ogs_pkbuf_free(sendbuf); return; } gnode = far->gnode; ogs_assert(gnode); ogs_assert(gnode->sock); memset(>p_hdesc, 0, sizeof(gtp_hdesc)); memset(&ext_hdesc, 0, sizeof(ext_hdesc)); gtp_hdesc.type = type; gtp_hdesc.teid = far->outer_header_creation.teid; if (pdr->qer && pdr->qer->qfi) ext_hdesc.qos_flow_identifier = pdr->qer->qfi; ogs_gtp2_send_user_plane(gnode, >p_hdesc, &ext_hdesc, sendbuf); } int ogs_pfcp_send_end_marker(ogs_pfcp_pdr_t *pdr) { ogs_gtp_node_t *gnode = NULL; ogs_pfcp_far_t *far = NULL; ogs_pkbuf_t *sendbuf = NULL; ogs_gtp2_header_t gtp_hdesc; ogs_gtp2_extension_header_t ext_hdesc; ogs_assert(pdr); far = pdr->far; ogs_assert(far); gnode = far->gnode; if (!gnode) { ogs_error("No GTP Node Setup"); return OGS_DONE; } if (!gnode->sock) { ogs_error("No GTP Socket Setup"); return OGS_DONE; } sendbuf = ogs_pkbuf_alloc(NULL, OGS_GTPV1U_5GC_HEADER_LEN); if (!sendbuf) { ogs_error("ogs_pkbuf_alloc() failed"); return OGS_ERROR; } ogs_pkbuf_reserve(sendbuf, OGS_GTPV1U_5GC_HEADER_LEN); memset(>p_hdesc, 0, sizeof(gtp_hdesc)); memset(&ext_hdesc, 0, sizeof(ext_hdesc)); gtp_hdesc.type = OGS_GTPU_MSGTYPE_END_MARKER; gtp_hdesc.teid = far->outer_header_creation.teid; if (pdr->qer && pdr->qer->qfi) ext_hdesc.qos_flow_identifier = pdr->qer->qfi; ogs_gtp2_send_user_plane(gnode, >p_hdesc, &ext_hdesc, sendbuf); return OGS_OK; } void ogs_pfcp_send_buffered_packet(ogs_pfcp_pdr_t *pdr) { ogs_pfcp_far_t *far = NULL; int i; ogs_assert(pdr); far = pdr->far; if (far && far->gnode) { if (far->apply_action & OGS_PFCP_APPLY_ACTION_FORW) { for (i = 0; i < far->num_of_buffered_packet; i++) { ogs_pfcp_send_g_pdu( pdr, OGS_GTPU_MSGTYPE_GPDU, far->buffered_packet[i]); } far->num_of_buffered_packet = 0; } } } void ogs_pfcp_send_error_message( ogs_pfcp_xact_t *xact, uint64_t seid, uint8_t type, uint8_t cause_value, uint16_t offending_ie_value) { int rv; ogs_pfcp_message_t errmsg; ogs_pfcp_tlv_cause_t *cause = NULL; ogs_pfcp_tlv_offending_ie_t *offending_ie = NULL; ogs_pkbuf_t *pkbuf = NULL; ogs_assert(xact); memset(&errmsg, 0, sizeof(ogs_pfcp_message_t)); errmsg.h.seid = seid; errmsg.h.type = type; switch (type) { case OGS_PFCP_PFD_MANAGEMENT_RESPONSE_TYPE: cause = &errmsg.pfcp_pfd_management_response.cause; offending_ie = &errmsg.pfcp_pfd_management_response.offending_ie; break; case OGS_PFCP_ASSOCIATION_SETUP_RESPONSE_TYPE: cause = &errmsg.pfcp_association_setup_response.cause; break; case OGS_PFCP_ASSOCIATION_UPDATE_RESPONSE_TYPE: cause = &errmsg.pfcp_association_update_response.cause; break; case OGS_PFCP_ASSOCIATION_RELEASE_RESPONSE_TYPE: cause = &errmsg.pfcp_association_release_response.cause; break; case OGS_PFCP_NODE_REPORT_RESPONSE_TYPE: cause = &errmsg.pfcp_node_report_response.cause; offending_ie = &errmsg.pfcp_node_report_response.offending_ie; break; case OGS_PFCP_SESSION_SET_DELETION_RESPONSE_TYPE: cause = &errmsg.pfcp_session_set_deletion_response.cause; offending_ie = &errmsg.pfcp_session_set_deletion_response.offending_ie; break; case OGS_PFCP_SESSION_ESTABLISHMENT_RESPONSE_TYPE: cause = &errmsg.pfcp_session_establishment_response.cause; offending_ie = &errmsg.pfcp_session_establishment_response.offending_ie; break; case OGS_PFCP_SESSION_MODIFICATION_RESPONSE_TYPE: cause = &errmsg.pfcp_session_modification_response.cause; offending_ie = &errmsg.pfcp_session_modification_response.offending_ie; break; case OGS_PFCP_SESSION_DELETION_RESPONSE_TYPE: cause = &errmsg.pfcp_session_deletion_response.cause; offending_ie = &errmsg.pfcp_session_deletion_response.offending_ie; break; case OGS_PFCP_SESSION_REPORT_RESPONSE_TYPE: cause = &errmsg.pfcp_session_report_response.cause; offending_ie = &errmsg.pfcp_session_report_response.offending_ie; break; default: ogs_assert_if_reached(); return; } ogs_assert(cause); cause->presence = 1; cause->u8 = cause_value; if (offending_ie && offending_ie_value) { offending_ie->presence = 1; offending_ie->u16 = offending_ie_value; } pkbuf = ogs_pfcp_build_msg(&errmsg); if (!pkbuf) { ogs_error("ogs_pfcp_build_msg() failed"); return; } rv = ogs_pfcp_xact_update_tx(xact, &errmsg.h, pkbuf); if (rv != OGS_OK) { ogs_error("ogs_pfcp_xact_update_tx() failed"); return; } rv = ogs_pfcp_xact_commit(xact); ogs_expect(rv == OGS_OK); }