Added NAS timer functionality to the EPC. This entails:

* Adding timer creation at NAS using timerfd_create
  * Adding the ability to add and remove the timer file descriptor to the MME main select loop
  * Adding the ability to the MME for the NAS to check if a timer is running
  * Adding the timer expiry handling functionality at the MME
master
Pedro Alvarez 6 years ago committed by Andre Puschmann
parent 9463b95dc1
commit 1db959c661

@ -29,6 +29,10 @@ namespace srsepc {
class nas;
enum nas_timer_type {
T_3413,
};
/******************
* MME Interfaces *
******************/
@ -74,6 +78,20 @@ public:
virtual bool resync_sqn(uint64_t imsi, uint8_t* auts) = 0;
};
class mme_interface_nas //NAS -> MME
{
public:
virtual bool add_nas_timer(int timer_fd, enum nas_timer_type type, uint64_t imsi) = 0;
virtual bool is_nas_timer_running(enum nas_timer_type type, uint64_t imsi) = 0;
virtual bool remove_nas_timer(enum nas_timer_type type, uint64_t imsi) = 0;
};
class s1ap_interface_mme //MME -> S1AP
{
public:
virtual bool expire_nas_timer(enum nas_timer_type type, uint64_t imsi) = 0;
};
/*******************
* SPGW Interfaces *
*******************/

@ -17,6 +17,7 @@
# (default: EEA0, support: EEA1, EEA2)
# integrity_algo: Preferred integrity protection algorithm for NAS
# (default: EIA1, support: EIA1, EIA2 (EIA0 not support)
# paging_timer: Value of paging timer in seconds (T3413)
#
#####################################################################
[mme]
@ -30,6 +31,7 @@ apn = srsapn
dns_addr = 8.8.8.8
encryption_algo = EEA0
integrity_algo = EIA1
paging_timer = 2
#####################################################################
# HSS configuration

@ -49,7 +49,13 @@ typedef struct {
// gtpc_args_t gtpc_args;
} mme_args_t;
class mme : public thread
typedef struct {
int fd;
uint64_t imsi;
enum nas_timer_type type;
} mme_timer_t;
class mme : public thread, public mme_interface_nas
{
public:
static mme* get_instance(void);
@ -63,6 +69,11 @@ public:
int get_s1_mme();
void run_thread();
// Timer Methods
virtual bool add_nas_timer(int timer_fd, enum nas_timer_type type, uint64_t imsi);
virtual bool is_nas_timer_running(enum nas_timer_type type, uint64_t imsi);
virtual bool remove_nas_timer(enum nas_timer_type type, uint64_t imsi);
private:
mme();
virtual ~mme();
@ -74,7 +85,13 @@ private:
srslte::byte_buffer_pool* m_pool;
fd_set m_set;
/*Logs*/
// Timer map
std::vector<mme_timer_t> m_timers;
// Timer Methods
void handle_timer_expire(int timer_fd);
// Logs
srslte::log_filter* m_nas_log;
srslte::log_filter* m_s1ap_log;
srslte::log_filter* m_mme_gtpc_log;

@ -131,6 +131,7 @@ typedef struct {
uint8_t mme_code;
uint16_t mme_group;
uint16_t tac;
uint16_t paging_timer;
std::string apn;
std::string dns;
srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo;
@ -141,6 +142,7 @@ typedef struct {
s1ap_interface_nas* s1ap;
gtpc_interface_nas* gtpc;
hss_interface_nas* hss;
mme_interface_nas* mme;
} nas_if_t;
class nas
@ -250,6 +252,10 @@ public:
void cipher_decrypt(srslte::byte_buffer_t* pdu);
void cipher_encrypt(srslte::byte_buffer_t* pdu);
/*Timer functions*/
bool start_timer(enum nas_timer_type type);
bool expire_timer(enum nas_timer_type type);
/* UE Context */
emm_ctx_t m_emm_ctx;
ecm_ctx_t m_ecm_ctx;
@ -262,6 +268,7 @@ private:
gtpc_interface_nas* m_gtpc;
s1ap_interface_nas* m_s1ap;
hss_interface_nas* m_hss;
mme_interface_nas* m_mme;
uint16_t m_mcc;
uint16_t m_mnc;
@ -270,6 +277,13 @@ private:
uint16_t m_tac;
std::string m_apn;
std::string m_dns;
// Timers timeout values
uint16_t m_t3413;
// Timer functions
bool start_t3413();
bool expire_t3413();
};
} // namespace srsepc

