1051 lines
37 KiB
C++
1051 lines
37 KiB
C++
/*
|
|
* Copyright 2013-2019 Software Radio Systems Limited
|
|
*
|
|
* This file is part of srsLTE.
|
|
*
|
|
* srsLTE 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.
|
|
*
|
|
* srsLTE 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 "srsenb/hdr/upper/s1ap.h"
|
|
#include "srsenb/hdr/upper/common_enb.h"
|
|
#include "srslte/common/bcd_helpers.h"
|
|
#include "srslte/common/int_helpers.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h> //for close(), sleep()
|
|
#include <stdlib.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/sctp.h>
|
|
#include <arpa/inet.h> //for inet_ntop()
|
|
|
|
using srslte::s1ap_mccmnc_to_plmn;
|
|
using srslte::uint32_to_uint8;
|
|
|
|
namespace srsenb{
|
|
|
|
bool s1ap::init(s1ap_args_t args_, rrc_interface_s1ap *rrc_, srslte::log *s1ap_log_)
|
|
{
|
|
rrc = rrc_;
|
|
args = args_;
|
|
s1ap_log = s1ap_log_;
|
|
|
|
pool = srslte::byte_buffer_pool::get_instance();
|
|
mme_connected = false;
|
|
running = false;
|
|
next_eNB_UE_S1AP_ID = 1;
|
|
next_ue_stream_id = 1;
|
|
|
|
build_tai_cgi();
|
|
|
|
start(S1AP_THREAD_PRIO);
|
|
|
|
return true;
|
|
}
|
|
|
|
void s1ap::stop()
|
|
{
|
|
if(running) {
|
|
running = false;
|
|
thread_cancel();
|
|
wait_thread_finish();
|
|
}
|
|
|
|
if(close(socket_fd) == -1) {
|
|
s1ap_log->error("Failed to close SCTP socket\n");
|
|
}
|
|
return;
|
|
}
|
|
|
|
void s1ap::get_metrics(s1ap_metrics_t &m)
|
|
{
|
|
if(!running) {
|
|
m.status = S1AP_ERROR;
|
|
return;
|
|
}
|
|
if(mme_connected) {
|
|
m.status = S1AP_READY;
|
|
}else{
|
|
m.status = S1AP_ATTACHING;
|
|
}
|
|
return;
|
|
}
|
|
|
|
void s1ap::run_thread()
|
|
{
|
|
srslte::unique_byte_buffer pdu = srslte::allocate_unique_buffer(*pool, "s1ap::run_thread");
|
|
if (!pdu) {
|
|
s1ap_log->error("Fatal Error: Couldn't allocate buffer in s1ap::run_thread().\n");
|
|
return;
|
|
}
|
|
|
|
uint32_t sz = SRSLTE_MAX_BUFFER_SIZE_BYTES - SRSLTE_BUFFER_HEADER_OFFSET;
|
|
running = true;
|
|
|
|
// Connect to MME
|
|
while(running && !connect_mme()) {
|
|
s1ap_log->error("Failed to connect to MME - retrying in 10 seconds\n");
|
|
s1ap_log->console("Failed to connect to MME - retrying in 10 seconds\n");
|
|
sleep(10);
|
|
}
|
|
if(!setup_s1()) {
|
|
s1ap_log->error("S1 setup failed\n");
|
|
s1ap_log->console("S1 setup failed\n");
|
|
running = false;
|
|
return;
|
|
}
|
|
|
|
// S1AP rx loop
|
|
while(running) {
|
|
pdu->reset();
|
|
pdu->N_bytes = recv(socket_fd, pdu->msg, sz, 0);
|
|
|
|
if(pdu->N_bytes <= 0) {
|
|
mme_connected = false;
|
|
do {
|
|
s1ap_log->error("Disconnected - attempting reconnection in 10 seconds\n");
|
|
s1ap_log->console("Disconnected - attempting reconnection in 10 seconds\n");
|
|
sleep(10);
|
|
} while(running && !connect_mme());
|
|
|
|
if(!setup_s1()) {
|
|
s1ap_log->error("S1 setup failed\n");
|
|
s1ap_log->console("S1 setup failed\n");
|
|
running = false;
|
|
return;
|
|
}
|
|
}
|
|
|
|
s1ap_log->info_hex(pdu->msg, pdu->N_bytes, "Received S1AP PDU");
|
|
handle_s1ap_rx_pdu(pdu.get());
|
|
}
|
|
}
|
|
|
|
// Generate common S1AP protocol IEs from config args
|
|
void s1ap::build_tai_cgi()
|
|
{
|
|
uint32_t plmn;
|
|
uint32_t tmp32;
|
|
uint16_t tmp16;
|
|
|
|
// TAI
|
|
tai.ext = false;
|
|
tai.iE_Extensions_present = false;
|
|
s1ap_mccmnc_to_plmn(args.mcc, args.mnc, &plmn);
|
|
tmp32 = htonl(plmn);
|
|
tai.pLMNidentity.buffer[0] = ((uint8_t*)&tmp32)[1];
|
|
tai.pLMNidentity.buffer[1] = ((uint8_t*)&tmp32)[2];
|
|
tai.pLMNidentity.buffer[2] = ((uint8_t*)&tmp32)[3];
|
|
tmp16 = htons(args.tac);
|
|
memcpy(tai.tAC.buffer, (uint8_t*)&tmp16, 2);
|
|
|
|
// EUTRAN_CGI
|
|
eutran_cgi.ext = false;
|
|
eutran_cgi.iE_Extensions_present = false;
|
|
s1ap_mccmnc_to_plmn(args.mcc, args.mnc, &plmn);
|
|
tmp32 = htonl(plmn);
|
|
eutran_cgi.pLMNidentity.buffer[0] = ((uint8_t*)&tmp32)[1];
|
|
eutran_cgi.pLMNidentity.buffer[1] = ((uint8_t*)&tmp32)[2];
|
|
eutran_cgi.pLMNidentity.buffer[2] = ((uint8_t*)&tmp32)[3];
|
|
|
|
tmp32 = htonl(args.enb_id);
|
|
uint8_t enb_id_bits[4*8];
|
|
liblte_unpack((uint8_t*)&tmp32, 4, enb_id_bits);
|
|
uint8_t cell_id_bits[1*8];
|
|
liblte_unpack(&args.cell_id, 1, cell_id_bits);
|
|
memcpy(eutran_cgi.cell_ID.buffer, &enb_id_bits[32-LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN], LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN);
|
|
memcpy(&eutran_cgi.cell_ID.buffer[LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN], cell_id_bits, 8);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
/* RRC interface
|
|
********************************************************************************/
|
|
void s1ap::initial_ue(uint16_t rnti, LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM cause, srslte::unique_byte_buffer pdu)
|
|
{
|
|
ue_ctxt_map[rnti].eNB_UE_S1AP_ID = next_eNB_UE_S1AP_ID++;
|
|
ue_ctxt_map[rnti].stream_id = 1;
|
|
ue_ctxt_map[rnti].release_requested = false;
|
|
enbid_to_rnti_map[ue_ctxt_map[rnti].eNB_UE_S1AP_ID] = rnti;
|
|
send_initialuemessage(rnti, cause, std::move(pdu), false);
|
|
}
|
|
|
|
void s1ap::initial_ue(uint16_t rnti,
|
|
LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM cause,
|
|
srslte::unique_byte_buffer pdu,
|
|
uint32_t m_tmsi,
|
|
uint8_t mmec)
|
|
{
|
|
ue_ctxt_map[rnti].eNB_UE_S1AP_ID = next_eNB_UE_S1AP_ID++;
|
|
ue_ctxt_map[rnti].stream_id = 1;
|
|
ue_ctxt_map[rnti].release_requested = false;
|
|
enbid_to_rnti_map[ue_ctxt_map[rnti].eNB_UE_S1AP_ID] = rnti;
|
|
send_initialuemessage(rnti, cause, std::move(pdu), true, m_tmsi, mmec);
|
|
}
|
|
|
|
void s1ap::write_pdu(uint16_t rnti, srslte::unique_byte_buffer pdu)
|
|
{
|
|
s1ap_log->info_hex(pdu->msg, pdu->N_bytes, "Received RRC SDU");
|
|
|
|
if(ue_ctxt_map.end() == ue_ctxt_map.find(rnti)) {
|
|
s1ap_log->warning("User RNTI:0x%x context not found\n", rnti);
|
|
return;
|
|
}
|
|
|
|
send_ulnastransport(rnti, std::move(pdu));
|
|
}
|
|
|
|
bool s1ap::user_release(uint16_t rnti, LIBLTE_S1AP_CAUSERADIONETWORK_ENUM cause_radio)
|
|
{
|
|
s1ap_log->info("User inactivity - RNTI:0x%x\n", rnti);
|
|
|
|
if(ue_ctxt_map.end() == ue_ctxt_map.find(rnti)) {
|
|
s1ap_log->warning("User RNTI:0x%x context not found\n", rnti);
|
|
return false;
|
|
}
|
|
|
|
if(ue_ctxt_map[rnti].release_requested) {
|
|
s1ap_log->warning("UE context for RNTI:0x%x is in zombie state. Releasing...\n", rnti);
|
|
ue_ctxt_map.erase(rnti);
|
|
rrc->release_complete(rnti);
|
|
return false;
|
|
}
|
|
|
|
LIBLTE_S1AP_CAUSE_STRUCT cause;
|
|
cause.ext = false;
|
|
cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK;
|
|
cause.choice.radioNetwork.ext = false;
|
|
cause.choice.radioNetwork.e = cause_radio;
|
|
|
|
ue_ctxt_map[rnti].release_requested = true;
|
|
return send_uectxtreleaserequest(rnti, &cause);
|
|
}
|
|
|
|
bool s1ap::user_exists(uint16_t rnti)
|
|
{
|
|
return ue_ctxt_map.end() != ue_ctxt_map.find(rnti);
|
|
}
|
|
|
|
void s1ap::ue_ctxt_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res)
|
|
{
|
|
if(res->E_RABSetupListCtxtSURes.len > 0) {
|
|
send_initial_ctxt_setup_response(rnti, res);
|
|
} else {
|
|
send_initial_ctxt_setup_failure(rnti);
|
|
}
|
|
}
|
|
|
|
void s1ap::ue_erab_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res)
|
|
{
|
|
send_erab_setup_response(rnti, res);
|
|
}
|
|
|
|
//void ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps)
|
|
//{
|
|
|
|
//}
|
|
|
|
bool s1ap::is_mme_connected()
|
|
{
|
|
return mme_connected;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
/* S1AP connection helpers
|
|
********************************************************************************/
|
|
|
|
bool s1ap::connect_mme()
|
|
{
|
|
socket_fd = 0;
|
|
|
|
s1ap_log->info("Connecting to MME %s:%d\n", args.mme_addr.c_str(), MME_PORT);
|
|
|
|
if((socket_fd = socket(ADDR_FAMILY, SOCK_TYPE, PROTO)) == -1) {
|
|
s1ap_log->error("Failed to create S1AP socket\n");
|
|
return false;
|
|
}
|
|
|
|
// Bind to the local address
|
|
struct sockaddr_in local_addr;
|
|
memset(&local_addr, 0, sizeof(struct sockaddr_in));
|
|
local_addr.sin_family = ADDR_FAMILY;
|
|
local_addr.sin_port = 0; // Any local port will do
|
|
if(inet_pton(AF_INET, args.s1c_bind_addr.c_str(), &(local_addr.sin_addr)) != 1) {
|
|
s1ap_log->error("Error converting IP address (%s) to sockaddr_in structure\n", args.s1c_bind_addr.c_str());
|
|
return false;
|
|
}
|
|
if (bind(socket_fd, (struct sockaddr *)&local_addr, sizeof(local_addr)) != 0) {
|
|
s1ap_log->error("Failed to bind on S1-C address %s: %s errno %d\n", args.s1c_bind_addr.c_str(), strerror(errno), errno);
|
|
return false;
|
|
}
|
|
|
|
// Connect to the MME address
|
|
memset(&mme_addr, 0, sizeof(struct sockaddr_in));
|
|
mme_addr.sin_family = ADDR_FAMILY;
|
|
mme_addr.sin_port = htons(MME_PORT);
|
|
if(inet_pton(AF_INET, args.mme_addr.c_str(), &(mme_addr.sin_addr)) != 1) {
|
|
s1ap_log->error("Error converting IP address (%s) to sockaddr_in structure\n", args.mme_addr.c_str());
|
|
return false;
|
|
}
|
|
|
|
if(connect(socket_fd, (struct sockaddr*)&mme_addr, sizeof(mme_addr)) == -1) {
|
|
s1ap_log->error("Failed to establish socket connection to MME\n");
|
|
return false;
|
|
}
|
|
|
|
s1ap_log->info("SCTP socket established with MME\n");
|
|
return true;
|
|
}
|
|
|
|
bool s1ap::setup_s1()
|
|
{
|
|
uint32_t tmp32;
|
|
uint16_t tmp16;
|
|
srslte::byte_buffer_t msg;
|
|
LIBLTE_S1AP_S1AP_PDU_STRUCT pdu;
|
|
bzero(&pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT));
|
|
|
|
pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE;
|
|
|
|
LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &pdu.choice.initiatingMessage;
|
|
|
|
init->procedureCode = LIBLTE_S1AP_PROC_ID_S1SETUP;
|
|
init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_S1SETUPREQUEST;
|
|
|
|
LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRUCT *s1setup = &init->choice.S1SetupRequest;
|
|
s1setup->ext = false;
|
|
s1setup->CSG_IdList_present = false;
|
|
|
|
s1setup->Global_ENB_ID.ext = false;
|
|
s1setup->Global_ENB_ID.iE_Extensions_present = false;
|
|
uint32_t plmn;
|
|
s1ap_mccmnc_to_plmn(args.mcc, args.mnc, &plmn);
|
|
tmp32 = htonl(plmn);
|
|
s1setup->Global_ENB_ID.pLMNidentity.buffer[0] = ((uint8_t*)&tmp32)[1];
|
|
s1setup->Global_ENB_ID.pLMNidentity.buffer[1] = ((uint8_t*)&tmp32)[2];
|
|
s1setup->Global_ENB_ID.pLMNidentity.buffer[2] = ((uint8_t*)&tmp32)[3];
|
|
|
|
s1setup->Global_ENB_ID.ext = false;
|
|
s1setup->Global_ENB_ID.eNB_ID.ext = false;
|
|
s1setup->Global_ENB_ID.eNB_ID.choice_type = LIBLTE_S1AP_ENB_ID_CHOICE_MACROENB_ID;
|
|
tmp32 = htonl(args.enb_id);
|
|
uint8_t enb_id_bits[4*8];
|
|
liblte_unpack((uint8_t*)&tmp32, 4, enb_id_bits);
|
|
memcpy(s1setup->Global_ENB_ID.eNB_ID.choice.macroENB_ID.buffer, &enb_id_bits[32-LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN], LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN);
|
|
|
|
s1setup->eNBname_present = true;
|
|
s1setup->eNBname.ext = false;
|
|
if(args.enb_name.length() >= 150) {
|
|
args.enb_name.resize(150-1);
|
|
}
|
|
memcpy(s1setup->eNBname.buffer, args.enb_name.c_str(), args.enb_name.length());
|
|
s1setup->eNBname.n_octets = args.enb_name.length();
|
|
|
|
s1setup->SupportedTAs.len = 1;
|
|
s1setup->SupportedTAs.buffer[0].ext = false;
|
|
s1setup->SupportedTAs.buffer[0].iE_Extensions_present = false;
|
|
tmp16 = htons(args.tac);
|
|
memcpy(s1setup->SupportedTAs.buffer[0].tAC.buffer, (uint8_t*)&tmp16, 2);
|
|
s1setup->SupportedTAs.buffer[0].broadcastPLMNs.len = 1;
|
|
tmp32 = htonl(plmn);
|
|
s1setup->SupportedTAs.buffer[0].broadcastPLMNs.buffer[0].buffer[0] = ((uint8_t*)&tmp32)[1];
|
|
s1setup->SupportedTAs.buffer[0].broadcastPLMNs.buffer[0].buffer[1] = ((uint8_t*)&tmp32)[2];
|
|
s1setup->SupportedTAs.buffer[0].broadcastPLMNs.buffer[0].buffer[2] = ((uint8_t*)&tmp32)[3];
|
|
|
|
s1setup->DefaultPagingDRX.ext = false;
|
|
s1setup->DefaultPagingDRX.e = LIBLTE_S1AP_PAGINGDRX_V128; // Todo: add to args, config file
|
|
|
|
liblte_s1ap_pack_s1ap_pdu(&pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg);
|
|
s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending s1SetupRequest");
|
|
|
|
ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes,
|
|
(struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in),
|
|
htonl(PPID), 0, NONUE_STREAM_ID, 0, 0);
|
|
if(n_sent == -1) {
|
|
s1ap_log->error("Failed to send s1SetupRequest\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
/* S1AP message handlers
|
|
********************************************************************************/
|
|
|
|
bool s1ap::handle_s1ap_rx_pdu(srslte::byte_buffer_t* pdu)
|
|
{
|
|
LIBLTE_S1AP_S1AP_PDU_STRUCT rx_pdu;
|
|
|
|
if(liblte_s1ap_unpack_s1ap_pdu((LIBLTE_BYTE_MSG_STRUCT*)pdu, &rx_pdu) != LIBLTE_SUCCESS) {
|
|
s1ap_log->error("Failed to unpack received PDU\n");
|
|
return false;
|
|
}
|
|
|
|
switch(rx_pdu.choice_type) {
|
|
case LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE:
|
|
return handle_initiatingmessage(&rx_pdu.choice.initiatingMessage);
|
|
case LIBLTE_S1AP_S1AP_PDU_CHOICE_SUCCESSFULOUTCOME:
|
|
return handle_successfuloutcome(&rx_pdu.choice.successfulOutcome);
|
|
case LIBLTE_S1AP_S1AP_PDU_CHOICE_UNSUCCESSFULOUTCOME:
|
|
return handle_unsuccessfuloutcome(&rx_pdu.choice.unsuccessfulOutcome);
|
|
default:
|
|
s1ap_log->error("Unhandled PDU type %d\n", rx_pdu.choice_type);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool s1ap::handle_initiatingmessage(LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *msg)
|
|
{
|
|
switch(msg->choice_type) {
|
|
case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT:
|
|
return handle_dlnastransport(&msg->choice.DownlinkNASTransport);
|
|
case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_INITIALCONTEXTSETUPREQUEST:
|
|
return handle_initialctxtsetuprequest(&msg->choice.InitialContextSetupRequest);
|
|
case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTRELEASECOMMAND:
|
|
return handle_uectxtreleasecommand(&msg->choice.UEContextReleaseCommand);
|
|
case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_PAGING:
|
|
return handle_paging(&msg->choice.Paging);
|
|
case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_E_RABSETUPREQUEST:
|
|
return handle_erabsetuprequest(&msg->choice.E_RABSetupRequest);
|
|
default:
|
|
s1ap_log->error("Unhandled intiating message: %s\n", liblte_s1ap_initiatingmessage_choice_text[msg->choice_type]);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool s1ap::handle_successfuloutcome(LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *msg)
|
|
{
|
|
switch(msg->choice_type) {
|
|
case LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_S1SETUPRESPONSE:
|
|
return handle_s1setupresponse(&msg->choice.S1SetupResponse);
|
|
default:
|
|
s1ap_log->error("Unhandled successful outcome message: %s\n", liblte_s1ap_successfuloutcome_choice_text[msg->choice_type]);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool s1ap::handle_unsuccessfuloutcome(LIBLTE_S1AP_UNSUCCESSFULOUTCOME_STRUCT *msg)
|
|
{
|
|
switch(msg->choice_type) {
|
|
case LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_S1SETUPFAILURE:
|
|
return handle_s1setupfailure(&msg->choice.S1SetupFailure);
|
|
default:
|
|
s1ap_log->error("Unhandled unsuccessful outcome message: %s\n", liblte_s1ap_unsuccessfuloutcome_choice_text[msg->choice_type]);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool s1ap::handle_s1setupresponse(LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT *msg)
|
|
{
|
|
s1ap_log->info("Received S1SetupResponse\n");
|
|
s1setupresponse = *msg;
|
|
mme_connected = true;
|
|
return true;
|
|
}
|
|
|
|
bool s1ap::handle_dlnastransport(LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *msg)
|
|
{
|
|
s1ap_log->info("Received DownlinkNASTransport\n");
|
|
if(msg->ext) {
|
|
s1ap_log->warning("Not handling S1AP message extension\n");
|
|
}
|
|
if(enbid_to_rnti_map.end() == enbid_to_rnti_map.find(msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID)) {
|
|
s1ap_log->warning("eNB_UE_S1AP_ID not found - discarding message\n");
|
|
return false;
|
|
}
|
|
uint16_t rnti = enbid_to_rnti_map[msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID];
|
|
ue_ctxt_map[rnti].MME_UE_S1AP_ID = msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID;
|
|
|
|
if(msg->HandoverRestrictionList_present) {
|
|
s1ap_log->warning("Not handling HandoverRestrictionList\n");
|
|
}
|
|
if(msg->SubscriberProfileIDforRFP_present) {
|
|
s1ap_log->warning("Not handling SubscriberProfileIDforRFP\n");
|
|
}
|
|
|
|
srslte::unique_byte_buffer pdu = srslte::allocate_unique_buffer(*pool);
|
|
if (pdu) {
|
|
memcpy(pdu->msg, msg->NAS_PDU.buffer, msg->NAS_PDU.n_octets);
|
|
pdu->N_bytes = msg->NAS_PDU.n_octets;
|
|
rrc->write_dl_info(rnti, std::move(pdu));
|
|
return true;
|
|
} else {
|
|
s1ap_log->error("Fatal Error: Couldn't allocate buffer in s1ap::run_thread().\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool s1ap::handle_initialctxtsetuprequest(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *msg)
|
|
{
|
|
s1ap_log->info("Received InitialContextSetupRequest\n");
|
|
if(msg->ext) {
|
|
s1ap_log->warning("Not handling S1AP message extension\n");
|
|
}
|
|
if(enbid_to_rnti_map.end() == enbid_to_rnti_map.find(msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID)) {
|
|
s1ap_log->warning("eNB_UE_S1AP_ID not found - discarding message\n");
|
|
return false;
|
|
}
|
|
uint16_t rnti = enbid_to_rnti_map[msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID];
|
|
if(msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID != ue_ctxt_map[rnti].MME_UE_S1AP_ID) {
|
|
s1ap_log->warning("MME_UE_S1AP_ID has changed - old:%d, new:%d\n",
|
|
ue_ctxt_map[rnti].MME_UE_S1AP_ID,
|
|
msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID);
|
|
ue_ctxt_map[rnti].MME_UE_S1AP_ID = msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID;
|
|
}
|
|
|
|
// Setup UE ctxt in RRC
|
|
if(!rrc->setup_ue_ctxt(rnti, msg)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool s1ap::handle_paging(LIBLTE_S1AP_MESSAGE_PAGING_STRUCT *msg)
|
|
{
|
|
if(msg->ext) {
|
|
s1ap_log->warning("Not handling S1AP message extension\n");
|
|
}
|
|
uint8_t *ptr = msg->UEIdentityIndexValue.buffer;
|
|
uint32_t ueid = srslte_bit_pack(&ptr, 10);
|
|
|
|
rrc->add_paging_id(ueid, msg->UEPagingID);
|
|
return true;
|
|
}
|
|
|
|
bool s1ap::handle_erabsetuprequest(LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT *msg)
|
|
{
|
|
s1ap_log->info("Received ERABSetupRequest\n");
|
|
if(msg->ext) {
|
|
s1ap_log->warning("Not handling S1AP message extension\n");
|
|
}
|
|
|
|
if(enbid_to_rnti_map.end() == enbid_to_rnti_map.find(msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID)) {
|
|
s1ap_log->warning("eNB_UE_S1AP_ID not found - discarding message\n");
|
|
return false;
|
|
}
|
|
uint16_t rnti = enbid_to_rnti_map[msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID];
|
|
if(msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID != ue_ctxt_map[rnti].MME_UE_S1AP_ID) {
|
|
s1ap_log->warning("MME_UE_S1AP_ID has changed - old:%d, new:%d\n",
|
|
ue_ctxt_map[rnti].MME_UE_S1AP_ID,
|
|
msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID);
|
|
ue_ctxt_map[rnti].MME_UE_S1AP_ID = msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID;
|
|
}
|
|
|
|
// Setup UE ctxt in RRC
|
|
if(!rrc->setup_ue_erabs(rnti, msg)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool s1ap::handle_uectxtreleasecommand(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMAND_STRUCT *msg)
|
|
{
|
|
s1ap_log->info("Received UEContextReleaseCommand\n");
|
|
if(msg->ext) {
|
|
s1ap_log->warning("Not handling S1AP message extension\n");
|
|
}
|
|
if(msg->UE_S1AP_IDs.ext) {
|
|
s1ap_log->warning("Not handling S1AP message extension\n");
|
|
}
|
|
|
|
uint16_t rnti = 0;
|
|
if(msg->UE_S1AP_IDs.choice_type == LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_UE_S1AP_ID_PAIR) {
|
|
|
|
if(msg->UE_S1AP_IDs.choice.uE_S1AP_ID_pair.ext) {
|
|
s1ap_log->warning("Not handling S1AP message extension\n");
|
|
}
|
|
if(msg->UE_S1AP_IDs.choice.uE_S1AP_ID_pair.iE_Extensions_present) {
|
|
s1ap_log->warning("Not handling S1AP message iE_Extensions\n");
|
|
}
|
|
uint32_t enb_ue_id = msg->UE_S1AP_IDs.choice.uE_S1AP_ID_pair.eNB_UE_S1AP_ID.ENB_UE_S1AP_ID;
|
|
if(enbid_to_rnti_map.end() == enbid_to_rnti_map.find(enb_ue_id)) {
|
|
s1ap_log->warning("eNB_UE_S1AP_ID:%d not found - discarding message\n", enb_ue_id);
|
|
return false;
|
|
}
|
|
rnti = enbid_to_rnti_map[enb_ue_id];
|
|
enbid_to_rnti_map.erase(enb_ue_id);
|
|
|
|
} else { // LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_MME_UE_S1AP_ID
|
|
|
|
uint32_t mme_ue_id = msg->UE_S1AP_IDs.choice.mME_UE_S1AP_ID.MME_UE_S1AP_ID;
|
|
uint32_t enb_ue_id;
|
|
if(!find_mme_ue_id(mme_ue_id, &rnti, &enb_ue_id)) {
|
|
s1ap_log->warning("UE for MME_UE_S1AP_ID:%d not found - discarding message\n", mme_ue_id);
|
|
return false;
|
|
}
|
|
enbid_to_rnti_map.erase(enb_ue_id);
|
|
}
|
|
|
|
if(ue_ctxt_map.end() == ue_ctxt_map.find(rnti)) {
|
|
s1ap_log->warning("UE context for RNTI:0x%x not found - discarding message\n", rnti);
|
|
return false;
|
|
}
|
|
|
|
rrc->release_erabs(rnti);
|
|
send_uectxtreleasecomplete(rnti, ue_ctxt_map[rnti].MME_UE_S1AP_ID, ue_ctxt_map[rnti].eNB_UE_S1AP_ID);
|
|
ue_ctxt_map.erase(rnti);
|
|
s1ap_log->info("UE context for RNTI:0x%x released\n", rnti);
|
|
rrc->release_complete(rnti);
|
|
return true;
|
|
}
|
|
|
|
bool s1ap::handle_s1setupfailure(LIBLTE_S1AP_MESSAGE_S1SETUPFAILURE_STRUCT *msg) {
|
|
std::string cause = get_cause(&msg->Cause);
|
|
s1ap_log->error("S1 Setup Failure. Cause: %s\n", cause.c_str());
|
|
s1ap_log->console("S1 Setup Failure. Cause: %s\n", cause.c_str());
|
|
return true;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
/* S1AP message senders
|
|
********************************************************************************/
|
|
|
|
bool s1ap::send_initialuemessage(uint16_t rnti,
|
|
LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_ENUM cause,
|
|
srslte::unique_byte_buffer pdu,
|
|
bool has_tmsi,
|
|
uint32_t m_tmsi,
|
|
uint8_t mmec)
|
|
{
|
|
if(!mme_connected) {
|
|
return false;
|
|
}
|
|
srslte::byte_buffer_t msg;
|
|
|
|
LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu;
|
|
tx_pdu.ext = false;
|
|
tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE;
|
|
|
|
LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage;
|
|
init->procedureCode = LIBLTE_S1AP_PROC_ID_INITIALUEMESSAGE;
|
|
init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_INITIALUEMESSAGE;
|
|
|
|
LIBLTE_S1AP_MESSAGE_INITIALUEMESSAGE_STRUCT *initue = &init->choice.InitialUEMessage;
|
|
initue->ext = false;
|
|
initue->CellAccessMode_present = false;
|
|
initue->CSG_Id_present = false;
|
|
initue->GUMMEIType_present = false;
|
|
initue->GUMMEI_ID_present = false;
|
|
initue->GW_TransportLayerAddress_present = false;
|
|
initue->LHN_ID_present = false;
|
|
initue->RelayNode_Indicator_present = false;
|
|
initue->SIPTO_L_GW_TransportLayerAddress_present = false;
|
|
initue->S_TMSI_present = false;
|
|
initue->Tunnel_Information_for_BBF_present = false;
|
|
|
|
// S_TMSI
|
|
if(has_tmsi) {
|
|
initue->S_TMSI_present = true;
|
|
initue->S_TMSI.ext = false;
|
|
initue->S_TMSI.iE_Extensions_present = false;
|
|
|
|
uint32_to_uint8(m_tmsi, initue->S_TMSI.m_TMSI.buffer);
|
|
initue->S_TMSI.mMEC.buffer[0] = mmec;
|
|
}
|
|
|
|
// ENB_UE_S1AP_ID
|
|
initue->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID;
|
|
|
|
// NAS_PDU
|
|
memcpy(initue->NAS_PDU.buffer, pdu->msg, pdu->N_bytes);
|
|
initue->NAS_PDU.n_octets = pdu->N_bytes;
|
|
|
|
// TAI
|
|
memcpy(&initue->TAI, &tai, sizeof(LIBLTE_S1AP_TAI_STRUCT));
|
|
|
|
// EUTRAN_CGI
|
|
memcpy(&initue->EUTRAN_CGI, &eutran_cgi, sizeof(LIBLTE_S1AP_EUTRAN_CGI_STRUCT));
|
|
|
|
// RRC Establishment Cause
|
|
initue->RRC_Establishment_Cause.ext = false;
|
|
initue->RRC_Establishment_Cause.e = cause;
|
|
|
|
liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg);
|
|
s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending InitialUEMessage for RNTI:0x%x", rnti);
|
|
|
|
ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes,
|
|
(struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in),
|
|
htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0);
|
|
if(n_sent == -1) {
|
|
s1ap_log->error("Failed to send InitialUEMessage for RNTI:0x%x\n", rnti);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool s1ap::send_ulnastransport(uint16_t rnti, srslte::unique_byte_buffer pdu)
|
|
{
|
|
if(!mme_connected) {
|
|
return false;
|
|
}
|
|
srslte::byte_buffer_t msg;
|
|
|
|
LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu;
|
|
tx_pdu.ext = false;
|
|
tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE;
|
|
|
|
LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage;
|
|
init->procedureCode = LIBLTE_S1AP_PROC_ID_UPLINKNASTRANSPORT;
|
|
init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UPLINKNASTRANSPORT;
|
|
|
|
LIBLTE_S1AP_MESSAGE_UPLINKNASTRANSPORT_STRUCT *ultx = &init->choice.UplinkNASTransport;
|
|
ultx->ext = false;
|
|
ultx->GW_TransportLayerAddress_present = false;
|
|
ultx->LHN_ID_present = false;
|
|
ultx->SIPTO_L_GW_TransportLayerAddress_present = false;
|
|
|
|
// MME_UE_S1AP_ID
|
|
ultx->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID;
|
|
// ENB_UE_S1AP_ID
|
|
ultx->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID;
|
|
|
|
// NAS_PDU
|
|
memcpy(ultx->NAS_PDU.buffer, pdu->msg, pdu->N_bytes);
|
|
ultx->NAS_PDU.n_octets = pdu->N_bytes;
|
|
|
|
// EUTRAN_CGI
|
|
memcpy(&ultx->EUTRAN_CGI, &eutran_cgi, sizeof(LIBLTE_S1AP_EUTRAN_CGI_STRUCT));
|
|
|
|
// TAI
|
|
memcpy(&ultx->TAI, &tai, sizeof(LIBLTE_S1AP_TAI_STRUCT));
|
|
|
|
liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg);
|
|
s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending UplinkNASTransport for RNTI:0x%x", rnti);
|
|
|
|
ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes,
|
|
(struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in),
|
|
htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0);
|
|
if(n_sent == -1) {
|
|
s1ap_log->error("Failed to send UplinkNASTransport for RNTI:0x%x\n", rnti);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool s1ap::send_uectxtreleaserequest(uint16_t rnti, LIBLTE_S1AP_CAUSE_STRUCT *cause)
|
|
{
|
|
if(!mme_connected) {
|
|
return false;
|
|
}
|
|
srslte::byte_buffer_t msg;
|
|
|
|
LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu;
|
|
tx_pdu.ext = false;
|
|
tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE;
|
|
|
|
LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage;
|
|
init->procedureCode = LIBLTE_S1AP_PROC_ID_UECONTEXTRELEASEREQUEST;
|
|
init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTRELEASEREQUEST;
|
|
|
|
LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASEREQUEST_STRUCT *req = &init->choice.UEContextReleaseRequest;
|
|
req->ext = false;
|
|
req->GWContextReleaseIndication_present = false;
|
|
|
|
// MME_UE_S1AP_ID
|
|
req->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID;
|
|
// ENB_UE_S1AP_ID
|
|
req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID;
|
|
|
|
// Cause
|
|
memcpy(&req->Cause, cause, sizeof(LIBLTE_S1AP_CAUSE_STRUCT));
|
|
|
|
liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg);
|
|
s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending UEContextReleaseRequest for RNTI:0x%x", rnti);
|
|
|
|
ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes,
|
|
(struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in),
|
|
htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0);
|
|
if(n_sent == -1) {
|
|
s1ap_log->error("Failed to send UEContextReleaseRequest for RNTI:0x%x\n", rnti);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool s1ap::send_uectxtreleasecomplete(uint16_t rnti, uint32_t mme_ue_id, uint32_t enb_ue_id)
|
|
{
|
|
if(!mme_connected) {
|
|
return false;
|
|
}
|
|
srslte::byte_buffer_t msg;
|
|
|
|
LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu;
|
|
tx_pdu.ext = false;
|
|
tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_SUCCESSFULOUTCOME;
|
|
|
|
LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *succ = &tx_pdu.choice.successfulOutcome;
|
|
succ->procedureCode = LIBLTE_S1AP_PROC_ID_UECONTEXTRELEASE;
|
|
succ->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_UECONTEXTRELEASECOMPLETE;
|
|
|
|
LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMPLETE_STRUCT *comp = &succ->choice.UEContextReleaseComplete;
|
|
comp->ext = false;
|
|
comp->CriticalityDiagnostics_present = false;
|
|
comp->UserLocationInformation_present = false;
|
|
|
|
comp->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = enb_ue_id;
|
|
comp->MME_UE_S1AP_ID.MME_UE_S1AP_ID = mme_ue_id;
|
|
|
|
liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg);
|
|
s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending UEContextReleaseComplete for RNTI:0x%x", rnti);
|
|
|
|
ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes,
|
|
(struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in),
|
|
htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0);
|
|
if(n_sent == -1) {
|
|
s1ap_log->error("Failed to send UEContextReleaseComplete for RNTI:0x%x\n", rnti);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool s1ap::send_initial_ctxt_setup_response(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res_)
|
|
{
|
|
if(!mme_connected) {
|
|
return false;
|
|
}
|
|
srslte::unique_byte_buffer buf = srslte::allocate_unique_buffer(*pool);
|
|
if (!buf) {
|
|
s1ap_log->error("Fatal Error: Couldn't allocate buffer in s1ap::send_initial_ctxt_setup_response().\n");
|
|
return false;
|
|
}
|
|
|
|
LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu;
|
|
|
|
tx_pdu.ext = false;
|
|
tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_SUCCESSFULOUTCOME;
|
|
|
|
LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *succ = &tx_pdu.choice.successfulOutcome;
|
|
succ->procedureCode = LIBLTE_S1AP_PROC_ID_INITIALCONTEXTSETUP;
|
|
succ->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_INITIALCONTEXTSETUPRESPONSE;
|
|
|
|
// Copy in the provided response message
|
|
LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res = &succ->choice.InitialContextSetupResponse;
|
|
memcpy(res, res_, sizeof(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT));
|
|
|
|
// Fill in the GTP bind address for all bearers
|
|
for(uint32_t i=0;i<res->E_RABSetupListCtxtSURes.len; i++) {
|
|
uint8_t addr[4];
|
|
inet_pton(AF_INET, args.gtp_bind_addr.c_str(), addr);
|
|
liblte_unpack(addr, 4, res->E_RABSetupListCtxtSURes.buffer[i].transportLayerAddress.buffer);
|
|
res->E_RABSetupListCtxtSURes.buffer[i].transportLayerAddress.n_bits = 32;
|
|
res->E_RABSetupListCtxtSURes.buffer[i].transportLayerAddress.ext = false;
|
|
}
|
|
|
|
// Fill in the MME and eNB IDs
|
|
res->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID;
|
|
res->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID;
|
|
|
|
liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)buf.get());
|
|
s1ap_log->info_hex(buf->msg, buf->N_bytes, "Sending InitialContextSetupResponse for RNTI:0x%x", rnti);
|
|
|
|
ssize_t n_sent = sctp_sendmsg(socket_fd, buf->msg, buf->N_bytes,
|
|
(struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in),
|
|
htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0);
|
|
|
|
if(n_sent == -1) {
|
|
s1ap_log->error("Failed to send InitialContextSetupResponse for RNTI:0x%x\n", rnti);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool s1ap::send_erab_setup_response(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res_)
|
|
{
|
|
if(!mme_connected) {
|
|
return false;
|
|
}
|
|
srslte::unique_byte_buffer buf = srslte::allocate_unique_buffer(*pool);
|
|
if (!buf) {
|
|
s1ap_log->error("Fatal Error: Couldn't allocate buffer in s1ap::send_erab_setup_response().\n");
|
|
return false;
|
|
}
|
|
|
|
LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu;
|
|
|
|
tx_pdu.ext = false;
|
|
tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_SUCCESSFULOUTCOME;
|
|
|
|
LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *succ = &tx_pdu.choice.successfulOutcome;
|
|
succ->procedureCode = LIBLTE_S1AP_PROC_ID_E_RABSETUP;
|
|
succ->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_E_RABSETUPRESPONSE;
|
|
|
|
// Copy in the provided response message
|
|
LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res = &succ->choice.E_RABSetupResponse;
|
|
memcpy(res, res_, sizeof(LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT));
|
|
|
|
// Fill in the GTP bind address for all bearers
|
|
for(uint32_t i=0;i<res->E_RABSetupListBearerSURes.len; i++) {
|
|
uint8_t addr[4];
|
|
inet_pton(AF_INET, args.gtp_bind_addr.c_str(), addr);
|
|
liblte_unpack(addr, 4, res->E_RABSetupListBearerSURes.buffer[i].transportLayerAddress.buffer);
|
|
res->E_RABSetupListBearerSURes.buffer[i].transportLayerAddress.n_bits = 32;
|
|
res->E_RABSetupListBearerSURes.buffer[i].transportLayerAddress.ext = false;
|
|
}
|
|
|
|
// Fill in the MME and eNB IDs
|
|
res->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID;
|
|
res->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID;
|
|
|
|
liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)buf.get());
|
|
s1ap_log->info_hex(buf->msg, buf->N_bytes, "Sending E_RABSetupResponse for RNTI:0x%x", rnti);
|
|
|
|
ssize_t n_sent = sctp_sendmsg(socket_fd, buf->msg, buf->N_bytes,
|
|
(struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in),
|
|
htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0);
|
|
|
|
if(n_sent == -1) {
|
|
s1ap_log->error("Failed to send E_RABSetupResponse for RNTI:0x%x\n", rnti);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool s1ap::send_initial_ctxt_setup_failure(uint16_t rnti)
|
|
{
|
|
if(!mme_connected) {
|
|
return false;
|
|
}
|
|
srslte::unique_byte_buffer buf = srslte::allocate_unique_buffer(*pool);
|
|
if (!buf) {
|
|
s1ap_log->error("Fatal Error: Couldn't allocate buffer in s1ap::send_initial_ctxt_setup_failure().\n");
|
|
return false;
|
|
}
|
|
|
|
LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu;
|
|
tx_pdu.ext = false;
|
|
tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_UNSUCCESSFULOUTCOME;
|
|
|
|
LIBLTE_S1AP_UNSUCCESSFULOUTCOME_STRUCT *unsucc = &tx_pdu.choice.unsuccessfulOutcome;
|
|
unsucc->procedureCode = LIBLTE_S1AP_PROC_ID_INITIALCONTEXTSETUP;
|
|
unsucc->choice_type = LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_INITIALCONTEXTSETUPFAILURE;
|
|
|
|
LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPFAILURE_STRUCT *fail = &unsucc->choice.InitialContextSetupFailure;
|
|
fail->ext = false;
|
|
fail->CriticalityDiagnostics_present = false;
|
|
|
|
fail->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID;
|
|
fail->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID;
|
|
|
|
fail->Cause.ext = false;
|
|
fail->Cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK;
|
|
fail->Cause.choice.radioNetwork.ext = false;
|
|
fail->Cause.choice.radioNetwork.e = LIBLTE_S1AP_CAUSERADIONETWORK_UNSPECIFIED;
|
|
|
|
liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)buf.get());
|
|
s1ap_log->info_hex(buf->msg, buf->N_bytes, "Sending InitialContextSetupFailure for RNTI:0x%x", rnti);
|
|
|
|
ssize_t n_sent = sctp_sendmsg(socket_fd, buf->msg, buf->N_bytes,
|
|
(struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in),
|
|
htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0);
|
|
|
|
if(n_sent == -1) {
|
|
s1ap_log->error("Failed to send UplinkNASTransport for RNTI:0x%x\n", rnti);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//bool s1ap::send_ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps)
|
|
//{
|
|
// srslte::byte_buffer_t msg;
|
|
|
|
// LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu;
|
|
// tx_pdu.ext = false;
|
|
// tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE;
|
|
|
|
// LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage;
|
|
// init->procedureCode = LIBLTE_S1AP_PROC_ID_UPLINKNASTRANSPORT;
|
|
// init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECAPABILITYINFOINDICATION;
|
|
|
|
// LIBLTE_S1AP_MESSAGE_UECAPABILITYINFOINDICATION_STRUCT *caps = &init->choice.UECapabilityInfoIndication;
|
|
// caps->ext = false;
|
|
// caps->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID;
|
|
// caps->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID;
|
|
// // TODO: caps->UERadioCapability.
|
|
|
|
// liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg);
|
|
// s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending UERadioCapabilityInfo for RNTI:0x%x", rnti);
|
|
|
|
// ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes,
|
|
// (struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in),
|
|
// htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0);
|
|
// if(n_sent == -1) {
|
|
// s1ap_log->error("Failed to send UplinkNASTransport for RNTI:0x%x\n", rnti);
|
|
// return false;
|
|
// }
|
|
|
|
// return true;
|
|
//}
|
|
|
|
/*******************************************************************************
|
|
/* General helpers
|
|
********************************************************************************/
|
|
|
|
bool s1ap::find_mme_ue_id(uint32_t mme_ue_id, uint16_t *rnti, uint32_t *enb_ue_id)
|
|
{
|
|
typedef std::map<uint16_t, ue_ctxt_t>::iterator it_t;
|
|
for(it_t it=ue_ctxt_map.begin(); it!=ue_ctxt_map.end(); ++it) {
|
|
if(it->second.MME_UE_S1AP_ID == mme_ue_id) {
|
|
*rnti = it->second.rnti;
|
|
*enb_ue_id = it->second.eNB_UE_S1AP_ID;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::string s1ap::get_cause(LIBLTE_S1AP_CAUSE_STRUCT *c)
|
|
{
|
|
std::string cause = liblte_s1ap_cause_choice_text[c->choice_type];
|
|
cause += " - ";
|
|
switch(c->choice_type) {
|
|
case LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK:
|
|
cause += liblte_s1ap_causeradionetwork_text[c->choice.radioNetwork.e];
|
|
break;
|
|
case LIBLTE_S1AP_CAUSE_CHOICE_TRANSPORT:
|
|
cause += liblte_s1ap_causetransport_text[c->choice.transport.e];
|
|
break;
|
|
case LIBLTE_S1AP_CAUSE_CHOICE_NAS:
|
|
cause += liblte_s1ap_causenas_text[c->choice.nas.e];
|
|
break;
|
|
case LIBLTE_S1AP_CAUSE_CHOICE_PROTOCOL:
|
|
cause += liblte_s1ap_causeprotocol_text[c->choice.protocol.e];
|
|
break;
|
|
case LIBLTE_S1AP_CAUSE_CHOICE_MISC:
|
|
cause += liblte_s1ap_causemisc_text[c->choice.misc.e];
|
|
break;
|
|
default:
|
|
cause += "unknown";
|
|
break;
|
|
}
|
|
return cause;
|
|
}
|
|
|
|
} // namespace srsenb
|