From 3a27102e59caeaf2389e738c647b88778ddfe3b2 Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Thu, 21 Jan 2021 18:44:23 +0100 Subject: [PATCH] Initial handling support for RIM messages This code doesn't do anything yet app-related with the received RIM messages, but already provides the initial infrastructure to handle them in the future, and does first checkings. Related: SYS#5103 Change-Id: Ia0ade0e97ea781ec655439c008b6cefaf3e90dec --- src/Makefile.am | 2 + src/gprs_bssgp_pcu.c | 17 ++++- src/gprs_bssgp_rim.c | 169 +++++++++++++++++++++++++++++++++++++++++++ src/gprs_bssgp_rim.h | 24 ++++++ src/gprs_debug.cpp | 7 ++ src/gprs_debug.h | 1 + src/gprs_pcu.c | 10 +++ src/gprs_pcu.h | 1 + 8 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 src/gprs_bssgp_rim.c create mode 100644 src/gprs_bssgp_rim.h diff --git a/src/Makefile.am b/src/Makefile.am index de924a68..f85a4568 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -43,6 +43,7 @@ libgprs_la_SOURCES = \ csn1.c \ gsm_rlcmac.c \ gprs_bssgp_pcu.c \ + gprs_bssgp_rim.c \ gprs_rlcmac.cpp \ gprs_rlcmac_sched.cpp \ gprs_rlcmac_meas.cpp \ @@ -81,6 +82,7 @@ noinst_HEADERS = \ csn1.h \ gsm_rlcmac.h \ gprs_bssgp_pcu.h \ + gprs_bssgp_rim.h \ gprs_rlcmac.h \ gprs_ms.h \ gprs_ms_storage.h \ diff --git a/src/gprs_bssgp_pcu.c b/src/gprs_bssgp_pcu.c index c5cc39df..81a65184 100644 --- a/src/gprs_bssgp_pcu.c +++ b/src/gprs_bssgp_pcu.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -419,10 +420,22 @@ static int gprs_bssgp_pcu_rcvmsg(struct msgb *msg) int rc = 0; struct bssgp_bvc_ctx *bctx; - if (pdu_type == BSSGP_PDUT_STATUS) + switch (pdu_type) { + case BSSGP_PDUT_STATUS: /* Pass the message to the generic BSSGP parser, which handles * STATUS and RESET messages in either direction. */ + case BSSGP_PDUT_RAN_INFO: + case BSSGP_PDUT_RAN_INFO_REQ: + case BSSGP_PDUT_RAN_INFO_ACK: + case BSSGP_PDUT_RAN_INFO_ERROR: + case BSSGP_PDUT_RAN_INFO_APP_ERROR: + /* Also pass all RIM related messages to the generic BSSGP + * parser so that it can deliver primitive to the RIM SAP + * (SAP_BSSGP_RIM) */ return bssgp_rcvmsg(msg); + default: + break; + } /* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */ @@ -552,6 +565,8 @@ int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) if (oph->primitive == PRIM_NM_STATUS) handle_nm_status(bp); break; + case SAP_BSSGP_RIM: + return handle_rim(bp); default: break; } diff --git a/src/gprs_bssgp_rim.c b/src/gprs_bssgp_rim.c new file mode 100644 index 00000000..cf43a8f4 --- /dev/null +++ b/src/gprs_bssgp_rim.c @@ -0,0 +1,169 @@ +/* gprs_bssgp_pcu.cpp + * + * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "gprs_debug.h" +#include "gprs_pcu.h" + +#define LOGPRIM(nsei, level, fmt, args...) \ + LOGP(DRIM, level, "(NSEI=%u) " fmt, nsei, ## args) + +static inline void gprs_ra_id_ci_to_cgi_ps(struct osmo_cell_global_id_ps *cgi_ps, + const struct gprs_ra_id *raid, uint16_t cid) +{ + *cgi_ps = (struct osmo_cell_global_id_ps) { + .rai.lac.plmn.mcc = raid->mcc, + .rai.lac.plmn.mnc = raid->mnc, + .rai.lac.plmn.mnc_3_digits = raid->mnc_3_digits, + .rai.lac.lac = raid->lac, + .rai.rac = raid->rac, + .cell_identity = cid, + }; +} + +/* Mirror RIM routing information of a given PDU, see also 3GPP TS 48.018, section 8c.1.4.3 */ +static void mirror_rim_routing_info(struct bssgp_ran_information_pdu *to_pdu, + const struct bssgp_ran_information_pdu *from_pdu) +{ + memcpy(&to_pdu->routing_info_dest, &from_pdu->routing_info_src, sizeof(to_pdu->routing_info_dest)); + memcpy(&to_pdu->routing_info_src, &from_pdu->routing_info_dest, sizeof(to_pdu->routing_info_src)); +} + +/* Format a RAN INFORMATION ERROR PDU */ +static void format_response_pdu_err(struct bssgp_ran_information_pdu *resp_pdu, + const struct bssgp_ran_information_pdu *req_pdu) +{ + memset(resp_pdu, 0, sizeof(*resp_pdu)); + mirror_rim_routing_info(resp_pdu, req_pdu); + + resp_pdu->decoded.err_rim_cont = (struct bssgp_ran_inf_err_rim_cont) { + .app_id = BSSGP_RAN_INF_APP_ID_NACC, + .prot_ver = 1, + .err_pdu = req_pdu->rim_cont, + .err_pdu_len = req_pdu->rim_cont_len, + }; + + resp_pdu->decoded_present = true; + resp_pdu->rim_cont_iei = BSSGP_IE_RI_ERROR_RIM_COINTAINER; +} + +/* Check if the application ID in the request PDU is actually BSSGP_RAN_INF_APP_ID_NACC */ +static const enum bssgp_ran_inf_app_id *get_app_id(const struct bssgp_ran_information_pdu *pdu) +{ + switch (pdu->rim_cont_iei) { + case BSSGP_IE_RI_REQ_RIM_CONTAINER: + return &pdu->decoded.req_rim_cont.app_id; + case BSSGP_IE_RI_RIM_CONTAINER: + return &pdu->decoded.rim_cont.app_id; + case BSSGP_IE_RI_APP_ERROR_RIM_CONT: + return &pdu->decoded.app_err_rim_cont.app_id; + case BSSGP_IE_RI_ACK_RIM_CONTAINER: + return &pdu->decoded.ack_rim_cont.app_id; + case BSSGP_IE_RI_ERROR_RIM_COINTAINER: + return &pdu->decoded.err_rim_cont.app_id; + default: + return NULL; + } +} + +/* Check if the application ID in the request PDU is of a certain type */ +static bool match_app_id(const struct bssgp_ran_information_pdu *pdu, enum bssgp_ran_inf_app_id exp_app_id) +{ + const enum bssgp_ran_inf_app_id *app_id = get_app_id(pdu); + if (app_id && *app_id == exp_app_id) + return true; + return false; +} + +int handle_rim(struct osmo_bssgp_prim *bp) +{ + struct msgb *msg = bp->oph.msg; + uint16_t nsei = msgb_nsei(msg); + struct bssgp_ran_information_pdu *pdu = &bp->u.rim_pdu; + struct bssgp_ran_information_pdu resp_pdu; + struct osmo_cell_global_id_ps dst_addr; + struct gprs_rlcmac_bts *bts; + + OSMO_ASSERT (bp->oph.sap == SAP_BSSGP_RIM); + + /* At the moment we only support GERAN, so we block all other network + * types here. */ + if (pdu->routing_info_dest.discr != BSSGP_RIM_ROUTING_INFO_GERAN) { + LOGPRIM(nsei, LOGL_ERROR, + "Only GERAN supported, destination cell is not a GERAN cell -- rejected.\n"); + return bssgp_tx_status(BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg); + } + + /* Check if the RIM pdu is really addressed to this PCU. In case we + * receive a RIM PDU for a cell that is not parented by this PCU we + * are supposed to reject it with a BSSGP STATUS. + * see also: 3GPP TS 48.018, section 8c.3.1.2 */ + gprs_ra_id_ci_to_cgi_ps(&dst_addr, &pdu->routing_info_dest.geran.raid, + pdu->routing_info_dest.geran.cid); + bts = gprs_pcu_get_bts_by_cgi_ps(the_pcu, &dst_addr); + if (!bts) { + LOGPRIM(nsei, LOGL_ERROR, "Cell %s unknown to this pcu\n", + osmo_cgi_ps_name(&dst_addr)); + return bssgp_tx_status(BSSGP_CAUSE_UNKN_DST, NULL, msg); + } + + /* Check if the incoming RIM PDU is parseable, if not we must report + * an error to the controlling BSS 3GPP TS 48.018, 8c.3.4 and 8c.3.4.2 */ + if (!pdu->decoded_present) { + LOGPRIM(nsei, LOGL_ERROR, "Errornous RIM PDU received -- rejected.\n"); + format_response_pdu_err(&resp_pdu, pdu); + return 0; + } + + /* Check if the RIM container inside the incoming RIM PDU has the correct + * application ID */ + if (!match_app_id(pdu, BSSGP_RAN_INF_APP_ID_NACC)) { + LOGPRIM(nsei, LOGL_ERROR, "RIM PDU with unknown/wrong application ID received -- rejected.\n"); + format_response_pdu_err(&resp_pdu, pdu); + return 0; + } + + /* Handle incoming RIM container */ + switch (pdu->rim_cont_iei) { + case BSSGP_IE_RI_REQ_RIM_CONTAINER: + LOGPRIM(nsei, LOGL_NOTICE, "Responding to RAN INFORMATION REQUEST not yet implemented!\n"); + break; + case BSSGP_IE_RI_RIM_CONTAINER: + LOGPRIM(nsei, LOGL_NOTICE, "Responding to RAN INFORMATION not yet implemented!\n"); + break; + case BSSGP_IE_RI_APP_ERROR_RIM_CONT: + case BSSGP_IE_RI_ACK_RIM_CONTAINER: + case BSSGP_IE_RI_ERROR_RIM_COINTAINER: + LOGPRIM(nsei, LOGL_ERROR, "RIM PDU not handled by this application\n"); + return -EINVAL; + default: + /* This should never happen. If the RIM PDU is parsed correctly, then the rim_cont_iei will + * be set to one of the cases above and if parsing fails this switch statement is guarded + * by the check on decoded_present above */ + OSMO_ASSERT(false); + } + + return 0; +} diff --git a/src/gprs_bssgp_rim.h b/src/gprs_bssgp_rim.h new file mode 100644 index 00000000..5fac56f1 --- /dev/null +++ b/src/gprs_bssgp_rim.h @@ -0,0 +1,24 @@ +/* gprs_bssgp_rim.h + * + * Copyright (C) 2021 by sysmocom - s.f.m.c. GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#pragma once + +struct osmo_bssgp_prim; + +int handle_rim(struct osmo_bssgp_prim *bp); diff --git a/src/gprs_debug.cpp b/src/gprs_debug.cpp index a790e3f6..669ea270 100644 --- a/src/gprs_debug.cpp +++ b/src/gprs_debug.cpp @@ -119,6 +119,13 @@ static const struct log_info_cat default_categories[] = { .loglevel = LOGL_NOTICE, .enabled = 1, }, + [DRIM] = { + .name = "DRIM", + .color = "\033[1;38m", + .description = "RAN Information Management (RIM)", + .loglevel = LOGL_NOTICE, + .enabled = 1, + }, }; static int filter_fn(const struct log_context *ctx, diff --git a/src/gprs_debug.h b/src/gprs_debug.h index 84a0a073..8df405af 100644 --- a/src/gprs_debug.h +++ b/src/gprs_debug.h @@ -46,6 +46,7 @@ enum { DTBFUL, DNS, DPCU, + DRIM, aDebug_LastEntry }; diff --git a/src/gprs_pcu.c b/src/gprs_pcu.c index d291e71e..32259b8d 100644 --- a/src/gprs_pcu.c +++ b/src/gprs_pcu.c @@ -116,6 +116,16 @@ struct gprs_rlcmac_bts *gprs_pcu_get_bts_by_nr(struct gprs_pcu *pcu, uint8_t bts return NULL; } +struct gprs_rlcmac_bts *gprs_pcu_get_bts_by_cgi_ps(struct gprs_pcu *pcu, struct osmo_cell_global_id_ps *cgi_ps) +{ + struct gprs_rlcmac_bts *pos; + llist_for_each_entry(pos, &pcu->bts_list, list) { + if (memcmp(&pos->cgi_ps, cgi_ps, sizeof(*cgi_ps)) == 0) + return pos; + } + return NULL; +} + void gprs_pcu_set_initial_cs(struct gprs_pcu *pcu, uint8_t cs_dl, uint8_t cs_ul) { struct gprs_rlcmac_bts *bts; diff --git a/src/gprs_pcu.h b/src/gprs_pcu.h index 058d1023..a1cd1ed1 100644 --- a/src/gprs_pcu.h +++ b/src/gprs_pcu.h @@ -124,6 +124,7 @@ extern struct gprs_pcu *the_pcu; struct gprs_pcu *gprs_pcu_alloc(void *ctx); struct gprs_rlcmac_bts *gprs_pcu_get_bts_by_nr(struct gprs_pcu *pcu, uint8_t bts_nr); +struct gprs_rlcmac_bts *gprs_pcu_get_bts_by_cgi_ps(struct gprs_pcu *pcu, struct osmo_cell_global_id_ps *cgi_ps); void gprs_pcu_set_initial_cs(struct gprs_pcu *pcu, uint8_t cs_dl, uint8_t cs_ul); void gprs_pcu_set_initial_mcs(struct gprs_pcu *pcu, uint8_t mcs_dl, uint8_t mcs_ul);