neels has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-hnbgw/+/33133 )
Change subject: cnpool: extract Mobile Identity from RANAP payload ......................................................................
cnpool: extract Mobile Identity from RANAP payload
For InitialUE messages, decode the RANAP and NAS PDU to obtain the Mobile Identity. Store it in map->l3.
A following patch will use this mobile identity to decide on which CN links to pick from the CN pools for this subscriber.
Add the new information to LOG_MAP() as ubiquitous logging context.
Related: SYS#6412 Change-Id: I373d665c9684b607207f68094188eab63209db51 --- M include/osmocom/hnbgw/context_map.h M include/osmocom/hnbgw/hnbgw.h M src/osmo-hnbgw/Makefile.am A src/osmo-hnbgw/hnbgw_l3.c M src/osmo-hnbgw/hnbgw_rua.c 5 files changed, 353 insertions(+), 3 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-hnbgw refs/changes/33/33133/1
diff --git a/include/osmocom/hnbgw/context_map.h b/include/osmocom/hnbgw/context_map.h index bcc3238..2920438 100644 --- a/include/osmocom/hnbgw/context_map.h +++ b/include/osmocom/hnbgw/context_map.h @@ -3,12 +3,19 @@ #include <stdint.h> #include <osmocom/core/linuxlist.h> #include <osmocom/hnbgw/hnbgw.h> +#include <osmocom/gsm/gsm48.h>
#define LOG_MAP(HNB_CTX_MAP, SUBSYS, LEVEL, FMT, ARGS...) \ LOGHNB((HNB_CTX_MAP) ? (HNB_CTX_MAP)->hnb_ctx : NULL, \ - SUBSYS, LEVEL, "RUA-%u %s: " FMT, \ + SUBSYS, LEVEL, "RUA-%u %s MI=%s%s%s: " FMT, \ (HNB_CTX_MAP) ? (HNB_CTX_MAP)->rua_ctx_id : 0, \ - (HNB_CTX_MAP) ? ((HNB_CTX_MAP)->is_ps ? "PS" : "CS") : "NULL", \ + (HNB_CTX_MAP) ? \ + ( (HNB_CTX_MAP)->cnlink ? (HNB_CTX_MAP)->cnlink->name \ + : ((HNB_CTX_MAP)->is_ps ? "PS" : "CS") ) \ + : "NULL", \ + (HNB_CTX_MAP) ? osmo_mobile_identity_to_str_c(OTC_SELECT, &(HNB_CTX_MAP)->l3.mi) : "null", \ + (HNB_CTX_MAP) && (HNB_CTX_MAP)->l3.from_other_plmn ? " (from other PLMN)" : "", \ + (HNB_CTX_MAP) && (HNB_CTX_MAP)->l3.is_emerg ? " EMERGENCY" : "", \ ##ARGS)
/* All these events' data argument may either be NULL, or point to a RANAP msgb. @@ -75,6 +82,21 @@ struct hnb_context; struct hnbgw_cnlink;
+struct hnbgw_l3_peek { + /* L3 message type, like GSM48_PDISC_MM+GSM48_MT_MM_LOC_UPD_REQUEST... / GSM48_PDISC_MM_GPRS+GSM48_MT_GMM_ATTACH_REQ... */ + uint8_t gsm48_pdisc; + uint8_t gsm48_msg_type; + /* The Mobile Identity from MM and GMM messages */ + struct osmo_mobile_identity mi; + /* On PS, the "TMSI Based NRI Container", 10 bit integer, or -1 if not present. + * This is only for PS -- for CS, the NRI is in the TMSI obtained from 'mi' above. */ + int gmm_nri_container; + /* For a CM Service Request for voice call, true if this is for an Emergency Call, false otherwise. */ + bool is_emerg; + /* True if the NAS PDU indicates that the UE was previously attached to a different PLMN than the local PLMN. */ + bool from_other_plmn; +}; + struct hnbgw_context_map { /* entry in the per-CN list of mappings */ struct llist_head hnbgw_cnlink_entry; @@ -103,6 +125,9 @@ /* False for CS, true for PS */ bool is_ps;
+ /* Information extracted from RUA Connect's RANAP InitialUE message */ + struct hnbgw_l3_peek l3; + /* When an FSM is asked to disconnect but must still wait for a response, it may set this flag, to continue to * disconnect once the response is in. In particular, when SCCP is asked to disconnect after an SCCP Connection * Request was already sent and while waiting for a Connection Confirmed, we should still wait for the SCCP CC diff --git a/include/osmocom/hnbgw/hnbgw.h b/include/osmocom/hnbgw/hnbgw.h index cc32f93..8183cbc 100644 --- a/include/osmocom/hnbgw/hnbgw.h +++ b/include/osmocom/hnbgw/hnbgw.h @@ -312,3 +312,5 @@ }
struct msgb *hnbgw_ranap_msg_alloc(const char *name); + +int hnbgw_peek_l3(struct hnbgw_context_map *map, struct msgb *ranap_msg); diff --git a/src/osmo-hnbgw/Makefile.am b/src/osmo-hnbgw/Makefile.am index e8b4966..5fbdf74 100644 --- a/src/osmo-hnbgw/Makefile.am +++ b/src/osmo-hnbgw/Makefile.am @@ -33,6 +33,7 @@ libhnbgw_la_SOURCES = \ hnbgw.c \ hnbgw_hnbap.c \ + hnbgw_l3.c \ hnbgw_rua.c \ hnbgw_ranap.c \ hnbgw_vty.c \ diff --git a/src/osmo-hnbgw/hnbgw_l3.c b/src/osmo-hnbgw/hnbgw_l3.c new file mode 100644 index 0000000..97d958e --- /dev/null +++ b/src/osmo-hnbgw/hnbgw_l3.c @@ -0,0 +1,296 @@ +/* OsmoHNBGW implementation of CS and PS Level3 message decoding (NAS PDU */ + +/* Copyright 2023 by sysmocom - s.f.m.c. GmbH info@sysmocom.de + * All Rights Reserved + * + * Author: Neels Janosch Hofmeyr nhofmeyr@sysmocom.de + * + * This program 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. + * + * This program 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. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +#include "config.h" + +#include "asn1helpers.h" + +#include <osmocom/gsm/gsm48.h> +#include <osmocom/gsm/protocol/gsm_04_08_gprs.h> + +#include <osmocom/hnbgw/hnbgw.h> +#include <osmocom/hnbgw/hnbgw_rua.h> +#include <osmocom/hnbgw/context_map.h> +#include <osmocom/ranap/ranap_ies_defs.h> + +static const struct tlv_definition gsm48_gmm_att_tlvdef = { + .def = { + [GSM48_IE_GMM_CIPH_CKSN] = { TLV_TYPE_FIXED, 1 }, + [GSM48_IE_GMM_TIMER_READY] = { TLV_TYPE_TV, 1 }, + [GSM48_IE_GMM_TMSI_BASED_NRI_C] = { TLV_TYPE_TLV }, + [GSM48_IE_GMM_ALLOC_PTMSI] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GMM_PTMSI_SIG] = { TLV_TYPE_FIXED, 3 }, + [GSM48_IE_GMM_AUTH_RAND] = { TLV_TYPE_FIXED, 16 }, + [GSM48_IE_GMM_AUTH_SRES] = { TLV_TYPE_FIXED, 4 }, + [GSM48_IE_GMM_IMEISV] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GMM_DRX_PARAM] = { TLV_TYPE_FIXED, 2 }, + [GSM48_IE_GMM_MS_NET_CAPA] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GMM_PDP_CTX_STATUS] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GMM_PS_LCS_CAPA] = { TLV_TYPE_TLV, 0 }, + [GSM48_IE_GMM_GMM_MBMS_CTX_ST] = { TLV_TYPE_TLV, 0 }, + }, +}; + +static void decode_gmm_tlv(struct osmo_mobile_identity *mi, + struct osmo_routing_area_id *old_ra, + int *nri, + const uint8_t *tlv_data, size_t tlv_len, bool allow_hex) +{ + struct tlv_parsed tp; + struct tlv_p_entry *e; + + tlv_parse(&tp, &gsm48_gmm_att_tlvdef, tlv_data, tlv_len, 0, 0); + + e = TLVP_GET(&tp, GSM48_IE_GMM_TMSI_BASED_NRI_C); + if (e) { + *nri = e->val[0]; + *nri <<= 2; + *nri |= e->val[1] >> 6; + } +} + +static int mobile_identity_decode_from_gmm_att_req(struct osmo_mobile_identity *mi, + struct osmo_routing_area_id *old_ra, + int *nri, + const uint8_t *l3_data, size_t l3_len, bool allow_hex) +{ + const struct gsm48_hdr *gh = (void *)l3_data; + const uint8_t *cur = gh->data; + const uint8_t *mi_data; + uint8_t mi_len; + uint8_t msnc_len; + uint8_t ms_ra_acc_cap_len; + const struct gsm48_ra_id *ra_id; + int rc; + + /* MS network capability 10.5.5.12 */ + msnc_len = *cur++; + cur += msnc_len; + + /* aTTACH Type 10.5.5.2 */ + cur++; + + /* DRX parameter 10.5.5.6 */ + cur += 2; + + /* Mobile Identity (P-TMSI or IMSI) 10.5.1.4 */ + mi_len = *cur++; + mi_data = cur; + cur += mi_len; + + rc = osmo_mobile_identity_decode(mi, mi_data, mi_len, allow_hex); + if (rc) + return rc; + + /* Old routing area identification 10.5.5.15. */ + ra_id = (void*)cur; + osmo_plmn_from_bcd(ra_id->digits, &old_ra->lac.plmn); + old_ra->lac.lac = osmo_load16be(&ra_id->lac); + old_ra->rac = ra_id->rac; + cur += sizeof(*ra_id); + + /* MS Radio Access Capability 10.5.5.12a */ + ms_ra_acc_cap_len = *cur++; + cur += ms_ra_acc_cap_len; + + decode_gmm_tlv(mi, old_ra, nri, cur, (l3_data + l3_len) - cur, allow_hex); + return 0; +} + +static int mobile_identity_decode_from_gmm_rau_req(struct osmo_mobile_identity *mi, + struct osmo_routing_area_id *old_ra, + int *nri, + const uint8_t *l3_data, size_t l3_len, bool allow_hex) +{ + const struct gsm48_hdr *gh = (void *)l3_data; + const uint8_t *cur = gh->data; + uint8_t ms_ra_acc_cap_len; + const struct gsm48_ra_id *ra_id; + + /* Update Type 10.5.5.18 */ + cur++; + + /* Old routing area identification 10.5.5.15 */ + ra_id = (void*)cur; + osmo_plmn_from_bcd(ra_id->digits, &old_ra->lac.plmn); + old_ra->lac.lac = osmo_load16be(&ra_id->lac); + old_ra->rac = ra_id->rac; + cur += sizeof(*ra_id); + + /* MS Radio Access Capability 10.5.5.12a */ + ms_ra_acc_cap_len = *cur++; + cur += ms_ra_acc_cap_len; + + decode_gmm_tlv(mi, old_ra, nri, cur, (l3_data + l3_len) - cur, allow_hex); + return 0; +} + +static int peek_l3_nas(struct hnbgw_context_map *map, const uint8_t *nas_pdu, size_t len, + const struct osmo_plmn_id *local_plmn) +{ + const struct gsm48_hdr *gh; + int8_t pdisc; + uint8_t mtype; + const struct gsm48_loc_upd_req *lu; + struct gsm48_service_request *cm; + struct osmo_location_area_id old_lai; + struct osmo_routing_area_id old_ra = {}; + int nri = -1; + + map->l3 = (struct hnbgw_l3_peek){ + .gmm_nri_container = -1, + }; + + /* Get the mobile identity from CS MM -- the PS GMM happens further down. + * This will return an error for GMM messages, ignore that. */ + if (!map->is_ps) + osmo_mobile_identity_decode_from_l3_buf(&map->l3.mi, nas_pdu, len, false); + + /* Get is_emerg and from_other_plmn */ + if (len < sizeof(*gh)) { + LOGP(DCN, LOGL_ERROR, "Layer 3 message too short for header\n"); + return -EINVAL; + } + + gh = (void*)nas_pdu; + pdisc = gsm48_hdr_pdisc(gh); + mtype = gsm48_hdr_msg_type(gh); + + map->l3.gsm48_pdisc = pdisc; + map->l3.gsm48_msg_type = mtype; + + /* Determine from_other_plmn and is_emerg */ + switch (pdisc) { + case GSM48_PDISC_MM: + + switch (mtype) { + case GSM48_MT_MM_LOC_UPD_REQUEST: + if (len < sizeof(*gh) + sizeof(*lu)) { + LOGP(DCN, LOGL_ERROR, "LU Req message too short\n"); + break; + } + + lu = (struct gsm48_loc_upd_req*)gh->data; + gsm48_decode_lai2(&lu->lai, &old_lai); + + map->l3.from_other_plmn = (osmo_plmn_cmp(&old_lai.plmn, local_plmn) != 0); + LOGP(DCN, LOGL_DEBUG, "LU from_other_plmn=%d\n", map->l3.from_other_plmn); + break; + + case GSM48_MT_MM_CM_SERV_REQ: + if (len < sizeof(*gh) + sizeof(*cm)) { + LOGP(DCN, LOGL_ERROR, "CM Service Req message too short\n"); + break; + } + cm = (struct gsm48_service_request *)&gh->data[0]; + map->l3.is_emerg = (cm->cm_service_type == GSM48_CMSERV_EMERGENCY); + LOGP(DCN, LOGL_DEBUG, "CM Service is_emerg=%d\n", map->l3.is_emerg); + break; + + default: + break; + } + break; + + case GSM48_PDISC_MM_GPRS: + switch (mtype) { + case GSM48_MT_GMM_ATTACH_REQ: + mobile_identity_decode_from_gmm_att_req(&map->l3.mi, &old_ra, &nri, nas_pdu, len, false); + + LOGP(DRUA, LOGL_DEBUG, "GMM Attach Req mi=%s old_ra=%s nri:%d=0x%x\n", + osmo_mobile_identity_to_str_c(OTC_SELECT, &map->l3.mi), + osmo_rai_name2_c(OTC_SELECT, &old_ra), + nri, nri); + + if (old_ra.lac.plmn.mcc && osmo_plmn_cmp(&old_ra.lac.plmn, local_plmn)) { + map->l3.from_other_plmn = true; + LOGP(DRUA, LOGL_INFO, "GMM Attach Req from other PLMN: old RAI=%s my PLMN=%s\n", + osmo_rai_name2_c(OTC_SELECT, &old_ra), + osmo_plmn_name_c(OTC_SELECT, local_plmn)); + } + + if (nri >= 0) + map->l3.gmm_nri_container = nri; + + break; + + case GSM48_MT_GMM_RA_UPD_REQ: + mobile_identity_decode_from_gmm_rau_req(&map->l3.mi, &old_ra, &nri, nas_pdu, len, false); + + LOGP(DRUA, LOGL_DEBUG, "GMM Routing Area Upd Req mi=%s old_ra=%s nri:%d=0x%x\n", + osmo_mobile_identity_to_str_c(OTC_SELECT, &map->l3.mi), + osmo_rai_name2_c(OTC_SELECT, &old_ra), + nri, nri); + + if (old_ra.lac.plmn.mcc && osmo_plmn_cmp(&old_ra.lac.plmn, local_plmn)) { + map->l3.from_other_plmn = true; + LOGP(DRUA, LOGL_INFO, "GMM Routing Area Upd Req from other PLMN: old RAI=%s my PLMN=%s\n", + osmo_rai_name2_c(OTC_SELECT, &old_ra), + osmo_plmn_name_c(OTC_SELECT, local_plmn)); + } + + if (nri >= 0) + map->l3.gmm_nri_container = nri; + + break; + } + break; + + default: + break; + } + + return 0; +} + +static int peek_l3_initial_ue(struct hnbgw_context_map *map, const RANAP_InitialUE_MessageIEs_t *ies) +{ + struct osmo_plmn_id plmn; + + if (ies->lai.pLMNidentity.size < 3) { + LOGP(DCN, LOGL_ERROR, "Missing PLMN in RANAP InitialUE message\n"); + return -EINVAL; + } + osmo_plmn_from_bcd(ies->lai.pLMNidentity.buf, &plmn); + + return peek_l3_nas(map, ies->nas_pdu.buf, ies->nas_pdu.size, &plmn); +} + +/* Extract a Layer 3 message (NAS PDU) from the RANAP message, and put the info obtained in map->l3. This is relevant + * for CN pooling, to decide which CN link to map the RUA context to. */ +int hnbgw_peek_l3(struct hnbgw_context_map *map, struct msgb *ranap_msg) +{ + ranap_message *message = hnbgw_decode_ranap_co(ranap_msg); + if (!message) { + LOGP(DCN, LOGL_ERROR, "Failed to decode RANAP PDU\n"); + return -EINVAL; + } + + switch (message->procedureCode) { + case RANAP_ProcedureCode_id_InitialUE_Message: + return peek_l3_initial_ue(map, &message->msg.initialUE_MessageIEs); + default: + LOGP(DCN, LOGL_ERROR, "unexpected RANAP PDU in RUA Connect message: %s\n", + get_value_string(ranap_procedure_code_vals, message->procedureCode)); + return -ENOTSUP; + } +} diff --git a/src/osmo-hnbgw/hnbgw_rua.c b/src/osmo-hnbgw/hnbgw_rua.c index abda5ea..2a7dc20 100644 --- a/src/osmo-hnbgw/hnbgw_rua.c +++ b/src/osmo-hnbgw/hnbgw_rua.c @@ -38,7 +38,9 @@ #include <osmocom/rua/rua_common.h> #include <osmocom/rua/rua_ies_defs.h> #include <osmocom/hnbgw/context_map.h> +#include <osmocom/hnbgw/hnbgw_rua.h> #include <osmocom/hnbap/HNBAP_CN-DomainIndicator.h> +#include <osmocom/ranap/ranap_ies_defs.h>
static const char *cn_domain_indicator_to_str(RUA_CN_DomainIndicator_t cN_DomainIndicator) { @@ -208,7 +210,13 @@ map = context_map_alloc(hnb, rua_ctx_id, is_ps); OSMO_ASSERT(map);
- /* FUTURE: extract mobile identity and store in map-> */ + if (hnbgw_peek_l3(map, ranap_msg)) { + LOGP(DCN, LOGL_NOTICE, "Failed to extract Mobile Identity from RUA Connect message's RANAP payload\n"); + LOGP(DCN, LOGL_DEBUG, "Connect message's RANAP payload: %s\n", + osmo_hexdump_nospc_c(OTC_SELECT, ranap_msg->data, ranap_msg->len)); + /* still continue to select a hopefully adequate link */ + } + /* map->l3 now contains all the interesting information from the NAS PDU */
cnlink = hnbgw_cnlink_select(map); if (!cnlink) {