Adding Downlink data notification and paging functionality to the EPC.

This entails:

  * Adding S1AP paging messaging to the MME.
  * Adding the Downlink Data Notification, Donlink Data Notification Acknoledgment, and Downlink Data Notification Failure Messages.
  * Adding the logic at the GTP-U to detect that Downlink Data notification needs to be sent.
  * Proper handling of the new GTP-C messages at the MME GTPC and SPGW GTPC classes
  * Add queuing mechanisms at the GTP-U while waiting for paging response. Queue is flushed when paging fails (timeout or other).
  * Make sure eNB's SCTP information is properly stored.
  * Make sure UE's GUTI information is properly stored.
This commit is contained in:
Pedro Alvarez 2019-04-08 13:22:01 +01:00 committed by Andre Puschmann
parent 1db959c661
commit ca603810ce
21 changed files with 761 additions and 102 deletions

View File

@ -68,14 +68,17 @@ typedef struct gtpc_header {
* Union that hold the different structures for the possible message types.
***************************************************************************/
typedef union gtpc_msg_choice {
struct gtpc_create_session_request create_session_request;
struct gtpc_create_session_response create_session_response;
struct gtpc_modify_bearer_request modify_bearer_request;
struct gtpc_modify_bearer_response modify_bearer_response;
struct gtpc_release_access_bearers_request release_access_bearers_request;
struct gtpc_release_access_bearers_response release_access_bearers_response;
struct gtpc_delete_session_request delete_session_request;
struct gtpc_delete_session_response delete_session_response;
struct gtpc_create_session_request create_session_request;
struct gtpc_create_session_response create_session_response;
struct gtpc_modify_bearer_request modify_bearer_request;
struct gtpc_modify_bearer_response modify_bearer_response;
struct gtpc_release_access_bearers_request release_access_bearers_request;
struct gtpc_release_access_bearers_response release_access_bearers_response;
struct gtpc_delete_session_request delete_session_request;
struct gtpc_delete_session_response delete_session_response;
struct gtpc_downlink_data_notification downlink_data_notification;
struct gtpc_downlink_data_notification_acknowledge downlink_data_notification_acknowledge;
struct gtpc_downlink_data_notification_failure_indication downlink_data_notification_failure_indication;
} gtpc_msg_choice_t;
/****************************************************************************

View File

@ -399,6 +399,51 @@ struct gtpc_delete_session_response {
// Private extension
};
/****************************************************************************
*
* GTP-C v2 Downlink Data Notification
* Ref: 3GPP TS 29.274 v10.14.0 Table 7.2.11.1-1
*
***************************************************************************/
struct gtpc_downlink_data_notification
{
bool cause_present;
struct gtpc_cause_ie cause;
bool eps_bearer_id_present;
uint8_t eps_bearer_id;
bool allocation_retention_priority_present;
// struct gtpc_allocation_rention_priority_ie
bool imsi_present;
uint64_t imsi;
//Private extension
};
/****************************************************************************
*
* GTP-C v2 Downlink Data Notification Acknowledge
* Ref: 3GPP TS 29.274 v10.14.0 Table 7.2.11.2-1
*
***************************************************************************/
struct gtpc_downlink_data_notification_acknowledge
{
struct gtpc_cause_ie cause;
//Data Notification Delay
//Recovery
//Private extension
};
/****************************************************************************
*
* GTP-C v2 Downlink Data Notification Failure Indication
* Ref: 3GPP TS 29.274 v10.14.0 Table 7.2.11.3-1
*
***************************************************************************/
struct gtpc_downlink_data_notification_failure_indication
{
struct gtpc_cause_ie cause;
//Private extension
};
/****************************************************************************
*
* GTP-C v2 Release Access Bearers Request

View File

@ -24,6 +24,7 @@
#include "srslte/asn1/gtpc_ies.h"
#include "srslte/common/common.h"
#include <netinet/sctp.h>
#include <queue>
namespace srsepc {
@ -42,6 +43,8 @@ public:
virtual bool send_create_session_request(uint64_t imsi) = 0;
virtual bool send_modify_bearer_request(uint64_t imsi, uint16_t erab_to_modify, srslte::gtp_fteid_t* enb_fteid) = 0;
virtual bool send_delete_session_request(uint64_t imsi) = 0;
virtual bool send_downlink_data_notification_failure_indication(uint64_t imsi,
enum srslte::gtpc_cause_value cause) = 0;
};
class s1ap_interface_gtpc // GTP-C -> S1AP
@ -102,6 +105,16 @@ public:
virtual bool modify_gtpu_tunnel(in_addr_t ue_ipv4, srslte::gtpc_f_teid_ie dw_user_fteid, uint32_t up_ctrl_teid) = 0;
virtual bool delete_gtpu_tunnel(in_addr_t ue_ipv4) = 0;
virtual bool delete_gtpc_tunnel(in_addr_t ue_ipv4) = 0;
virtual void send_all_queued_packets(srslte::gtp_fteid_t dw_user_fteid,
std::queue<srslte::byte_buffer_t*>& pkt_queue) = 0;
};
class gtpc_interface_gtpu //GTP-U -> GTP-C
{
public:
virtual bool queue_downlink_packet(uint32_t spgw_ctr_teid, srslte::byte_buffer_t *msg) = 0;
virtual bool send_downlink_data_notification(uint32_t spgw_ctr_teid) = 0;
};
} // namespace srsepc

View File

@ -44,20 +44,21 @@ paging_timer = 2
auth_algo = xor
db_file = user_db.csv
#####################################################################
# SP-GW configuration
#
# gtpu_bind_addr: GTP-U bind address.
# sgi_if_addr: SGi TUN interface IP address.
# sgi_if_name: SGi TUN interface name.
# max_paging_queue: Maximum packets in paging queue (per UE).
#
#####################################################################
[spgw]
gtpu_bind_addr = 127.0.1.100
sgi_if_addr = 172.16.0.1
sgi_if_name = srs_spgw_sgi
gtpu_bind_addr = 127.0.1.100
sgi_if_addr = 172.16.0.1
sgi_if_name = srs_spgw_sgi
max_paging_queue = 100
####################################################################
# PCAP configuration

View File

@ -60,6 +60,9 @@ public:
void handle_modify_bearer_response(srslte::gtpc_pdu* mb_resp_pdu);
void send_release_access_bearers_request(uint64_t imsi);
virtual bool send_delete_session_request(uint64_t imsi);
bool handle_downlink_data_notification(srslte::gtpc_pdu* dl_not_pdu);
void send_downlink_data_notification_acknowledge(uint64_t imsi, enum srslte::gtpc_cause_value cause);
virtual bool send_downlink_data_notification_failure_indication(uint64_t imsi, enum srslte::gtpc_cause_value cause);
int get_s11();
@ -70,10 +73,7 @@ private:
srslte::log_filter* m_mme_gtpc_log;
srslte::byte_buffer_pool* m_pool;
s1ap* m_s1ap;
spgw* m_spgw;
in_addr_t m_mme_gtpc_ip;
s1ap* m_s1ap;
uint32_t m_next_ctrl_teid;
std::map<uint32_t, uint64_t> m_mme_ctr_teid_to_imsi;
@ -81,6 +81,7 @@ private:
int m_s11;
struct sockaddr_un m_mme_addr, m_spgw_addr;
in_addr_t m_mme_gtpc_ip;
bool init_s11();
uint32_t get_new_ctrl_teid();

View File

@ -31,6 +31,7 @@
#include "s1ap_ctx_mngmt_proc.h"
#include "s1ap_mngmt_proc.h"
#include "s1ap_nas_transport.h"
#include "s1ap_paging.h"
#include "srsepc/hdr/hss/hss.h"
#include "srslte/asn1/gtpc.h"
#include "srslte/asn1/liblte_mme.h"
@ -102,8 +103,10 @@ public:
s1ap_mngmt_proc* m_s1ap_mngmt_proc;
s1ap_nas_transport* m_s1ap_nas_transport;
s1ap_ctx_mngmt_proc* m_s1ap_ctx_mngmt_proc;
s1ap_paging* m_s1ap_paging;
std::map<uint32_t, uint64_t> m_tmsi_to_imsi;
std::map<uint32_t, uint64_t> m_tmsi_to_imsi;
std::map<uint16_t, enb_ctx_t*> m_active_enbs;
// Interfaces
virtual bool send_initial_context_setup_request(uint64_t imsi, uint16_t erab_to_setup);
@ -112,6 +115,7 @@ public:
uint32_t mme_ue_s1ap_id,
srslte::byte_buffer_t* nas_msg,
struct sctp_sndrcvinfo enb_sri);
virtual bool send_paging(uint64_t imsi, uint16_t erab_to_setup);
virtual bool expire_nas_timer(enum nas_timer_type type, uint64_t imsi);
@ -126,7 +130,6 @@ private:
hss_interface_nas* m_hss;
int m_s1mme;
std::map<uint16_t, enb_ctx_t*> m_active_enbs;
std::map<int32_t, uint16_t> m_sctp_to_enb_id;
std::map<int32_t, std::set<uint32_t> > m_enb_assoc_to_ue_ids;
@ -136,7 +139,7 @@ private:
uint32_t m_next_mme_ue_s1ap_id;
uint32_t m_next_m_tmsi;
// FIXME the GTP-C should be moved to the MME class, when the packaging of GTP-C messages is done.
// GTP-C Interface
mme_gtpc* m_mme_gtpc;
// PCAP

View File

@ -0,0 +1,65 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2017 Software Radio Systems Limited
*
* \section LICENSE
*
* 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/.
*
*/
#ifndef SRSEPC_S1AP_PAGING_H
#define SRSEPC_S1AP_PAGING_H
#include "s1ap_common.h"
#include "srslte/asn1/liblte_s1ap.h"
#include "srslte/common/buffer_pool.h"
#include "srslte/common/common.h"
#include "srslte/common/log_filter.h"
namespace srsepc {
class s1ap;
class mme;
class s1ap_paging
{
public:
static s1ap_paging* m_instance;
static s1ap_paging* get_instance(void);
static void cleanup(void);
void init(void);
// Packing/unpacking helper functions
bool send_paging(uint64_t imsi, uint16_t erab_to_setup);
private:
s1ap_paging();
virtual ~s1ap_paging();
mme* m_mme;
s1ap* m_s1ap;
srslte::log_filter* m_s1ap_log;
s1ap_args_t m_s1ap_args;
srslte::byte_buffer_pool* m_pool;
};
} // namespace srsepc
#endif // SRSEPC_S1AP_PAGING_H

View File

@ -34,7 +34,7 @@
namespace srsepc {
class spgw::gtpc
class spgw::gtpc : public gtpc_interface_gtpu
{
public:
gtpc();
@ -60,10 +60,20 @@ public:
const srslte::gtpc_delete_session_request& del_req);
void handle_release_access_bearers_request(const srslte::gtpc_header& header,
const srslte::gtpc_release_access_bearers_request& rel_req);
void
handle_downlink_data_notification_acknowledge(const srslte::gtpc_header& header,
const srslte::gtpc_downlink_data_notification_acknowledge& not_ack);
void handle_downlink_data_notification_failure_indication(
const srslte::gtpc_header& header, const srslte::gtpc_downlink_data_notification_failure_indication& not_fail);
virtual bool queue_downlink_packet(uint32_t spgw_ctr_teid, srslte::byte_buffer_t* msg);
virtual bool send_downlink_data_notification(uint32_t spgw_ctr_teid);
spgw_tunnel_ctx_t* create_gtpc_ctx(const srslte::gtpc_create_session_request& cs_req);
bool delete_gtpc_ctx(uint32_t ctrl_teid);
bool free_all_queued_packets(spgw_tunnel_ctx_t* tunnel_ctx);
spgw* m_spgw;
gtpu_interface_gtpc* m_gtpu;
@ -73,6 +83,7 @@ public:
uint32_t m_h_next_ue_ip;
uint64_t m_next_ctrl_teid;
uint64_t m_next_user_teid;
uint32_t m_max_paging_queue;
std::map<uint64_t, uint32_t> m_imsi_to_ctr_teid; // IMSI to control TEID map. Important to check if UE
// is previously connected

View File

@ -32,56 +32,67 @@
#include "srslte/common/buffer_pool.h"
#include "srslte/interfaces/epc_interfaces.h"
#include <cstddef>
#include <queue>
namespace srsepc {
class spgw::gtpu : public gtpu_interface_gtpc {
public:
class spgw::gtpu : public gtpu_interface_gtpc
{
public:
gtpu();
virtual ~gtpu();
int init(spgw_args_t *args, spgw *spgw, srslte::log_filter *gtpu_log);
int init(spgw_args_t* args, spgw* spgw, gtpc_interface_gtpu* gtpc, srslte::log_filter* gtpu_log);
void stop();
srslte::error_t init_sgi(spgw_args_t *args);
srslte::error_t init_s1u(spgw_args_t *args);
int get_sgi();
int get_s1u();
srslte::error_t init_sgi(spgw_args_t* args);
srslte::error_t init_s1u(spgw_args_t* args);
int get_sgi();
int get_s1u();
void handle_sgi_pdu(srslte::byte_buffer_t *msg);
void handle_s1u_pdu(srslte::byte_buffer_t *msg);
void send_s1u_pdu(srslte::gtp_fteid_t enb_fteid, srslte::byte_buffer_t *msg);
void handle_sgi_pdu(srslte::byte_buffer_t* msg);
void handle_s1u_pdu(srslte::byte_buffer_t* msg);
void send_s1u_pdu(srslte::gtp_fteid_t enb_fteid, srslte::byte_buffer_t* msg);
virtual in_addr_t get_s1u_addr();
virtual bool modify_gtpu_tunnel(in_addr_t ue_ipv4, srslte::gtp_fteid_t dw_user_fteid, uint32_t up_ctr_fteid);
virtual bool delete_gtpu_tunnel(in_addr_t ue_ipv4);
virtual bool delete_gtpc_tunnel(in_addr_t ue_ipv4);
virtual void send_all_queued_packets(srslte::gtp_fteid_t dw_user_fteid,
std::queue<srslte::byte_buffer_t*>& pkt_queue);
std::string gtpu_ntoa(uint32_t addr);
spgw *m_spgw;
spgw* m_spgw;
gtpc_interface_gtpu* m_gtpc;
bool m_sgi_up;
int m_sgi;
int m_sgi;
bool m_s1u_up;
int m_s1u;
bool m_s1u_up;
int m_s1u;
sockaddr_in m_s1u_addr;
std::map<in_addr_t, srslte::gtpc_f_teid_ie> m_ip_to_usr_teid; // Map IP to User-plane TEID for downlink traffic
std::map<in_addr_t, srslte::gtp_fteid_t> m_ip_to_usr_teid; // Map IP to User-plane TEID for downlink traffic
std::map<in_addr_t, uint32_t> m_ip_to_ctr_teid; // IP to control TEID map. Important to check if
// UE is attached without an active user-plane
// for downlink notifications.
srslte::log_filter *m_gtpu_log;
srslte::log_filter* m_gtpu_log;
private:
srslte::byte_buffer_pool *m_pool;
srslte::byte_buffer_pool* m_pool;
};
inline int spgw::gtpu::get_sgi(){
inline int spgw::gtpu::get_sgi()
{
return m_sgi;
}
}
inline int spgw::gtpu::get_s1u(){
inline int spgw::gtpu::get_s1u()
{
return m_s1u;
}
}
inline in_addr_t spgw::gtpu::get_s1u_addr()
{
@ -89,17 +100,18 @@ inline in_addr_t spgw::gtpu::get_s1u_addr()
}
// Helper function to return a string from IPv4 address for easy printing
inline std::string spgw::gtpu::gtpu_ntoa(uint32_t addr){
char tmp_str[INET_ADDRSTRLEN+1];
inline std::string spgw::gtpu::gtpu_ntoa(uint32_t addr)
{
char tmp_str[INET_ADDRSTRLEN + 1];
bzero(tmp_str, sizeof(tmp_str));
struct in_addr tmp_addr;
tmp_addr.s_addr = addr;
tmp_addr.s_addr = addr;
const char* tmp_ptr = inet_ntop(AF_INET, &tmp_addr, tmp_str, INET_ADDRSTRLEN);
if(tmp_ptr == NULL){
if (tmp_ptr == NULL) {
return std::string("Invalid IPv4 address");
}
return std::string(tmp_str);
}
}
}// namespace srsepc
} // namespace srsepc
#endif // SRSEPC_GTPU_H

View File

@ -40,6 +40,7 @@
#include "srslte/common/logger_file.h"
#include "srslte/common/threads.h"
#include <cstddef>
#include <queue>
namespace srsepc {
@ -51,16 +52,19 @@ typedef struct {
std::string gtpu_bind_addr;
std::string sgi_if_addr;
std::string sgi_if_name;
uint32_t max_paging_queue;
} spgw_args_t;
typedef struct spgw_tunnel_ctx {
uint64_t imsi;
in_addr_t ue_ipv4;
uint8_t ebi;
srslte::gtp_fteid_t up_ctrl_fteid;
srslte::gtp_fteid_t up_user_fteid;
srslte::gtp_fteid_t dw_ctrl_fteid;
srslte::gtp_fteid_t dw_user_fteid;
uint64_t imsi;
in_addr_t ue_ipv4;
uint8_t ebi;
srslte::gtp_fteid_t up_ctrl_fteid;
srslte::gtp_fteid_t up_user_fteid;
srslte::gtp_fteid_t dw_ctrl_fteid;
srslte::gtp_fteid_t dw_user_fteid;
bool paging_pending;
std::queue<srslte::byte_buffer_t*> paging_queue;
} spgw_tunnel_ctx_t;
class spgw : public thread

View File

@ -92,6 +92,7 @@ void parse_args(all_args_t* args, int argc, char* argv[])
string encryption_algo;
string integrity_algo;
uint16_t paging_timer;
uint32_t max_paging_queue;
string spgw_bind_addr;
string sgi_if_addr;
string sgi_if_name;
@ -127,6 +128,7 @@ void parse_args(all_args_t* args, int argc, char* argv[])
("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")
("spgw.sgi_if_addr", bpo::value<string>(&sgi_if_addr)->default_value("176.16.0.1"), "IP address of TUN interface for the SGi connection")
("spgw.sgi_if_name", bpo::value<string>(&sgi_if_name)->default_value("srs_spgw_sgi"), "Name of TUN interface for the SGi connection")
("spgw.max_paging_queue", bpo::value<uint32_t>(&max_paging_queue)->default_value(100), "Max number of packets in paging queue")
("pcap.enable", bpo::value<bool>(&args->mme_args.s1ap_args.pcap_enable)->default_value(false), "Enable S1AP PCAP")
("pcap.filename", bpo::value<string>(&args->mme_args.s1ap_args.pcap_filename)->default_value("/tmp/epc.pcap"), "PCAP filename")
@ -268,6 +270,7 @@ void parse_args(all_args_t* args, int argc, char* argv[])
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;
args->spgw_args.max_paging_queue = max_paging_queue;
args->hss_args.db_file = hss_db_file;
args->hss_args.auth_algo = hss_auth_algo;

View File

@ -204,6 +204,7 @@ void mme::run_thread()
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;

View File

@ -74,7 +74,6 @@ bool mme_gtpc::init(srslte::log_filter* mme_gtpc_log)
m_next_ctrl_teid = 1;
m_s1ap = s1ap::get_instance();
m_spgw = spgw::get_instance();
if (!init_s11()) {
m_mme_gtpc_log->error("Error Initializing MME S11 Interface\n");
@ -158,6 +157,9 @@ void mme_gtpc::handle_s11_pdu(srslte::byte_buffer_t *msg)
case srslte::GTPC_MSG_TYPE_MODIFY_BEARER_RESPONSE:
handle_modify_bearer_response(pdu);
break;
case srslte::GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION:
handle_downlink_data_notification(pdu);
break;
default:
m_mme_gtpc_log->error("Unhandled GTP-C Message type\n");
}
@ -437,4 +439,86 @@ void mme_gtpc::send_release_access_bearers_request(uint64_t imsi)
return;
}
bool mme_gtpc::handle_downlink_data_notification(srslte::gtpc_pdu* dl_not_pdu)
{
uint32_t mme_ctrl_teid = dl_not_pdu->header.teid;
srslte::gtpc_downlink_data_notification* dl_not = &dl_not_pdu->choice.downlink_data_notification;
std::map<uint32_t, uint64_t>::iterator imsi_it = m_mme_ctr_teid_to_imsi.find(mme_ctrl_teid);
if (imsi_it == m_mme_ctr_teid_to_imsi.end()) {
m_mme_gtpc_log->error("Could not find IMSI from control TEID\n");
return false;
}
if (!dl_not->eps_bearer_id_present) {
m_mme_gtpc_log->error("No EPS bearer Id in downlink data notification\n");
return false;
}
uint8_t ebi = dl_not->eps_bearer_id;
m_mme_gtpc_log->debug("Downlink Data Notification -- IMSI: %lu, EBI %d\n", imsi_it->second, ebi);
m_s1ap->send_paging(imsi_it->second, ebi);
return true;
}
void mme_gtpc::send_downlink_data_notification_acknowledge(uint64_t imsi, enum srslte::gtpc_cause_value cause)
{
m_mme_gtpc_log->debug("Sending GTP-C Data Notification Acknowledge. Cause %d\n", cause);
srslte::gtpc_pdu not_ack_pdu;
srslte::gtp_fteid_t sgw_ctr_fteid;
bzero(&not_ack_pdu, sizeof(srslte::gtpc_pdu));
// get s-gw ctr teid
std::map<uint64_t, gtpc_ctx_t>::iterator it_ctx = m_imsi_to_gtpc_ctx.find(imsi);
if (it_ctx == m_imsi_to_gtpc_ctx.end()) {
m_mme_gtpc_log->error("could not find gtp-c context to remove\n");
return;
}
sgw_ctr_fteid = it_ctx->second.sgw_ctr_fteid;
// set gtp-c header
srslte::gtpc_header* header = &not_ack_pdu.header;
header->teid_present = true;
header->teid = sgw_ctr_fteid.teid;
header->type = srslte::GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION_ACKNOWLEDGE;
srslte::gtpc_downlink_data_notification_acknowledge* not_ack =
&not_ack_pdu.choice.downlink_data_notification_acknowledge;
m_mme_gtpc_log->info("gtp-c downlink data notification acknowledge -- s-gw control teid %d\n", sgw_ctr_fteid.teid);
// send msg to spgw
send_s11_pdu(not_ack_pdu);
return;
}
bool mme_gtpc::send_downlink_data_notification_failure_indication(uint64_t imsi, enum srslte::gtpc_cause_value cause)
{
m_mme_gtpc_log->debug("Sending GTP-C Data Notification Failure Indication. Cause %d\n", cause);
srslte::gtpc_pdu not_fail_pdu;
srslte::gtp_fteid_t sgw_ctr_fteid;
bzero(&not_fail_pdu, sizeof(srslte::gtpc_pdu));
// get s-gw ctr teid
std::map<uint64_t, gtpc_ctx_t>::iterator it_ctx = m_imsi_to_gtpc_ctx.find(imsi);
if (it_ctx == m_imsi_to_gtpc_ctx.end()) {
m_mme_gtpc_log->error("could not find gtp-c context to send paging failure\n");
return false;
}
sgw_ctr_fteid = it_ctx->second.sgw_ctr_fteid;
// set gtp-c header
srslte::gtpc_header* header = &not_fail_pdu.header;
header->teid_present = true;
header->teid = sgw_ctr_fteid.teid;
header->type = srslte::GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION_FAILURE_INDICATION;
srslte::gtpc_downlink_data_notification_failure_indication* not_fail =
&not_fail_pdu.choice.downlink_data_notification_failure_indication;
not_fail->cause.cause_value = cause;
m_mme_gtpc_log->info("Downlink Data Notification Failure Indication -- SP-GW control teid %d\n", sgw_ctr_fteid.teid);
// send msg to spgw
send_s11_pdu(not_fail_pdu);
return true;
}
} // namespace srsepc

View File

@ -556,6 +556,7 @@ bool nas::handle_service_request(uint32_t m_tmsi,
s1ap_interface_nas* s1ap = itf.s1ap;
hss_interface_nas* hss = itf.hss;
gtpc_interface_nas* gtpc = itf.gtpc;
mme_interface_nas* mme = itf.mme;
LIBLTE_ERROR_ENUM err = liblte_mme_unpack_service_request_msg((LIBLTE_BYTE_MSG_STRUCT*)nas_rx, &service_req);
if (err != LIBLTE_SUCCESS) {
@ -629,7 +630,7 @@ bool nas::handle_service_request(uint32_t m_tmsi,
nas_log->error("UE has no valid IP assigned upon reception of service request");
}
nas_log->console("UE previously assigned IP: %s", inet_ntoa(emm_ctx->ue_ip));
nas_log->console("UE previously assigned IP: %s\n", inet_ntoa(emm_ctx->ue_ip));
// Re-generate K_eNB
srslte::security_generate_k_enb(sec_ctx->k_asme, sec_ctx->ul_nas_count, sec_ctx->k_enb);
@ -638,6 +639,11 @@ bool nas::handle_service_request(uint32_t m_tmsi,
nas_log->info_hex(sec_ctx->k_enb, 32, "Key eNodeB (k_enb)\n");
nas_log->console("UE Ctr TEID %d\n", emm_ctx->sgw_ctrl_fteid.teid);
// Stop T3413 if running
if (mme->is_nas_timer_running(T_3413, emm_ctx->imsi)) {
mme->remove_nas_timer(T_3413, emm_ctx->imsi);
}
// Save UE ctx to MME UE S1AP id
s1ap->add_nas_ctx_to_mme_ue_s1ap_id_map(nas_ctx);
s1ap->send_initial_context_setup_request(imsi, 5);
@ -1232,6 +1238,8 @@ bool nas::pack_attach_accept(srslte::byte_buffer_t* nas_buffer)
attach_accept.guti.guti.mcc, attach_accept.guti.guti.mnc, attach_accept.guti.guti.mme_group_id,
attach_accept.guti.guti.mme_code, attach_accept.guti.guti.m_tmsi);
memcpy(&m_sec_ctx.guti, &attach_accept.guti, sizeof(LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT));
// Set up LAI for combined EPS/IMSI attach
attach_accept.lai_present = true;
attach_accept.lai.mcc = mcc;
@ -1634,11 +1642,13 @@ bool nas::start_t3413()
bool nas::expire_t3413()
{
m_nas_log->info("T3413 expired -- Could not page the ue.\n");
m_nas_log->console("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)
// Send Paging Failure to the SPGW
m_gtpc->send_downlink_data_notification_failure_indication(m_emm_ctx.imsi, srslte::GTPC_CAUSE_VALUE_UE_NOT_RESPONDING);
return true;
}

View File

@ -88,6 +88,8 @@ int s1ap::init(s1ap_args_t s1ap_args, srslte::log_filter* nas_log, srslte::log_f
m_s1ap_nas_transport->init();
m_s1ap_ctx_mngmt_proc = s1ap_ctx_mngmt_proc::get_instance(); // Context Management Procedures
m_s1ap_ctx_mngmt_proc->init();
m_s1ap_paging = s1ap_paging::get_instance(); // Paging
m_s1ap_paging->init();
// Get pointer to GTP-C class
m_mme_gtpc = mme_gtpc::get_instance();
@ -596,7 +598,15 @@ void s1ap::print_enb_ctx_info(const std::string& prefix, const enb_ctx_t& enb_ct
/*
* Interfaces
*/
/* GTP-C || NAS -> S1AP interface */
// GTP-C -> S1AP interface
bool s1ap::send_paging(uint64_t imsi, uint16_t erab_to_setup)
{
m_s1ap_paging->send_paging(imsi, erab_to_setup);
return true;
}
// GTP-C || NAS -> S1AP interface
bool s1ap::send_initial_context_setup_request(uint64_t imsi, uint16_t erab_to_setup)
{
nas* nas_ctx = find_nas_ctx_from_imsi(imsi);
@ -608,7 +618,7 @@ bool s1ap::send_initial_context_setup_request(uint64_t imsi, uint16_t erab_to_se
return true;
}
/* NAS -> S1AP interface */
// NAS -> S1AP interface
bool s1ap::send_ue_context_release_command(uint32_t mme_ue_s1ap_id)
{
nas* nas_ctx = find_nas_ctx_from_mme_ue_s1ap_id(mme_ue_s1ap_id);

View File

@ -84,6 +84,9 @@ bool s1ap_mngmt_proc::handle_s1_setup_request(LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST
return false;
}
//Store SCTP sendrecv info
memcpy(&enb_ctx.sri, enb_sri,sizeof(struct sctp_sndrcvinfo));
// Log S1 Setup Request Info
m_s1ap_log->console("Received S1 Setup Request.\n");
m_s1ap->print_enb_ctx_info(std::string("S1 Setup Request"), enb_ctx);

View File

@ -24,6 +24,7 @@
*
*/
#include "srsepc/hdr/mme/mme.h"
#include "srsepc/hdr/mme/s1ap_nas_transport.h"
#include "srsepc/hdr/mme/mme.h"
#include "srsepc/hdr/mme/s1ap.h"

View File

@ -0,0 +1,164 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2017 Software Radio Systems Limited
*
* \section LICENSE
*
* 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 "srsepc/hdr/mme/s1ap_paging.h"
#include "srsepc/hdr/mme/mme.h"
#include "srsepc/hdr/mme/s1ap.h"
#include "srslte/common/bcd_helpers.h"
#include "srslte/common/int_helpers.h"
namespace srsepc {
s1ap_paging* s1ap_paging::m_instance = NULL;
pthread_mutex_t s1ap_paging_instance_mutex = PTHREAD_MUTEX_INITIALIZER;
s1ap_paging::s1ap_paging()
{
return;
}
s1ap_paging::~s1ap_paging()
{
return;
}
s1ap_paging* s1ap_paging::get_instance(void)
{
pthread_mutex_lock(&s1ap_paging_instance_mutex);
if (NULL == m_instance) {
m_instance = new s1ap_paging();
}
pthread_mutex_unlock(&s1ap_paging_instance_mutex);
return (m_instance);
}
void s1ap_paging::cleanup(void)
{
pthread_mutex_lock(&s1ap_paging_instance_mutex);
if (NULL != m_instance) {
delete m_instance;
m_instance = NULL;
}
pthread_mutex_unlock(&s1ap_paging_instance_mutex);
}
void s1ap_paging::init(void)
{
m_s1ap = s1ap::get_instance();
m_mme = mme::get_instance();
m_s1ap_log = m_s1ap->m_s1ap_log;
m_s1ap_args = m_s1ap->m_s1ap_args;
m_pool = srslte::byte_buffer_pool::get_instance();
}
bool s1ap_paging::send_paging(uint64_t imsi, uint16_t erab_to_setup)
{
m_s1ap_log->info("Preparing to Page UE -- IMSI %lu\n", imsi);
// Prepare reply PDU
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_PAGING;
init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_PAGING;
LIBLTE_S1AP_MESSAGE_PAGING_STRUCT* paging = &init->choice.Paging;
// Getting UE NAS Context
nas* nas_ctx = m_s1ap->find_nas_ctx_from_imsi(imsi);
if (nas_ctx == NULL) {
m_s1ap_log->error("Could not find UE to page NAS context\n");
return false;
}
// UE Identity Index
uint16_t ue_index = imsi % 1024; // LIBLTE_S1AP_UEIDENTITYINDEXVALUE_BIT_STRING_LEN == 10
uint8_t* tmp_ptr = paging->UEIdentityIndexValue.buffer;
liblte_value_2_bits(ue_index, &tmp_ptr, 10);
// UE Paging Id
paging->UEPagingID.choice_type = LIBLTE_S1AP_UEPAGINGID_CHOICE_S_TMSI;
paging->UEPagingID.choice.s_TMSI.ext = false;
paging->UEPagingID.choice.s_TMSI.mMEC.buffer[0] = m_s1ap->m_s1ap_args.mme_code;
uint32_t m_tmsi = nas_ctx->m_sec_ctx.guti.m_tmsi;
srslte::uint32_to_uint8(m_tmsi, paging->UEPagingID.choice.s_TMSI.m_TMSI.buffer);
paging->UEPagingID.choice.s_TMSI.iE_Extensions_present = false;
// Paging DRX
paging->pagingDRX_present = false;
// CMDomain
paging->CNDomain = LIBLTE_S1AP_CNDOMAIN_PS;
// TAI List
paging->TAIList.len = 1;
paging->TAIList.buffer[0].ext = false;
paging->TAIList.buffer[0].tAI.ext = false;
uint32_t plmn = htonl(m_s1ap->get_plmn()); // LIBLTE_S1AP_TBCD_STRING_OCTET_STRING_LEN == 3
paging->TAIList.buffer[0].tAI.pLMNidentity.buffer[0] = ((uint8_t*)&plmn)[1];
paging->TAIList.buffer[0].tAI.pLMNidentity.buffer[1] = ((uint8_t*)&plmn)[2];
paging->TAIList.buffer[0].tAI.pLMNidentity.buffer[2] = ((uint8_t*)&plmn)[3];
uint16_t tac = htons(m_s1ap->m_s1ap_args.tac); // LIBLTE_S1AP_TAC_OCTET_STRING_LEN == 2
memcpy(paging->TAIList.buffer[0].tAI.tAC.buffer, &tac, sizeof(uint16_t));
paging->TAIList.buffer[0].tAI.iE_Extensions_present = false;
paging->TAIList.buffer[0].iE_Extensions_present = false;
// CSG Id List
paging->CSG_IdList_present = false;
// Paging Priority
paging->PagingPriority_present = false;
// Start T3413
if (!nas_ctx->start_timer(T_3413)) {
m_s1ap_log->error("Could not start T3413 -- Aborting paging\n");
// TODO Send data notification failure to SPGW
return false;
}
// Send Paging to eNBs
m_s1ap_log->info("Paging UE -- M-TMSI :0x%x\n", m_tmsi);
srslte::byte_buffer_t* reply_buffer = m_pool->allocate();
LIBLTE_ERROR_ENUM err = liblte_s1ap_pack_s1ap_pdu(&pdu, (LIBLTE_BYTE_MSG_STRUCT*)reply_buffer);
if (err != LIBLTE_SUCCESS) {
m_s1ap_log->error("Could not pack Paging Message\n");
m_pool->deallocate(reply_buffer);
return false;
}
for (std::map<uint16_t, enb_ctx_t*>::iterator it = m_s1ap->m_active_enbs.begin(); it != m_s1ap->m_active_enbs.end();
it++) {
enb_ctx_t* enb_ctx = it->second;
if (!m_s1ap->s1ap_tx_pdu(reply_buffer, &enb_ctx->sri)) {
m_s1ap_log->error("Error paging to eNB. eNB Id: 0x%x.\n", enb_ctx->enb_id);
m_pool->deallocate(reply_buffer);
return false;
}
}
return true;
}
} // namespace srsepc

View File

@ -44,7 +44,7 @@ namespace srsepc {
* comminication with the MME
*
**********************************************/
spgw::gtpc::gtpc() : m_h_next_ue_ip(0), m_next_ctrl_teid(1), m_next_user_teid(1)
spgw::gtpc::gtpc() : m_h_next_ue_ip(0), m_next_ctrl_teid(1), m_next_user_teid(1), m_max_paging_queue(0)
{
return;
}
@ -80,6 +80,9 @@ int spgw::gtpc::init(spgw_args_t* args, spgw* spgw, gtpu_interface_gtpc* gtpu, s
return -1;
}
// Limit paging queue
m_max_paging_queue = args->max_paging_queue;
m_gtpc_log->info("SPGW S11 Initialized.\n");
m_gtpc_log->console("SPGW S11 Initialized.\n");
return 0;
@ -151,8 +154,8 @@ bool spgw::gtpc::send_s11_pdu(const srslte::gtpc_pdu& pdu)
void spgw::gtpc::handle_s11_pdu(srslte::byte_buffer_t* msg)
{
//FIXME add deserialization code here
srslte::gtpc_pdu *pdu = (srslte::gtpc_pdu*) msg->msg;
// FIXME add deserialization code here
srslte::gtpc_pdu* pdu = (srslte::gtpc_pdu*)msg->msg;
m_gtpc_log->console("Received GTP-C PDU. Message type: %s\n", srslte::gtpc_msg_type_to_str(pdu->header.type));
m_gtpc_log->debug("Received GTP-C PDU. Message type: %s\n", srslte::gtpc_msg_type_to_str(pdu->header.type));
switch (pdu->header.type) {
@ -168,6 +171,13 @@ void spgw::gtpc::handle_s11_pdu(srslte::byte_buffer_t* msg)
case srslte::GTPC_MSG_TYPE_RELEASE_ACCESS_BEARERS_REQUEST:
handle_release_access_bearers_request(pdu->header, pdu->choice.release_access_bearers_request);
break;
case srslte::GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION_ACKNOWLEDGE:
handle_downlink_data_notification_acknowledge(pdu->header, pdu->choice.downlink_data_notification_acknowledge);
break;
case srslte::GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION_FAILURE_INDICATION:
handle_downlink_data_notification_failure_indication(pdu->header,
pdu->choice.downlink_data_notification_failure_indication);
break;
default:
m_gtpc_log->error("Unhandled GTP-C message type\n");
}
@ -191,7 +201,7 @@ void spgw::gtpc::handle_create_session_request(const struct srslte::gtpc_create_
tunnel_ctx = create_gtpc_ctx(cs_req);
// Create session response message
srslte::gtpc_pdu cs_resp_pdu;
srslte::gtpc_pdu cs_resp_pdu;
srslte::gtpc_header* header = &cs_resp_pdu.header;
srslte::gtpc_create_session_response* cs_resp = &cs_resp_pdu.choice.create_session_response;
@ -264,6 +274,14 @@ void spgw::gtpc::handle_modify_bearer_request(const struct srslte::gtpc_header&
// Setup IP to F-TEID map
m_gtpu->modify_gtpu_tunnel(tunnel_ctx->ue_ipv4, tunnel_ctx->dw_user_fteid, tunnel_ctx->up_ctrl_fteid.teid);
// Mark paging as done & send queued packets
if (tunnel_ctx->paging_pending == true) {
tunnel_ctx->paging_pending = false;
m_gtpc_log->debug("Modify Bearer Request received after Downling Data Notification was sent\n");
m_gtpc_log->console("Modify Bearer Request received after Downling Data Notification was sent\n");
m_gtpu->send_all_queued_packets(tunnel_ctx->dw_user_fteid, tunnel_ctx->paging_queue);
}
// Setting up Modify bearer response PDU
// Header
srslte::gtpc_pdu mb_resp_pdu;
@ -279,7 +297,8 @@ void spgw::gtpc::handle_modify_bearer_request(const struct srslte::gtpc_header&
mb_resp->eps_bearer_context_modified.ebi = tunnel_ctx->ebi;
mb_resp->eps_bearer_context_modified.cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED;
//Send Modify Bearer Response PDU
// Send Modify Bearer Response PDU
send_s11_pdu(mb_resp_pdu);
return;
}
@ -317,6 +336,102 @@ void spgw::gtpc::handle_release_access_bearers_request(const srslte::gtpc_header
return;
}
bool spgw::gtpc::send_downlink_data_notification(uint32_t spgw_ctr_teid)
{
m_gtpc_log->debug("Sending Downlink Notification Request\n");
struct srslte::gtpc_pdu dl_not_pdu;
struct srslte::gtpc_header* header = &dl_not_pdu.header;
struct srslte::gtpc_downlink_data_notification* dl_not = &dl_not_pdu.choice.downlink_data_notification;
bzero(&dl_not_pdu, sizeof(struct srslte::gtpc_pdu));
// Find MME Ctrl TEID
std::map<uint32_t, spgw_tunnel_ctx_t*>::iterator tunnel_it = m_teid_to_tunnel_ctx.find(spgw_ctr_teid);
if (tunnel_it == m_teid_to_tunnel_ctx.end()) {
m_gtpc_log->warning("Could not find TEID 0x%x to send downlink notification.\n", spgw_ctr_teid);
return false;
}
spgw_tunnel_ctx_t* tunnel_ctx = tunnel_it->second;
// Check if there is no Paging already pending.
if (tunnel_ctx->paging_pending == true) {
m_gtpc_log->console("UE Downlink Data Notification still pending.\n");
m_gtpc_log->warning("UE Downlink Data Notification still pending.\n");
return false;
}
tunnel_ctx->paging_pending = true;
m_gtpc_log->console("Found UE for Downlink Notification \n");
m_gtpc_log->console("MME Ctr TEID 0x%x, IMSI: %lu\n", tunnel_ctx->dw_ctrl_fteid.teid, tunnel_ctx->imsi);
// Setup GTP-C header
header->piggyback = false;
header->teid_present = true;
header->teid = tunnel_ctx->dw_ctrl_fteid.teid; // Send downlink data notification to the UE's MME Ctrl TEID
header->type = srslte::GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION;
dl_not->eps_bearer_id_present = true;
dl_not->eps_bearer_id = 5; // Only default bearer supported.
// Send Downlink Data Notification PDU
send_s11_pdu(dl_not_pdu);
return true;
}
void spgw::gtpc::handle_downlink_data_notification_acknowledge(
const srslte::gtpc_header& header, const srslte::gtpc_downlink_data_notification_acknowledge& not_ack)
{
m_gtpc_log->debug("Handling downlink data notification acknowledge\n");
// Find tunel ctxt
uint32_t ctrl_teid = header.teid;
std::map<uint32_t, spgw_tunnel_ctx_t*>::iterator tunnel_it = m_teid_to_tunnel_ctx.find(ctrl_teid);
if (tunnel_it == m_teid_to_tunnel_ctx.end()) {
m_gtpc_log->warning("Could not find TEID 0x%x to handle notification acknowldge\n", ctrl_teid);
return;
}
spgw_tunnel_ctx_t* tunnel_ctx = tunnel_it->second;
if (not_ack.cause.cause_value == srslte::GTPC_CAUSE_VALUE_CONTEXT_NOT_FOUND ||
not_ack.cause.cause_value == srslte::GTPC_CAUSE_VALUE_UE_ALREADY_RE_ATTACHED ||
not_ack.cause.cause_value == srslte::GTPC_CAUSE_VALUE_UNABLE_TO_PAGE_UE ||
not_ack.cause.cause_value == srslte::GTPC_CAUSE_VALUE_UNABLE_TO_PAGE_UE_DUE_TO_SUSPENSION) {
m_gtpc_log->warning("Downlink Data Notification Acknowledge indicates failure.\n");
free_all_queued_packets(tunnel_ctx);
tunnel_ctx->paging_pending = false;
} else if (not_ack.cause.cause_value != srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED) {
m_gtpc_log->warning("Invalid cause in Downlink Data Notification Acknowledge.\n");
free_all_queued_packets(tunnel_ctx);
tunnel_ctx->paging_pending = false;
}
return;
}
void spgw::gtpc::handle_downlink_data_notification_failure_indication(
const srslte::gtpc_header& header, const srslte::gtpc_downlink_data_notification_failure_indication& not_fail)
{
m_gtpc_log->debug("Handling downlink data notification failure indication\n");
// Find tunel ctxt
uint32_t ctrl_teid = header.teid;
std::map<uint32_t, spgw_tunnel_ctx_t*>::iterator tunnel_it = m_teid_to_tunnel_ctx.find(ctrl_teid);
if (tunnel_it == m_teid_to_tunnel_ctx.end()) {
m_gtpc_log->warning("Could not find TEID 0x%x to handle notification failure indication\n", ctrl_teid);
return;
}
spgw_tunnel_ctx_t* tunnel_ctx = tunnel_it->second;
if (not_fail.cause.cause_value == srslte::GTPC_CAUSE_VALUE_UE_NOT_RESPONDING ||
not_fail.cause.cause_value == srslte::GTPC_CAUSE_VALUE_SERVICE_DENIED ||
not_fail.cause.cause_value == srslte::GTPC_CAUSE_VALUE_UE_ALREADY_RE_ATTACHED) {
m_gtpc_log->debug("Downlink Data Notification failure indication cause: %d.\n", not_fail.cause.cause_value);
} else {
m_gtpc_log->warning("Invalid cause in Downlink Data Notification Failure Indication %d\n",
not_fail.cause.cause_value);
}
free_all_queued_packets(tunnel_ctx);
tunnel_ctx->paging_pending = false;
return;
}
/*
* Context management functions
*/
@ -340,8 +455,8 @@ spgw_tunnel_ctx_t* spgw::gtpc::create_gtpc_ctx(const struct srslte::gtpc_create_
// Save the UE IP to User TEID map
spgw_tunnel_ctx_t* tunnel_ctx = new spgw_tunnel_ctx_t;
tunnel_ctx->imsi = cs_req.imsi;
tunnel_ctx->ebi = default_bearer_id;
tunnel_ctx->imsi = cs_req.imsi;
tunnel_ctx->ebi = default_bearer_id;
tunnel_ctx->up_ctrl_fteid.teid = spgw_uplink_ctrl_teid;
tunnel_ctx->ue_ipv4 = ue_ip;
@ -365,6 +480,9 @@ bool spgw::gtpc::delete_gtpc_ctx(uint32_t ctrl_teid)
}
tunnel_ctx = m_teid_to_tunnel_ctx[ctrl_teid];
// Remove Ctrl TEID from GTP-U Mapping
m_gtpu->delete_gtpc_tunnel(tunnel_ctx->ue_ipv4);
// Remove Ctrl TEID from IMSI to control TEID map
m_imsi_to_ctr_teid.erase(tunnel_ctx->imsi);
@ -374,4 +492,47 @@ bool spgw::gtpc::delete_gtpc_ctx(uint32_t ctrl_teid)
return true;
}
/*
* Queueing functions
*/
bool spgw::gtpc::queue_downlink_packet(uint32_t ctrl_teid, srslte::byte_buffer_t* msg)
{
spgw_tunnel_ctx_t* tunnel_ctx;
if (!m_teid_to_tunnel_ctx.count(ctrl_teid)) {
m_gtpc_log->error("Could not find GTP context to queue.\n");
return false;
}
tunnel_ctx = m_teid_to_tunnel_ctx[ctrl_teid];
if (tunnel_ctx->paging_pending) {
m_gtpc_log->error("Paging not pending. Not queueing packet\n");
return false;
}
if (tunnel_ctx->paging_queue.size() < m_max_paging_queue) {
tunnel_ctx->paging_queue.push(msg);
m_gtpc_log->debug("Queued packet. IMSI %" PRIu64 ", Packets in Queue %lu\n", tunnel_ctx->imsi,
tunnel_ctx->paging_queue.size());
} else {
m_gtpc_log->warning("Paging queue full. IMSI %" PRIu64 ", Packets in Queue %lu\n", tunnel_ctx->imsi,
tunnel_ctx->paging_queue.size());
m_pool->deallocate(msg);
}
return true;
}
bool spgw::gtpc::free_all_queued_packets(spgw_tunnel_ctx_t* tunnel_ctx)
{
if (tunnel_ctx->paging_pending) {
m_gtpc_log->warning("Freeing queue with paging not pending.\n");
}
while (!tunnel_ctx->paging_queue.empty()) {
srslte::byte_buffer_t* pkt = tunnel_ctx->paging_queue.front();
m_gtpc_log->debug("Dropping packet. Bytes %d\n", pkt->N_bytes);
m_pool->deallocate(pkt);
tunnel_ctx->paging_queue.pop();
}
return true;
}
} // namespace srsepc

View File

@ -57,7 +57,7 @@ spgw::gtpu::~gtpu()
return;
}
int spgw::gtpu::init(spgw_args_t* args, spgw* spgw, srslte::log_filter* gtpu_log)
int spgw::gtpu::init(spgw_args_t* args, spgw* spgw, gtpc_interface_gtpu* gtpc, srslte::log_filter* gtpu_log)
{
srslte::error_t err;
@ -66,6 +66,7 @@ int spgw::gtpu::init(spgw_args_t* args, spgw* spgw, srslte::log_filter* gtpu_log
// Store interfaces
m_spgw = spgw;
m_gtpc = gtpc;
// Init SGi interface
err = init_sgi(args);
@ -98,10 +99,10 @@ void spgw::gtpu::stop()
}
}
srslte::error_t spgw::gtpu::init_sgi(spgw_args_t *args)
srslte::error_t spgw::gtpu::init_sgi(spgw_args_t* args)
{
struct ifreq ifr;
int sgi_sock;
int sgi_sock;
if (m_sgi_up) {
return (srslte::ERROR_ALREADY_STARTED);
@ -144,7 +145,7 @@ srslte::error_t spgw::gtpu::init_sgi(spgw_args_t *args)
}
// Set IP of the interface
struct sockaddr_in *addr = (struct sockaddr_in *)&ifr.ifr_addr;
struct sockaddr_in* addr = (struct sockaddr_in*)&ifr.ifr_addr;
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = inet_addr(args->sgi_if_addr.c_str());
addr->sin_port = 0;
@ -157,8 +158,8 @@ srslte::error_t spgw::gtpu::init_sgi(spgw_args_t *args)
return srslte::ERROR_CANT_START;
}
ifr.ifr_netmask.sa_family = AF_INET;
((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr("255.255.255.0");
ifr.ifr_netmask.sa_family = AF_INET;
((struct sockaddr_in*)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr("255.255.255.0");
if (ioctl(sgi_sock, SIOCSIFNETMASK, &ifr) < 0) {
m_gtpu_log->error("Failed to set TUN interface Netmask. Error: %s\n", strerror(errno));
close(m_sgi);
@ -198,19 +199,19 @@ srslte::error_t spgw::gtpu::init_s1u(spgw_args_t* args)
return srslte::ERROR_NONE;
}
void spgw::gtpu::handle_sgi_pdu(srslte::byte_buffer_t *msg)
void spgw::gtpu::handle_sgi_pdu(srslte::byte_buffer_t* msg)
{
uint8_t version = 0;
uint8_t version = 0;
uint32_t dest_ip;
bool usr_found = false;
bool ctr_found = false;
bool usr_found = false;
bool ctr_found = false;
struct in_addr dest_addr;
struct in_addr dest_addr;
std::map<uint32_t, srslte::gtpc_f_teid_ie>::iterator gtpu_fteid_it;
std::map<in_addr_t, uint32_t>::iterator gtpc_teid_it;
srslte::gtpc_f_teid_ie enb_fteid;
uint32_t spgw_teid;
struct iphdr *iph = (struct iphdr *)msg->msg;
std::map<in_addr_t, uint32_t>::iterator gtpc_teid_it;
srslte::gtpc_f_teid_ie enb_fteid;
uint32_t spgw_teid;
struct iphdr* iph = (struct iphdr*)msg->msg;
m_gtpu_log->debug("Received SGi PDU. Bytes %d\n", msg->N_bytes);
if (iph->version != 4) {
@ -222,7 +223,7 @@ void spgw::gtpu::handle_sgi_pdu(srslte::byte_buffer_t *msg)
return;
}
//Logging PDU info
// Logging PDU info
m_gtpu_log->debug("SGi PDU -- IP version %d, Total length %d\n", iph->version, ntohs(iph->tot_len));
m_gtpu_log->debug("SGi PDU -- IP src addr %s\n", gtpu_ntoa(iph->saddr).c_str());
m_gtpu_log->debug("SGi PDU -- IP dst addr %s\n", gtpu_ntoa(iph->daddr).c_str());
@ -233,14 +234,33 @@ void spgw::gtpu::handle_sgi_pdu(srslte::byte_buffer_t *msg)
usr_found = true;
enb_fteid = gtpu_fteid_it->second;
}
gtpc_teid_it = m_ip_to_ctr_teid.find(iph->daddr);
if (gtpc_teid_it != m_ip_to_ctr_teid.end()) {
ctr_found = true;
spgw_teid = gtpc_teid_it->second;
}
if (usr_found == false) {
m_gtpu_log->debug("Packet for unknown UE. Discarding packet.\n");
// Handle SGi packet
if (usr_found == false && ctr_found == false) {
m_gtpu_log->debug("Packet for unknown UE.\n");
goto pkt_discard_out;
} else if (usr_found == false && ctr_found == true) {
m_gtpu_log->debug("Packet for attached UE that is not ECM connected.\n");
m_gtpu_log->debug("Triggering Donwlink Notification Requset.\n");
m_gtpc->queue_downlink_packet(spgw_teid, msg);
m_gtpc->send_downlink_data_notification(spgw_teid);
return;
} else if (usr_found == false && ctr_found == true) {
m_gtpu_log->error("User plane tunnel found without a control plane tunnel present.\n");
goto pkt_discard_out;
} else {
send_s1u_pdu(enb_fteid, msg);
}
return;
pkt_discard_out:
m_pool->deallocate(msg);
return;
}
void spgw::gtpu::handle_s1u_pdu(srslte::byte_buffer_t* msg)
@ -278,32 +298,49 @@ void spgw::gtpu::send_s1u_pdu(srslte::gtp_fteid_t enb_fteid, srslte::byte_buffer
m_gtpu_log->debug("eNB F-TEID -- eNB IP %s, eNB TEID 0x%x.\n", inet_ntoa(enb_addr.sin_addr), enb_fteid.teid);
// Write header into packet
int n;
if (!srslte::gtpu_write_header(&header, msg, m_gtpu_log)) {
m_gtpu_log->error("Error writing GTP-U header on PDU\n");
return;
goto out;
}
// Send packet to destination
int n = sendto(m_s1u, msg->msg, msg->N_bytes, 0, (struct sockaddr*)&enb_addr, sizeof(enb_addr));
n = sendto(m_s1u, msg->msg, msg->N_bytes, 0, (struct sockaddr*)&enb_addr, sizeof(enb_addr));
if (n < 0) {
m_gtpu_log->error("Error sending packet to eNB\n");
} else if ((unsigned int)n != msg->N_bytes) {
m_gtpu_log->error("Mis-match between packet bytes and sent bytes: Sent: %d/%d\n", n, msg->N_bytes);
}
out:
m_gtpu_log->debug("Deallocating packet after sending S1-U message\n");
m_pool->deallocate(msg);
return;
}
void spgw::gtpu::send_all_queued_packets(srslte::gtp_fteid_t dw_user_fteid,
std::queue<srslte::byte_buffer_t*>& pkt_queue)
{
m_gtpu_log->debug("Sending all queued packets\n");
while (!pkt_queue.empty()) {
srslte::byte_buffer_t* msg = pkt_queue.front();
send_s1u_pdu(dw_user_fteid, msg);
pkt_queue.pop();
}
return;
}
/*
* Tunnel managment
*/
bool spgw::gtpu::modify_gtpu_tunnel(in_addr_t ue_ipv4, srslte::gtpc_f_teid_ie dw_user_fteid,
uint32_t up_ctrl_teid)
bool spgw::gtpu::modify_gtpu_tunnel(in_addr_t ue_ipv4, srslte::gtpc_f_teid_ie dw_user_fteid, uint32_t up_ctrl_teid)
{
m_gtpu_log->info("Modifying GTP-U Tunnel.\n");
m_gtpu_log->info("UE IP %s\n", gtpu_ntoa(ue_ipv4).c_str());
m_gtpu_log->info("Downlink eNB addr %s, U-TEID 0x%x\n", gtpu_ntoa(dw_user_fteid.ipv4).c_str(), dw_user_fteid.teid);
m_gtpu_log->info("Uplink C-TEID: 0x%x\n", up_ctrl_teid);
m_ip_to_usr_teid[ue_ipv4] = dw_user_fteid;
m_ip_to_ctr_teid[ue_ipv4] = up_ctrl_teid;
return true;
}
@ -319,4 +356,16 @@ bool spgw::gtpu::delete_gtpu_tunnel(in_addr_t ue_ipv4)
return true;
}
bool spgw::gtpu::delete_gtpc_tunnel(in_addr_t ue_ipv4)
{
// Remove Ctrl TEID from IP mapping.
if (m_ip_to_ctr_teid.count(ue_ipv4)) {
m_ip_to_ctr_teid.erase(ue_ipv4);
} else {
m_gtpu_log->error("Could not find GTP-C Tunnel info to delete.\n");
return false;
}
return true;
}
} // namespace srsepc

View File

@ -80,10 +80,9 @@ int spgw::init(spgw_args_t* args,
// Init log
m_spgw_log = spgw_log;
m_mme_gtpc = mme_gtpc::get_instance();
// Init GTP-U
if (m_gtpu->init(args, this, gtpu_log) != 0) {
if (m_gtpu->init(args, this, m_gtpc, gtpu_log) != 0) {
m_spgw_log->console("Could not initialize the SPGW's GTP-U.\n");
return -1;
}
@ -116,8 +115,9 @@ void spgw::run_thread()
{
// Mark the thread as running
m_running = true;
srslte::byte_buffer_t* msg;
msg = m_pool->allocate();
srslte::byte_buffer_t *sgi_msg, *s1u_msg, *s11_msg;
s1u_msg = m_pool->allocate("spgw::run_thread::s1u");
s11_msg = m_pool->allocate("spgw::run_thread::s11");
struct sockaddr_in src_addr_in;
struct sockaddr_un src_addr_un;
@ -134,7 +134,10 @@ void spgw::run_thread()
int max_fd = std::max(s1u, sgi);
max_fd = std::max(max_fd, s11);
while (m_running) {
msg->reset();
s1u_msg->reset();
s11_msg->reset();
FD_ZERO(&set);
FD_SET(s1u, &set);
FD_SET(sgi, &set);
@ -144,24 +147,36 @@ void spgw::run_thread()
if (n == -1) {
m_spgw_log->error("Error from select\n");
} else if (n) {
if (FD_ISSET(s1u, &set)) {
msg->N_bytes = recvfrom(s1u, msg->msg, buf_len, 0, (struct sockaddr*)&src_addr_in, &addrlen);
m_gtpu->handle_s1u_pdu(msg);
}
if (FD_ISSET(sgi, &set)) {
msg->N_bytes = read(sgi, msg->msg, buf_len);
m_gtpu->handle_sgi_pdu(msg);
/*
* SGi messages may need to be queued when waiting for UE Paging procedure.
* For this reason, buffers for SGi pdus are allocated here and deallocated
* at the gtpu::send_s1u_pdu() when the PDU is sent, at handle_sgi_pdu() when the PDU is dropped or at
* gtpc::free_all_queued_packets, which is called when the Downlink Data Notification
* procedure fails (see handle_downlink_data_notification_acknowledgment and
* handle_downlink_data_notification_failure)
*/
m_spgw_log->debug("Message received at SPGW: SGi Message\n");
sgi_msg = m_pool->allocate("spgw::run_thread::sgi_msg");
sgi_msg->N_bytes = read(sgi, sgi_msg->msg, buf_len);
m_gtpu->handle_sgi_pdu(sgi_msg);
}
if (FD_ISSET(s1u, &set)) {
m_spgw_log->debug("Message received at SPGW: S1-U Message\n");
s1u_msg->N_bytes = recvfrom(s1u, s1u_msg->msg, buf_len, 0, (struct sockaddr*)&src_addr_in, &addrlen);
m_gtpu->handle_s1u_pdu(s1u_msg);
}
if (FD_ISSET(s11, &set)) {
m_spgw_log->debug("Message received at SPGW: S11 Message\n");
msg->N_bytes = recvfrom(s11, msg->msg, buf_len, 0, (struct sockaddr*)&src_addr_un, &addrlen);
m_gtpc->handle_s11_pdu(msg);
s11_msg->N_bytes = recvfrom(s11, s11_msg->msg, buf_len, 0, (struct sockaddr*)&src_addr_un, &addrlen);
m_gtpc->handle_s11_pdu(s11_msg);
}
} else {
m_spgw_log->debug("No data from select.\n");
}
}
m_pool->deallocate(msg);
m_pool->deallocate(s1u_msg);
m_pool->deallocate(s11_msg);
return;
}