/* * 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 "nas-security.h" #include "esm-build.h" #include "mme-sm.h" #undef OGS_LOG_DOMAIN #define OGS_LOG_DOMAIN __esm_log_domain ogs_pkbuf_t *esm_build_pdn_connectivity_reject( mme_sess_t *sess, ogs_nas_esm_cause_t esm_cause, int create_action) { mme_ue_t *mme_ue = NULL; ogs_nas_eps_message_t message; ogs_nas_eps_pdn_connectivity_reject_t *pdn_connectivity_reject = &message.esm.pdn_connectivity_reject; ogs_assert(sess); mme_ue = sess->mme_ue; ogs_assert(mme_ue); ogs_debug("PDN connectivity reject"); ogs_debug(" IMSI[%s] PTI[%d] Cause[%d]", mme_ue->imsi_bcd, sess->pti, esm_cause); memset(&message, 0, sizeof(message)); if (create_action == OGS_GTP_CREATE_IN_ATTACH_REQUEST) { /* Nothing */ } else { message.h.security_header_type = OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED_AND_CIPHERED; message.h.protocol_discriminator = OGS_NAS_PROTOCOL_DISCRIMINATOR_EMM; } message.esm.h.eps_bearer_identity = 0; message.esm.h.protocol_discriminator = OGS_NAS_PROTOCOL_DISCRIMINATOR_ESM; message.esm.h.procedure_transaction_identity = sess->pti; message.esm.h.message_type = OGS_NAS_EPS_PDN_CONNECTIVITY_REJECT; pdn_connectivity_reject->esm_cause = esm_cause; if (create_action == OGS_GTP_CREATE_IN_ATTACH_REQUEST) return ogs_nas_eps_plain_encode(&message); else return nas_eps_security_encode(mme_ue, &message); } ogs_pkbuf_t *esm_build_information_request(mme_bearer_t *bearer) { ogs_nas_eps_message_t message; mme_ue_t *mme_ue = NULL; mme_sess_t *sess = NULL; ogs_assert(bearer); sess = bearer->sess; ogs_assert(sess); mme_ue = bearer->mme_ue; ogs_assert(mme_ue); ogs_debug("ESM information request"); ogs_debug(" IMSI[%s] PTI[%d] EBI[%d]", mme_ue->imsi_bcd, sess->pti, bearer->ebi); memset(&message, 0, sizeof(message)); message.h.security_header_type = OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED_AND_CIPHERED; message.h.protocol_discriminator = OGS_NAS_PROTOCOL_DISCRIMINATOR_EMM; message.esm.h.protocol_discriminator = OGS_NAS_PROTOCOL_DISCRIMINATOR_ESM; message.esm.h.procedure_transaction_identity = sess->pti; message.esm.h.message_type = OGS_NAS_EPS_ESM_INFORMATION_REQUEST; return nas_eps_security_encode(mme_ue, &message); } ogs_pkbuf_t *esm_build_activate_default_bearer_context_request( mme_sess_t *sess, int create_action) { ogs_nas_eps_message_t message; ogs_nas_eps_activate_default_eps_bearer_context_request_t *activate_default_eps_bearer_context_request = &message.esm.activate_default_eps_bearer_context_request; ogs_nas_eps_quality_of_service_t *eps_qos = &activate_default_eps_bearer_context_request->eps_qos; ogs_nas_access_point_name_t *access_point_name = &activate_default_eps_bearer_context_request->access_point_name; ogs_nas_pdn_address_t *pdn_address = &activate_default_eps_bearer_context_request->pdn_address; ogs_nas_apn_aggregate_maximum_bit_rate_t *apn_ambr = &activate_default_eps_bearer_context_request->apn_ambr; ogs_nas_protocol_configuration_options_t *protocol_configuration_options = &activate_default_eps_bearer_context_request ->protocol_configuration_options; ogs_nas_extended_protocol_configuration_options_t *extended_protocol_configuration_options = &activate_default_eps_bearer_context_request ->extended_protocol_configuration_options; mme_ue_t *mme_ue = NULL; mme_bearer_t *bearer = NULL; ogs_session_t *session = NULL; ogs_assert(sess); mme_ue = sess->mme_ue; ogs_assert(mme_ue); session = sess->session; ogs_assert(session); ogs_assert(session->name); bearer = mme_default_bearer_in_sess(sess); ogs_assert(bearer); ogs_assert(mme_bearer_next(bearer) == NULL); ogs_debug("Activate default bearer context request"); ogs_debug(" IMSI[%s] PTI[%d] EBI[%d]", mme_ue->imsi_bcd, sess->pti, bearer->ebi); memset(&message, 0, sizeof(message)); if (create_action == OGS_GTP_CREATE_IN_ATTACH_REQUEST) { /* Nothing */ } else { message.h.security_header_type = OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED_AND_CIPHERED; message.h.protocol_discriminator = OGS_NAS_PROTOCOL_DISCRIMINATOR_EMM; } message.esm.h.eps_bearer_identity = bearer->ebi; message.esm.h.protocol_discriminator = OGS_NAS_PROTOCOL_DISCRIMINATOR_ESM; message.esm.h.procedure_transaction_identity = sess->pti; message.esm.h.message_type = OGS_NAS_EPS_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST; memcpy(&bearer->qos, &session->qos, sizeof(ogs_qos_t)); eps_qos_build(eps_qos, bearer->qos.index, bearer->qos.mbr.downlink, bearer->qos.mbr.uplink, bearer->qos.gbr.downlink, bearer->qos.gbr.uplink); access_point_name->length = strlen(session->name); ogs_cpystrn(access_point_name->apn, session->name, ogs_min(access_point_name->length, OGS_MAX_APN_LEN) + 1); ogs_debug(" APN[%s]", session->name); /* * In TS24.301 V15.6.0 * 6.5.1.3 UE requested PDN connectivity procedure accepted by the network * * If connectivity with the requested PDN is accepted, * but with a restriction of IP version (i.e. both an IPv4 address and * an IPv6 prefix is requested, but only one particular IP version, or * only single IP version bearers are supported/allowed by the network), * ESM cause #50 "PDN type IPv4 only allowed", * #51 "PDN type IPv6 only allowed", or * #52 "single address bearers only allowed", respectively, shall be * included in the ACTIVATE DEFAULT EPS BEARER CONTEXT REQUEST message. */ pdn_address->pdn_type = session->paa.session_type; if (sess->request_type.type == OGS_NAS_EPS_PDN_TYPE_IPV4V6) { if (session->paa.session_type == OGS_PDU_SESSION_TYPE_IPV4) { pdn_address->pdn_type = OGS_PDU_SESSION_TYPE_IPV4; activate_default_eps_bearer_context_request->esm_cause = ESM_CAUSE_PDN_TYPE_IPV4_ONLY_ALLOWED; activate_default_eps_bearer_context_request->presencemask |= OGS_NAS_EPS_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_ESM_CAUSE_PRESENT; } else if (session->paa.session_type == OGS_PDU_SESSION_TYPE_IPV6) { pdn_address->pdn_type = OGS_PDU_SESSION_TYPE_IPV6; activate_default_eps_bearer_context_request->esm_cause = ESM_CAUSE_PDN_TYPE_IPV6_ONLY_ALLOWED; activate_default_eps_bearer_context_request->presencemask |= OGS_NAS_EPS_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_ESM_CAUSE_PRESENT; } } else if (sess->request_type.type == OGS_PDU_SESSION_TYPE_IPV4) { if (session->paa.session_type == OGS_PDU_SESSION_TYPE_IPV6) { pdn_address->pdn_type = OGS_PDU_SESSION_TYPE_IPV6; activate_default_eps_bearer_context_request->esm_cause = ESM_CAUSE_PDN_TYPE_IPV6_ONLY_ALLOWED; activate_default_eps_bearer_context_request->presencemask |= OGS_NAS_EPS_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_ESM_CAUSE_PRESENT; } } else if (sess->request_type.type == OGS_PDU_SESSION_TYPE_IPV6) { if (session->paa.session_type == OGS_PDU_SESSION_TYPE_IPV4) { pdn_address->pdn_type = OGS_PDU_SESSION_TYPE_IPV4; activate_default_eps_bearer_context_request->esm_cause = ESM_CAUSE_PDN_TYPE_IPV4_ONLY_ALLOWED; activate_default_eps_bearer_context_request->presencemask |= OGS_NAS_EPS_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_ESM_CAUSE_PRESENT; } } if (pdn_address->pdn_type == OGS_PDU_SESSION_TYPE_IPV4) { pdn_address->addr = session->paa.addr; pdn_address->length = OGS_NAS_PDU_ADDRESS_IPV4_LEN; ogs_debug(" IPv4"); } else if (pdn_address->pdn_type == OGS_PDU_SESSION_TYPE_IPV6) { memcpy(pdn_address->addr6, session->paa.addr6+(OGS_IPV6_LEN>>1), OGS_IPV6_LEN>>1); pdn_address->length = OGS_NAS_PDU_ADDRESS_IPV6_LEN; ogs_debug(" IPv6"); } else if (pdn_address->pdn_type == OGS_PDU_SESSION_TYPE_IPV4V6) { pdn_address->both.addr = session->paa.both.addr; memcpy(pdn_address->both.addr6, session->paa.both.addr6+(OGS_IPV6_LEN>>1), OGS_IPV6_LEN>>1); pdn_address->length = OGS_NAS_PDU_ADDRESS_IPV4V6_LEN; ogs_debug(" IPv4v6"); } else { ogs_fatal("Unexpected PDN Type %u", pdn_address->pdn_type); ogs_assert_if_reached(); } if (session->ambr.downlink || session->ambr.uplink) { activate_default_eps_bearer_context_request->presencemask |= OGS_NAS_EPS_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_APN_AMBR_PRESENT; apn_ambr_build(apn_ambr, session->ambr.downlink, session->ambr.uplink); } if (sess->pgw_pco.presence && sess->pgw_pco.len && sess->pgw_pco.data) { if (mme_ue->ue_network_capability. extended_protocol_configuration_options) { activate_default_eps_bearer_context_request->presencemask |= OGS_NAS_EPS_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_EXTENDED_PROTOCOL_CONFIGURATION_OPTIONS_PRESENT; extended_protocol_configuration_options->length = sess->pgw_pco.len; extended_protocol_configuration_options->buffer = sess->pgw_pco.data; } else { activate_default_eps_bearer_context_request->presencemask |= OGS_NAS_EPS_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_PROTOCOL_CONFIGURATION_OPTIONS_PRESENT; protocol_configuration_options->length = sess->pgw_pco.len; memcpy(protocol_configuration_options->buffer, sess->pgw_pco.data, protocol_configuration_options->length); } } if (create_action == OGS_GTP_CREATE_IN_ATTACH_REQUEST) return ogs_nas_eps_plain_encode(&message); else return nas_eps_security_encode(mme_ue, &message); } ogs_pkbuf_t *esm_build_activate_dedicated_bearer_context_request( mme_bearer_t *bearer) { mme_ue_t *mme_ue = NULL; mme_bearer_t *linked_bearer = NULL; ogs_nas_eps_message_t message; ogs_nas_eps_activate_dedicated_eps_bearer_context_request_t *activate_dedicated_eps_bearer_context_request = &message.esm.activate_dedicated_eps_bearer_context_request; ogs_nas_linked_eps_bearer_identity_t *linked_ebi = &activate_dedicated_eps_bearer_context_request-> linked_eps_bearer_identity; ogs_nas_eps_quality_of_service_t *eps_qos = &activate_dedicated_eps_bearer_context_request->eps_qos; ogs_nas_traffic_flow_template_t *tft = &activate_dedicated_eps_bearer_context_request->tft; ogs_assert(bearer); mme_ue = bearer->mme_ue; ogs_assert(mme_ue); linked_bearer = mme_linked_bearer(bearer); ogs_assert(linked_bearer); ogs_debug("Activate dedicated bearer context request"); ogs_debug(" IMSI[%s] EBI[%d] Linked-EBI[%d]", mme_ue->imsi_bcd, bearer->ebi, linked_bearer->ebi); memset(&message, 0, sizeof(message)); message.h.security_header_type = OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED_AND_CIPHERED; message.h.protocol_discriminator = OGS_NAS_PROTOCOL_DISCRIMINATOR_EMM; message.esm.h.eps_bearer_identity = bearer->ebi; message.esm.h.protocol_discriminator = OGS_NAS_PROTOCOL_DISCRIMINATOR_ESM; message.esm.h.procedure_transaction_identity = 0; message.esm.h.message_type = OGS_NAS_EPS_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_REQUEST; linked_ebi->eps_bearer_identity = linked_bearer->ebi; eps_qos_build(eps_qos, bearer->qos.index, bearer->qos.mbr.downlink, bearer->qos.mbr.uplink, bearer->qos.gbr.downlink, bearer->qos.gbr.uplink); tft->length = bearer->tft.len; ogs_assert(tft->length); ogs_assert(bearer->tft.data); memcpy(tft->buffer, bearer->tft.data, tft->length); return nas_eps_security_encode(mme_ue, &message); } ogs_pkbuf_t *esm_build_modify_bearer_context_request( mme_bearer_t *bearer, int qos_presence, int tft_presence) { mme_ue_t *mme_ue = NULL; mme_sess_t *sess = NULL; ogs_nas_eps_message_t message; ogs_nas_eps_modify_eps_bearer_context_request_t *modify_eps_bearer_context_request = &message.esm.modify_eps_bearer_context_request; ogs_nas_eps_quality_of_service_t *new_eps_qos = &modify_eps_bearer_context_request->new_eps_qos; ogs_nas_traffic_flow_template_t *tft = &modify_eps_bearer_context_request->tft; ogs_assert(bearer); sess = bearer->sess; ogs_assert(sess); mme_ue = bearer->mme_ue; ogs_assert(mme_ue); ogs_debug("Modify bearer context request"); ogs_debug(" IMSI[%s] PTI[%d] EBI[%d]", mme_ue->imsi_bcd, sess->pti, bearer->ebi); memset(&message, 0, sizeof(message)); message.h.security_header_type = OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED_AND_CIPHERED; message.h.protocol_discriminator = OGS_NAS_PROTOCOL_DISCRIMINATOR_EMM; message.esm.h.eps_bearer_identity = bearer->ebi; message.esm.h.protocol_discriminator = OGS_NAS_PROTOCOL_DISCRIMINATOR_ESM; message.esm.h.procedure_transaction_identity = sess->pti; message.esm.h.message_type = OGS_NAS_EPS_MODIFY_EPS_BEARER_CONTEXT_REQUEST; if (qos_presence == 1) { modify_eps_bearer_context_request->presencemask |= OGS_NAS_EPS_MODIFY_EPS_BEARER_CONTEXT_REQUEST_NEW_EPS_QOS_PRESENT; eps_qos_build(new_eps_qos, bearer->qos.index, bearer->qos.mbr.downlink, bearer->qos.mbr.uplink, bearer->qos.gbr.downlink, bearer->qos.gbr.uplink); } if (tft_presence == 1) { modify_eps_bearer_context_request->presencemask |= OGS_NAS_EPS_MODIFY_EPS_BEARER_CONTEXT_REQUEST_TFT_PRESENT; tft->length = bearer->tft.len; ogs_assert(tft->length); ogs_assert(bearer->tft.data); memcpy(tft->buffer, bearer->tft.data, tft->length); } return nas_eps_security_encode(mme_ue, &message); } ogs_pkbuf_t *esm_build_deactivate_bearer_context_request( mme_bearer_t *bearer, ogs_nas_esm_cause_t esm_cause) { mme_ue_t *mme_ue = NULL; mme_sess_t *sess = NULL; ogs_nas_eps_message_t message; ogs_nas_eps_deactivate_eps_bearer_context_request_t *deactivate_eps_bearer_context_request = &message.esm.deactivate_eps_bearer_context_request; ogs_assert(bearer); sess = bearer->sess; ogs_assert(sess); mme_ue = bearer->mme_ue; ogs_assert(mme_ue); ogs_debug("Deactivate bearer context request"); ogs_debug(" IMSI[%s] PTI[%d] EBI[%d]", mme_ue->imsi_bcd, sess->pti, bearer->ebi); ogs_debug(" Cause[%d]", esm_cause); memset(&message, 0, sizeof(message)); message.h.security_header_type = OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED_AND_CIPHERED; message.h.protocol_discriminator = OGS_NAS_PROTOCOL_DISCRIMINATOR_EMM; message.esm.h.eps_bearer_identity = bearer->ebi; message.esm.h.protocol_discriminator = OGS_NAS_PROTOCOL_DISCRIMINATOR_ESM; message.esm.h.procedure_transaction_identity = sess->pti; message.esm.h.message_type = OGS_NAS_EPS_DEACTIVATE_EPS_BEARER_CONTEXT_REQUEST; deactivate_eps_bearer_context_request->esm_cause = esm_cause; return nas_eps_security_encode(mme_ue, &message); } ogs_pkbuf_t *esm_build_bearer_resource_allocation_reject( mme_ue_t *mme_ue, uint8_t pti, ogs_nas_esm_cause_t esm_cause) { ogs_nas_eps_message_t message; ogs_nas_eps_bearer_resource_allocation_reject_t *bearer_resource_allocation_reject = &message.esm.bearer_resource_allocation_reject; ogs_assert(mme_ue); ogs_assert(pti != OGS_NAS_PROCEDURE_TRANSACTION_IDENTITY_UNASSIGNED); ogs_debug("Bearer resource allocation reject"); ogs_debug(" IMSI[%s] PTI[%d] Cause[%d]", mme_ue->imsi_bcd, pti, esm_cause); memset(&message, 0, sizeof(message)); message.h.security_header_type = OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED_AND_CIPHERED; message.h.protocol_discriminator = OGS_NAS_PROTOCOL_DISCRIMINATOR_EMM; message.esm.h.eps_bearer_identity = 0; message.esm.h.protocol_discriminator = OGS_NAS_PROTOCOL_DISCRIMINATOR_ESM; message.esm.h.procedure_transaction_identity = pti; message.esm.h.message_type = OGS_NAS_EPS_BEARER_RESOURCE_ALLOCATION_REJECT; bearer_resource_allocation_reject->esm_cause = esm_cause; return nas_eps_security_encode(mme_ue, &message); } ogs_pkbuf_t *esm_build_bearer_resource_modification_reject( mme_ue_t *mme_ue, uint8_t pti, ogs_nas_esm_cause_t esm_cause) { ogs_nas_eps_message_t message; ogs_nas_eps_bearer_resource_modification_reject_t *bearer_resource_modification_reject = &message.esm.bearer_resource_modification_reject; ogs_assert(mme_ue); ogs_assert(pti != OGS_NAS_PROCEDURE_TRANSACTION_IDENTITY_UNASSIGNED); ogs_debug("Bearer resource modification reject"); ogs_debug(" IMSI[%s] PTI[%d] Cause[%d]", mme_ue->imsi_bcd, pti, esm_cause); memset(&message, 0, sizeof(message)); message.h.security_header_type = OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED_AND_CIPHERED; message.h.protocol_discriminator = OGS_NAS_PROTOCOL_DISCRIMINATOR_EMM; message.esm.h.eps_bearer_identity = 0; message.esm.h.protocol_discriminator = OGS_NAS_PROTOCOL_DISCRIMINATOR_ESM; message.esm.h.procedure_transaction_identity = pti; message.esm.h.message_type = OGS_NAS_EPS_BEARER_RESOURCE_MODIFICATION_REJECT; bearer_resource_modification_reject->esm_cause = esm_cause; return nas_eps_security_encode(mme_ue, &message); }