Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
pespin committed Jan 2, 2024
1 parent 8df194b commit 994d0c2
Show file tree
Hide file tree
Showing 10 changed files with 245 additions and 7 deletions.
26 changes: 25 additions & 1 deletion src/mme/emm-handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ int emm_handle_attach_request(mme_ue_t *mme_ue,
OGS_NAS_SECURITY_ALGORITHMS_EIA0) {
ogs_warn("Encrypt[0x%x] can be skipped with EEA0, "
"but Integrity[0x%x] cannot be bypassed with EIA0",
mme_selected_enc_algorithm(mme_ue),
mme_selected_enc_algorithm(mme_ue),
mme_selected_int_algorithm(mme_ue));
r = nas_eps_send_attach_reject(mme_ue,
OGS_NAS_EMM_CAUSE_UE_SECURITY_CAPABILITIES_MISMATCH,
Expand Down Expand Up @@ -549,6 +549,26 @@ int emm_handle_service_request(
return OGS_OK;
}

bool emm_tau_request_guti_comes_from_sgsn(const ogs_nas_eps_tracking_area_update_request_t *tau_request)
{
if (tau_request->presencemask &
OGS_NAS_EPS_TRACKING_AREA_UPDATE_REQUEST_OLD_GUTI_TYPE_PRESENT) {
/* 0 = Native, 1 = Mapped */
return tau_request->old_guti_type.guti_type;
} else {
/* TS 23.003 2.8.2.2.2:
* "The most significant bit of the <LAC> shall be set to zero;
* and the most significant bit of <MME group id> shall be set to
* one. Based on this definition, the most significant bit of the
* <MME group id> can be used to distinguish the node type, i.e.
* whether it is an MME or SGSN */
const ogs_nas_eps_mobile_identity_t *eps_mobile_identity = &tau_request->old_guti;
if (eps_mobile_identity->imsi.type != OGS_NAS_EPS_MOBILE_IDENTITY_GUTI)
return false;
return !(eps_mobile_identity->guti.mme_gid & 0x8000);
}
}

int emm_handle_tau_request(mme_ue_t *mme_ue,
ogs_nas_eps_tracking_area_update_request_t *tau_request, ogs_pkbuf_t *pkbuf)
{
Expand Down Expand Up @@ -679,6 +699,10 @@ int emm_handle_tau_request(mme_ue_t *mme_ue,
nas_guti.m_tmsi,
MME_UE_HAVE_IMSI(mme_ue)
? mme_ue->imsi_bcd : "Unknown");

memcpy(&mme_ue->next.guti,
&nas_guti, sizeof(ogs_nas_eps_guti_t));

break;
default:
ogs_error("Not implemented[%d]", eps_mobile_identity->imsi.type);
Expand Down
3 changes: 3 additions & 0 deletions src/mme/emm-handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ int emm_handle_extended_service_request(mme_ue_t *mme_ue,
int emm_handle_security_mode_complete(mme_ue_t *mme_ue,
ogs_nas_eps_security_mode_complete_t *security_mode_complete);

bool emm_tau_request_guti_comes_from_sgsn(
const ogs_nas_eps_tracking_area_update_request_t *tau_request);

#ifdef __cplusplus
}
#endif
Expand Down
23 changes: 23 additions & 0 deletions src/mme/emm-sm.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "mme-event.h"
#include "mme-timer.h"
#include "s1ap-handler.h"
#include "mme-gn-handler.h"
#include "mme-fd-path.h"
#include "emm-handler.h"
#include "emm-build.h"
Expand Down Expand Up @@ -285,7 +286,9 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e,

mme_ue_t *mme_ue = NULL;
enb_ue_t *enb_ue = NULL;
mme_sgsn_t *sgsn = NULL;
ogs_nas_eps_message_t *message = NULL;
ogs_nas_rai_t rai;
ogs_nas_security_header_type_t h;

ogs_assert(e);
Expand Down Expand Up @@ -488,6 +491,26 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e,
break;
}

if (emm_tau_request_guti_comes_from_sgsn(&message->emm.tracking_area_update_request)) {
ogs_info("TAU request : UE comes from SGSN, attempt retrieving context");
guti_to_rai_ptmsi(&mme_ue->next.guti, &rai, NULL, NULL);
sgsn = mme_sgsn_find_by_routing_address(&rai, 0xffff);
if (!sgsn) {
ogs_plmn_id_t plmn_id;
ogs_nas_to_plmn_id(&plmn_id, &rai.lai.nas_plmn_id);
ogs_warn("No SGSN route matching RAI[MCC:%u MNC:%u LAC:%u RAC:%u]",
ogs_plmn_id_mcc(&plmn_id), ogs_plmn_id_mnc(&plmn_id),
rai.lai.lac, rai.rac);
r = nas_eps_send_tau_reject(mme_ue,
OGS_NAS_EMM_CAUSE_UE_IDENTITY_CANNOT_BE_DERIVED_BY_THE_NETWORK);
OGS_FSM_TRAN(s, &emm_state_exception);
break;
}
mme_gtp1_send_sgsn_context_request(sgsn, mme_ue);
OGS_FSM_TRAN(s, &emm_state_registered); /* FIXME: use a specific FSM state here? */
break;
}

if (!MME_UE_HAVE_IMSI(mme_ue)) {
ogs_info("TAU request : Unknown UE");
r = nas_eps_send_tau_reject(mme_ue,
Expand Down
9 changes: 9 additions & 0 deletions src/mme/mme-context.c
Original file line number Diff line number Diff line change
Expand Up @@ -2488,6 +2488,15 @@ mme_sgsn_t *mme_sgsn_find_by_routing_address(const ogs_nas_rai_t *rai, uint16_t
}
}

/* If no exact match found, try using any with same RAI: */
ogs_list_for_each(&self.sgsn_list, sgsn) {
mme_sgsn_route_t *rt = NULL;
ogs_list_for_each(&sgsn->route_list, rt) {
if (memcmp(&rt->rai, rai, sizeof(ogs_nas_rai_t)) == 0)
return sgsn;
}
}

/* No route found, return default route if available: */
return mme_sgsn_find_by_default_routing_address();
}
Expand Down
117 changes: 117 additions & 0 deletions src/mme/mme-gn-build.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "mme-context.h"

#include "mme-gn-build.h"
#include "mme-gn-handler.h"

static int sess_fill_mm_context_decoded(mme_sess_t *sess, ogs_gtp1_mm_context_decoded_t *mmctx_dec)
{
Expand Down Expand Up @@ -177,6 +178,122 @@ static int sess_fill_pdp_context_decoded(mme_sess_t *sess, ogs_gtp1_pdp_context_
return OGS_OK;
}

/* 3GPP TS 29.060 7.5.3 SGSN Context Request */
ogs_pkbuf_t *mme_gn_build_sgsn_context_request(
mme_ue_t *mme_ue)
{
ogs_gtp1_message_t gtp1_message;
ogs_gtp1_sgsn_context_request_t *req = NULL;
ogs_nas_rai_t rai;
mme_p_tmsi_t ptmsi;
uint32_t ptmsi_sig;
ogs_gtp1_gsn_addr_t mme_gnc_gsnaddr, mme_gnc_alt_gsnaddr;
int gsn_len;
int rv;

ogs_debug("[Gn] build SGSN Context Request");

ogs_assert(mme_ue);

req = &gtp1_message.sgsn_context_request;
memset(&gtp1_message, 0, sizeof(ogs_gtp1_message_t));

guti_to_rai_ptmsi(&mme_ue->next.guti, &rai, &ptmsi, &ptmsi_sig);

req->imsi.presence = 0;

req->routeing_area_identity.presence = 1;
req->routeing_area_identity.data = &rai;
req->routeing_area_identity.len = sizeof(ogs_nas_rai_t);

req->temporary_logical_link_identifier.presence = 0;

req->packet_tmsi.presence = 1;
req->packet_tmsi.u32 = be32toh(ptmsi);

req->p_tmsi_signature.presence = 1;
req->p_tmsi_signature.u24 = ptmsi_sig;

req->ms_validated.presence = 0;

req->tunnel_endpoint_identifier_control_plane.presence = 1;
req->tunnel_endpoint_identifier_control_plane.u32 = mme_ue->gn.mme_gn_teid;

#if 0
/* SGSN Address for Control Plane */
if (ogs_gtp_self()->gtpc_addr6 &&
(mme_ue->gn.sgsn_gn_ip.ipv6 || mme_ue->gn.sgsn_gn_ip_alt.ipv6)) {
rv = ogs_gtp1_sockaddr_to_gsn_addr(NULL, ogs_gtp_self()->gtpc_addr6,
&mme_gnc_gsnaddr, &gsn_len);
if (rv != OGS_OK) {
ogs_error("ogs_gtp1_sockaddr_to_gsn_addr() failed");
return NULL;
}
} else {
rv = ogs_gtp1_sockaddr_to_gsn_addr(ogs_gtp_self()->gtpc_addr, NULL,
&mme_gnc_gsnaddr, &gsn_len);
if (rv != OGS_OK) {
ogs_error("ogs_gtp1_sockaddr_to_gsn_addr() failed");
return NULL;
}
}
#endif

if (ogs_gtp_self()->gtpc_addr && ogs_gtp_self()->gtpc_addr6) {
rv = ogs_gtp1_sockaddr_to_gsn_addr(NULL, ogs_gtp_self()->gtpc_addr6,
&mme_gnc_gsnaddr, &gsn_len);
if (rv != OGS_OK) {
ogs_error("ogs_gtp1_sockaddr_to_gsn_addr() failed");
return NULL;
}
req->sgsn_address_for_control_plane.presence = 1;
req->sgsn_address_for_control_plane.data = &mme_gnc_gsnaddr;
req->sgsn_address_for_control_plane.len = gsn_len;

rv = ogs_gtp1_sockaddr_to_gsn_addr(ogs_gtp_self()->gtpc_addr, NULL,
&mme_gnc_alt_gsnaddr, &gsn_len);
if (rv != OGS_OK) {
ogs_error("ogs_gtp1_sockaddr_to_gsn_addr() failed");
return NULL;
}
req->sgsn_address_for_control_plane.presence = 1;
req->sgsn_address_for_control_plane.data = &mme_gnc_alt_gsnaddr;
req->sgsn_address_for_control_plane.len = gsn_len;
} else if (ogs_gtp_self()->gtpc_addr6) {
rv = ogs_gtp1_sockaddr_to_gsn_addr(NULL, ogs_gtp_self()->gtpc_addr6,
&mme_gnc_gsnaddr, &gsn_len);
if (rv != OGS_OK) {
ogs_error("ogs_gtp1_sockaddr_to_gsn_addr() failed");
return NULL;
}
req->sgsn_address_for_control_plane.presence = 1;
req->sgsn_address_for_control_plane.data = &mme_gnc_gsnaddr;
req->sgsn_address_for_control_plane.len = gsn_len;
req->alternative_sgsn_address_for_control_plane.presence = 0;
} else {
rv = ogs_gtp1_sockaddr_to_gsn_addr(ogs_gtp_self()->gtpc_addr, NULL,
&mme_gnc_gsnaddr, &gsn_len);
if (rv != OGS_OK) {
ogs_error("ogs_gtp1_sockaddr_to_gsn_addr() failed");
return NULL;
}
req->sgsn_address_for_control_plane.presence = 1;
req->sgsn_address_for_control_plane.data = &mme_gnc_gsnaddr;
req->sgsn_address_for_control_plane.len = gsn_len;
req->alternative_sgsn_address_for_control_plane.presence = 0;
}

req->sgsn_number.presence = 0;

req->rat_type.presence = 1;
req->rat_type.u8 = OGS_GTP1_RAT_TYPE_EUTRAN;

req->hop_counter.presence = 0;

gtp1_message.h.type = OGS_GTP1_SGSN_CONTEXT_REQUEST_TYPE;
return ogs_gtp1_build_msg(&gtp1_message);
}

/* 3GPP TS 29.060 7.5.4 SGSN Context Response */
ogs_pkbuf_t *mme_gn_build_sgsn_context_response(
mme_ue_t *mme_ue, uint8_t cause)
Expand Down
3 changes: 3 additions & 0 deletions src/mme/mme-gn-build.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ extern "C" {
ogs_pkbuf_t *mme_gn_build_sgsn_context_response(
mme_ue_t *mme_ue, uint8_t cause);

ogs_pkbuf_t *mme_gn_build_sgsn_context_request(
mme_ue_t *mme_ue);

ogs_pkbuf_t *mme_gn_build_ran_information_relay(
uint8_t type, const uint8_t *buf, size_t len,
const ogs_nas_rai_t *rai, uint16_t cell_id);
Expand Down
26 changes: 20 additions & 6 deletions src/mme/mme-gn-handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,26 @@ static int decode_global_enb_id(S1AP_Global_ENB_ID_t *glob_enb_id, const uint8_t
return OGS_OK;
}

/* 3GPP TS 23.003 2.8.2.2 Mapping from RAI and P-TMSI to GUT */
/* 3GPP TS 23.003 2.8.2.1 Mapping from GUTI to RAI, P-TMSI and P-TMSI signature */
void guti_to_rai_ptmsi(const ogs_nas_eps_guti_t *nas_guti, ogs_nas_rai_t *rai, mme_p_tmsi_t *ptmsi, uint32_t *ptmsi_sig)
{
rai->lai.nas_plmn_id = nas_guti->nas_plmn_id;
rai->lai.lac = nas_guti->mme_gid;
rai->rac = nas_guti->mme_code;
if (ptmsi)
*ptmsi = 0xC0000000 |
(nas_guti->m_tmsi & 0x3f000000) |
(nas_guti->mme_code & 0x0ff) << 16 |
(nas_guti->m_tmsi & 0x0000ffff);
if (ptmsi_sig)
*ptmsi_sig = (nas_guti->m_tmsi & 0x00ff0000);
}

/* 3GPP TS 23.003 2.8.2.2 Mapping from RAI and P-TMSI to GUTI */
static void rai_ptmsi_to_guti(const ogs_nas_rai_t *rai, mme_p_tmsi_t ptmsi, uint32_t ptmsi_sig, ogs_nas_eps_guti_t *nas_guti)
{
uint16_t lac = be16toh(rai->lai.lac);;
nas_guti->nas_plmn_id =rai->lai.nas_plmn_id;
nas_guti->mme_gid = lac;
nas_guti->nas_plmn_id = rai->lai.nas_plmn_id;
nas_guti->mme_gid = rai->lai.lac;
nas_guti->mme_code = rai->rac;
nas_guti->m_tmsi = 0xC0000000 | (ptmsi & 0x3f000000) | (ptmsi_sig & 0x00ff0000) | (ptmsi & 0x0000ffff);
}
Expand Down Expand Up @@ -135,7 +149,7 @@ void mme_gn_handle_sgsn_context_request(
ogs_warn("[Gn] Rx SGSN Context Request with 'P-TMSI' but no P-TMSI Signature! Assuming value 0.");
req->p_tmsi_signature.u24 = 0;
}
rai_ptmsi_to_guti(rai, req->packet_tmsi.u32, req->p_tmsi_signature.u24, &nas_guti);
rai_ptmsi_to_guti(rai, htobe16(req->packet_tmsi.u32), req->p_tmsi_signature.u24, &nas_guti);
mme_ue = mme_ue_find_by_guti(&nas_guti);
} else if (req->temporary_logical_link_identifier.presence) {
if (!req->p_tmsi_signature.presence) {
Expand All @@ -145,7 +159,7 @@ void mme_gn_handle_sgsn_context_request(
/* TS 29.060 7.5.3 "The TLLI/P-TMSI and RAI is a foreign TLLI/P-TMSI and an RAI in the old SGSN."
* A foregin TLLI is "tlli = (p_tmsi & 0x3fffffff) | 0x80000000", and since we only use 0x3fffffff
* bits of P-TMSI to derive the GUTI, it's totally fine passing the TLLI as P-TMSI. */
rai_ptmsi_to_guti(rai, req->temporary_logical_link_identifier.u32, req->p_tmsi_signature.u24, &nas_guti);
rai_ptmsi_to_guti(rai, htobe16(req->temporary_logical_link_identifier.u32), req->p_tmsi_signature.u24, &nas_guti);
mme_ue = mme_ue_find_by_guti(&nas_guti);
}

Expand Down
3 changes: 3 additions & 0 deletions src/mme/mme-gn-handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ void mme_gn_handle_sgsn_context_acknowledge(
void mme_gn_handle_ran_information_relay(
ogs_gtp_xact_t *xact, ogs_gtp1_ran_information_relay_t *req);

void guti_to_rai_ptmsi(const ogs_nas_eps_guti_t *nas_guti, ogs_nas_rai_t *rai,
mme_p_tmsi_t *ptmsi, uint32_t *ptmsi_sig);

#ifdef __cplusplus
}
#endif
Expand Down
39 changes: 39 additions & 0 deletions src/mme/mme-gtp-path.c
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,45 @@ int mme_gtp_send_bearer_resource_command(
return rv;
}

/*************************
* GTPv1C (Gn interface):
*************************/

int mme_gtp1_send_sgsn_context_request(
mme_sgsn_t *sgsn, mme_ue_t *mme_ue)
{
int rv;
ogs_gtp1_header_t h;
ogs_pkbuf_t *pkbuf = NULL;
ogs_gtp_xact_t *xact = NULL;

ogs_assert(sgsn);

memset(&h, 0, sizeof(ogs_gtp1_header_t));
h.type = OGS_GTP1_SGSN_CONTEXT_REQUEST_TYPE;
h.teid = 0;

pkbuf = mme_gn_build_sgsn_context_request(mme_ue);
if (!pkbuf) {
ogs_error("mme_gn_build_ran_information_relay() failed");
return OGS_ERROR;
}

xact = ogs_gtp1_xact_local_create(&sgsn->gnode, &h, pkbuf, NULL, NULL);
if (!xact) {
ogs_error("ogs_gtp1_xact_local_create() failed");
return OGS_ERROR;
}
/* TS 29.060 8.2: "The SGSN Context Request message, where the Tunnel
* Endpoint Identifier shall be set to all zeroes." */
xact->local_teid = 0;

rv = ogs_gtp_xact_commit(xact);
ogs_expect(rv == OGS_OK);

return rv;
}

int mme_gtp1_send_sgsn_context_response(
mme_ue_t *mme_ue, uint8_t cause, ogs_gtp_xact_t *xact)
{
Expand Down
3 changes: 3 additions & 0 deletions src/mme/mme-gtp-path.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ int mme_gtp_send_delete_indirect_data_forwarding_tunnel_request(
int mme_gtp_send_bearer_resource_command(
mme_bearer_t *bearer, ogs_nas_eps_message_t *nas_message);

int mme_gtp1_send_sgsn_context_request(
mme_sgsn_t *sgsn, mme_ue_t *mme_ue);

int mme_gtp1_send_sgsn_context_response(
mme_ue_t *mme_ue, uint8_t cause, ogs_gtp_xact_t *xact);

Expand Down

0 comments on commit 994d0c2

Please sign in to comment.