@ -52,7 +52,7 @@ namespace srsepc {
const uint16_t S1MME_PORT = 36412;
class s1ap : public s1ap_interface_nas, public s1ap_interface_gtpc
class s1ap : public s1ap_interface_nas, public s1ap_interface_gtpc, public s1ap_interface_mme
{
public:
static s1ap* get_instance();
@ -113,6 +113,8 @@ public:
srslte::byte_buffer_t* nas_msg,
struct sctp_sndrcvinfo enb_sri);
virtual bool expire_nas_timer(enum nas_timer_type type, uint64_t imsi);
private:
s1ap();
virtual ~s1ap();

@ -32,20 +32,21 @@ namespace srsepc{
static const uint8_t MAX_TA=255; //Maximum TA supported
static const uint8_t MAX_BPLMN=6; //Maximum broadcasted PLMNs per TAC
typedef struct{
uint8_t mme_code;
uint16_t mme_group;
uint16_t tac; // 16-bit tac
uint16_t mcc; // BCD-coded with 0xF filler
uint16_t mnc; // BCD-coded with 0xF filler
std::string mme_bind_addr;
std::string mme_name;
std::string dns_addr;
std::string mme_apn;
bool pcap_enable;
std::string pcap_filename;
srslte::CIPHERING_ALGORITHM_ID_ENUM encryption_algo;
srslte::INTEGRITY_ALGORITHM_ID_ENUM integrity_algo;
typedef struct {
uint8_t mme_code;
uint16_t mme_group;
uint16_t tac; // 16-bit tac
uint16_t mcc; // BCD-coded with 0xF filler
uint16_t mnc; // BCD-coded with 0xF filler
uint16_t paging_timer; // Paging timer in sec (T3413)
std::string mme_bind_addr;
std::string mme_name;
std::string dns_addr;
std::string mme_apn;
bool pcap_enable;
std::string pcap_filename;
srslte::CIPHERING_ALGORITHM_ID_ENUM encryption_algo;
srslte::INTEGRITY_ALGORITHM_ID_ENUM integrity_algo;
} s1ap_args_t;
typedef struct{

@ -81,23 +81,24 @@ string config_file;
void parse_args(all_args_t* args, int argc, char* argv[])
{
string mme_name;
string mme_code;
string mme_group;
string tac;
string mcc;
string mnc;
string mme_bind_addr;
string mme_apn;
string encryption_algo;
string integrity_algo;
string spgw_bind_addr;
string sgi_if_addr;
string sgi_if_name;
string dns_addr;
string hss_db_file;
string hss_auth_algo;
string log_filename;
string mme_name;
string mme_code;
string mme_group;
string tac;
string mcc;
string mnc;
string mme_bind_addr;
string mme_apn;
string encryption_algo;
string integrity_algo;
uint16_t paging_timer;
string spgw_bind_addr;
string sgi_if_addr;
string sgi_if_name;
string dns_addr;
string hss_db_file;
string hss_auth_algo;
string log_filename;
// Command line only options
bpo::options_description general("General options");
@ -120,6 +121,7 @@ void parse_args(all_args_t* args, int argc, char* argv[])
("mme.apn", bpo::value<string>(&mme_apn)->default_value(""), "Set Access Point Name (APN) for data services")
("mme.encryption_algo", bpo::value<string>(&encryption_algo)->default_value("EEA0"), "Set preferred encryption algorithm for NAS layer ")
("mme.integrity_algo", bpo::value<string>(&integrity_algo)->default_value("EIA1"), "Set preferred integrity protection algorithm for NAS")
("mme.paging_timer", bpo::value<uint16_t>(&paging_timer)->default_value(2), "Set paging timer value in seconds (T3413)")
("hss.db_file", bpo::value<string>(&hss_db_file)->default_value("ue_db.csv"), ".csv file that stores UE's keys")
("hss.auth_algo", bpo::value<string>(&hss_auth_algo)->default_value("milenage"), "HSS uthentication algorithm.")
("spgw.gtpu_bind_addr", bpo::value<string>(&spgw_bind_addr)->default_value("127.0.0.1"), "IP address of SP-GW for the S1-U connection")
@ -262,6 +264,7 @@ void parse_args(all_args_t* args, int argc, char* argv[])
args->mme_args.s1ap_args.mme_name = mme_name;
args->mme_args.s1ap_args.dns_addr = dns_addr;
args->mme_args.s1ap_args.mme_apn = mme_apn;
args->mme_args.s1ap_args.paging_timer = paging_timer;
args->spgw_args.gtpu_bind_addr = spgw_bind_addr;
args->spgw_args.sgi_if_addr = sgi_if_addr;
args->spgw_args.sgi_if_name = sgi_if_name;

@ -136,6 +136,13 @@ void mme::run_thread()
FD_SET(s1mme, &m_set);
FD_SET(s11, &m_set);
// Add timers to select
for (std::vector<mme_timer_t>::iterator it = m_timers.begin(); it != m_timers.end(); ++it) {
FD_SET(it->fd, &m_set);
max_fd = std::max(max_fd, it->fd);
m_s1ap_log->debug("Adding Timer fd %d to fd_set\n", it->fd);
}
m_s1ap_log->debug("Waiting for S1-MME or S11 Message\n");
int n = select(max_fd + 1, &m_set, NULL, NULL, NULL);
if (n == -1) {
@ -171,10 +178,71 @@ void mme::run_thread()
pdu->N_bytes = recvfrom(s11, pdu->msg, SRSLTE_MAX_BUFFER_SIZE_BYTES, 0, NULL, NULL);
m_mme_gtpc->handle_s11_pdu(pdu);
}
// Handle NAS Timers
for (std::vector<mme_timer_t>::iterator it = m_timers.begin(); it != m_timers.end();) {
if (FD_ISSET(it->fd, &m_set)) {
m_s1ap_log->info("Timer expired\n");
uint64_t exp;
rd_sz = read(it->fd, &exp, sizeof(uint64_t));
m_s1ap->expire_nas_timer(it->type, it->imsi);
close(it->fd);
m_timers.erase(it);
} else {
++it;
}
}
} else {
m_s1ap_log->debug("No data from select.\n");
}
}
return;
}
/*
* Timer Handling
*/
bool mme::add_nas_timer(int timer_fd, nas_timer_type type, uint64_t imsi)
{
m_s1ap_log->debug("Adding NAS timer to MME. IMSI %" PRIu64 ", Type %d, Fd: %d\n", imsi, type, timer_fd);
mme_timer_t timer;
timer.fd = timer_fd;
timer.type = type;
timer.imsi = imsi;
m_timers.push_back(timer);
return true;
}
bool mme::is_nas_timer_running(nas_timer_type type, uint64_t imsi)
{
std::vector<mme_timer_t>::iterator it;
for (it = m_timers.begin(); it != m_timers.end(); ++it) {
if (it->type == type && it->imsi == imsi) {
return true; // found timer
}
}
return false;
}
bool mme::remove_nas_timer(nas_timer_type type, uint64_t imsi)
{
std::vector<mme_timer_t>::iterator it;
for (it = m_timers.begin(); it != m_timers.end(); ++it) {
if (it->type == type && it->imsi == imsi) {
break; // found timer to remove
}
}
if (it == m_timers.end()) {
m_s1ap_log->warning("Could not find timer to remove. IMSI %" PRIu64 ", Type %d\n", imsi, type);
return false;
}
// removing timer
m_s1ap_log->debug("Removing NAS timer from MME. IMSI %" PRIu64 ", Type %d, Fd: %d\n", imsi, type, it->fd);
FD_CLR(it->fd, &m_set);
close(it->fd);
m_timers.erase(it);
return true;
}
} // namespace srsepc

@ -47,6 +47,7 @@ void nas::init(nas_init_t args, nas_if_t itf, srslte::log* nas_log)
m_tac = args.tac;
m_apn = args.apn;
m_dns = args.dns;
m_t3413 = args.paging_timer;
m_sec_ctx.integ_algo = args.integ_algo;
m_sec_ctx.cipher_algo = args.cipher_algo;
@ -54,6 +55,7 @@ void nas::init(nas_init_t args, nas_if_t itf, srslte::log* nas_log)
m_s1ap = itf.s1ap;
m_gtpc = itf.gtpc;
m_hss = itf.hss;
m_mme = itf.mme;
m_nas_log = nas_log;
m_nas_log->info("NAS Context Initialized. MCC: 0x%x, MNC 0x%x\n", m_mcc, m_mnc);
}
@ -1565,4 +1567,79 @@ void nas::cipher_encrypt(srslte::byte_buffer_t* pdu)
break;
}
}
/**************************
*
* Timer related functions
*
**************************/
bool nas::start_timer(enum nas_timer_type type)
{
m_nas_log->debug("Starting NAS timer\n");
bool err = false;
switch (type) {
case T_3413:
err = start_t3413();
break;
default:
m_nas_log->error("Invalid timer type\n");
}
return err;
}
bool nas::expire_timer(enum nas_timer_type type)
{
m_nas_log->debug("NAS timer expired\n");
bool err = false;
switch (type) {
case T_3413:
err = expire_t3413();
break;
default:
m_nas_log->error("Invalid timer type\n");
}
return err;
}
// T3413 -> Paging timer
bool nas::start_t3413()
{
m_nas_log->info("Starting T3413 Timer: Timeout value %d\n", m_t3413);
if (m_emm_ctx.state != EMM_STATE_REGISTERED) {
m_nas_log->error("EMM invalid status to start T3413\n");
return false;
}
int fdt = timerfd_create(CLOCK_MONOTONIC, 0);
if (fdt < 0) {
m_nas_log->error("Error creating timer. %s\n", strerror(errno));
return false;
}
struct itimerspec t_value;
t_value.it_value.tv_sec = m_t3413;
t_value.it_value.tv_nsec = 0;
t_value.it_interval.tv_sec = 0;
t_value.it_interval.tv_nsec = 0;
if (timerfd_settime(fdt, 0, &t_value, NULL) == -1) {
m_nas_log->error("Could not set timer\n");
close(fdt);
return false;
}
m_mme->add_nas_timer(fdt, T_3413, m_emm_ctx.imsi); // TODO timers without IMSI?
return true;
}
bool nas::expire_t3413()
{
m_nas_log->info("T3413 expired -- Could not page the ue.\n");
if (m_emm_ctx.state != EMM_STATE_REGISTERED) {
m_nas_log->error("EMM invalid status upon T3413 expiration\n");
return false;
}
// Send Paging Failure to the SPGW (TODO)
return true;
}
} // namespace srsepc

