open5gs/src/smf/gx-path.c

1522 lines
54 KiB
C
Raw Normal View History

2021-06-21 13:36:38 +00:00
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "fd-path.h"
static struct session_handler *smf_gx_reg = NULL;
static struct disp_hdl *hdl_gx_fb = NULL;
static struct disp_hdl *hdl_gx_rar = NULL;
2021-06-21 13:36:38 +00:00
struct sess_state {
os0_t gx_sid; /* Gx Session-Id */
os0_t peer_host; /* Peer Host */
2021-06-21 13:36:38 +00:00
#define MAX_CC_REQUEST_NUMBER 32
smf_sess_t *sess;
ogs_gtp_xact_t *xact[MAX_CC_REQUEST_NUMBER];
2021-06-21 13:36:38 +00:00
uint32_t cc_request_type;
uint32_t cc_request_number;
struct timespec ts; /* Time of sending the message */
};
static OGS_POOL(sess_state_pool, struct sess_state);
static ogs_thread_mutex_t sess_state_mutex;
static int decode_pcc_rule_definition(
ogs_pcc_rule_t *pcc_rule, struct avp *avpch1, int *perror);
static void smf_gx_cca_cb(void *data, struct msg **msg);
static __inline__ struct sess_state *new_state(os0_t sid)
{
struct sess_state *new = NULL;
ogs_thread_mutex_lock(&sess_state_mutex);
ogs_pool_alloc(&sess_state_pool, &new);
ogs_expect_or_return_val(new, NULL);
memset(new, 0, sizeof(*new));
2021-06-21 13:36:38 +00:00
ogs_thread_mutex_unlock(&sess_state_mutex);
new->gx_sid = (os0_t)ogs_strdup((char *)sid);
ogs_expect_or_return_val(new->gx_sid, NULL);
return new;
}
static void state_cleanup(struct sess_state *sess_data, os0_t sid, void *opaque)
{
if (sess_data->gx_sid)
ogs_free(sess_data->gx_sid);
if (sess_data->peer_host)
ogs_free(sess_data->peer_host);
2021-06-21 13:36:38 +00:00
ogs_thread_mutex_lock(&sess_state_mutex);
ogs_pool_free(&sess_state_pool, sess_data);
ogs_thread_mutex_unlock(&sess_state_mutex);
}
void smf_gx_send_ccr(smf_sess_t *sess, ogs_gtp_xact_t *xact,
uint32_t cc_request_type)
{
int ret;
smf_ue_t *smf_ue = NULL;
struct msg *req = NULL;
struct avp *avp;
struct avp *avpch1, *avpch2;
union avp_value val;
struct sess_state *sess_data = NULL, *svg;
struct session *session = NULL;
int new;
ogs_paa_t paa; /* For changing Framed-IPv6-Prefix Length to 64 */
2021-06-21 13:36:38 +00:00
char buf[OGS_PLMNIDSTRLEN];
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
uint32_t charging_id;
2021-06-21 13:36:38 +00:00
ogs_assert(sess);
ogs_assert(sess->ipv4 || sess->ipv6);
smf_ue = sess->smf_ue;
ogs_assert(smf_ue);
ogs_debug("[Credit-Control-Request]");
/* Create the request */
ret = fd_msg_new(ogs_diam_gx_cmd_ccr, MSGFL_ALLOC_ETEID, &req);
ogs_assert(ret == 0);
{
struct msg_hdr *h;
ret = fd_msg_hdr(req, &h);
ogs_assert(ret == 0);
h->msg_appl = OGS_DIAM_GX_APPLICATION_ID;
}
2021-06-21 13:36:38 +00:00
/* Find Diameter Gx Session */
if (sess->gx_sid) {
/* Retrieve session by Session-Id */
size_t sidlen = strlen(sess->gx_sid);
ret = fd_sess_fromsid_msg((os0_t)sess->gx_sid, sidlen, &session, &new);
ogs_assert(ret == 0);
ogs_assert(new == 0);
ogs_debug(" Found GX Session-Id: [%s]", sess->gx_sid);
/* Add Session-Id to the message */
ret = ogs_diam_message_session_id_set(req, (os0_t)sess->gx_sid, sidlen);
ogs_assert(ret == 0);
/* Save the session associated with the message */
ret = fd_msg_sess_set(req, session);
} else {
/* Create a new session */
#define OGS_DIAM_GX_APP_SID_OPT "app_gx"
ret = fd_msg_new_session(req, (os0_t)OGS_DIAM_GX_APP_SID_OPT,
2021-06-21 13:36:38 +00:00
CONSTSTRLEN(OGS_DIAM_GX_APP_SID_OPT));
ogs_assert(ret == 0);
ret = fd_msg_sess_get(fd_g_config->cnf_dict, req, &session, NULL);
ogs_assert(ret == 0);
ogs_debug(" Create a New Session");
}
/* Retrieve session state in this session */
ret = fd_sess_state_retrieve(smf_gx_reg, session, &sess_data);
if (!sess_data) {
os0_t sid;
size_t sidlen;
ret = fd_sess_getsid(session, &sid, &sidlen);
ogs_assert(ret == 0);
/* Allocate new session state memory */
sess_data = new_state(sid);
ogs_assert(sess_data);
ogs_debug(" Allocate new session: [%s]", sess_data->gx_sid);
/* Save Session-Id to SMF Session Context */
sess->gx_sid = (char *)sess_data->gx_sid;
} else
ogs_debug(" Retrieve session: [%s]", sess_data->gx_sid);
/*
2021-06-21 13:36:38 +00:00
* 8.2. CC-Request-Number AVP
*
* The CC-Request-Number AVP (AVP Code 415) is of type Unsigned32 and
* identifies this request within one session. As Session-Id AVPs are
* globally unique, the combination of Session-Id and CC-Request-Number
* AVPs is also globally unique and can be used in matching credit-
* control messages with confirmations. An easy way to produce unique
* numbers is to set the value to 0 for a credit-control request of type
* INITIAL_REQUEST and EVENT_REQUEST and to set the value to 1 for the
* first UPDATE_REQUEST, to 2 for the second, and so on until the value
* for TERMINATION_REQUEST is one more than for the last UPDATE_REQUEST.
*/
sess_data->cc_request_type = cc_request_type;
if (cc_request_type == OGS_DIAM_GX_CC_REQUEST_TYPE_INITIAL_REQUEST ||
cc_request_type == OGS_DIAM_GX_CC_REQUEST_TYPE_EVENT_REQUEST)
sess_data->cc_request_number = 0;
else
sess_data->cc_request_number++;
ogs_debug(" CC Request Type[%d] Number[%d]",
2021-06-21 13:36:38 +00:00
sess_data->cc_request_type, sess_data->cc_request_number);
ogs_assert(sess_data->cc_request_number <= MAX_CC_REQUEST_NUMBER);
/* Update session state */
sess_data->sess = sess;
sess_data->xact[sess_data->cc_request_number] = xact;
2021-06-21 13:36:38 +00:00
/* Set Origin-Host & Origin-Realm */
ret = fd_msg_add_origin(req, 0);
ogs_assert(ret == 0);
2021-06-21 13:36:38 +00:00
/* Set the Destination-Realm AVP */
ret = fd_msg_avp_new(ogs_diam_destination_realm, 0, &avp);
ogs_assert(ret == 0);
val.os.data = (unsigned char *)(fd_g_config->cnf_diamrlm);
val.os.len = strlen(fd_g_config->cnf_diamrlm);
ret = fd_msg_avp_setvalue(avp, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
/* Set the Auth-Application-Id AVP */
ret = fd_msg_avp_new(ogs_diam_auth_application_id, 0, &avp);
ogs_assert(ret == 0);
val.i32 = OGS_DIAM_GX_APPLICATION_ID;
ret = fd_msg_avp_setvalue(avp, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
/* Set CC-Request-Type, CC-Request-Number */
ret = fd_msg_avp_new(ogs_diam_gx_cc_request_type, 0, &avp);
ogs_assert(ret == 0);
val.i32 = sess_data->cc_request_type;
ret = fd_msg_avp_setvalue(avp, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
ret = fd_msg_avp_new(ogs_diam_gx_cc_request_number, 0, &avp);
ogs_assert(ret == 0);
val.i32 = sess_data->cc_request_number;
ret = fd_msg_avp_setvalue(avp, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
/* Set the Destination-Host AVP */
if (sess_data->peer_host) {
ret = fd_msg_avp_new(ogs_diam_destination_host, 0, &avp);
ogs_assert(ret == 0);
val.os.data = sess_data->peer_host;
val.os.len = strlen((char *)sess_data->peer_host);
ret = fd_msg_avp_setvalue(avp, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
}
2021-06-21 13:36:38 +00:00
/* Set Subscription-Id */
ret = fd_msg_avp_new(ogs_diam_subscription_id, 0, &avp);
ogs_assert(ret == 0);
ret = fd_msg_avp_new(ogs_diam_subscription_id_type, 0, &avpch1);
ogs_assert(ret == 0);
val.i32 = OGS_DIAM_SUBSCRIPTION_ID_TYPE_END_USER_IMSI;
ret = fd_msg_avp_setvalue (avpch1, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add (avp, MSG_BRW_LAST_CHILD, avpch1);
ogs_assert(ret == 0);
ret = fd_msg_avp_new(ogs_diam_subscription_id_data, 0, &avpch1);
ogs_assert(ret == 0);
val.os.data = (uint8_t *)smf_ue->imsi_bcd;
val.os.len = strlen(smf_ue->imsi_bcd);
ret = fd_msg_avp_setvalue (avpch1, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add (avp, MSG_BRW_LAST_CHILD, avpch1);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
if (cc_request_type != OGS_DIAM_GX_CC_REQUEST_TYPE_TERMINATION_REQUEST) {
/* Set Supported-Features */
ret = fd_msg_avp_new(ogs_diam_gx_supported_features, 0, &avp);
ogs_assert(ret == 0);
ret = fd_msg_avp_new(ogs_diam_vendor_id, 0, &avpch1);
ogs_assert(ret == 0);
val.i32 = OGS_3GPP_VENDOR_ID;
ret = fd_msg_avp_setvalue (avpch1, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add (avp, MSG_BRW_LAST_CHILD, avpch1);
ogs_assert(ret == 0);
2021-06-21 13:36:38 +00:00
ret = fd_msg_avp_new(ogs_diam_gx_feature_list_id, 0, &avpch1);
ogs_assert(ret == 0);
val.i32 = 1;
ret = fd_msg_avp_setvalue (avpch1, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add (avp, MSG_BRW_LAST_CHILD, avpch1);
ogs_assert(ret == 0);
ret = fd_msg_avp_new(ogs_diam_gx_feature_list, 0, &avpch1);
ogs_assert(ret == 0);
val.u32 = 0x0000000b;
ret = fd_msg_avp_setvalue (avpch1, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add (avp, MSG_BRW_LAST_CHILD, avpch1);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
/* Set Network-Request-Support */
ret = fd_msg_avp_new(ogs_diam_gx_network_request_support, 0, &avp);
ogs_assert(ret == 0);
val.i32 = 1;
ret = fd_msg_avp_setvalue(avp, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
/* Set Framed-IP-Address */
if (sess->ipv4) {
ret = fd_msg_avp_new(ogs_diam_gx_framed_ip_address, 0, &avp);
ogs_assert(ret == 0);
val.os.data = (uint8_t*)&sess->ipv4->addr;
val.os.len = OGS_IPV4_LEN;
ret = fd_msg_avp_setvalue(avp, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
}
/* Set Framed-IPv6-Prefix */
if (sess->ipv6) {
ret = fd_msg_avp_new(ogs_diam_gx_framed_ipv6_prefix, 0, &avp);
ogs_assert(ret == 0);
/* As per 3GPP TS 23.401 version 15.12.0, section 5.3.1.2.2
* The PDN GW allocates a globally unique /64
* IPv6 prefix via Router Advertisement to a given UE.
*
* After the UE has received the Router Advertisement message, it
* constructs a full IPv6 address via IPv6 Stateless Address
* autoconfiguration in accordance with RFC 4862 using the interface
* identifier assigned by PDN GW.
*
* For stateless address autoconfiguration however, the UE can
* choose any interface identifier to generate IPv6 addresses, other
* than link-local, without involving the network.
*
* And, from section 5.3.1.1, Both EPS network elements and UE shall
* support the following mechanisms:
*
* /64 IPv6 prefix allocation via IPv6 Stateless Address
* autoconfiguration according to RFC 4862 [18], if IPv6 is
* supported.
*/
memset(&paa, 0 , sizeof(paa));
memcpy(&paa.addr6, &sess->ipv6->addr,
OGS_IPV6_DEFAULT_PREFIX_LEN >> 3);
#define FRAMED_IPV6_PREFIX_LENGTH 64 /* from spec document */
paa.len = FRAMED_IPV6_PREFIX_LENGTH;
2021-06-21 13:36:38 +00:00
val.os.data = (uint8_t*)&paa;
/* Reserved (1 byte) + Prefix length (1 byte) +
* IPv6 Prefix (8 bytes)
*/
val.os.len = (OGS_IPV6_DEFAULT_PREFIX_LEN >> 3) + 2;
2021-06-21 13:36:38 +00:00
ret = fd_msg_avp_setvalue(avp, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
}
/* Set IP-Can-Type */
ret = fd_msg_avp_new(ogs_diam_gx_ip_can_type, 0, &avp);
ogs_assert(ret == 0);
switch (sess->gtp_rat_type) {
case OGS_GTP2_RAT_TYPE_UTRAN:
case OGS_GTP2_RAT_TYPE_GERAN:
case OGS_GTP2_RAT_TYPE_HSPA_EVOLUTION:
case OGS_GTP2_RAT_TYPE_EUTRAN:
2021-06-21 13:36:38 +00:00
val.i32 = OGS_DIAM_GX_IP_CAN_TYPE_3GPP_EPS;
break;
case OGS_GTP2_RAT_TYPE_WLAN:
2021-06-21 13:36:38 +00:00
val.i32 = OGS_DIAM_GX_IP_CAN_TYPE_NON_3GPP_EPS;
break;
default:
ogs_error("Unknown RAT Type [%d]", sess->gtp_rat_type);
ogs_assert_if_reached();
}
ret = fd_msg_avp_setvalue(avp, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
/* Set RAT-Type */
ret = fd_msg_avp_new(ogs_diam_rat_type, 0, &avp);
ogs_assert(ret == 0);
switch (sess->gtp_rat_type) {
case OGS_GTP2_RAT_TYPE_UTRAN:
Introduce Gn interface (GTPv1C) Support to PGW (#1351) * [CORE] tlv: Store mode in ogs_tlv_t This allows specifying the format of the IE for each individual IE, hence allowing messages containing IEs formatted in different ways. This is needed in order to support parsing GTPv1-C, since messages contain IEs with different structure (TLV vs TV). Hence, this is a preparation patch to add support for parsing TVs in ogs-tlv.c/.h. * [CORE] tlv: Support parsing msg with both TLV and TV in it IEs of type TV are sometimes used in GTPv1-C. Current tlv parser/builder doesn't provide with ways to parse messages which contain TV formatted IEs. This patch adds the relevant types and ways to encode/decode them. Furthermore, the current parser/builder allows parsing/building messages containing the exact same format in all its IEs. A new parser function is added which allows parsing messages of different types (TV, TLV) mixed in the same message. In order to be able to do so, it uses the general msg_mode passed to it in order to know the general TLV format (in essence, the length of the Tag field, and also the length of the Length field if applicable each IE). Looking up the instance in the TLV description is left undone and hadcoded to 0, since the only user so far requiring this API is GTPv1-C, which has no instances. * [CORE] tlv: Support repeated tag+instance parsing TLV message In GTPv2C, repeated IEs (same tag) are easily differentiated by the Instance byte, which provides info to match different decoded structures. In GTPv1C though, there's no Instance byte, and we still encounter repeated IEs (like GSN Address in Create PDP Context Request). Hence, the TLV decoder needs to be updated to track count of IEs found (identified by tag+instance, where instance is always 0 in GTPv1C) and get the proper description index + offset into the decoded structure. * [GTP]: Move GTPv2-C specifics to its own libgtp subdir This will allow adding GTPv1-C code by the side. Most GTPv2 code is left in this patch as "gtp" instead of renaming it to "gtp2" in order to avoid massive changes. It can be done at a later stage if wanted. * [GTP] Support generating GTPv1-C messages * [SMF] Add Gn interface support This patch introduces GTPv1C support to open5gs-smfd. With it, open5gs-becomes a GGSN too, where SGSN can connect to, hence supporting GERAN and UTRAN networks.
2022-02-18 13:23:45 +00:00
val.i32 = OGS_DIAM_RAT_TYPE_UTRAN;
break;
case OGS_GTP2_RAT_TYPE_GERAN:
Introduce Gn interface (GTPv1C) Support to PGW (#1351) * [CORE] tlv: Store mode in ogs_tlv_t This allows specifying the format of the IE for each individual IE, hence allowing messages containing IEs formatted in different ways. This is needed in order to support parsing GTPv1-C, since messages contain IEs with different structure (TLV vs TV). Hence, this is a preparation patch to add support for parsing TVs in ogs-tlv.c/.h. * [CORE] tlv: Support parsing msg with both TLV and TV in it IEs of type TV are sometimes used in GTPv1-C. Current tlv parser/builder doesn't provide with ways to parse messages which contain TV formatted IEs. This patch adds the relevant types and ways to encode/decode them. Furthermore, the current parser/builder allows parsing/building messages containing the exact same format in all its IEs. A new parser function is added which allows parsing messages of different types (TV, TLV) mixed in the same message. In order to be able to do so, it uses the general msg_mode passed to it in order to know the general TLV format (in essence, the length of the Tag field, and also the length of the Length field if applicable each IE). Looking up the instance in the TLV description is left undone and hadcoded to 0, since the only user so far requiring this API is GTPv1-C, which has no instances. * [CORE] tlv: Support repeated tag+instance parsing TLV message In GTPv2C, repeated IEs (same tag) are easily differentiated by the Instance byte, which provides info to match different decoded structures. In GTPv1C though, there's no Instance byte, and we still encounter repeated IEs (like GSN Address in Create PDP Context Request). Hence, the TLV decoder needs to be updated to track count of IEs found (identified by tag+instance, where instance is always 0 in GTPv1C) and get the proper description index + offset into the decoded structure. * [GTP]: Move GTPv2-C specifics to its own libgtp subdir This will allow adding GTPv1-C code by the side. Most GTPv2 code is left in this patch as "gtp" instead of renaming it to "gtp2" in order to avoid massive changes. It can be done at a later stage if wanted. * [GTP] Support generating GTPv1-C messages * [SMF] Add Gn interface support This patch introduces GTPv1C support to open5gs-smfd. With it, open5gs-becomes a GGSN too, where SGSN can connect to, hence supporting GERAN and UTRAN networks.
2022-02-18 13:23:45 +00:00
val.i32 = OGS_DIAM_RAT_TYPE_GERAN;
break;
case OGS_GTP2_RAT_TYPE_HSPA_EVOLUTION:
Introduce Gn interface (GTPv1C) Support to PGW (#1351) * [CORE] tlv: Store mode in ogs_tlv_t This allows specifying the format of the IE for each individual IE, hence allowing messages containing IEs formatted in different ways. This is needed in order to support parsing GTPv1-C, since messages contain IEs with different structure (TLV vs TV). Hence, this is a preparation patch to add support for parsing TVs in ogs-tlv.c/.h. * [CORE] tlv: Support parsing msg with both TLV and TV in it IEs of type TV are sometimes used in GTPv1-C. Current tlv parser/builder doesn't provide with ways to parse messages which contain TV formatted IEs. This patch adds the relevant types and ways to encode/decode them. Furthermore, the current parser/builder allows parsing/building messages containing the exact same format in all its IEs. A new parser function is added which allows parsing messages of different types (TV, TLV) mixed in the same message. In order to be able to do so, it uses the general msg_mode passed to it in order to know the general TLV format (in essence, the length of the Tag field, and also the length of the Length field if applicable each IE). Looking up the instance in the TLV description is left undone and hadcoded to 0, since the only user so far requiring this API is GTPv1-C, which has no instances. * [CORE] tlv: Support repeated tag+instance parsing TLV message In GTPv2C, repeated IEs (same tag) are easily differentiated by the Instance byte, which provides info to match different decoded structures. In GTPv1C though, there's no Instance byte, and we still encounter repeated IEs (like GSN Address in Create PDP Context Request). Hence, the TLV decoder needs to be updated to track count of IEs found (identified by tag+instance, where instance is always 0 in GTPv1C) and get the proper description index + offset into the decoded structure. * [GTP]: Move GTPv2-C specifics to its own libgtp subdir This will allow adding GTPv1-C code by the side. Most GTPv2 code is left in this patch as "gtp" instead of renaming it to "gtp2" in order to avoid massive changes. It can be done at a later stage if wanted. * [GTP] Support generating GTPv1-C messages * [SMF] Add Gn interface support This patch introduces GTPv1C support to open5gs-smfd. With it, open5gs-becomes a GGSN too, where SGSN can connect to, hence supporting GERAN and UTRAN networks.
2022-02-18 13:23:45 +00:00
val.i32 = OGS_DIAM_RAT_TYPE_HSPA_EVOLUTION;
break;
case OGS_GTP2_RAT_TYPE_EUTRAN:
2021-06-21 13:36:38 +00:00
val.i32 = OGS_DIAM_RAT_TYPE_EUTRAN;
break;
case OGS_GTP2_RAT_TYPE_WLAN:
2021-06-21 13:36:38 +00:00
val.i32 = OGS_DIAM_RAT_TYPE_WLAN;
break;
default:
ogs_error("Unknown RAT Type [%d]", sess->gtp_rat_type);
ogs_assert_if_reached();
}
ret = fd_msg_avp_setvalue(avp, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
/* Set QoS-Information */
if (sess->session.ambr.downlink || sess->session.ambr.uplink) {
ret = fd_msg_avp_new(ogs_diam_gx_qos_information, 0, &avp);
ogs_assert(ret == 0);
if (sess->session.ambr.uplink) {
ret = fd_msg_avp_new(ogs_diam_gx_apn_aggregate_max_bitrate_ul,
0, &avpch1);
ogs_assert(ret == 0);
val.u32 = sess->session.ambr.uplink;
ret = fd_msg_avp_setvalue (avpch1, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add (avp, MSG_BRW_LAST_CHILD, avpch1);
ogs_assert(ret == 0);
}
2021-06-21 13:36:38 +00:00
if (sess->session.ambr.downlink) {
ret = fd_msg_avp_new(
ogs_diam_gx_apn_aggregate_max_bitrate_dl, 0, &avpch1);
ogs_assert(ret == 0);
val.u32 = sess->session.ambr.downlink;
ret = fd_msg_avp_setvalue (avpch1, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add (avp, MSG_BRW_LAST_CHILD, avpch1);
ogs_assert(ret == 0);
}
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
}
/* Set Default-EPS-Bearer-QoS */
ret = fd_msg_avp_new(ogs_diam_gx_default_eps_bearer_qos, 0, &avp);
ogs_assert(ret == 0);
ret = fd_msg_avp_new(ogs_diam_gx_qos_class_identifier, 0, &avpch1);
ogs_assert(ret == 0);
val.u32 = sess->session.qos.index;
ret = fd_msg_avp_setvalue (avpch1, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add (avp, MSG_BRW_LAST_CHILD, avpch1);
ogs_assert(ret == 0);
ret = fd_msg_avp_new(
ogs_diam_gx_allocation_retention_priority, 0, &avpch1);
ogs_assert(ret == 0);
ret = fd_msg_avp_new(ogs_diam_gx_priority_level, 0, &avpch2);
ogs_assert(ret == 0);
val.u32 = sess->session.qos.arp.priority_level;
ret = fd_msg_avp_setvalue (avpch2, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add (avpch1, MSG_BRW_LAST_CHILD, avpch2);
ogs_assert(ret == 0);
ret = fd_msg_avp_new(ogs_diam_gx_pre_emption_capability, 0, &avpch2);
ogs_assert(ret == 0);
val.u32 = sess->session.qos.arp.pre_emption_capability;
ret = fd_msg_avp_setvalue (avpch2, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add (avpch1, MSG_BRW_LAST_CHILD, avpch2);
ogs_assert(ret == 0);
ret = fd_msg_avp_new(ogs_diam_gx_pre_emption_vulnerability, 0, &avpch2);
ogs_assert(ret == 0);
val.u32 = sess->session.qos.arp.pre_emption_vulnerability;
ret = fd_msg_avp_setvalue (avpch2, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add (avpch1, MSG_BRW_LAST_CHILD, avpch2);
ogs_assert(ret == 0);
ret = fd_msg_avp_add (avp, MSG_BRW_LAST_CHILD, avpch1);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
/* 3GPP-User-Location-Info, 3GPP TS 29.061 16.4.7.2 22 */
smf_fd_msg_avp_add_3gpp_uli(sess, avpch1);
2021-06-21 13:36:38 +00:00
/* Set 3GPP-MS-Timezone */
if (sess->gtp.ue_timezone.presence &&
sess->gtp.ue_timezone.len && sess->gtp.ue_timezone.data) {
ret = fd_msg_avp_new(ogs_diam_gx_3gpp_ms_timezone, 0, &avp);
ogs_assert(ret == 0);
val.os.data = sess->gtp.ue_timezone.data;
val.os.len = sess->gtp.ue_timezone.len;
ret = fd_msg_avp_setvalue(avp, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
}
/* Set 3GPP-SGSN-MCC-MNC */
ret = fd_msg_avp_new(ogs_diam_gx_3gpp_sgsn_mcc_mnc, 0, &avp);
ogs_assert(ret == 0);
val.os.data = (uint8_t *)ogs_plmn_id_to_string(&sess->plmn_id, buf);
val.os.len = strlen(buf);
ret = fd_msg_avp_setvalue(avp, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
/* Set AN-GW-Address - Upto 2 address */
if (sess->sgw_s5c_ip.ipv4) {
ret = fd_msg_avp_new(ogs_diam_gx_an_gw_address, 0, &avp);
ogs_assert(ret == 0);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = sess->sgw_s5c_ip.addr;
ret = fd_msg_avp_value_encode(&sin, avp);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
}
if (sess->sgw_s5c_ip.ipv6) {
ret = fd_msg_avp_new(ogs_diam_gx_an_gw_address, 0, &avp);
ogs_assert(ret == 0);
sin6.sin6_family = AF_INET6;
memcpy(sin6.sin6_addr.s6_addr,
sess->sgw_s5c_ip.addr6, OGS_IPV6_LEN);
ret = fd_msg_avp_value_encode(&sin6, avp);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
}
}
/* Set Called-Station-Id */
ret = fd_msg_avp_new(ogs_diam_gx_called_station_id, 0, &avp);
ogs_assert(ret == 0);
ogs_assert(sess->session.name);
val.os.data = (uint8_t*)sess->session.name;
val.os.len = strlen(sess->session.name);
ret = fd_msg_avp_setvalue(avp, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
if (cc_request_type != OGS_DIAM_GX_CC_REQUEST_TYPE_TERMINATION_REQUEST) {
/* Set Online to DISABLE */
ret = fd_msg_avp_new(ogs_diam_gx_online, 0, &avp);
ogs_assert(ret == 0);
val.u32 = OGS_DIAM_GX_DISABLE_ONLINE;
ret = fd_msg_avp_setvalue(avp, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
/* Set Offline to ENABLE */
ret = fd_msg_avp_new(ogs_diam_gx_offline, 0, &avp);
ogs_assert(ret == 0);
val.u32 = OGS_DIAM_GX_ENABLE_OFFLINE;
ret = fd_msg_avp_setvalue(avp, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
/* Set Access-Network-Charging-Address - Only 1 address */
if (ogs_gtp_self()->gtpc_addr) {
ret = fd_msg_avp_new(
ogs_diam_gx_access_network_charging_address, 0, &avp);
ogs_assert(ret == 0);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr =
ogs_gtp_self()->gtpc_addr->sin.sin_addr.s_addr;
ret = fd_msg_avp_value_encode(&sin, avp);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
} else if (ogs_gtp_self()->gtpc_addr6) {
ret = fd_msg_avp_new(
ogs_diam_gx_access_network_charging_address, 0, &avp);
ogs_assert(ret == 0);
sin6.sin6_family = AF_INET6;
memcpy(sin6.sin6_addr.s6_addr,
ogs_gtp_self()->gtpc_addr6->sin6.sin6_addr.s6_addr,
OGS_IPV6_LEN);
ret = fd_msg_avp_value_encode(&sin6, avp);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
}
/* Set Access-Network-Charging-Identitifer-Gx */
ret = fd_msg_avp_new(
ogs_diam_gx_access_network_charging_identifier_gx, 0, &avp);
ogs_assert(ret == 0);
ret = fd_msg_avp_new(
ogs_diam_gx_access_network_charging_identifier_value, 0,
&avpch1);
ogs_assert(ret == 0);
charging_id = htobe32(sess->charging.id);
val.os.data = (uint8_t *)&charging_id;
val.os.len = sizeof(charging_id);
2021-06-21 13:36:38 +00:00
ret = fd_msg_avp_setvalue (avpch1, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add (avp, MSG_BRW_LAST_CHILD, avpch1);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
ret = fd_msg_avp_new(ogs_diam_gx_an_trusted, 0, &avp);
ogs_assert(ret == 0);
val.u32 = OGS_DIAM_GX_AN_UNTRUSTED;
ret = fd_msg_avp_setvalue (avp, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
}
2021-06-21 13:36:38 +00:00
ret = clock_gettime(CLOCK_REALTIME, &sess_data->ts);
/* Keep a pointer to the session data for debug purpose,
2021-06-21 13:36:38 +00:00
* in real life we would not need it */
svg = sess_data;
2021-06-21 13:36:38 +00:00
/* Store this value in the session */
ret = fd_sess_state_store(smf_gx_reg, session, &sess_data);
ogs_assert(ret == 0);
ogs_assert(sess_data == NULL);
2021-06-21 13:36:38 +00:00
/* Send the request */
ret = fd_msg_send(&req, smf_gx_cca_cb, svg);
ogs_assert(ret == 0);
/* Increment the counter */
ogs_assert(pthread_mutex_lock(&ogs_diam_logger_self()->stats_lock) == 0);
ogs_diam_logger_self()->stats.nb_sent++;
ogs_assert(pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0);
}
static void smf_gx_cca_cb(void *data, struct msg **msg)
{
int rv;
int ret;
struct sess_state *sess_data = NULL;
struct timespec ts;
struct session *session;
struct avp *avp, *avpch1, *avpch2;
struct avp_hdr *hdr;
unsigned long dur;
int error = 0;
int new;
struct msg *req = NULL;
2021-06-21 13:36:38 +00:00
smf_event_t *e = NULL;
ogs_gtp_xact_t *xact = NULL;
smf_sess_t *sess = NULL;
ogs_diam_gx_message_t *gx_message = NULL;
uint32_t cc_request_number = 0;
ogs_debug("[Credit-Control-Answer]");
2021-06-21 13:36:38 +00:00
ret = clock_gettime(CLOCK_REALTIME, &ts);
ogs_assert(ret == 0);
/* Get originating request of received message, if any */
ret = fd_msg_answ_getq(*msg, &req);
ogs_assert(ret == 0);
2021-06-21 13:36:38 +00:00
/* Search the session, retrieve its data */
ret = fd_msg_sess_get(fd_g_config->cnf_dict, *msg, &session, &new);
ogs_assert(ret == 0);
ogs_assert(new == 0);
ogs_debug(" Search the session");
2021-06-21 13:36:38 +00:00
ret = fd_sess_state_retrieve(smf_gx_reg, session, &sess_data);
ogs_assert(ret == 0);
ogs_assert(sess_data);
ogs_assert((void *)sess_data == data);
ogs_debug(" Retrieve its data: [%s]", sess_data->gx_sid);
/* Value of CC-Request-Number */
ret = fd_msg_search_avp(*msg, ogs_diam_gx_cc_request_number, &avp);
ogs_assert(ret == 0);
if (!avp && req) {
/* Attempt searching for CC-Request-* in original request. Error
* messages (like DIAMETER_UNABLE_TO_DELIVER) crafted internally may not
* have them. */
ret = fd_msg_search_avp(req, ogs_diam_gx_cc_request_number, &avp);
ogs_assert(ret == 0);
}
if (!avp) {
ogs_error("no_CC-Request-Number");
ogs_assert_if_reached();
}
ret = fd_msg_avp_hdr(avp, &hdr);
ogs_assert(ret == 0);
cc_request_number = hdr->avp_value->i32;
ogs_debug(" CC-Request-Number[%d]", cc_request_number);
xact = sess_data->xact[cc_request_number];
2021-06-21 13:36:38 +00:00
sess = sess_data->sess;
ogs_assert(sess);
gx_message = ogs_calloc(1, sizeof(ogs_diam_gx_message_t));
2021-06-21 13:36:38 +00:00
ogs_assert(gx_message);
/* Set Credit Control Command */
gx_message->cmd_code = OGS_DIAM_GX_CMD_CODE_CREDIT_CONTROL;
2021-06-21 13:36:38 +00:00
/* Value of Result Code */
ret = fd_msg_search_avp(*msg, ogs_diam_result_code, &avp);
ogs_assert(ret == 0);
if (avp) {
ret = fd_msg_avp_hdr(avp, &hdr);
ogs_assert(ret == 0);
gx_message->result_code = hdr->avp_value->i32;
gx_message->err = &gx_message->result_code;
ogs_debug(" Result Code: %d", hdr->avp_value->i32);
} else {
ret = fd_msg_search_avp(*msg, ogs_diam_experimental_result, &avp);
ogs_assert(ret == 0);
if (avp) {
ret = fd_avp_search_avp(
avp, ogs_diam_experimental_result_code, &avpch1);
ogs_assert(ret == 0);
if (avpch1) {
ret = fd_msg_avp_hdr(avpch1, &hdr);
ogs_assert(ret == 0);
gx_message->result_code = hdr->avp_value->i32;
gx_message->exp_err = &gx_message->result_code;
ogs_debug(" Experimental Result Code: %d",
gx_message->result_code);
}
} else {
ogs_error("no Result-Code");
error++;
}
}
/* Value of Origin-Host */
ret = fd_msg_search_avp(*msg, ogs_diam_origin_host, &avp);
ogs_assert(ret == 0);
if (avp) {
ret = fd_msg_avp_hdr(avp, &hdr);
ogs_assert(ret == 0);
ogs_debug(" From '%.*s'",
(int)hdr->avp_value->os.len, hdr->avp_value->os.data);
} else {
ogs_error("no_Origin-Host");
error++;
}
/* Value of Origin-Realm */
ret = fd_msg_search_avp(*msg, ogs_diam_origin_realm, &avp);
ogs_assert(ret == 0);
if (avp) {
ret = fd_msg_avp_hdr(avp, &hdr);
ogs_assert(ret == 0);
ogs_debug(" ('%.*s')",
(int)hdr->avp_value->os.len, hdr->avp_value->os.data);
} else {
ogs_error("no_Origin-Realm");
error++;
}
/* Value of CC-Request-Type */
ret = fd_msg_search_avp(*msg, ogs_diam_gx_cc_request_type, &avp);
ogs_assert(ret == 0);
if (!avp && req) {
/* Attempt searching for CC-Request-* in original request. Error
* messages (like DIAMETER_UNABLE_TO_DELIVER) crafted internally may not
* have them. */
ret = fd_msg_search_avp(req, ogs_diam_gx_cc_request_type, &avp);
2021-06-21 13:36:38 +00:00
ogs_assert(ret == 0);
}
if (!avp) {
ogs_error("no_CC-Request-Number");
error++;
2021-06-21 13:36:38 +00:00
}
ret = fd_msg_avp_hdr(avp, &hdr);
ogs_assert(ret == 0);
gx_message->cc_request_type = hdr->avp_value->i32;
2021-06-21 13:36:38 +00:00
if (gx_message->result_code != ER_DIAMETER_SUCCESS) {
ogs_warn("ERROR DIAMETER Result Code(%d)", gx_message->result_code);
goto out;
}
2021-06-21 13:36:38 +00:00
ret = fd_msg_search_avp(*msg, ogs_diam_gx_qos_information, &avp);
ogs_assert(ret == 0);
if (avp) {
ret = fd_avp_search_avp(
avp, ogs_diam_gx_apn_aggregate_max_bitrate_ul, &avpch1);
ogs_assert(ret == 0);
if (avpch1) {
ret = fd_msg_avp_hdr(avpch1, &hdr);
ogs_assert(ret == 0);
gx_message->session_data.session.ambr.uplink = hdr->avp_value->u32;
}
ret = fd_avp_search_avp(
avp, ogs_diam_gx_apn_aggregate_max_bitrate_dl, &avpch1);
ogs_assert(ret == 0);
if (avpch1) {
ret = fd_msg_avp_hdr(avpch1, &hdr);
ogs_assert(ret == 0);
gx_message->session_data.session.ambr.downlink =
hdr->avp_value->u32;
}
}
ret = fd_msg_search_avp(*msg, ogs_diam_gx_default_eps_bearer_qos, &avp);
ogs_assert(ret == 0);
if (avp) {
ret = fd_avp_search_avp(avp, ogs_diam_gx_qos_class_identifier, &avpch1);
ogs_assert(ret == 0);
if (avpch1) {
ret = fd_msg_avp_hdr(avpch1, &hdr);
ogs_assert(ret == 0);
gx_message->session_data.session.qos.index = hdr->avp_value->u32;
}
ret = fd_avp_search_avp(
avp, ogs_diam_gx_allocation_retention_priority, &avpch1);
ogs_assert(ret == 0);
if (avpch1) {
ret = fd_avp_search_avp(
avpch1, ogs_diam_gx_priority_level, &avpch2);
ogs_assert(ret == 0);
if (avpch2) {
ret = fd_msg_avp_hdr(avpch2, &hdr);
ogs_assert(ret == 0);
gx_message->session_data.session.qos.arp.priority_level =
hdr->avp_value->u32;
}
/*
* Ch 7.3.40 Allocation-Retenion-Proirty in TS 29.272 V15.9.0
*
* If the Pre-emption-Capability AVP is not present in the
* Allocation-Retention-Priority AVP, the default value shall be
* PRE-EMPTION_CAPABILITY_DISABLED (1).
*
* If the Pre-emption-Vulnerability AVP is not present in the
* Allocation-Retention-Priority AVP, the default value shall be
* PRE-EMPTION_VULNERABILITY_ENABLED (0).
*
* However, to easily set up VoLTE service,
* enable Pre-emption Capability/Vulnerablility
* in Default Bearer
*/
ret = fd_avp_search_avp(
avpch1, ogs_diam_gx_pre_emption_capability, &avpch2);
ogs_assert(ret == 0);
if (avpch2) {
ret = fd_msg_avp_hdr(avpch2, &hdr);
ogs_assert(ret == 0);
gx_message->session_data.
session.qos.arp.pre_emption_capability =
hdr->avp_value->u32;
} else {
gx_message->session_data.
session.qos.arp.pre_emption_capability =
OGS_EPC_PRE_EMPTION_DISABLED;
}
ret = fd_avp_search_avp(avpch1,
ogs_diam_gx_pre_emption_vulnerability, &avpch2);
ogs_assert(ret == 0);
if (avpch2) {
ret = fd_msg_avp_hdr(avpch2, &hdr);
ogs_assert(ret == 0);
gx_message->session_data.
session.qos.arp.pre_emption_vulnerability =
hdr->avp_value->u32;
} else {
gx_message->session_data.
session.qos.arp.pre_emption_vulnerability =
OGS_EPC_PRE_EMPTION_ENABLED;
}
}
}
ret = fd_msg_browse(*msg, MSG_BRW_FIRST_CHILD, &avp, NULL);
ogs_assert(ret == 0);
while (avp) {
ret = fd_msg_avp_hdr(avp, &hdr);
ogs_assert(ret == 0);
switch (hdr->avp_code) {
case AC_SESSION_ID:
case AC_ORIGIN_HOST:
if (sess_data->peer_host)
ogs_free(sess_data->peer_host);
sess_data->peer_host =
(os0_t)ogs_strdup((char *)hdr->avp_value->os.data);
ogs_assert(sess_data->peer_host);
break;
2021-06-21 13:36:38 +00:00
case AC_ORIGIN_REALM:
case AC_DESTINATION_REALM:
case AC_RESULT_CODE:
case AC_ROUTE_RECORD:
case AC_PROXY_INFO:
case AC_AUTH_APPLICATION_ID:
break;
case OGS_DIAM_GX_AVP_CODE_CC_REQUEST_TYPE:
case OGS_DIAM_GX_AVP_CODE_CC_REQUEST_NUMBER:
case OGS_DIAM_GX_AVP_CODE_SUPPORTED_FEATURES:
break;
case OGS_DIAM_GX_AVP_CODE_QOS_INFORMATION:
case OGS_DIAM_GX_AVP_CODE_DEFAULT_EPS_BEARER_QOS:
break;
case OGS_DIAM_GX_AVP_CODE_CHARGING_RULE_INSTALL:
ret = fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &avpch1, NULL);
ogs_assert(ret == 0);
while (avpch1) {
ret = fd_msg_avp_hdr(avpch1, &hdr);
ogs_assert(ret == 0);
switch (hdr->avp_code) {
case OGS_DIAM_GX_AVP_CODE_CHARGING_RULE_DEFINITION:
if (gx_message->session_data.num_of_pcc_rule <
OGS_MAX_NUM_OF_PCC_RULE) {
ogs_pcc_rule_t *pcc_rule = NULL;
smf_bearer_t *bearer = NULL;
int num_of_flow = 0;
pcc_rule = &gx_message->session_data.pcc_rule
[gx_message->session_data.num_of_pcc_rule];
rv = decode_pcc_rule_definition(
pcc_rule, avpch1, &error);
ogs_assert(rv == OGS_OK);
num_of_flow = pcc_rule->num_of_flow;
bearer = smf_bearer_find_by_pcc_rule_name(
sess, pcc_rule->name);
if (bearer)
num_of_flow += ogs_list_count(&bearer->pf_list);
if (num_of_flow < OGS_MAX_NUM_OF_FLOW_IN_BEARER) {
pcc_rule->type = OGS_PCC_RULE_TYPE_INSTALL;
gx_message->session_data.num_of_pcc_rule++;
} else {
ogs_error("Overflow : Num Of Flow %d", num_of_flow);
OGS_PCC_RULE_FREE(pcc_rule);
error++;
}
} else {
ogs_error("Overflow: Number of PCCRule");
error++;
}
2021-06-21 13:36:38 +00:00
break;
default:
ogs_error("Not supported(%d)", hdr->avp_code);
break;
}
fd_msg_browse(avpch1, MSG_BRW_NEXT, &avpch1, NULL);
}
break;
default:
ogs_warn("Not supported(%d)", hdr->avp_code);
break;
}
fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL);
}
out:
if (!error) {
e = smf_event_new(SMF_EVT_GX_MESSAGE);
ogs_assert(e);
e->sess = sess;
e->gx_message = gx_message;
2021-06-21 13:36:38 +00:00
e->gtp_xact = xact;
rv = ogs_queue_push(ogs_app()->queue, e);
if (rv != OGS_OK) {
ogs_warn("ogs_queue_push() failed:%d", (int)rv);
ogs_session_data_free(&gx_message->session_data);
ogs_free(gx_message);
2021-06-21 13:36:38 +00:00
smf_event_free(e);
} else {
ogs_pollset_notify(ogs_app()->pollset);
}
} else {
ogs_session_data_free(&gx_message->session_data);
ogs_free(gx_message);
2021-06-21 13:36:38 +00:00
}
/* Free the message */
ogs_assert(pthread_mutex_lock(&ogs_diam_logger_self()->stats_lock) == 0);
dur = ((ts.tv_sec - sess_data->ts.tv_sec) * 1000000) +
2021-06-21 13:36:38 +00:00
((ts.tv_nsec - sess_data->ts.tv_nsec) / 1000);
if (ogs_diam_logger_self()->stats.nb_recv) {
/* Ponderate in the avg */
ogs_diam_logger_self()->stats.avg = (ogs_diam_logger_self()->stats.avg *
2021-06-21 13:36:38 +00:00
ogs_diam_logger_self()->stats.nb_recv + dur) /
(ogs_diam_logger_self()->stats.nb_recv + 1);
/* Min, max */
if (dur < ogs_diam_logger_self()->stats.shortest)
ogs_diam_logger_self()->stats.shortest = dur;
if (dur > ogs_diam_logger_self()->stats.longest)
ogs_diam_logger_self()->stats.longest = dur;
} else {
ogs_diam_logger_self()->stats.shortest = dur;
ogs_diam_logger_self()->stats.longest = dur;
ogs_diam_logger_self()->stats.avg = dur;
}
if (error)
ogs_diam_logger_self()->stats.nb_errs++;
else
2021-06-21 13:36:38 +00:00
ogs_diam_logger_self()->stats.nb_recv++;
ogs_assert(pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0);
2021-06-21 13:36:38 +00:00
/* Display how long it took */
if (ts.tv_nsec > sess_data->ts.tv_nsec)
ogs_trace("in %d.%06ld sec",
2021-06-21 13:36:38 +00:00
(int)(ts.tv_sec - sess_data->ts.tv_sec),
(long)(ts.tv_nsec - sess_data->ts.tv_nsec) / 1000);
else
ogs_trace("in %d.%06ld sec",
2021-06-21 13:36:38 +00:00
(int)(ts.tv_sec + 1 - sess_data->ts.tv_sec),
(long)(1000000000 + ts.tv_nsec - sess_data->ts.tv_nsec) / 1000);
ogs_debug(" CC-Request-Type[%d] Number[%d] in Session Data",
2021-06-21 13:36:38 +00:00
sess_data->cc_request_type, sess_data->cc_request_number);
ogs_debug(" Current CC-Request-Number[%d]", cc_request_number);
if (sess_data->cc_request_type ==
OGS_DIAM_GX_CC_REQUEST_TYPE_TERMINATION_REQUEST &&
sess_data->cc_request_number <= cc_request_number) {
ogs_debug(" [LAST] state_cleanup(): [%s]", sess_data->gx_sid);
state_cleanup(sess_data, NULL, NULL);
} else {
ogs_debug(" fd_sess_state_store(): [%s]", sess_data->gx_sid);
ret = fd_sess_state_store(smf_gx_reg, session, &sess_data);
ogs_assert(ret == 0);
ogs_assert(sess_data == NULL);
}
ret = fd_msg_free(*msg);
ogs_assert(ret == 0);
*msg = NULL;
2021-06-21 13:36:38 +00:00
return;
}
static int smf_gx_fb_cb(struct msg **msg, struct avp *avp,
2021-06-21 13:36:38 +00:00
struct session *sess, void *opaque, enum disp_action *act)
{
/* This CB should never be called */
ogs_warn("Unexpected message received!");
2021-06-21 13:36:38 +00:00
return ENOTSUP;
}
static int smf_gx_rar_cb( struct msg **msg, struct avp *avp,
2021-06-21 13:36:38 +00:00
struct session *session, void *opaque, enum disp_action *act)
{
int rv;
int ret;
struct msg *ans, *qry;
struct avp *avpch1;
struct avp_hdr *hdr;
union avp_value val;
struct sess_state *sess_data = NULL;
smf_event_t *e = NULL;
smf_sess_t *sess = NULL;
ogs_diam_gx_message_t *gx_message = NULL;
uint32_t result_code = OGS_DIAM_UNKNOWN_SESSION_ID;
int error = 0;
2021-06-21 13:36:38 +00:00
ogs_assert(msg);
ogs_debug("Re-Auth-Request");
gx_message = ogs_calloc(1, sizeof(ogs_diam_gx_message_t));
2021-06-21 13:36:38 +00:00
ogs_assert(gx_message);
/* Set Credit Control Command */
gx_message->cmd_code = OGS_DIAM_GX_CMD_RE_AUTH;
/* Create answer header */
qry = *msg;
ret = fd_msg_new_answer_from_req(fd_g_config->cnf_dict, msg, 0);
ogs_assert(ret == 0);
ans = *msg;
ret = fd_sess_state_retrieve(smf_gx_reg, session, &sess_data);
ogs_assert(ret == 0);
if (!sess_data) {
ogs_error("No Session Data");
goto out;
}
/* Get Session Information */
sess = sess_data->sess;
ogs_assert(sess);
ret = fd_msg_browse(qry, MSG_BRW_FIRST_CHILD, &avp, NULL);
ogs_assert(ret == 0);
while (avp) {
ret = fd_msg_avp_hdr(avp, &hdr);
ogs_assert(ret == 0);
switch(hdr->avp_code) {
case AC_SESSION_ID:
case AC_ORIGIN_HOST:
case AC_ORIGIN_REALM:
case AC_DESTINATION_REALM:
case AC_DESTINATION_HOST:
case AC_ROUTE_RECORD:
case AC_PROXY_INFO:
case AC_AUTH_APPLICATION_ID:
break;
case OGS_DIAM_GX_AVP_CODE_RE_AUTH_REQUEST_TYPE:
break;
case OGS_DIAM_GX_AVP_CODE_CHARGING_RULE_INSTALL:
ret = fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &avpch1, NULL);
ogs_assert(ret == 0);
while(avpch1) {
ret = fd_msg_avp_hdr(avpch1, &hdr);
ogs_assert(ret == 0);
switch(hdr->avp_code) {
case OGS_DIAM_GX_AVP_CODE_CHARGING_RULE_DEFINITION:
if (gx_message->session_data.num_of_pcc_rule <
OGS_MAX_NUM_OF_PCC_RULE) {
ogs_pcc_rule_t *pcc_rule = NULL;
smf_bearer_t *bearer = NULL;
int num_of_flow = 0;
pcc_rule = &gx_message->session_data.pcc_rule
[gx_message->session_data.num_of_pcc_rule];
rv = decode_pcc_rule_definition(
pcc_rule, avpch1, &error);
ogs_assert(rv == OGS_OK);
if (error) {
ogs_error("decode_pcc_rule_definition() failed");
OGS_PCC_RULE_FREE(pcc_rule);
result_code = OGS_DIAM_GX_DIAMETER_PCC_RULE_EVENT;
goto out;
}
num_of_flow = pcc_rule->num_of_flow;
bearer = smf_bearer_find_by_pcc_rule_name(
sess, pcc_rule->name);
if (bearer)
num_of_flow += ogs_list_count(&bearer->pf_list);
if (num_of_flow < OGS_MAX_NUM_OF_FLOW_IN_BEARER) {
pcc_rule->type = OGS_PCC_RULE_TYPE_INSTALL;
gx_message->session_data.num_of_pcc_rule++;
} else {
ogs_error("Overflow : Num Of Flow %d", num_of_flow);
OGS_PCC_RULE_FREE(pcc_rule);
result_code = OGS_DIAM_GX_DIAMETER_PCC_RULE_EVENT;
goto out;
}
} else {
ogs_error("Overflow: Number of PCCRule");
}
2021-06-21 13:36:38 +00:00
break;
default:
ogs_error("Not supported(%d)", hdr->avp_code);
break;
}
fd_msg_browse(avpch1, MSG_BRW_NEXT, &avpch1, NULL);
}
break;
case OGS_DIAM_GX_AVP_CODE_CHARGING_RULE_REMOVE:
ret = fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &avpch1, NULL);
ogs_assert(ret == 0);
while (avpch1) {
ret = fd_msg_avp_hdr(avpch1, &hdr);
ogs_assert(ret == 0);
switch (hdr->avp_code) {
case OGS_DIAM_GX_AVP_CODE_CHARGING_RULE_NAME:
if (gx_message->session_data.num_of_pcc_rule <
OGS_MAX_NUM_OF_PCC_RULE) {
ogs_pcc_rule_t *pcc_rule =
&gx_message->session_data.pcc_rule
[gx_message->session_data.num_of_pcc_rule];
pcc_rule->name = ogs_strdup(
(char*)hdr->avp_value->os.data);
ogs_assert(pcc_rule->name);
pcc_rule->type = OGS_PCC_RULE_TYPE_REMOVE;
gx_message->session_data.num_of_pcc_rule++;
} else {
ogs_error("Overflow: Number of PCCRule");
}
2021-06-21 13:36:38 +00:00
break;
default:
ogs_error("Not supported(%d)", hdr->avp_code);
break;
}
fd_msg_browse(avpch1, MSG_BRW_NEXT, &avpch1, NULL);
}
break;
default:
ogs_warn("Not supported(%d)", hdr->avp_code);
break;
}
fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL);
}
/* Send Gx Event to SMF State Machine */
e = smf_event_new(SMF_EVT_GX_MESSAGE);
ogs_assert(e);
e->sess = sess;
e->gx_message = gx_message;
2021-06-21 13:36:38 +00:00
rv = ogs_queue_push(ogs_app()->queue, e);
if (rv != OGS_OK) {
ogs_warn("ogs_queue_push() failed:%d", (int)rv);
ogs_session_data_free(&gx_message->session_data);
ogs_free(gx_message);
2021-06-21 13:36:38 +00:00
smf_event_free(e);
} else {
ogs_pollset_notify(ogs_app()->pollset);
}
/* Set the Auth-Application-Id AVP */
ret = fd_msg_avp_new(ogs_diam_auth_application_id, 0, &avp);
ogs_assert(ret == 0);
val.i32 = OGS_DIAM_GX_APPLICATION_ID;
ret = fd_msg_avp_setvalue(avp, &val);
ogs_assert(ret == 0);
ret = fd_msg_avp_add(ans, MSG_BRW_LAST_CHILD, avp);
ogs_assert(ret == 0);
/* Set the Origin-Host, Origin-Realm, andResult-Code AVPs */
ret = fd_msg_rescode_set(ans, (char *)"DIAMETER_SUCCESS", NULL, NULL, 1);
ogs_assert(ret == 0);
/* Store this value in the session */
ret = fd_sess_state_store(smf_gx_reg, session, &sess_data);
ogs_assert(ret == 0);
ogs_assert(sess_data == NULL);
/* Send the answer */
ret = fd_msg_send(msg, NULL, NULL);
ogs_assert(ret == 0);
ogs_debug("Re-Auth-Answer");
/* Add this value to the stats */
ogs_assert(pthread_mutex_lock(&ogs_diam_logger_self()->stats_lock) == 0);
ogs_diam_logger_self()->stats.nb_echoed++;
ogs_assert(pthread_mutex_unlock(&ogs_diam_logger_self()->stats_lock) == 0);
return 0;
out:
if (result_code == OGS_DIAM_UNKNOWN_SESSION_ID) {
ret = fd_msg_rescode_set(ans,
(char *)"DIAMETER_UNKNOWN_SESSION_ID", NULL, NULL, 1);
ogs_assert(ret == 0);
} else {
ret = ogs_diam_message_experimental_rescode_set(ans, result_code);
ogs_assert(ret == 0);
}
/* Store this value in the session */
ret = fd_sess_state_store(smf_gx_reg, session, &sess_data);
ogs_assert(sess_data == NULL);
2021-06-21 13:36:38 +00:00
ret = fd_msg_send(msg, NULL, NULL);
2021-06-21 13:36:38 +00:00
ogs_assert(ret == 0);
ogs_session_data_free(&gx_message->session_data);
ogs_free(gx_message);
2021-06-21 13:36:38 +00:00
return 0;
}
int smf_gx_init(void)
{
int ret;
struct disp_when data;
ogs_thread_mutex_init(&sess_state_mutex);
ogs_pool_init(&sess_state_pool, ogs_app()->pool.sess);
/* Install objects definitions for this application */
ret = ogs_diam_gx_init();
ogs_assert(ret == 0);
/* Create handler for sessions */
ret = fd_sess_handler_create(&smf_gx_reg, state_cleanup, NULL, NULL);
ogs_assert(ret == 0);
memset(&data, 0, sizeof(data));
data.app = ogs_diam_gx_application;
ret = fd_disp_register(smf_gx_fb_cb, DISP_HOW_APPID, &data, NULL,
&hdl_gx_fb);
ogs_assert(ret == 0);
data.command = ogs_diam_gx_cmd_rar;
ret = fd_disp_register(smf_gx_rar_cb, DISP_HOW_CC, &data, NULL,
&hdl_gx_rar);
ogs_assert(ret == 0);
/* Advertise the support for the application in the peer */
ret = fd_disp_app_support(ogs_diam_gx_application, ogs_diam_vendor, 1, 0);
ogs_assert(ret == 0);
return OGS_OK;
}
void smf_gx_final(void)
{
int ret;
ret = fd_sess_handler_destroy(&smf_gx_reg, NULL);
ogs_assert(ret == 0);
if (hdl_gx_fb)
(void) fd_disp_unregister(&hdl_gx_fb, NULL);
if (hdl_gx_rar)
(void) fd_disp_unregister(&hdl_gx_rar, NULL);
ogs_pool_final(&sess_state_pool);
ogs_thread_mutex_destroy(&sess_state_mutex);
}
static int decode_pcc_rule_definition(
ogs_pcc_rule_t *pcc_rule, struct avp *avpch1, int *perror)
{
int ret = 0, error = 0;
struct avp *avpch2, *avpch3, *avpch4;
struct avp_hdr *hdr;
ogs_assert(pcc_rule);
ogs_assert(avpch1);
ret = fd_msg_browse(avpch1, MSG_BRW_FIRST_CHILD, &avpch2, NULL);
ogs_assert(ret == 0);
while (avpch2) {
ogs_flow_t *flow = NULL;
ret = fd_msg_avp_hdr(avpch2, &hdr);
ogs_assert(ret == 0);
switch (hdr->avp_code) {
case OGS_DIAM_GX_AVP_CODE_CHARGING_RULE_NAME:
if (pcc_rule->name) {
ogs_error("PCC Rule Name has already been defined");
ogs_free(pcc_rule->name);
}
pcc_rule->name = ogs_strdup((char*)hdr->avp_value->os.data);
ogs_assert(pcc_rule->name);
break;
case OGS_DIAM_GX_AVP_CODE_FLOW_INFORMATION:
if (pcc_rule->num_of_flow < OGS_MAX_NUM_OF_FLOW_IN_PCC_RULE) {
flow = &pcc_rule->flow[pcc_rule->num_of_flow];
2021-06-21 13:36:38 +00:00
ret = fd_avp_search_avp(
avpch2, ogs_diam_gx_flow_direction, &avpch3);
2021-06-21 13:36:38 +00:00
ogs_assert(ret == 0);
if (avpch3) {
ret = fd_msg_avp_hdr( avpch3, &hdr);
ogs_assert(ret == 0);
flow->direction = hdr->avp_value->i32;
}
2021-06-21 13:36:38 +00:00
ret = fd_avp_search_avp(
avpch2, ogs_diam_gx_flow_description, &avpch3);
2021-06-21 13:36:38 +00:00
ogs_assert(ret == 0);
if (avpch3) {
ret = fd_msg_avp_hdr(avpch3, &hdr);
ogs_assert(ret == 0);
flow->description = ogs_strndup(
(char*)hdr->avp_value->os.data, hdr->avp_value->os.len);
ogs_assert(flow->description);
}
2021-06-21 13:36:38 +00:00
pcc_rule->num_of_flow++;
} else {
ogs_error("Overflow: Num of Flow [%d]", pcc_rule->num_of_flow);
error++;
}
2021-06-21 13:36:38 +00:00
break;
case OGS_DIAM_GX_AVP_CODE_FLOW_STATUS:
pcc_rule->flow_status = hdr->avp_value->i32;
break;
case OGS_DIAM_GX_AVP_CODE_QOS_INFORMATION:
ret = fd_avp_search_avp(avpch2,
ogs_diam_gx_qos_class_identifier, &avpch3);
ogs_assert(ret == 0);
if (avpch3) {
ret = fd_msg_avp_hdr(avpch3, &hdr);
ogs_assert(ret == 0);
pcc_rule->qos.index = hdr->avp_value->u32;
} else {
ogs_error("no_QCI");
error++;
}
ret = fd_avp_search_avp(avpch2,
ogs_diam_gx_allocation_retention_priority, &avpch3);
ogs_assert(ret == 0);
if (avpch3) {
ret = fd_avp_search_avp(
avpch3, ogs_diam_gx_priority_level, &avpch4);
ogs_assert(ret == 0);
if (avpch4) {
ret = fd_msg_avp_hdr(avpch4, &hdr);
ogs_assert(ret == 0);
pcc_rule->qos.arp.priority_level = hdr->avp_value->u32;
} else {
ogs_error("no_Priority-Level");
error++;
}
/*
* Ch 7.3.40 Allocation-Retenion-Proirty in TS 29.272 V15.9.0
*
* If the Pre-emption-Capability AVP is not present in the
* Allocation-Retention-Priority AVP, the default value shall be
* PRE-EMPTION_CAPABILITY_DISABLED (1).
*
* If the Pre-emption-Vulnerability AVP is not present in the
* Allocation-Retention-Priority AVP, the default value shall be
* PRE-EMPTION_VULNERABILITY_ENABLED (0).
*
* However, to easily set up VoLTE service,
* enable Pre-emption Capability/Vulnerablility
* in Default Bearer
*/
ret = fd_avp_search_avp(avpch3,
ogs_diam_gx_pre_emption_capability, &avpch4);
ogs_assert(ret == 0);
if (avpch4) {
ret = fd_msg_avp_hdr(avpch4, &hdr);
ogs_assert(ret == 0);
pcc_rule->qos.arp.pre_emption_capability =
hdr->avp_value->u32;
} else {
pcc_rule->qos.arp.pre_emption_capability =
OGS_EPC_PRE_EMPTION_DISABLED;
}
ret = fd_avp_search_avp(avpch3,
ogs_diam_gx_pre_emption_vulnerability, &avpch4);
ogs_assert(ret == 0);
if (avpch4) {
ret = fd_msg_avp_hdr(avpch4, &hdr);
ogs_assert(ret == 0);
pcc_rule->qos.arp.pre_emption_vulnerability =
hdr->avp_value->u32;
} else {
pcc_rule->qos.arp.pre_emption_vulnerability =
OGS_EPC_PRE_EMPTION_ENABLED;
}
} else {
ogs_error("no_ARP");
error++;
}
ret = fd_avp_search_avp(avpch2,
ogs_diam_gx_max_requested_bandwidth_ul, &avpch3);
ogs_assert(ret == 0);
if (avpch3) {
ret = fd_msg_avp_hdr(avpch3, &hdr);
ogs_assert(ret == 0);
pcc_rule->qos.mbr.uplink = hdr->avp_value->u32;
}
ret = fd_avp_search_avp(avpch2,
ogs_diam_gx_max_requested_bandwidth_dl, &avpch3);
ogs_assert(ret == 0);
if (avpch3) {
ret = fd_msg_avp_hdr(avpch3, &hdr);
ogs_assert(ret == 0);
pcc_rule->qos.mbr.downlink = hdr->avp_value->u32;
}
ret = fd_avp_search_avp(avpch2,
ogs_diam_gx_guaranteed_bitrate_ul, &avpch3);
ogs_assert(ret == 0);
if (avpch3) {
ret = fd_msg_avp_hdr(avpch3, &hdr);
ogs_assert(ret == 0);
pcc_rule->qos.gbr.uplink = hdr->avp_value->u32;
}
ret = fd_avp_search_avp(avpch2,
ogs_diam_gx_guaranteed_bitrate_dl, &avpch3);
ogs_assert(ret == 0);
if (avpch3) {
ret = fd_msg_avp_hdr(avpch3, &hdr);
ogs_assert(ret == 0);
pcc_rule->qos.gbr.downlink = hdr->avp_value->u32;
}
break;
case OGS_DIAM_GX_AVP_CODE_PRECEDENCE:
pcc_rule->precedence = hdr->avp_value->i32;
break;
default:
ogs_error("Not implemented(%d)", hdr->avp_code);
break;
}
fd_msg_browse(avpch2, MSG_BRW_NEXT, &avpch2, NULL);
}
if (perror)
*perror = error;
return OGS_OK;
}