/* * Copyright (C) 2019-2023 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 "mme-context.h" #include "mme-s11-build.h" ogs_pkbuf_t *mme_s11_build_create_session_request( uint8_t type, mme_sess_t *sess, int create_action) { int rv; ogs_session_t *session = NULL; mme_ue_t *mme_ue = NULL; sgw_ue_t *sgw_ue = NULL; mme_bearer_t *bearer = NULL; ogs_gtp2_message_t gtp_message; ogs_gtp2_create_session_request_t *req = >p_message.create_session_request; ogs_gtp2_uli_t uli; char uli_buf[OGS_GTP2_MAX_ULI_LEN]; ogs_gtp2_f_teid_t mme_s11_teid, pgw_s5c_teid; int len; ogs_gtp2_f_teid_t enb_s1u_teid[OGS_BEARER_PER_UE]; int enb_s1u_len[OGS_BEARER_PER_UE]; ogs_gtp2_f_teid_t pgw_s5u_teid[OGS_BEARER_PER_UE]; int pgw_s5u_len[OGS_BEARER_PER_UE]; ogs_gtp2_ambr_t ambr; ogs_gtp2_bearer_qos_t bearer_qos; char bearer_qos_buf[OGS_BEARER_PER_UE][GTP2_BEARER_QOS_LEN]; ogs_gtp2_ue_timezone_t ue_timezone; struct timeval now; struct tm time_exp; char apn[OGS_MAX_APN_LEN+1]; ogs_gtp2_indication_t indication; ogs_assert(sess); session = sess->session; ogs_assert(session); ogs_assert(session->name); mme_ue = sess->mme_ue; ogs_assert(mme_ue); sgw_ue = mme_ue->sgw_ue; ogs_assert(sgw_ue); if (create_action == OGS_GTP_CREATE_IN_PATH_SWITCH_REQUEST) { sgw_ue = sgw_ue_cycle(sgw_ue->target_ue); ogs_assert(sgw_ue); } ogs_debug("Create Session Request"); ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", mme_ue->mme_s11_teid, sgw_ue->sgw_s11_teid); memset(>p_message, 0, sizeof(ogs_gtp2_message_t)); ogs_assert(mme_ue->imsi_len); req->imsi.presence = 1; req->imsi.data = mme_ue->imsi; req->imsi.len = mme_ue->imsi_len; if (mme_ue->imeisv_len) { req->me_identity.presence = 1; req->me_identity.data = mme_ue->imeisv; req->me_identity.len = mme_ue->imeisv_len; } if (mme_ue->msisdn_len) { req->msisdn.presence = 1; req->msisdn.data = mme_ue->msisdn; req->msisdn.len = mme_ue->msisdn_len; } memset(&uli, 0, sizeof(ogs_gtp2_uli_t)); uli.flags.e_cgi = 1; uli.flags.tai = 1; ogs_nas_from_plmn_id(&uli.tai.nas_plmn_id, &mme_ue->tai.plmn_id); uli.tai.tac = mme_ue->tai.tac; ogs_nas_from_plmn_id(&uli.e_cgi.nas_plmn_id, &mme_ue->e_cgi.plmn_id); uli.e_cgi.cell_id = mme_ue->e_cgi.cell_id; req->user_location_information.presence = 1; ogs_gtp2_build_uli(&req->user_location_information, &uli, uli_buf, OGS_GTP2_MAX_ULI_LEN); req->serving_network.presence = 1; req->serving_network.data = &uli.tai.nas_plmn_id; req->serving_network.len = sizeof(uli.tai.nas_plmn_id); req->rat_type.presence = 1; req->rat_type.u8 = OGS_GTP2_RAT_TYPE_EUTRAN; memset(&mme_s11_teid, 0, sizeof(ogs_gtp2_f_teid_t)); mme_s11_teid.interface_type = OGS_GTP2_F_TEID_S11_MME_GTP_C; mme_s11_teid.teid = htobe32(mme_ue->mme_s11_teid); rv = ogs_gtp2_sockaddr_to_f_teid( ogs_gtp_self()->gtpc_addr, ogs_gtp_self()->gtpc_addr6, &mme_s11_teid, &len); ogs_assert(rv == OGS_OK); req->sender_f_teid_for_control_plane.presence = 1; req->sender_f_teid_for_control_plane.data = &mme_s11_teid; req->sender_f_teid_for_control_plane.len = len; memset(&pgw_s5c_teid, 0, sizeof(ogs_gtp2_f_teid_t)); pgw_s5c_teid.interface_type = OGS_GTP2_F_TEID_S5_S8_PGW_GTP_C; pgw_s5c_teid.teid = htobe32(sess->pgw_s5c_teid); if (session->smf_ip.ipv4 || session->smf_ip.ipv6) { pgw_s5c_teid.ipv4 = session->smf_ip.ipv4; pgw_s5c_teid.ipv6 = session->smf_ip.ipv6; if (pgw_s5c_teid.ipv4 && pgw_s5c_teid.ipv6) { pgw_s5c_teid.both.addr = session->smf_ip.addr; memcpy(pgw_s5c_teid.both.addr6, session->smf_ip.addr6, sizeof session->smf_ip.addr6); req->pgw_s5_s8_address_for_control_plane_or_pmip.len = OGS_GTP2_F_TEID_IPV4V6_LEN; } else if (pgw_s5c_teid.ipv4) { pgw_s5c_teid.addr = session->smf_ip.addr; req->pgw_s5_s8_address_for_control_plane_or_pmip.len = OGS_GTP2_F_TEID_IPV4_LEN; } else if (pgw_s5c_teid.ipv6) { memcpy(pgw_s5c_teid.addr6, session->smf_ip.addr6, sizeof session->smf_ip.addr6); req->pgw_s5_s8_address_for_control_plane_or_pmip.len = OGS_GTP2_F_TEID_IPV6_LEN; } req->pgw_s5_s8_address_for_control_plane_or_pmip.presence = 1; req->pgw_s5_s8_address_for_control_plane_or_pmip.data = &pgw_s5c_teid; } else { ogs_sockaddr_t *pgw_addr = NULL; ogs_sockaddr_t *pgw_addr6 = NULL; pgw_addr = mme_pgw_addr_find_by_apn_enb( &mme_self()->pgw_list, AF_INET, sess); pgw_addr6 = mme_pgw_addr_find_by_apn_enb( &mme_self()->pgw_list, AF_INET6, sess); if (!pgw_addr && !pgw_addr6) { pgw_addr = mme_self()->pgw_addr; pgw_addr6 = mme_self()->pgw_addr6; } rv = ogs_gtp2_sockaddr_to_f_teid( pgw_addr, pgw_addr6, &pgw_s5c_teid, &len); ogs_assert(rv == OGS_OK); req->pgw_s5_s8_address_for_control_plane_or_pmip.presence = 1; req->pgw_s5_s8_address_for_control_plane_or_pmip.data = &pgw_s5c_teid; req->pgw_s5_s8_address_for_control_plane_or_pmip.len = len; } req->access_point_name.presence = 1; req->access_point_name.len = ogs_fqdn_build( apn, session->name, strlen(session->name)); req->access_point_name.data = apn; req->selection_mode.presence = 1; req->selection_mode.u8 = OGS_GTP2_SELECTION_MODE_MS_OR_NETWORK_PROVIDED_APN; ogs_assert(sess->request_type.type == OGS_NAS_EPS_PDN_TYPE_IPV4 || sess->request_type.type == OGS_NAS_EPS_PDN_TYPE_IPV6 || sess->request_type.type == OGS_NAS_EPS_PDN_TYPE_IPV4V6); if (session->session_type == OGS_PDU_SESSION_TYPE_IPV4 || session->session_type == OGS_PDU_SESSION_TYPE_IPV6 || session->session_type == OGS_PDU_SESSION_TYPE_IPV4V6) { req->pdn_type.u8 = (session->session_type & sess->request_type.type); if (req->pdn_type.u8 == 0) { ogs_fatal("Cannot derive PDN Type [UE:%d,HSS:%d]", sess->request_type.type, session->session_type); ogs_assert_if_reached(); } } else { ogs_fatal("Invalid PDN_TYPE[%d]", session->session_type); ogs_assert_if_reached(); } req->pdn_type.presence = 1; /* If we started with both addrs (IPV4V6) but the above code * (pdn_type & sess->request_type) truncates us down to just one, * we need to change position of addresses in struct. */ if (req->pdn_type.u8 == OGS_PDU_SESSION_TYPE_IPV4 && session->session_type == OGS_PDU_SESSION_TYPE_IPV4V6) { uint32_t addr = session->paa.both.addr; session->paa.addr = addr; } if (req->pdn_type.u8 == OGS_PDU_SESSION_TYPE_IPV6 && session->session_type == OGS_PDU_SESSION_TYPE_IPV4V6) { uint8_t addr[16]; memcpy(&addr, session->paa.both.addr6, OGS_IPV6_LEN); memcpy(session->paa.addr6, &addr, OGS_IPV6_LEN); } memset(&indication, 0, sizeof(ogs_gtp2_indication_t)); req->indication_flags.presence = 1; req->indication_flags.data = &indication; req->indication_flags.len = sizeof(ogs_gtp2_indication_t); indication.change_reporting_support_indication = 1; indication.enb_change_reporting_support_indication = 1; if (req->pdn_type.u8 == OGS_PDU_SESSION_TYPE_IPV4V6) indication.dual_address_bearer_flag = 1; if (sess->request_type.value == OGS_NAS_EPS_REQUEST_TYPE_HANDOVER) indication.handover_indication = 1; if (create_action == OGS_GTP_CREATE_IN_PATH_SWITCH_REQUEST || create_action == OGS_GTP_CREATE_IN_TRACKING_AREA_UPDATE) indication.operation_indication = 1; session->paa.session_type = req->pdn_type.u8; req->pdn_address_allocation.data = &session->paa; if (req->pdn_type.u8 == OGS_PDU_SESSION_TYPE_IPV4) req->pdn_address_allocation.len = OGS_PAA_IPV4_LEN; else if (req->pdn_type.u8 == OGS_PDU_SESSION_TYPE_IPV6) req->pdn_address_allocation.len = OGS_PAA_IPV6_LEN; else if (req->pdn_type.u8 == OGS_PDU_SESSION_TYPE_IPV4V6) req->pdn_address_allocation.len = OGS_PAA_IPV4V6_LEN; else ogs_assert_if_reached(); req->pdn_address_allocation.presence = 1; req->maximum_apn_restriction.presence = 1; req->maximum_apn_restriction.u8 = OGS_GTP2_APN_NO_RESTRICTION; if (session->ambr.uplink || session->ambr.downlink) { /* * Ch 8.7. Aggregate Maximum Bit Rate(AMBR) in TS 29.274 V15.9.0 * * AMBR is defined in clause 9.9.4.2 of 3GPP TS 24.301 [23], * but it shall be encoded as shown in Figure 8.7-1 as * Unsigned32 binary integer values in kbps (1000 bits per second). */ memset(&ambr, 0, sizeof(ogs_gtp2_ambr_t)); ambr.uplink = htobe32(session->ambr.uplink / 1000); ambr.downlink = htobe32(session->ambr.downlink / 1000); req->aggregate_maximum_bit_rate.presence = 1; req->aggregate_maximum_bit_rate.data = &ambr; req->aggregate_maximum_bit_rate.len = sizeof(ambr); } if (sess->ue_epco.length && sess->ue_epco.buffer) { req->extended_protocol_configuration_options.presence = 1; req->extended_protocol_configuration_options.data = sess->ue_epco.buffer; req->extended_protocol_configuration_options.len = sess->ue_epco.length; } else if (sess->ue_pco.length && sess->ue_pco.buffer) { req->protocol_configuration_options.presence = 1; req->protocol_configuration_options.data = sess->ue_pco.buffer; req->protocol_configuration_options.len = sess->ue_pco.length; } int i = 0; ogs_list_for_each(&sess->bearer_list, bearer) { ogs_assert(i < OGS_BEARER_PER_UE); /* Bearer Context : EBI */ req->bearer_contexts_to_be_created[i].presence = 1; req->bearer_contexts_to_be_created[i].eps_bearer_id.presence = 1; req->bearer_contexts_to_be_created[i].eps_bearer_id.u8 = bearer->ebi; if (create_action == OGS_GTP_CREATE_IN_PATH_SWITCH_REQUEST) { /* Data Plane(DL) : ENB-S1U */ memset(&enb_s1u_teid[i], 0, sizeof(ogs_gtp2_f_teid_t)); enb_s1u_teid[i].interface_type = OGS_GTP2_F_TEID_S1_U_ENODEB_GTP_U; enb_s1u_teid[i].teid = htobe32(bearer->enb_s1u_teid); ogs_assert(OGS_OK == ogs_gtp2_ip_to_f_teid( &bearer->enb_s1u_ip, &enb_s1u_teid[i], &enb_s1u_len[i])); req->bearer_contexts_to_be_created[i].s1_u_enodeb_f_teid. presence = 1; req->bearer_contexts_to_be_created[i].s1_u_enodeb_f_teid.data = &enb_s1u_teid[i]; req->bearer_contexts_to_be_created[i].s1_u_enodeb_f_teid.len = enb_s1u_len[i]; } if (create_action == OGS_GTP_CREATE_IN_PATH_SWITCH_REQUEST || create_action == OGS_GTP_CREATE_IN_TRACKING_AREA_UPDATE) { /* Data Plane(DL) : PGW-S5U */ memset(&pgw_s5u_teid[i], 0, sizeof(ogs_gtp2_f_teid_t)); pgw_s5u_teid[i].interface_type = OGS_GTP2_F_TEID_S5_S8_PGW_GTP_U; pgw_s5u_teid[i].teid = htobe32(bearer->pgw_s5u_teid); ogs_assert(OGS_OK == ogs_gtp2_ip_to_f_teid( &bearer->pgw_s5u_ip, &pgw_s5u_teid[i], &pgw_s5u_len[i])); req->bearer_contexts_to_be_created[i].s5_s8_u_sgw_f_teid. presence = 1; req->bearer_contexts_to_be_created[i].s5_s8_u_sgw_f_teid.data = &pgw_s5u_teid[i]; req->bearer_contexts_to_be_created[i].s5_s8_u_sgw_f_teid.len = pgw_s5u_len[i]; } memset(&bearer_qos, 0, sizeof(bearer_qos)); bearer_qos.qci = session->qos.index; bearer_qos.priority_level = session->qos.arp.priority_level; bearer_qos.pre_emption_capability = session->qos.arp.pre_emption_capability; bearer_qos.pre_emption_vulnerability = session->qos.arp.pre_emption_vulnerability; req->bearer_contexts_to_be_created[i].bearer_level_qos.presence = 1; ogs_gtp2_build_bearer_qos( &req->bearer_contexts_to_be_created[i].bearer_level_qos, &bearer_qos, bearer_qos_buf[i], GTP2_BEARER_QOS_LEN); i++; } /* UE Time Zone */ memset(&ue_timezone, 0, sizeof(ue_timezone)); ogs_gettimeofday(&now); ogs_localtime(now.tv_sec, &time_exp); if (time_exp.tm_gmtoff >= 0) { ue_timezone.timezone = OGS_GTP2_TIME_TO_BCD(time_exp.tm_gmtoff / 900); } else { ue_timezone.timezone = OGS_GTP2_TIME_TO_BCD((-time_exp.tm_gmtoff) / 900); ue_timezone.timezone |= 0x08; } /* quarters of an hour */ ue_timezone.daylight_saving_time = OGS_GTP2_UE_TIME_ZONE_NO_ADJUSTMENT_FOR_DAYLIGHT_SAVING_TIME; req->ue_time_zone.presence = 1; req->ue_time_zone.data = &ue_timezone; req->ue_time_zone.len = sizeof(ue_timezone); if (session->charging_characteristics_presence == true) { req->charging_characteristics.presence = 1; req->charging_characteristics.data = session->charging_characteristics; req->charging_characteristics.len = OGS_CHRGCHARS_LEN; } else if (mme_ue->charging_characteristics_presence == true) { req->charging_characteristics.presence = 1; req->charging_characteristics.data = mme_ue->charging_characteristics; req->charging_characteristics.len = OGS_CHRGCHARS_LEN; } gtp_message.h.type = type; return ogs_gtp2_build_msg(>p_message); } ogs_pkbuf_t *mme_s11_build_modify_bearer_request( uint8_t type, mme_ue_t *mme_ue, int uli_presence) { ogs_gtp2_message_t gtp_message; ogs_gtp2_modify_bearer_request_t *req = NULL; ogs_gtp2_f_teid_t enb_s1u_teid[OGS_BEARER_PER_UE]; int enb_s1u_len[OGS_BEARER_PER_UE], i; ogs_gtp2_uli_t uli; char uli_buf[OGS_GTP2_MAX_ULI_LEN]; ogs_gtp2_indication_t indication; sgw_ue_t *sgw_ue = NULL; mme_bearer_t *bearer = NULL; ogs_debug("Modifty Bearer Request"); ogs_assert(mme_ue); sgw_ue = mme_ue->sgw_ue; ogs_assert(sgw_ue); ogs_assert(ogs_list_count(&mme_ue->bearer_to_modify_list)); /* Initialize message */ memset(>p_message, 0, sizeof(ogs_gtp2_message_t)); req = >p_message.modify_bearer_request; ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", mme_ue->mme_s11_teid, sgw_ue->sgw_s11_teid); i = 0; ogs_list_for_each_entry( &mme_ue->bearer_to_modify_list, bearer, to_modify_node) { ogs_assert(i < OGS_BEARER_PER_UE); ogs_debug(" ENB_S1U_TEID[%d] SGW_S1U_TEID[%d]", bearer->enb_s1u_teid, bearer->sgw_s1u_teid); /* Bearer Context : EBI */ req->bearer_contexts_to_be_modified[i].presence = 1; req->bearer_contexts_to_be_modified[i].eps_bearer_id.presence = 1; req->bearer_contexts_to_be_modified[i].eps_bearer_id.u8 = bearer->ebi; /* Data Plane(DL) : ENB-S1U */ memset(&enb_s1u_teid[i], 0, sizeof(ogs_gtp2_f_teid_t)); enb_s1u_teid[i].interface_type = OGS_GTP2_F_TEID_S1_U_ENODEB_GTP_U; enb_s1u_teid[i].teid = htobe32(bearer->enb_s1u_teid); ogs_assert(OGS_OK == ogs_gtp2_ip_to_f_teid( &bearer->enb_s1u_ip, &enb_s1u_teid[i], &enb_s1u_len[i])); req->bearer_contexts_to_be_modified[i].s1_u_enodeb_f_teid.presence = 1; req->bearer_contexts_to_be_modified[i].s1_u_enodeb_f_teid.data = &enb_s1u_teid[i]; req->bearer_contexts_to_be_modified[i].s1_u_enodeb_f_teid.len = enb_s1u_len[i]; i++; } /* Indication */ memset(&indication, 0, sizeof(ogs_gtp2_indication_t)); ogs_list_for_each_entry( &mme_ue->bearer_to_modify_list, bearer, to_modify_node) { mme_sess_t *sess = bearer->sess; ogs_assert(sess); if (sess->request_type.value == OGS_NAS_EPS_REQUEST_TYPE_HANDOVER) { indication.handover_indication = 1; req->indication_flags.presence = 1; req->indication_flags.data = &indication; req->indication_flags.len = sizeof(ogs_gtp2_indication_t); break; } } /* User Location Information(ULI) */ if (uli_presence) { memset(&uli, 0, sizeof(ogs_gtp2_uli_t)); uli.flags.e_cgi = 1; uli.flags.tai = 1; ogs_nas_from_plmn_id(&uli.tai.nas_plmn_id, &mme_ue->tai.plmn_id); uli.tai.tac = mme_ue->tai.tac; ogs_nas_from_plmn_id(&uli.e_cgi.nas_plmn_id, &mme_ue->e_cgi.plmn_id); uli.e_cgi.cell_id = mme_ue->e_cgi.cell_id; req->user_location_information.presence = 1; ogs_gtp2_build_uli(&req->user_location_information, &uli, uli_buf, OGS_GTP2_MAX_ULI_LEN); } /* * 7.2.7 Modify Bearer Request * * Table 7.2.7-1: Information Elements in a Modify Bearer Request * * Delay Downlink Packet Nofication Request : Delay Value * * This IE shall be sent on the S11 interface for a UE triggered * Packet Notification Service Request and UE initiated Connection * Resume Request procedures. It shall contain the delay the SGW * shall apply between receiving downlink data and sending Downlink * Data Notification for all UEs served by that MME * (see clause 5.3.4.2 of 3GPP TS 23.401 [3]). */ if (mme_ue->nas_eps.type == MME_EPS_TYPE_SERVICE_REQUEST) { req->delay_downlink_packet_notification_request.presence = 1; req->delay_downlink_packet_notification_request.u8 = 0; } gtp_message.h.type = type; return ogs_gtp2_build_msg(>p_message); } ogs_pkbuf_t *mme_s11_build_delete_session_request( uint8_t type, mme_sess_t *sess, int action) { ogs_gtp2_message_t gtp_message; ogs_gtp2_delete_session_request_t *req = >p_message.delete_session_request; ogs_gtp2_uli_t uli; char uli_buf[OGS_GTP2_MAX_ULI_LEN]; ogs_gtp2_indication_t indication; mme_bearer_t *bearer = NULL; mme_ue_t *mme_ue = NULL; sgw_ue_t *sgw_ue = NULL; ogs_assert(sess); mme_ue = sess->mme_ue; ogs_assert(mme_ue); sgw_ue = mme_ue->sgw_ue; ogs_assert(sgw_ue); bearer = mme_default_bearer_in_sess(sess); ogs_assert(bearer); ogs_debug("Delete Session Request"); ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", mme_ue->mme_s11_teid, sgw_ue->sgw_s11_teid); memset(>p_message, 0, sizeof(ogs_gtp2_message_t)); req->linked_eps_bearer_id.presence = 1; req->linked_eps_bearer_id.u8 = bearer->ebi; /* User Location Information(ULI) */ memset(&uli, 0, sizeof(ogs_gtp2_uli_t)); uli.flags.e_cgi = 1; uli.flags.tai = 1; ogs_nas_from_plmn_id(&uli.tai.nas_plmn_id, &mme_ue->tai.plmn_id); uli.tai.tac = mme_ue->tai.tac; ogs_nas_from_plmn_id(&uli.e_cgi.nas_plmn_id, &mme_ue->e_cgi.plmn_id); uli.e_cgi.cell_id = mme_ue->e_cgi.cell_id; req->user_location_information.presence = 1; ogs_gtp2_build_uli(&req->user_location_information, &uli, uli_buf, OGS_GTP2_MAX_ULI_LEN); memset(&indication, 0, sizeof(ogs_gtp2_indication_t)); if (action == OGS_GTP_DELETE_IN_PATH_SWITCH_REQUEST) { indication.scope_indication = 1; } else { indication.operation_indication = 1; } req->indication_flags.presence = 1; req->indication_flags.data = &indication; req->indication_flags.len = sizeof(ogs_gtp2_indication_t); gtp_message.h.type = type; return ogs_gtp2_build_msg(>p_message); } ogs_pkbuf_t *mme_s11_build_create_bearer_response( uint8_t type, mme_bearer_t *bearer, uint8_t cause_value) { int rv; ogs_gtp2_message_t gtp_message; ogs_gtp2_create_bearer_response_t *rsp = >p_message.create_bearer_response; ogs_gtp2_cause_t cause; ogs_gtp2_f_teid_t enb_s1u_teid, sgw_s1u_teid; int len; ogs_gtp2_uli_t uli; char uli_buf[OGS_GTP2_MAX_ULI_LEN]; ogs_gtp2_ue_timezone_t ue_timezone; struct timeval now; struct tm time_exp; mme_ue_t *mme_ue = NULL; sgw_ue_t *sgw_ue = NULL; ogs_assert(bearer); mme_ue = bearer->mme_ue; ogs_assert(mme_ue); sgw_ue = mme_ue->sgw_ue; ogs_assert(sgw_ue); ogs_debug("Create Bearer Response"); ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", mme_ue->mme_s11_teid, sgw_ue->sgw_s11_teid); memset(>p_message, 0, sizeof(ogs_gtp2_message_t)); /* Set Cause */ memset(&cause, 0, sizeof(cause)); cause.value = cause_value; rsp->cause.presence = 1; rsp->cause.len = sizeof(cause); rsp->cause.data = &cause; if (cause_value == OGS_GTP2_CAUSE_REQUEST_ACCEPTED) { /* Bearer Context : EBI */ rsp->bearer_contexts.presence = 1; rsp->bearer_contexts.eps_bearer_id.presence = 1; rsp->bearer_contexts.eps_bearer_id.u8 = bearer->ebi; /* Data Plane(DL) : ENB-S1U */ memset(&enb_s1u_teid, 0, sizeof(ogs_gtp2_f_teid_t)); enb_s1u_teid.interface_type = OGS_GTP2_F_TEID_S1_U_ENODEB_GTP_U; enb_s1u_teid.teid = htobe32(bearer->enb_s1u_teid); rv = ogs_gtp2_ip_to_f_teid(&bearer->enb_s1u_ip, &enb_s1u_teid, &len); if (rv != OGS_OK) { ogs_error("ogs_gtp2_ip_to_f_teid() failed"); return NULL; } rsp->bearer_contexts.s1_u_enodeb_f_teid.presence = 1; rsp->bearer_contexts.s1_u_enodeb_f_teid.data = &enb_s1u_teid; rsp->bearer_contexts.s1_u_enodeb_f_teid.len = len; /* Data Plane(UL) : SGW-S1U */ memset(&sgw_s1u_teid, 0, sizeof(ogs_gtp2_f_teid_t)); sgw_s1u_teid.interface_type = OGS_GTP2_F_TEID_S1_U_SGW_GTP_U; sgw_s1u_teid.teid = htobe32(bearer->sgw_s1u_teid); rv = ogs_gtp2_ip_to_f_teid(&bearer->sgw_s1u_ip, &sgw_s1u_teid, &len); if (rv != OGS_OK) { ogs_error("ogs_gtp2_ip_to_f_teid() failed"); return NULL; } rsp->bearer_contexts.s4_u_sgsn_f_teid.presence = 1; rsp->bearer_contexts.s4_u_sgsn_f_teid.data = &sgw_s1u_teid; rsp->bearer_contexts.s4_u_sgsn_f_teid.len = OGS_GTP2_F_TEID_IPV4_LEN; /* Bearer Context : Cause */ rsp->bearer_contexts.cause.presence = 1; rsp->bearer_contexts.cause.len = sizeof(cause); rsp->bearer_contexts.cause.data = &cause; } /* User Location Information(ULI) */ memset(&uli, 0, sizeof(ogs_gtp2_uli_t)); uli.flags.e_cgi = 1; uli.flags.tai = 1; ogs_nas_from_plmn_id(&uli.tai.nas_plmn_id, &mme_ue->tai.plmn_id); uli.tai.tac = mme_ue->tai.tac; ogs_nas_from_plmn_id(&uli.e_cgi.nas_plmn_id, &mme_ue->e_cgi.plmn_id); uli.e_cgi.cell_id = mme_ue->e_cgi.cell_id; rsp->user_location_information.presence = 1; ogs_gtp2_build_uli(&rsp->user_location_information, &uli, uli_buf, OGS_GTP2_MAX_ULI_LEN); /* UE Time Zone */ memset(&ue_timezone, 0, sizeof(ue_timezone)); ogs_gettimeofday(&now); ogs_localtime(now.tv_sec, &time_exp); if (time_exp.tm_gmtoff >= 0) { ue_timezone.timezone = OGS_GTP2_TIME_TO_BCD(time_exp.tm_gmtoff / 900); } else { ue_timezone.timezone = OGS_GTP2_TIME_TO_BCD((-time_exp.tm_gmtoff) / 900); ue_timezone.timezone |= 0x08; } ue_timezone.daylight_saving_time = OGS_GTP2_UE_TIME_ZONE_NO_ADJUSTMENT_FOR_DAYLIGHT_SAVING_TIME; rsp->ue_time_zone.presence = 1; rsp->ue_time_zone.data = &ue_timezone; rsp->ue_time_zone.len = sizeof(ue_timezone); gtp_message.h.type = type; return ogs_gtp2_build_msg(>p_message); } ogs_pkbuf_t *mme_s11_build_update_bearer_response( uint8_t type, mme_bearer_t *bearer, uint8_t cause_value) { ogs_gtp2_message_t gtp_message; ogs_gtp2_update_bearer_response_t *rsp = >p_message.update_bearer_response; ogs_gtp2_cause_t cause; ogs_gtp2_uli_t uli; char uli_buf[OGS_GTP2_MAX_ULI_LEN]; ogs_gtp2_ue_timezone_t ue_timezone; struct timeval now; struct tm time_exp; mme_ue_t *mme_ue = NULL; sgw_ue_t *sgw_ue = NULL; ogs_assert(bearer); mme_ue = bearer->mme_ue; ogs_assert(mme_ue); sgw_ue = mme_ue->sgw_ue; ogs_assert(sgw_ue); ogs_debug("Update Bearer Response"); ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", mme_ue->mme_s11_teid, sgw_ue->sgw_s11_teid); memset(>p_message, 0, sizeof(ogs_gtp2_message_t)); /* Set Cause */ memset(&cause, 0, sizeof(cause)); cause.value = cause_value; rsp->cause.presence = 1; rsp->cause.len = sizeof(cause); rsp->cause.data = &cause; if (cause_value == OGS_GTP2_CAUSE_REQUEST_ACCEPTED) { /* Bearer Context : EBI */ rsp->bearer_contexts.presence = 1; rsp->bearer_contexts.eps_bearer_id.presence = 1; rsp->bearer_contexts.eps_bearer_id.u8 = bearer->ebi; /* Bearer Context : Cause */ rsp->bearer_contexts.cause.presence = 1; rsp->bearer_contexts.cause.len = sizeof(cause); rsp->bearer_contexts.cause.data = &cause; } /* User Location Information(ULI) */ memset(&uli, 0, sizeof(ogs_gtp2_uli_t)); uli.flags.e_cgi = 1; uli.flags.tai = 1; ogs_nas_from_plmn_id(&uli.tai.nas_plmn_id, &mme_ue->tai.plmn_id); uli.tai.tac = mme_ue->tai.tac; ogs_nas_from_plmn_id(&uli.e_cgi.nas_plmn_id, &mme_ue->e_cgi.plmn_id); uli.e_cgi.cell_id = mme_ue->e_cgi.cell_id; rsp->user_location_information.presence = 1; ogs_gtp2_build_uli(&rsp->user_location_information, &uli, uli_buf, OGS_GTP2_MAX_ULI_LEN); /* UE Time Zone */ memset(&ue_timezone, 0, sizeof(ue_timezone)); ogs_gettimeofday(&now); ogs_localtime(now.tv_sec, &time_exp); if (time_exp.tm_gmtoff >= 0) { ue_timezone.timezone = OGS_GTP2_TIME_TO_BCD(time_exp.tm_gmtoff / 900); } else { ue_timezone.timezone = OGS_GTP2_TIME_TO_BCD((-time_exp.tm_gmtoff) / 900); ue_timezone.timezone |= 0x08; } ue_timezone.daylight_saving_time = OGS_GTP2_UE_TIME_ZONE_NO_ADJUSTMENT_FOR_DAYLIGHT_SAVING_TIME; rsp->ue_time_zone.presence = 1; rsp->ue_time_zone.data = &ue_timezone; rsp->ue_time_zone.len = sizeof(ue_timezone); gtp_message.h.type = type; return ogs_gtp2_build_msg(>p_message); } ogs_pkbuf_t *mme_s11_build_delete_bearer_response( uint8_t type, mme_bearer_t *bearer, uint8_t cause_value) { ogs_gtp2_message_t gtp_message; ogs_gtp2_delete_bearer_response_t *rsp = >p_message.delete_bearer_response; ogs_gtp2_cause_t cause; ogs_gtp2_uli_t uli; char uli_buf[OGS_GTP2_MAX_ULI_LEN]; ogs_gtp2_ue_timezone_t ue_timezone; struct timeval now; struct tm time_exp; mme_ue_t *mme_ue = NULL; sgw_ue_t *sgw_ue = NULL; ogs_assert(bearer); mme_ue = bearer->mme_ue; ogs_assert(mme_ue); sgw_ue = mme_ue->sgw_ue; ogs_assert(sgw_ue); ogs_debug("Delete Bearer Response"); ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", mme_ue->mme_s11_teid, sgw_ue->sgw_s11_teid); memset(>p_message, 0, sizeof(ogs_gtp2_message_t)); /* Set Cause */ memset(&cause, 0, sizeof(cause)); cause.value = cause_value; rsp->cause.presence = 1; rsp->cause.len = sizeof(cause); rsp->cause.data = &cause; if (cause_value == OGS_GTP2_CAUSE_REQUEST_ACCEPTED) { mme_bearer_t *linked_bearer = mme_linked_bearer(bearer); ogs_assert(linked_bearer); if (bearer->ebi == linked_bearer->ebi) { /* * << Linked EPS Bearer ID >> * * 1. SMF sends Delete Bearer Request(DEFAULT BEARER) to SGW/MME. * 2. MME sends Delete Bearer Response to SGW/SMF. * * OR * * 1. SMF sends Delete Bearer Request(DEFAULT BEARER) to ePDG. * 2. ePDG sends Delete Bearer Response(DEFAULT BEARER) to SMF. */ rsp->linked_eps_bearer_id.presence = 1; rsp->linked_eps_bearer_id.u8 = bearer->ebi; } else { /* * << EPS Bearer IDs >> * * 1. MME sends Bearer Resource Command to SGW/SMF. * 2. SMF sends Delete Bearer Request(DEDICATED BEARER) to SGW/MME. * 3. MME sends Delete Bearer Response(DEDICATED BEARER) to SGW/SMF. * * OR * * 1. SMF sends Delete Bearer Request(DEDICATED BEARER) to SGW/MME. * 2. MME sends Delete Bearer Response(DEDICATED BEARER) to SGW/SMF. */ /* Bearer Context : EBI */ rsp->bearer_contexts.presence = 1; rsp->bearer_contexts.eps_bearer_id.presence = 1; rsp->bearer_contexts.eps_bearer_id.u8 = bearer->ebi; /* Bearer Context : Cause */ rsp->bearer_contexts.cause.presence = 1; rsp->bearer_contexts.cause.len = sizeof(cause); rsp->bearer_contexts.cause.data = &cause; } } /* User Location Information(ULI) */ memset(&uli, 0, sizeof(ogs_gtp2_uli_t)); uli.flags.e_cgi = 1; uli.flags.tai = 1; ogs_nas_from_plmn_id(&uli.tai.nas_plmn_id, &mme_ue->tai.plmn_id); uli.tai.tac = mme_ue->tai.tac; ogs_nas_from_plmn_id(&uli.e_cgi.nas_plmn_id, &mme_ue->e_cgi.plmn_id); uli.e_cgi.cell_id = mme_ue->e_cgi.cell_id; rsp->user_location_information.presence = 1; ogs_gtp2_build_uli(&rsp->user_location_information, &uli, uli_buf, OGS_GTP2_MAX_ULI_LEN); /* UE Time Zone */ memset(&ue_timezone, 0, sizeof(ue_timezone)); ogs_gettimeofday(&now); ogs_localtime(now.tv_sec, &time_exp); if (time_exp.tm_gmtoff >= 0) { ue_timezone.timezone = OGS_GTP2_TIME_TO_BCD(time_exp.tm_gmtoff / 900); } else { ue_timezone.timezone = OGS_GTP2_TIME_TO_BCD((-time_exp.tm_gmtoff) / 900); ue_timezone.timezone |= 0x08; } ue_timezone.daylight_saving_time = OGS_GTP2_UE_TIME_ZONE_NO_ADJUSTMENT_FOR_DAYLIGHT_SAVING_TIME; rsp->ue_time_zone.presence = 1; rsp->ue_time_zone.data = &ue_timezone; rsp->ue_time_zone.len = sizeof(ue_timezone); gtp_message.h.type = type; return ogs_gtp2_build_msg(>p_message); } ogs_pkbuf_t *mme_s11_build_release_access_bearers_request(uint8_t type) { ogs_gtp2_message_t gtp_message; ogs_gtp2_release_access_bearers_request_t *req = >p_message.release_access_bearers_request; ogs_debug("Release Access Bearers Request"); memset(>p_message, 0, sizeof(ogs_gtp2_message_t)); req->originating_node.presence = 1; req->originating_node.u8 = OGS_GTP2_NODE_TYPE_MME; gtp_message.h.type = type; return ogs_gtp2_build_msg(>p_message); } ogs_pkbuf_t *mme_s11_build_downlink_data_notification_ack( uint8_t type, uint8_t cause_value) { ogs_gtp2_message_t gtp_message; ogs_gtp2_downlink_data_notification_acknowledge_t *ack = >p_message.downlink_data_notification_acknowledge; ogs_gtp2_cause_t cause; ogs_debug("Downlink Data Notification Ackknowledge"); memset(>p_message, 0, sizeof(ogs_gtp2_message_t)); memset(&cause, 0, sizeof(cause)); cause.value = cause_value; ack->cause.presence = 1; ack->cause.data = &cause; ack->cause.len = sizeof(cause); ack->data_notification_delay.presence = 1; ack->data_notification_delay.u8 = 0; gtp_message.h.type = type; return ogs_gtp2_build_msg(>p_message); } ogs_pkbuf_t *mme_s11_build_create_indirect_data_forwarding_tunnel_request( uint8_t type, mme_ue_t *mme_ue) { int rv; int i; mme_sess_t *sess = NULL; mme_bearer_t *bearer = NULL; sgw_ue_t *sgw_ue = NULL; ogs_gtp2_message_t gtp_message; ogs_gtp2_create_indirect_data_forwarding_tunnel_request_t *req = >p_message.create_indirect_data_forwarding_tunnel_request; ogs_gtp2_f_teid_t dl_teid[OGS_GTP2_MAX_INDIRECT_TUNNEL]; ogs_gtp2_f_teid_t ul_teid[OGS_GTP2_MAX_INDIRECT_TUNNEL]; int len; ogs_assert(mme_ue); sgw_ue = mme_ue->sgw_ue; ogs_assert(sgw_ue); ogs_debug("Create Indirect Data Forwarding Tunnel Request"); ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", mme_ue->mme_s11_teid, sgw_ue->sgw_s11_teid); memset(>p_message, 0, sizeof(ogs_gtp2_message_t)); i = 0; sess = mme_sess_first(mme_ue); while (sess != NULL) { bearer = mme_bearer_first(sess); while (bearer != NULL) { if (MME_HAVE_ENB_DL_INDIRECT_TUNNEL(bearer)) { memset(&dl_teid[i], 0, sizeof(ogs_gtp2_f_teid_t)); dl_teid[i].interface_type = OGS_GTP2_F_TEID_ENODEB_GTP_U_FOR_DL_DATA_FORWARDING; dl_teid[i].teid = htobe32(bearer->enb_dl_teid); rv = ogs_gtp2_ip_to_f_teid( &bearer->enb_dl_ip, &dl_teid[i], &len); if (rv != OGS_OK) { ogs_error("ogs_gtp2_ip_to_f_teid() failed"); return NULL; } req->bearer_contexts[i].s1_u_enodeb_f_teid.presence = 1; req->bearer_contexts[i].s1_u_enodeb_f_teid.data = &dl_teid[i]; req->bearer_contexts[i].s1_u_enodeb_f_teid.len = len; } if (MME_HAVE_ENB_UL_INDIRECT_TUNNEL(bearer)) { memset(&ul_teid[i], 0, sizeof(ogs_gtp2_f_teid_t)); ul_teid[i].interface_type = OGS_GTP2_F_TEID_ENODEB_GTP_U_FOR_UL_DATA_FORWARDING; ul_teid[i].teid = htobe32(bearer->enb_ul_teid); rv = ogs_gtp2_ip_to_f_teid( &bearer->enb_ul_ip, &ul_teid[i], &len); if (rv != OGS_OK) { ogs_error("ogs_gtp2_ip_to_f_teid() failed"); return NULL; } req->bearer_contexts[i].s12_rnc_f_teid.presence = 1; req->bearer_contexts[i].s12_rnc_f_teid.data = &ul_teid[i]; req->bearer_contexts[i].s12_rnc_f_teid.len = len; } if (MME_HAVE_ENB_DL_INDIRECT_TUNNEL(bearer) || MME_HAVE_ENB_UL_INDIRECT_TUNNEL(bearer)) { req->bearer_contexts[i].presence = 1; req->bearer_contexts[i].eps_bearer_id.presence = 1; req->bearer_contexts[i].eps_bearer_id.u8 = bearer->ebi; i++; } bearer = mme_bearer_next(bearer); } sess = mme_sess_next(sess); } gtp_message.h.type = type; return ogs_gtp2_build_msg(>p_message); } ogs_pkbuf_t *mme_s11_build_bearer_resource_command( uint8_t type, mme_bearer_t *bearer, ogs_nas_eps_message_t *nas_message) { ogs_gtp2_message_t gtp_message; ogs_gtp2_bearer_resource_command_t *cmd = >p_message.bearer_resource_command; ogs_nas_eps_bearer_resource_allocation_request_t *allocation = NULL; ogs_nas_eps_bearer_resource_modification_request_t *modification = NULL; ogs_nas_eps_quality_of_service_t *qos = NULL; ogs_nas_traffic_flow_aggregate_description_t *tad = NULL; ogs_gtp2_flow_qos_t flow_qos; char flow_qos_buf[GTP2_FLOW_QOS_LEN]; mme_ue_t *mme_ue = NULL; sgw_ue_t *sgw_ue = NULL; mme_sess_t *sess = NULL; mme_bearer_t *linked_bearer = NULL; ogs_assert(bearer); sess = bearer->sess; ogs_assert(sess); mme_ue = sess->mme_ue; ogs_assert(mme_ue); sgw_ue = mme_ue->sgw_ue; ogs_assert(sgw_ue); ogs_debug("Bearer Resource Command"); ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", mme_ue->mme_s11_teid, sgw_ue->sgw_s11_teid); ogs_assert(nas_message); switch (nas_message->esm.h.message_type) { case OGS_NAS_EPS_BEARER_RESOURCE_ALLOCATION_REQUEST: allocation = &nas_message->esm.bearer_resource_allocation_request; qos = &allocation->required_traffic_flow_qos; tad = &allocation->traffic_flow_aggregate; break; case OGS_NAS_EPS_BEARER_RESOURCE_MODIFICATION_REQUEST: modification = &nas_message->esm.bearer_resource_modification_request; if (modification->presencemask & OGS_NAS_EPS_BEARER_RESOURCE_MODIFICATION_REQUEST_REQUIRED_TRAFFIC_FLOW_QOS_PRESENT) { qos = &modification->required_traffic_flow_qos; } tad = &modification->traffic_flow_aggregate; break; default: ogs_error("Invalid NAS ESM Type[%d]", nas_message->esm.h.message_type); return NULL; } linked_bearer = mme_linked_bearer(bearer); ogs_assert(linked_bearer); memset(>p_message, 0, sizeof(ogs_gtp2_message_t)); /* Linked Bearer Context : EBI */ cmd->linked_eps_bearer_id.presence = 1; cmd->linked_eps_bearer_id.u8 = linked_bearer->ebi; /* Procedure Transaction ID(PTI) */ cmd->procedure_transaction_id.presence = 1; cmd->procedure_transaction_id.u8 = sess->pti; /* Flow Quality of Service(QoS) */ if (qos) { memset(&flow_qos, 0, sizeof(flow_qos)); flow_qos.qci = qos->qci; /* Octet 4 * * In UE to network direction: * 00000000 Subscribed maximum bit rate * * In network to UE direction: * 00000000 Reserved */ flow_qos.ul_mbr = qos->ul_mbr == 0 ? bearer->qos.mbr.uplink : ogs_gtp2_qos_to_bps( qos->ul_mbr, qos->ul_mbr_extended, qos->ul_mbr_extended2); flow_qos.dl_mbr = qos->dl_mbr == 0 ? bearer->qos.mbr.downlink : ogs_gtp2_qos_to_bps( qos->dl_mbr, qos->dl_mbr_extended, qos->dl_mbr_extended2); flow_qos.ul_gbr = qos->ul_gbr == 0 ? bearer->qos.gbr.uplink : ogs_gtp2_qos_to_bps( qos->ul_gbr, qos->ul_gbr_extended, qos->ul_gbr_extended2); flow_qos.dl_gbr = qos->dl_gbr == 0 ? bearer->qos.gbr.downlink : ogs_gtp2_qos_to_bps( qos->dl_gbr, qos->dl_gbr_extended, qos->dl_gbr_extended2); ogs_gtp2_build_flow_qos( &cmd->flow_quality_of_service, &flow_qos, flow_qos_buf, GTP2_FLOW_QOS_LEN); cmd->flow_quality_of_service.presence = 1; } /* Traffic Aggregate Description(TAD) */ cmd->traffic_aggregate_description.presence = 1; cmd->traffic_aggregate_description.data = tad->buffer; cmd->traffic_aggregate_description.len = tad->length; switch (nas_message->esm.h.message_type) { case OGS_NAS_EPS_BEARER_RESOURCE_ALLOCATION_REQUEST: break; case OGS_NAS_EPS_BEARER_RESOURCE_MODIFICATION_REQUEST: /* Bearer Context : EBI */ cmd->eps_bearer_id.presence = 1; cmd->eps_bearer_id.u8 = bearer->ebi; break; default: ogs_error("Invalid NAS ESM Type[%d]", nas_message->esm.h.message_type); return NULL; } gtp_message.h.type = type; return ogs_gtp2_build_msg(>p_message); }