@ -627,4 +627,16 @@ bool s1ap::send_downlink_nas_transport(uint32_t enb_ue_s1ap_id,
{
return m_s1ap_nas_transport->send_downlink_nas_transport(enb_ue_s1ap_id, mme_ue_s1ap_id, nas_msg, enb_sri);
}
bool s1ap::expire_nas_timer(enum nas_timer_type type, uint64_t imsi)
{
nas* nas_ctx = find_nas_ctx_from_imsi(imsi);
if (nas_ctx == NULL) {
m_s1ap_log->error("Error finding NAS context to handle timer\n");
return false;
}
bool err = nas_ctx->expire_timer(type);
return err;
}
} // namespace srsepc

@ -25,6 +25,7 @@
*/
#include "srsepc/hdr/mme/s1ap_nas_transport.h"
#include "srsepc/hdr/mme/mme.h"
#include "srsepc/hdr/mme/s1ap.h"
#include "srslte/common/int_helpers.h"
#include "srslte/common/liblte_security.h"
@ -74,20 +75,22 @@ void s1ap_nas_transport::init()
m_pool = srslte::byte_buffer_pool::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.integ_algo = m_s1ap->m_s1ap_args.integrity_algo;
m_nas_init.cipher_algo = m_s1ap->m_s1ap_args.encryption_algo;
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.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;
//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.hss = hss::get_instance();
m_nas_if.mme = mme::get_instance();
}
bool s1ap_nas_transport::handle_initial_ue_message(LIBLTE_S1AP_MESSAGE_INITIALUEMESSAGE_STRUCT* init_ue,

Loading…
Cancel
Save