From d1b1f694292b13b52a027f08b7250fcb6e8372b4 Mon Sep 17 00:00:00 2001 From: Philipp Maier Date: Tue, 19 Jan 2021 23:58:39 +0100 Subject: WIP: --- include/osmocom/pcu/pcuif_proto.h | 10 +- src/Makefile.am | 2 + src/bts.h | 4 + src/gprs_bssgp_pcu.c | 26 +++++ src/gprs_bssgp_rim.c | 203 ++++++++++++++++++++++++++++++++++++++ src/gprs_bssgp_rim.h | 5 + src/gprs_debug.cpp | 6 ++ src/gprs_debug.h | 1 + src/pcu_l1_if.cpp | 27 +++++ 9 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 src/gprs_bssgp_rim.c create mode 100644 src/gprs_bssgp_rim.h diff --git a/include/osmocom/pcu/pcuif_proto.h b/include/osmocom/pcu/pcuif_proto.h index cdd73d95..6ac9a027 100644 --- a/include/osmocom/pcu/pcuif_proto.h +++ b/include/osmocom/pcu/pcuif_proto.h @@ -1,12 +1,13 @@ #ifndef _PCUIF_PROTO_H #define _PCUIF_PROTO_H +#include #include #include #define PCU_SOCK_DEFAULT "/tmp/pcu_bts" -#define PCU_IF_VERSION 0x0a +#define PCU_IF_VERSION 0x0b #define TXT_MAX_LEN 128 /* msg_type */ @@ -177,6 +178,13 @@ struct gsm_pcu_if_info_ind { struct in_addr v4; struct in6_addr v6; } remote_ip[PCU_IF_NUM_NSVC]; + /* RIM */ + uint8_t si1[GSM_MACBLOCK_LEN]; + uint8_t si1_is_set; + uint8_t si3[GSM_MACBLOCK_LEN]; + uint8_t si3_is_set; + uint8_t si13[GSM_MACBLOCK_LEN]; + uint8_t si13_is_set; } __attribute__ ((packed)); struct gsm_pcu_if_act_req { 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/bts.h b/src/bts.h index 7f437e37..195c44fe 100644 --- a/src/bts.h +++ b/src/bts.h @@ -215,6 +215,10 @@ struct gprs_rlcmac_bts { uint8_t n3105; struct gprs_rlcmac_trx trx[8]; + uint8_t si1[GSM_MACBLOCK_LEN]; + bool si1_is_set; + uint8_t si3[GSM_MACBLOCK_LEN]; + bool si3_is_set; uint8_t si13[GSM_MACBLOCK_LEN]; bool si13_is_set; diff --git a/src/gprs_bssgp_pcu.c b/src/gprs_bssgp_pcu.c index 956fb666..c5163a3d 100644 --- a/src/gprs_bssgp_pcu.c +++ b/src/gprs_bssgp_pcu.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -424,6 +425,20 @@ static int gprs_bssgp_pcu_rcvmsg(struct msgb *msg) * STATUS and RESET messages in either direction. */ return bssgp_rcvmsg(msg); + switch (pdu_type) { + /* Also pass all RIM related messages to the generic BSSGP + * parser so that it can deliver primitive to the RIM SAP + * (SAP_BSSGP_RIM) */ + 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: + return bssgp_rcvmsg(msg); + default: + break; + } + /* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */ /* UNITDATA BSSGP headers have TLLI in front */ @@ -544,14 +559,25 @@ static void handle_nm_status(struct osmo_bssgp_prim *bp) int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) { + struct gprs_rlcmac_bts *bts; struct osmo_bssgp_prim *bp; bp = container_of(oph, struct osmo_bssgp_prim, oph); + /* FIXME: This calculation needs to be redone to support multiple BTS */ + bts = llist_first_entry_or_null(&the_pcu->bts_list, struct gprs_rlcmac_bts, list); + if (!bts) { + LOGP(DBSSGP, LOGL_ERROR, "No bts\n"); + return -EIO; + } + switch (oph->sap) { case SAP_BSSGP_NM: if (oph->primitive == PRIM_NM_STATUS) handle_nm_status(bp); break; + case SAP_BSSGP_RIM: + sgsn_rim_rx(bp, oph->msg, bts); + break; default: break; } diff --git a/src/gprs_bssgp_rim.c b/src/gprs_bssgp_rim.c new file mode 100644 index 00000000..cd7b2757 --- /dev/null +++ b/src/gprs_bssgp_rim.c @@ -0,0 +1,203 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Match the destination ran information in the request PDU to the cell id / routing area id of the local bvc ctx */ +static bool match_destination_ran_info(struct bssgp_ran_information_pdu *req_pdu, uint16_t nsei) +{ + struct bssgp_bvc_ctx *bctx = gprs_bssgp_pcu_current_bctx(); + + if (req_pdu->routing_info_dest.geran.cid != bctx->cell_id) { + LOGP(DRIM, LOGL_ERROR, "BSSGP RIM (NSEI=%u) cell id (%04x != %04x) unknown to this pcu\n", + nsei, req_pdu->routing_info_dest.geran.cid, bctx->cell_id); + return false; + } + + if (memcmp + (&req_pdu->routing_info_dest.geran.raid, &bctx->ra_id, + sizeof(req_pdu->routing_info_dest.geran.raid) != 0)) { + LOGP(DRIM, LOGL_ERROR, "BSSGP RIM (NSEI=%u) routing area id (%s != %s) unknown to this pcu\n", nsei, + osmo_rai_name(&req_pdu->routing_info_dest.geran.raid), osmo_rai_name(&bctx->ra_id)); + return false; + } + + return true; +} + +/* Check if the application ID in the request PDU is actually BSSGP_RAN_INF_APP_ID_NACC */ +static bool match_app_id(struct bssgp_ran_information_pdu *req_pdu) +{ + switch (req_pdu->rim_cont_iei) { + case BSSGP_IE_RI_REQ_RIM_CONTAINER: + if (req_pdu->decoded.req_rim_cont.app_id == BSSGP_RAN_INF_APP_ID_NACC) + return true; + case BSSGP_IE_RI_RIM_CONTAINER: + if (req_pdu->decoded.rim_cont.app_id == BSSGP_RAN_INF_APP_ID_NACC) + return true; + case BSSGP_IE_RI_APP_ERROR_RIM_CONT: + if (req_pdu->decoded.app_err_rim_cont.app_id == BSSGP_RAN_INF_APP_ID_NACC) + return true; + case BSSGP_IE_RI_ACK_RIM_CONTAINER: + if (req_pdu->decoded.ack_rim_cont.app_id == BSSGP_RAN_INF_APP_ID_NACC) + return true; + case BSSGP_IE_RI_ERROR_RIM_COINTAINER: + if (req_pdu->decoded.err_rim_cont.app_id == BSSGP_RAN_INF_APP_ID_NACC) + return true; + default: + return false; + } +} + +/* 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 *resp_pdu, + struct bssgp_ran_information_pdu *req_pdu) +{ + memcpy(&resp_pdu->routing_info_dest, &req_pdu->routing_info_src, sizeof(resp_pdu->routing_info_dest)); + memcpy(&resp_pdu->routing_info_src, &req_pdu->routing_info_dest, sizeof(resp_pdu->routing_info_src)); +} + +/* Fill NACC application container with data (cell identifier, sysinfo) */ +static void fill_app_cont_nacc(struct bssgp_ran_inf_app_cont_nacc *app_cont, const struct gprs_rlcmac_bts *bts) +{ + struct bssgp_bvc_ctx *bctx = gprs_bssgp_pcu_current_bctx(); + + app_cont->reprt_cell.rai.lac.plmn.mcc = bctx->ra_id.mcc; + app_cont->reprt_cell.rai.lac.plmn.mnc = bctx->ra_id.mnc; + app_cont->reprt_cell.rai.lac.plmn.mnc_3_digits = bctx->ra_id.mnc_3_digits; + app_cont->reprt_cell.rai.lac.lac = bctx->ra_id.lac; + app_cont->reprt_cell.rai.rac = bctx->ra_id.rac; + app_cont->reprt_cell.cell_identity = bctx->cell_id; + app_cont->num_si = 0; + + if (bts->si1_is_set) { + app_cont->si[app_cont->num_si] = bts->si1 + 2; + app_cont->num_si++; + } + + if (bts->si3_is_set) { + app_cont->si[app_cont->num_si] = bts->si3 + 2; + app_cont->num_si++; + } + + if (bts->si13_is_set) { + app_cont->si[app_cont->num_si] = bts->si13 + 2; + app_cont->num_si++; + } + + /* Note: It is possible that the resulting PDU will not contain any system information, even if this is + * an unlikely case since the BTS immediately updates the system information after startup. The + * specification permits to send zero system information, see also: 3GPP TS 48.018 section 11.3.63.2.1 */ +} + +/* Format a RAN INFORMATION PDU that contains the requested system information */ +static void format_response_pdu(struct bssgp_ran_information_pdu *resp_pdu, struct bssgp_ran_information_pdu *req_pdu, + const struct gprs_rlcmac_bts *bts) +{ + memset(resp_pdu, 0, sizeof(*resp_pdu)); + mirror_rim_routing_info(resp_pdu, req_pdu); + + resp_pdu->decoded.rim_cont = (struct bssgp_ran_inf_rim_cont) { + .app_id = BSSGP_RAN_INF_APP_ID_NACC, + .seq_num = 1, /* single report has only one message in response */ + .pdu_ind = { + .pdu_type_ext = RIM_PDU_TYPE_SING_REP, + }, + .prot_ver = 1, + }; + + fill_app_cont_nacc(&resp_pdu->decoded.rim_cont.u.app_cont_nacc, bts); + resp_pdu->decoded_present = true; + resp_pdu->rim_cont_iei = BSSGP_IE_RI_RIM_CONTAINER; +} + +/* Format a RAN INFORMATION ERROR PDU */ +static void format_response_pdu_err(struct bssgp_ran_information_pdu *resp_pdu, + 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; +} + +int sgsn_rim_rx(struct osmo_bssgp_prim *bp, struct msgb *msg, struct gprs_rlcmac_bts *bts) +{ + uint16_t nsei = msgb_nsei(msg); + struct bssgp_ran_information_pdu *req_pdu = &bp->u.rim_pdu; + struct bssgp_ran_information_pdu resp_pdu; + + /* At the moment we only support GERAN, so we block all other network + * types here. */ + if (req_pdu->routing_info_dest.discr != BSSGP_RIM_ROUTING_INFO_GERAN) { + LOGP(DRIM, LOGL_ERROR, + "BSSGP RIM (NSEI=%u) only GERAN supported, destination cell is not a GERAN cell -- rejected.\n", + nsei); + return bssgp_tx_status(BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg); + } + if (req_pdu->routing_info_src.discr != BSSGP_RIM_ROUTING_INFO_GERAN) { + LOGP(DRIM, LOGL_ERROR, + "BSSGP RIM (NSEI=%u) only GERAN supported, source cell is not a GERAN cell -- rejected.\n", nsei); + return bssgp_tx_status(BSSGP_CAUSE_UNKN_RIM_AI, NULL, msg); + } + + /* Check if the RIM pdu is really addressed to this PCU, see also + * 3GPP TS 48.018, section 8c.3.1.2 */ + if (!match_destination_ran_info(req_pdu, nsei)) + 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 (!req_pdu->decoded_present) { + LOGP(DRIM, LOGL_ERROR, "BSSGP RIM (NSEI=%u) errornous RIM PDU received -- rejected.\n", nsei); + format_response_pdu_err(&resp_pdu, req_pdu); + return 0; + } + + /* Check if the RIM container inside the incoming RIM PDU has the correct + * application ID */ + if (!match_app_id(req_pdu)) { + LOGP(DRIM, LOGL_ERROR, + "BSSGP RIM (NSEI=%u) RIM PDU with unknown/wrong application ID received -- rejected.\n", nsei); + format_response_pdu_err(&resp_pdu, req_pdu); + return 0; + } + + /* Handle incoming RIM container */ + switch (req_pdu->rim_cont_iei) { + case BSSGP_IE_RI_REQ_RIM_CONTAINER: + LOGP(DRIM, LOGL_DEBUG, "BSSGP RIM (NSEI=%u) responding to RAN INFORMATION REQUEST ...\n", nsei); + format_response_pdu(&resp_pdu, req_pdu, bts); + bssgp_tx_rim(&resp_pdu, nsei); + break; + case BSSGP_IE_RI_RIM_CONTAINER: + case BSSGP_IE_RI_APP_ERROR_RIM_CONT: + case BSSGP_IE_RI_ACK_RIM_CONTAINER: + case BSSGP_IE_RI_ERROR_RIM_COINTAINER: + LOGP(DRIM, LOGL_ERROR, "BSSGP RIM (NSEI=%u) RIM PDU not handled by this application\n", nsei); + 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 failes 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..a34377d7 --- /dev/null +++ b/src/gprs_bssgp_rim.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +int sgsn_rim_rx(struct osmo_bssgp_prim *bp, struct msgb *msg, struct gprs_rlcmac_bts *bts); diff --git a/src/gprs_debug.cpp b/src/gprs_debug.cpp index a790e3f6..9f3b1b85 100644 --- a/src/gprs_debug.cpp +++ b/src/gprs_debug.cpp @@ -119,6 +119,12 @@ static const struct log_info_cat default_categories[] = { .loglevel = LOGL_NOTICE, .enabled = 1, }, + [DRIM] = { + .name = "DRIM", + .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/pcu_l1_if.cpp b/src/pcu_l1_if.cpp index 8ae74d52..7054d1bf 100644 --- a/src/pcu_l1_if.cpp +++ b/src/pcu_l1_if.cpp @@ -656,6 +656,33 @@ bssgp_failed: goto bssgp_failed; } + if (info_ind->si1_is_set) { + LOGP(DL1IF, LOGL_DEBUG, " si1=%s\n", osmo_hexdump_nospc(info_ind->si1, GSM_MACBLOCK_LEN)); + memcpy(bts->si1, info_ind->si1, GSM_MACBLOCK_LEN); + bts->si1_is_set = true; + } else { + LOGP(DL1IF, LOGL_DEBUG, " si1=(not set)\n"); + bts->si1_is_set = false; + } + + if (info_ind->si3_is_set) { + LOGP(DL1IF, LOGL_DEBUG, " si3=%s\n", osmo_hexdump_nospc(info_ind->si3, GSM_MACBLOCK_LEN)); + memcpy(bts->si3, info_ind->si3, GSM_MACBLOCK_LEN); + bts->si3_is_set = true; + } else { + LOGP(DL1IF, LOGL_DEBUG, " si3=(not set)\n"); + bts->si3_is_set = false; + } + + if (info_ind->si13_is_set) { + LOGP(DL1IF, LOGL_DEBUG, " si13=%s\n", osmo_hexdump_nospc(info_ind->si13, GSM_MACBLOCK_LEN)); + memcpy(bts->si13, info_ind->si13, GSM_MACBLOCK_LEN); + bts->si13_is_set = true; + } else { + LOGP(DL1IF, LOGL_DEBUG, " si13=(not set)\n"); + bts->si13_is_set = false; + } + if (info_ind->t3142) { /* if timer values are set */ osmo_tdef_set(bts->T_defs_bts, 3142, info_ind->t3142, OSMO_TDEF_S); osmo_tdef_set(bts->T_defs_bts, 3169, info_ind->t3169, OSMO_TDEF_S); -- cgit v1.2.3