srsLTE/srsepc/src/mme/s1ap_nas_transport.cc

380 lines
17 KiB
C++

/**
* Copyright 2013-2022 Software Radio Systems Limited
*
* This file is part of srsRAN.
*
* srsRAN 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.
*
* srsRAN 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 Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "srsepc/hdr/mme/s1ap_nas_transport.h"
#include "srsepc/hdr/mme/mme.h"
#include "srsepc/hdr/mme/s1ap.h"
#include "srsran/common/int_helpers.h"
#include "srsran/common/liblte_security.h"
#include "srsran/common/security.h"
#include <cmath>
#include <inttypes.h> // for printing uint64_t
namespace srsepc {
s1ap_nas_transport* s1ap_nas_transport::m_instance = NULL;
pthread_mutex_t s1ap_nas_transport_instance_mutex = PTHREAD_MUTEX_INITIALIZER;
s1ap_nas_transport::s1ap_nas_transport()
{
return;
}
s1ap_nas_transport::~s1ap_nas_transport()
{
return;
}
s1ap_nas_transport* s1ap_nas_transport::get_instance(void)
{
pthread_mutex_lock(&s1ap_nas_transport_instance_mutex);
if (NULL == m_instance) {
m_instance = new s1ap_nas_transport();
}
pthread_mutex_unlock(&s1ap_nas_transport_instance_mutex);
return (m_instance);
}
void s1ap_nas_transport::cleanup(void)
{
pthread_mutex_lock(&s1ap_nas_transport_instance_mutex);
if (NULL != m_instance) {
delete m_instance;
m_instance = NULL;
}
pthread_mutex_unlock(&s1ap_nas_transport_instance_mutex);
}
void s1ap_nas_transport::init()
{
m_s1ap = s1ap::get_instance();
// Init NAS args
m_nas_init.mcc = m_s1ap->m_s1ap_args.mcc;
m_nas_init.mnc = m_s1ap->m_s1ap_args.mnc;
m_nas_init.mme_code = m_s1ap->m_s1ap_args.mme_code;
m_nas_init.mme_group = m_s1ap->m_s1ap_args.mme_group;
m_nas_init.tac = m_s1ap->m_s1ap_args.tac;
m_nas_init.apn = m_s1ap->m_s1ap_args.mme_apn;
m_nas_init.dns = m_s1ap->m_s1ap_args.dns_addr;
m_nas_init.full_net_name = m_s1ap->m_s1ap_args.full_net_name;
m_nas_init.short_net_name = m_s1ap->m_s1ap_args.short_net_name;
m_nas_init.paging_timer = m_s1ap->m_s1ap_args.paging_timer;
m_nas_init.integ_algo = m_s1ap->m_s1ap_args.integrity_algo;
m_nas_init.cipher_algo = m_s1ap->m_s1ap_args.encryption_algo;
m_nas_init.request_imeisv = m_s1ap->m_s1ap_args.request_imeisv;
m_nas_init.lac = m_s1ap->m_s1ap_args.lac;
// Init NAS interface
m_nas_if.s1ap = s1ap::get_instance();
m_nas_if.gtpc = mme_gtpc::get_instance();
m_nas_if.hss = hss::get_instance();
m_nas_if.mme = mme::get_instance();
}
bool s1ap_nas_transport::handle_initial_ue_message(const asn1::s1ap::init_ue_msg_s& init_ue,
struct sctp_sndrcvinfo* enb_sri)
{
bool err, mac_valid;
uint8_t pd, msg_type, sec_hdr_type;
srsran::unique_byte_buffer_t nas_msg = srsran::make_byte_buffer();
memcpy(nas_msg->msg, init_ue->nas_pdu.value.data(), init_ue->nas_pdu.value.size());
nas_msg->N_bytes = init_ue->nas_pdu.value.size();
uint64_t imsi = 0;
uint32_t m_tmsi = 0;
uint32_t enb_ue_s1ap_id = init_ue->enb_ue_s1ap_id.value.value;
liblte_mme_parse_msg_header((LIBLTE_BYTE_MSG_STRUCT*)nas_msg.get(), &pd, &msg_type);
srsran::console("Initial UE message: %s\n", liblte_nas_msg_type_to_string(msg_type));
m_logger.info("Initial UE message: %s", liblte_nas_msg_type_to_string(msg_type));
if (init_ue->s_tmsi_present) {
srsran::uint8_to_uint32(init_ue->s_tmsi.value.m_tmsi.data(), &m_tmsi);
}
switch (msg_type) {
case LIBLTE_MME_MSG_TYPE_ATTACH_REQUEST:
srsran::console("Received Initial UE message -- Attach Request\n");
m_logger.info("Received Initial UE message -- Attach Request");
err = nas::handle_attach_request(enb_ue_s1ap_id, enb_sri, nas_msg.get(), m_nas_init, m_nas_if);
break;
case LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST:
srsran::console("Received Initial UE message -- Service Request\n");
m_logger.info("Received Initial UE message -- Service Request");
err = nas::handle_service_request(m_tmsi, enb_ue_s1ap_id, enb_sri, nas_msg.get(), m_nas_init, m_nas_if);
break;
case LIBLTE_MME_MSG_TYPE_DETACH_REQUEST:
srsran::console("Received Initial UE message -- Detach Request\n");
m_logger.info("Received Initial UE message -- Detach Request");
err = nas::handle_detach_request(m_tmsi, enb_ue_s1ap_id, enb_sri, nas_msg.get(), m_nas_init, m_nas_if);
break;
case LIBLTE_MME_MSG_TYPE_TRACKING_AREA_UPDATE_REQUEST:
srsran::console("Received Initial UE message -- Tracking Area Update Request\n");
m_logger.info("Received Initial UE message -- Tracking Area Update Request");
err = nas::handle_tracking_area_update_request(
m_tmsi, enb_ue_s1ap_id, enb_sri, nas_msg.get(), m_nas_init, m_nas_if);
break;
default:
m_logger.info("Unhandled Initial UE Message 0x%x ", msg_type);
srsran::console("Unhandled Initial UE Message 0x%x \n", msg_type);
err = false;
}
return err;
}
bool s1ap_nas_transport::handle_uplink_nas_transport(const asn1::s1ap::ul_nas_transport_s& ul_xport,
struct sctp_sndrcvinfo* enb_sri)
{
uint8_t pd, msg_type, sec_hdr_type;
uint32_t enb_ue_s1ap_id = ul_xport->enb_ue_s1ap_id.value.value;
uint32_t mme_ue_s1ap_id = ul_xport->mme_ue_s1ap_id.value.value;
bool mac_valid = false;
bool increase_ul_nas_cnt = true;
// Get UE NAS context
nas* nas_ctx = m_s1ap->find_nas_ctx_from_mme_ue_s1ap_id(mme_ue_s1ap_id);
if (nas_ctx == nullptr) {
m_logger.warning("Received uplink NAS, but could not find UE NAS context. MME-UE S1AP id: %d", mme_ue_s1ap_id);
return false;
}
m_logger.debug("Received uplink NAS and found UE NAS context. MME-UE S1AP id: %d", mme_ue_s1ap_id);
emm_ctx_t* emm_ctx = &nas_ctx->m_emm_ctx;
ecm_ctx_t* ecm_ctx = &nas_ctx->m_ecm_ctx;
sec_ctx_t* sec_ctx = &nas_ctx->m_sec_ctx;
// Parse NAS message header
srsran::unique_byte_buffer_t nas_msg = srsran::make_byte_buffer();
memcpy(nas_msg->msg, ul_xport->nas_pdu.value.data(), ul_xport->nas_pdu.value.size());
nas_msg->N_bytes = ul_xport->nas_pdu.value.size();
bool msg_encrypted = false;
// Parse the message security header
liblte_mme_parse_msg_sec_header((LIBLTE_BYTE_MSG_STRUCT*)nas_msg.get(), &pd, &sec_hdr_type);
// Invalid Security Header Type simply return function
if (!(sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS ||
sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY ||
sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED ||
sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_WITH_NEW_EPS_SECURITY_CONTEXT ||
sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED_WITH_NEW_EPS_SECURITY_CONTEXT)) {
m_logger.error("Unhandled security header type in Uplink NAS Transport: %d", sec_hdr_type);
return false;
}
// Some messages may have invalid MAC. Check wether we need to warn about MAC failures.
bool warn_integrity_fail = true;
if (sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY ||
sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_WITH_NEW_EPS_SECURITY_CONTEXT) {
// Avoid unecessary warnings for identity response and authentication response.
liblte_mme_parse_msg_header((LIBLTE_BYTE_MSG_STRUCT*)nas_msg.get(), &pd, &msg_type);
if (msg_type == LIBLTE_MME_MSG_TYPE_IDENTITY_RESPONSE || msg_type == LIBLTE_MME_MSG_TYPE_AUTHENTICATION_RESPONSE) {
warn_integrity_fail = false;
}
}
// Check MAC if message is integrity protected
if (sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY ||
sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_WITH_NEW_EPS_SECURITY_CONTEXT ||
sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED ||
sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED_WITH_NEW_EPS_SECURITY_CONTEXT) {
mac_valid = nas_ctx->integrity_check(nas_msg.get(), warn_integrity_fail);
if (not mac_valid) {
srslog::log_channel& channel = warn_integrity_fail ? m_logger.warning : m_logger.info;
channel("Invalid MAC message. Even if security header indicates integrity protection (Maybe: "
"Identity Response or Authentication Response)");
}
}
// Decrypt message if indicated
if (sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED ||
sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED_WITH_NEW_EPS_SECURITY_CONTEXT) {
m_logger.debug(nas_msg->msg, nas_msg->N_bytes, "Encrypted");
nas_ctx->cipher_decrypt(nas_msg.get());
msg_encrypted = true;
m_logger.debug(nas_msg->msg, nas_msg->N_bytes, "Decrypted");
}
// Now parse message header and handle message
liblte_mme_parse_msg_header((LIBLTE_BYTE_MSG_STRUCT*)nas_msg.get(), &pd, &msg_type);
// Find UE EMM context if message is security protected.
if (sec_hdr_type != LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS) {
// Make sure EMM context is set-up, to do integrity check/de-chiphering
if (emm_ctx->imsi == 0) {
// No EMM context found. Perhaps a temporary context is being created?
// This can happen with integrity protected identity reponse messages
if (!(msg_type == LIBLTE_MME_MSG_TYPE_IDENTITY_RESPONSE &&
sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY)) {
m_logger.warning(
"Uplink NAS: could not find security context for integrity protected message. MME-UE S1AP id: %d",
mme_ue_s1ap_id);
return false;
}
}
}
// Handle message and check if security requirements for messages
// 4.4.4.3 Integrity checking of NAS signalling messages in the MME
// Except the messages listed below, no NAS signalling messages shall be processed...
// - ATTACH REQUEST;
// - IDENTITY RESPONSE (if requested identification parameter is IMSI);
// - AUTHENTICATION RESPONSE;
// - AUTHENTICATION FAILURE;
// - SECURITY MODE REJECT;
// - DETACH REQUEST;
// - DETACH ACCEPT;
// - TRACKING AREA UPDATE REQUEST.
m_logger.info("UL NAS: sec_hdr_type: %s, mac_vaild: %s, msg_encrypted: %s",
liblte_nas_sec_hdr_type_to_string(sec_hdr_type),
mac_valid == true ? "yes" : "no",
msg_encrypted == true ? "yes" : "no");
switch (msg_type) {
case LIBLTE_MME_MSG_TYPE_ATTACH_REQUEST:
m_logger.info("UL NAS: Attach Request");
srsran::console("UL NAS: Attach Resquest\n");
nas_ctx->handle_attach_request(nas_msg.get());
break;
case LIBLTE_MME_MSG_TYPE_IDENTITY_RESPONSE:
m_logger.info("UL NAS: Received Identity Response");
srsran::console("UL NAS: Received Identity Response\n");
nas_ctx->handle_identity_response(nas_msg.get());
break;
case LIBLTE_MME_MSG_TYPE_AUTHENTICATION_RESPONSE:
m_logger.info("UL NAS: Received Authentication Response");
srsran::console("UL NAS: Received Authentication Response\n");
nas_ctx->handle_authentication_response(nas_msg.get());
// In case of a successful authentication response, security mode command follows.
// Reset counter for incoming security mode complete
sec_ctx->ul_nas_count = 0;
sec_ctx->dl_nas_count = 0;
increase_ul_nas_cnt = false;
break;
// Authentication failure with the option sync failure can be sent not integrity protected
case LIBLTE_MME_MSG_TYPE_AUTHENTICATION_FAILURE:
m_logger.info("UL NAS: Authentication Failure");
srsran::console("UL NAS: Authentication Failure\n");
nas_ctx->handle_authentication_failure(nas_msg.get());
break;
// Detach request can be sent not integrity protected when "power off" option is used
case LIBLTE_MME_MSG_TYPE_DETACH_REQUEST:
m_logger.info("UL NAS: Detach Request");
srsran::console("UL NAS: Detach Request\n");
// TODO: check integrity protection in detach request
nas_ctx->handle_detach_request(nas_msg.get());
break;
case LIBLTE_MME_MSG_TYPE_SECURITY_MODE_COMPLETE:
m_logger.info("UL NAS: Received Security Mode Complete");
srsran::console("UL NAS: Received Security Mode Complete\n");
if (sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED_WITH_NEW_EPS_SECURITY_CONTEXT &&
mac_valid == true) {
nas_ctx->handle_security_mode_complete(nas_msg.get());
} else {
// Security Mode Complete was not integrity protected
srsran::console("Security Mode Complete %s. Discard message.\n",
(mac_valid ? "not integrity protected" : "invalid integrity"));
m_logger.warning("Security Mode Complete %s. Discard message.",
(mac_valid ? "not integrity protected" : "invalid integrity"));
increase_ul_nas_cnt = false;
}
break;
case LIBLTE_MME_MSG_TYPE_ATTACH_COMPLETE:
m_logger.info("UL NAS: Received Attach Complete");
srsran::console("UL NAS: Received Attach Complete\n");
if (sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED && mac_valid == true) {
nas_ctx->handle_attach_complete(nas_msg.get());
} else {
// Attach Complete was not integrity protected
srsran::console("Attach Complete not integrity protected. Discard message.\n");
m_logger.warning("Attach Complete not integrity protected. Discard message.");
increase_ul_nas_cnt = false;
}
break;
case LIBLTE_MME_MSG_TYPE_ESM_INFORMATION_RESPONSE:
m_logger.info("UL NAS: Received ESM Information Response");
srsran::console("UL NAS: Received ESM Information Response\n");
if (sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED && mac_valid == true) {
nas_ctx->handle_esm_information_response(nas_msg.get());
} else {
// Attach Complete was not integrity protected
srsran::console("ESM Information Response %s. Discard message.\n",
(mac_valid ? "not integrity protected" : "invalid integrity"));
m_logger.warning("ESM Information Response %s. Discard message.",
(mac_valid ? "not integrity protected" : "invalid integrity"));
increase_ul_nas_cnt = false;
}
break;
case LIBLTE_MME_MSG_TYPE_TRACKING_AREA_UPDATE_REQUEST:
m_logger.info("UL NAS: Tracking Area Update Request");
srsran::console("UL NAS: Tracking Area Update Request\n");
nas_ctx->handle_tracking_area_update_request(nas_msg.get());
break;
case LIBLTE_MME_MSG_TYPE_PDN_CONNECTIVITY_REQUEST:
m_logger.info("UL NAS: PDN Connectivity Request");
srsran::console("UL NAS: PDN Connectivity Request\n");
nas_ctx->handle_pdn_connectivity_request(nas_msg.get());
break;
default:
m_logger.warning("Unhandled NAS integrity protected message %s", liblte_nas_msg_type_to_string(msg_type));
srsran::console("Unhandled NAS integrity protected message %s\n", liblte_nas_msg_type_to_string(msg_type));
return false;
}
// Increment UL NAS count. if counter not resetted in function, e.g., DL Security mode command after Authentication
// response
if (increase_ul_nas_cnt == true) {
sec_ctx->ul_nas_count++;
}
return true;
}
bool s1ap_nas_transport::send_downlink_nas_transport(uint32_t enb_ue_s1ap_id,
uint32_t mme_ue_s1ap_id,
srsran::byte_buffer_t* nas_msg,
struct sctp_sndrcvinfo enb_sri)
{
m_logger.debug("Sending message to eNB with SCTP association %d. MME UE S1AP ID %d, eNB UE S1AP ID %d",
enb_sri.sinfo_assoc_id,
mme_ue_s1ap_id,
enb_ue_s1ap_id);
// Setup initiating message
s1ap_pdu_t tx_pdu;
tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_DL_NAS_TRANSPORT);
// Setup Dw NAS structure
asn1::s1ap::dl_nas_transport_s& dw_nas = tx_pdu.init_msg().value.dl_nas_transport();
dw_nas->enb_ue_s1ap_id.value = enb_ue_s1ap_id;
dw_nas->mme_ue_s1ap_id.value = mme_ue_s1ap_id;
// Copy NAS PDU to Downlink NAS Trasport message buffer
dw_nas->nas_pdu.value.resize(nas_msg->N_bytes);
memcpy(dw_nas->nas_pdu.value.data(), nas_msg->msg, nas_msg->N_bytes);
// Send Downlink NAS Transport Message
m_s1ap->s1ap_tx_pdu(tx_pdu, &enb_sri);
return true;
}
} // namespace srsepc