diff --git a/src/mme/mme-context.h b/src/mme/mme-context.h index a528b1ca91..be5288431e 100644 --- a/src/mme/mme-context.h +++ b/src/mme/mme-context.h @@ -421,6 +421,11 @@ struct mme_ue_s { char a_msisdn_bcd[OGS_MAX_MSISDN_BCD_LEN+1]; mme_p_tmsi_t p_tmsi; + struct { + uint32_t sgsn_gn_teid; + ogs_ip_t sgsn_gn_ip; + ogs_ip_t sgsn_gn_ip_alt; + } gn; struct { mme_m_tmsi_t *m_tmsi; diff --git a/src/mme/mme-gn-build.c b/src/mme/mme-gn-build.c index dc88063dd1..ae95e8f18a 100644 --- a/src/mme/mme-gn-build.c +++ b/src/mme/mme-gn-build.c @@ -21,6 +21,75 @@ #include "mme-gn-build.h" +/* 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) +{ + ogs_gtp1_message_t gtp1_message; + ogs_gtp1_sgsn_context_response_t *rsp = NULL; + mme_sess_t *sess = NULL; + ogs_gtp1_gsn_addr_t mme_gnc_gsnaddr; + int gsn_len; + int rv; + + ogs_debug("[Gn] build SGSN Context Response"); + + + rsp = >p1_message.sgsn_context_response; + memset(>p1_message, 0, sizeof(ogs_gtp1_message_t)); + gtp1_message.h.type = OGS_GTP1_SGSN_CONTEXT_RESPONSE_TYPE; + + /* 3GPP TS 29.060 7.7.1 Cause, Mandatory */ + rsp->cause.presence = 1; + rsp->cause.u8 = cause; + + /* 3GPP TS 29.060 7.7.2 IMSI, Conditional */ + rsp->imsi.presence = !!mme_ue; + if (rsp->imsi.presence) { + rsp->imsi.data = mme_ue->imsi; + rsp->imsi.len = mme_ue->imsi_len; + } + + if (cause != OGS_GTP1_CAUSE_REQUEST_ACCEPTED) + goto build_ret; + + ogs_assert(mme_ue); + + /* FIXME: Reuse S11 TEID as local Gn interface for now */ + rsp->tunnel_endpoint_identifier_control_plane.presence = 1; + rsp->tunnel_endpoint_identifier_control_plane.u32 = mme_ue->mme_s11_teid; + + ogs_list_for_each(&mme_ue->sess_list, sess) { + if (!MME_HAVE_SGW_S1U_PATH(sess)) + continue; + /* TODO: fill some structure with pdp contexts */ + } + + /* 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(NULL, ogs_gtp_self()->gtpc_addr, + &mme_gnc_gsnaddr, &gsn_len); + if (rv != OGS_OK) { + ogs_error("ogs_gtp1_sockaddr_to_gsn_addr() failed"); + return NULL; + } + } + rsp->sgsn_address_for_control_plane.presence = 1; + rsp->sgsn_address_for_control_plane.data = &mme_gnc_gsnaddr; + rsp->sgsn_address_for_control_plane.len = gsn_len; + +build_ret: + return ogs_gtp1_build_msg(>p1_message); +} + /* 3GPP TS 29.060 7.5.14.1 RAN Information Relay */ ogs_pkbuf_t *mme_gn_build_ran_information_relay( uint8_t type, const uint8_t *buf, size_t len, @@ -59,4 +128,4 @@ ogs_pkbuf_t *mme_gn_build_ran_information_relay( gtp1_message.h.type = type; return ogs_gtp1_build_msg(>p1_message); -} \ No newline at end of file +} diff --git a/src/mme/mme-gn-build.h b/src/mme/mme-gn-build.h index fc66282c67..6a7af32b5a 100644 --- a/src/mme/mme-gn-build.h +++ b/src/mme/mme-gn-build.h @@ -30,6 +30,9 @@ extern "C" { } #endif +ogs_pkbuf_t *mme_gn_build_sgsn_context_response( + mme_ue_t *mme_ue, uint8_t cause); + 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); diff --git a/src/mme/mme-gn-handler.c b/src/mme/mme-gn-handler.c index b661d0de9a..9add9aa00b 100644 --- a/src/mme/mme-gn-handler.c +++ b/src/mme/mme-gn-handler.c @@ -68,6 +68,100 @@ 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 */ +static void rai_ptmsi_to_guti(const ogs_nas_rai_t *rai, mme_m_tmsi_t ptmsi, ogs_nas_eps_guti_t *nas_guti) +{ + nas_guti->nas_plmn_id =rai->lai.nas_plmn_id; + nas_guti->mme_gid = rai->lai.lac; + nas_guti->mme_code = 0; /* FIXME: The 8 most significant bits of GERAN/UTRAN map to the MME code. */ + nas_guti->m_tmsi = (ptmsi & 0x1f000000) | ((rai->rac & 0xff) << 16) | (ptmsi & 0x0000ffff); +} + +/* TS 29.060 7.5.3 SGSN Context Request */ +void mme_gn_handle_sgsn_context_request( + ogs_gtp_xact_t *xact, ogs_gtp1_sgsn_context_request_t *req) +{ + ogs_nas_eps_guti_t nas_guti; + ogs_nas_rai_t *rai; + mme_ue_t *mme_ue; + int rv; + + ogs_debug("[Gn] Rx SGSN Context Request"); + + if (!req->routeing_area_identity.presence) { + ogs_warn("[Gn] Rx SGSN Context Request with no RAI!"); + mme_gtp1_send_sgsn_context_response(NULL, OGS_GTP1_CAUSE_MANDATORY_IE_MISSING, xact); + return; + } + if (req->routeing_area_identity.len != sizeof(*rai)) { + ogs_warn("[Gn] Rx SGSN Context Request RAI wrong size %u vs exp %zu!", + req->routeing_area_identity.len, sizeof(*rai)); + mme_gtp1_send_sgsn_context_response(NULL, OGS_GTP1_CAUSE_MANDATORY_IE_INCORRECT, xact); + return; + } + if (!req->tunnel_endpoint_identifier_control_plane.presence) { + ogs_warn("[Gn] Rx SGSN Context Request with no Tunnel Endpoint Identifier Control Plane!"); + mme_gtp1_send_sgsn_context_response(NULL, OGS_GTP1_CAUSE_MANDATORY_IE_MISSING, xact); + return; + } + if (!req->sgsn_address_for_control_plane.presence) { + ogs_warn("[Gn] Rx SGSN Context Request with no SGSN Address for Control Plane!"); + mme_gtp1_send_sgsn_context_response(NULL, OGS_GTP1_CAUSE_MANDATORY_IE_MISSING, xact); + return; + } + if (!req->imsi.presence && + !req->temporary_logical_link_identifier.presence && + !req->packet_tmsi.presence) { + ogs_warn("[Gn] Rx SGSN Context Request with no IMSI/TLLI/P-TMSI!"); + mme_gtp1_send_sgsn_context_response(NULL, OGS_GTP1_CAUSE_MANDATORY_IE_MISSING, xact); + return; + } + + if (req->ms_validated.presence && + (req->ms_validated.u8 & 0x01) /* 1=> "Yes" */ + && !req->imsi.presence) { + ogs_warn("[Gn] Rx SGSN Context Request with 'MS Validated' but no IMSI!"); + mme_gtp1_send_sgsn_context_response(NULL, OGS_GTP1_CAUSE_MANDATORY_IE_MISSING, xact); + return; + } + + rai = req->routeing_area_identity.data; + + if (req->ms_validated.presence) { + mme_ue = mme_ue_find_by_imsi(req->imsi.data, req->imsi.len); + } else if (req->packet_tmsi.presence) { /* P-TMSI */ + rai_ptmsi_to_guti(rai, req->packet_tmsi.u32, &nas_guti); + mme_ue = mme_ue_find_by_guti(&nas_guti); + //mme_ue->p_tmsi = p_tmsi; // Update p-tmsi? not yet used? + } else if (req->temporary_logical_link_identifier.presence) { + /* This should not happen? TLLI not used in 4G case? */ + ogs_warn("[Gn] Rx SGSN Context Request with TLLI!"); + mme_gtp1_send_sgsn_context_response(NULL, OGS_GTP1_CAUSE_MANDATORY_IE_MISSING, xact); + return; + } + + if (!mme_ue) { + mme_gtp1_send_sgsn_context_response(NULL, OGS_GTP1_CAUSE_IMSI_IMEI_NOT_KNOWN, xact); + return; + } + + mme_ue->gn.sgsn_gn_teid = req->tunnel_endpoint_identifier_control_plane.u32; + + rv = ogs_gtp1_gsn_addr_to_ip(req->sgsn_address_for_control_plane.data, + req->sgsn_address_for_control_plane.len, + &mme_ue->gn.sgsn_gn_ip); + ogs_assert(rv == OGS_OK); + + if (req->alternative_sgsn_address_for_control_plane.presence) { + rv = ogs_gtp1_gsn_addr_to_ip(req->alternative_sgsn_address_for_control_plane.data, + req->alternative_sgsn_address_for_control_plane.len, + &mme_ue->gn.sgsn_gn_ip_alt); + ogs_assert(rv == OGS_OK); + } + + mme_gtp1_send_sgsn_context_response(mme_ue, OGS_GTP1_CAUSE_REQUEST_ACCEPTED, xact); +} + /* TS 29.060 7.5.14.1 */ void mme_gn_handle_ran_information_relay( ogs_gtp_xact_t *xact, ogs_gtp1_ran_information_relay_t *req) diff --git a/src/mme/mme-gn-handler.h b/src/mme/mme-gn-handler.h index 3cf2e661bc..70cdf7ea4a 100644 --- a/src/mme/mme-gn-handler.h +++ b/src/mme/mme-gn-handler.h @@ -32,6 +32,9 @@ void mme_gn_handle_echo_request( void mme_gn_handle_echo_response( ogs_gtp_xact_t *xact, ogs_gtp1_echo_response_t *req); +void mme_gn_handle_sgsn_context_request( + ogs_gtp_xact_t *xact, ogs_gtp1_sgsn_context_request_t *req); + void mme_gn_handle_ran_information_relay( ogs_gtp_xact_t *xact, ogs_gtp1_ran_information_relay_t *req); diff --git a/src/mme/mme-gtp-path.c b/src/mme/mme-gtp-path.c index cce8a714ee..92ed42b6c9 100644 --- a/src/mme/mme-gtp-path.c +++ b/src/mme/mme-gtp-path.c @@ -758,6 +758,36 @@ int mme_gtp_send_bearer_resource_command( return rv; } +int mme_gtp1_send_sgsn_context_response( + mme_ue_t *mme_ue, uint8_t cause, ogs_gtp_xact_t *xact) +{ + int rv; + ogs_gtp1_header_t h; + ogs_pkbuf_t *pkbuf = NULL; + + memset(&h, 0, sizeof(ogs_gtp1_header_t)); + h.type = OGS_GTP1_SGSN_CONTEXT_RESPONSE_TYPE; + h.teid = mme_ue ? mme_ue->gn.sgsn_gn_teid : 0; + + pkbuf = mme_gn_build_sgsn_context_response(mme_ue, cause); + if (!pkbuf) { + ogs_error("mme_gn_build_sgsn_context_response() failed"); + return OGS_ERROR; + } + /* FIXME: Reuse S11 TEID as local Gn interface for now */ + xact->local_teid = mme_ue ? mme_ue->mme_s11_teid : 0; + + rv = ogs_gtp1_xact_update_tx(xact, &h, pkbuf); + if (rv != OGS_OK) { + ogs_error("ogs_gtp1_xact_update_tx() failed"); + return OGS_ERROR; + } + + rv = ogs_gtp_xact_commit(xact); + ogs_expect(rv == OGS_OK); + + return rv; +} int mme_gtp1_send_ran_information_relay( mme_sgsn_t *sgsn, const uint8_t *buf, size_t len, diff --git a/src/mme/mme-gtp-path.h b/src/mme/mme-gtp-path.h index 37773e115d..1e41d34ffd 100644 --- a/src/mme/mme-gtp-path.h +++ b/src/mme/mme-gtp-path.h @@ -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_response( + mme_ue_t *mme_ue, uint8_t cause, ogs_gtp_xact_t *xact); + int mme_gtp1_send_ran_information_relay( mme_sgsn_t *sgsn, const uint8_t *buf, size_t len, const ogs_nas_rai_t *rai, uint16_t cell_id); diff --git a/src/mme/mme-sm.c b/src/mme/mme-sm.c index 1a437a5d59..84ce7227f1 100644 --- a/src/mme/mme-sm.c +++ b/src/mme/mme-sm.c @@ -662,6 +662,9 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e) case OGS_GTP1_ECHO_RESPONSE_TYPE: mme_gn_handle_echo_response(xact, >p1_message.echo_response); break; + case OGS_GTP1_SGSN_CONTEXT_REQUEST_TYPE: + mme_gn_handle_sgsn_context_request(xact, >p1_message.sgsn_context_request); + break; case OGS_GTP1_RAN_INFORMATION_RELAY_TYPE: mme_gn_handle_ran_information_relay(xact, >p1_message.ran_information_relay); break;