/* Osmocom CBC (Cell Broacast Centre) */ /* (C) 2022 by sysmocom - s.f.m.c. GmbH * All Rights Reserved * Author: Pau Espin Pedrol * * SPDX-License-Identifier: AGPL-3.0+ * * 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 . * */ #include #include #include #include #include #include #include #include #include /* 3GPP TS 36.413 9.2.1.53, 3GPP TS 23.041 9.3.35 */ #define SBCAP_WARN_MSG_CONTENTS_IE_MAX_LEN 9600 #if 0 /* Warning Area List * 3GPP TS 36.413 9.2.1.46, 3GPP TS 23.041 9.3.30 */ static void msgb_put_sbcap_cell_list(const struct cbc_message *cbcmsg, void *void_li) { static uint8_t ie_plm_id0[] = {0x05, 0xf5, 0x32}; static uint8_t ie_cell_id0[] = {0xa0, 0x00, 0x00, 0x10}; static uint8_t ie_cell_id1[] = {0xa0, 0x00, 0x00, 0x20}; SBcAP_EUTRAN_CGI_t *ecgi; A_SEQUENCE_OF(void) *li = void_li; ecgi = CALLOC(1, sizeof(*ecgi)); *ecgi = (SBcAP_EUTRAN_CGI_t) { .pLMNidentity = { .buf = ie_plm_id0, .size = sizeof(ie_plm_id0), }, .cell_ID = { /* SBcAP_CellIdentity_t*/ .buf = ie_cell_id0, .size = sizeof(ie_cell_id0), .bits_unused = 4, } }; ASN_SEQUENCE_ADD(li, ecgi); ecgi = CALLOC(1, sizeof(*ecgi)); *ecgi = (SBcAP_EUTRAN_CGI_t) { .pLMNidentity = { .buf = ie_plm_id0, .size = sizeof(ie_plm_id0), }, .cell_ID = { /* SBcAP_CellIdentity_t*/ .buf = ie_cell_id1, .size = sizeof(ie_cell_id1), .bits_unused = 4, } }; ASN_SEQUENCE_ADD(li, ecgi); } #endif /* generate a SBc-AP WRITE-REPLACE WARNING REQUEST from our internal representation. * 3GPP TS 36.413 9.1.13.1 */ SBcAP_SBC_AP_PDU_t *sbcap_gen_write_replace_warning_req(void *ctx, const struct cbc_message *cbcmsg) { const struct smscb_message *smscb = &cbcmsg->msg; SBcAP_SBC_AP_PDU_t *pdu; SBcAP_Write_Replace_Warning_Request_IEs_t *ie; unsigned int i; uint8_t *ptr; #if 0 A_SEQUENCE_OF(void) *as_warn_area_ecgi = NULL; #endif pdu = sbcap_pdu_alloc(); if (!pdu) return NULL; pdu->present = SBcAP_SBC_AP_PDU_PR_initiatingMessage; pdu->choice.initiatingMessage.procedureCode = SBcAP_ProcedureId_Write_Replace_Warning; pdu->choice.initiatingMessage.criticality = SBcAP_Criticality_reject; pdu->choice.initiatingMessage.value.present = SBcAP_InitiatingMessage__value_PR_Write_Replace_Warning_Request; A_SEQUENCE_OF(void) *as_pdu = (void *)&pdu->choice.initiatingMessage.value.choice.Write_Replace_Warning_Request.protocolIEs.list; /* Message Identifier: * 3GPP TS 36.413 9.2.1.44, 3GPP TS 23.041 9.4.1.3.6 * static const long asn_VAL_1_SBcAP_id_Message_Identifier = 5; */ ie = sbcap_alloc_Write_Replace_Warning_Request_IE(5, SBcAP_Criticality_reject, SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Message_Identifier); ie->value.choice.Message_Identifier.buf = MALLOC(sizeof(uint16_t)); ie->value.choice.Message_Identifier.size = sizeof(uint16_t); ie->value.choice.Message_Identifier.bits_unused = 0; osmo_store16be(smscb->message_id, ie->value.choice.Message_Identifier.buf); ASN_SEQUENCE_ADD(as_pdu, ie); /* Serial Number * 3GPP TS 36.413 9.2.1.45, 3GPP TS 23.041 9.4.1.2.1 * static const long asn_VAL_2_SBcAP_id_Serial_Number = 11; */ ie = sbcap_alloc_Write_Replace_Warning_Request_IE(11, SBcAP_Criticality_reject, SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Serial_Number); ie->value.choice.Serial_Number.buf = MALLOC(sizeof(uint16_t)); ie->value.choice.Serial_Number.size = sizeof(uint16_t); ie->value.choice.Serial_Number.bits_unused = 0; osmo_store16be(smscb->serial_nr, ie->value.choice.Serial_Number.buf); ASN_SEQUENCE_ADD(as_pdu, ie); switch (cbcmsg->scope) { case CBC_MSG_SCOPE_PLMN: break; /* Nothing to be done :*/ #if 0 case CBC_MSG_SCOPE_EUTRAN_CGI: /* Warning Area List * 3GPP TS 36.413 9.2.1.46, 3GPP TS 23.041 9.3.30 * static const long asn_VAL_25_SBcAP_id_Warning_Area_List = 15; */ ie = sbcap_alloc_Write_Replace_Warning_Request_IE(15, SBcAP_Criticality_ignore, SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Warning_Area_List); ASN_SEQUENCE_ADD(as_pdu, ie); as_warn_area_ecgi = (void *)ie->value.choice.Warning_Area_List.choice.cell_ID_List.list; msgb_put_sbcap_cell_list(cbcmsg, as_warn_area_ecgi); break; #endif default: OSMO_ASSERT(0); } /* Repetition Period * 3GPP TS 36.413 9.2.1.48, 3GPP TS 23.041 9.3.8 * static const long asn_VAL_5_SBcAP_id_Repetition_Period = 10; */ ie = sbcap_alloc_Write_Replace_Warning_Request_IE(10, SBcAP_Criticality_reject, SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Repetition_Period); ie->value.choice.Repetition_Period = cbcmsg->rep_period; /*seconds */ ASN_SEQUENCE_ADD(as_pdu, ie); /* Number of Broadcasts Requested * 3GPP TS 36.413 9.2.1.49, 3GPP TS 23.041 9.3.9 * static const long asn_VAL_7_SBcAP_id_Number_of_Broadcasts_Requested = 7; */ ie = sbcap_alloc_Write_Replace_Warning_Request_IE(7, SBcAP_Criticality_reject, SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Number_of_Broadcasts_Requested); ie->value.choice.Number_of_Broadcasts_Requested = cbcmsg->num_bcast; ASN_SEQUENCE_ADD(as_pdu, ie); if (smscb->is_etws) { /* Warning Type * 3GPP TS 36.413 sec 9.2.1.50, 3GPP TS 23.041 9.3.24 * static const long asn_VAL_8_SBcAP_id_Warning_Type = 18; */ ie = sbcap_alloc_Write_Replace_Warning_Request_IE(18, SBcAP_Criticality_ignore, SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Warning_Type); ie->value.choice.Warning_Type.buf = MALLOC(2); ie->value.choice.Warning_Type.size = 2; ie->value.choice.Warning_Type.buf[0] = ((smscb->etws.warning_type & 0x7f) << 1); if (smscb->etws.user_alert) ie->value.choice.Warning_Type.buf[0] |= 0x01; ie->value.choice.Warning_Type.buf[1] = (smscb->etws.popup_on_display) ? 0x80 : 0x0; ASN_SEQUENCE_ADD(as_pdu, ie); /* Warning Security Information * 3GPP TS 36.413 sec 9.2.1.51, 3GPP TS 23.041 9.3.25 * static const long asn_VAL_9_SBcAP_id_Warning_Security_Information = 17 */ ie = sbcap_alloc_Write_Replace_Warning_Request_IE(17, SBcAP_Criticality_ignore, SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Warning_Security_Information); ie->value.choice.Warning_Security_Information.buf = MALLOC(sizeof(smscb->etws.warning_sec_info)); ie->value.choice.Warning_Security_Information.size = sizeof(smscb->etws.warning_sec_info); memcpy(ie->value.choice.Warning_Security_Information.buf, smscb->etws.warning_sec_info, sizeof(smscb->etws.warning_sec_info)); ASN_SEQUENCE_ADD(as_pdu, ie); } else { /* Data Coding Scheme * 3GPP TS 36.413 9.2.1.52, 3GPP TS 23.041 9.4.1.2.3 * static const long asn_VAL_10_SBcAP_id_Data_Coding_Scheme = 3; */ ie = sbcap_alloc_Write_Replace_Warning_Request_IE(3, SBcAP_Criticality_ignore, SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Data_Coding_Scheme); ie->value.choice.Data_Coding_Scheme.buf = MALLOC(1); ie->value.choice.Data_Coding_Scheme.size = 1; ie->value.choice.Data_Coding_Scheme.bits_unused = 0; *ie->value.choice.Data_Coding_Scheme.buf = smscb->cbs.dcs; ASN_SEQUENCE_ADD(as_pdu, ie); /* Warning Message Contents * 3GPP TS 36.413 9.2.1.53, 3GPP TS 23.041 9.3.35 * static const long asn_VAL_11_SBcAP_id_Warning_Message_Content = 16; */ ie = sbcap_alloc_Write_Replace_Warning_Request_IE(16, SBcAP_Criticality_ignore, SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Warning_Message_Content); ie->value.choice.Warning_Message_Content.buf = MALLOC(1 + smscb->cbs.num_pages * (SMSCB_RAW_PAGE_LEN+1)); ie->value.choice.Warning_Message_Content.size = 1 + smscb->cbs.num_pages * (SMSCB_RAW_PAGE_LEN+1); ptr = &ie->value.choice.Warning_Message_Content.buf[0]; *ptr = (uint8_t)smscb->cbs.num_pages; ptr++; for (i = 0; i < smscb->cbs.num_pages; i++) { unsigned len = 0; if (i == smscb->cbs.num_pages - 1) len = smscb->cbs.data_user_len - (i * SMSCB_RAW_PAGE_LEN); else len = SMSCB_RAW_PAGE_LEN; if (len > 0) { memcpy(ptr, smscb->cbs.data[i], SMSCB_RAW_PAGE_LEN); ptr += SMSCB_RAW_PAGE_LEN; } *ptr = (uint8_t)len; ptr++; } ASN_SEQUENCE_ADD(as_pdu, ie); /* Concurrent Warning Message Indicator * 3GPP TS 36.413 8.12.1.2 and 9.2.1.72, 3GPP TS 23.041 9.3.32 * static const long asn_VAL_13_SBcAP_id_Concurrent_Warning_Message_Indicator = 20; */ ie = sbcap_alloc_Write_Replace_Warning_Request_IE(20, SBcAP_Criticality_reject, SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Concurrent_Warning_Message_Indicator); ie->value.choice.Concurrent_Warning_Message_Indicator = SBcAP_Concurrent_Warning_Message_Indicator_true; ASN_SEQUENCE_ADD(as_pdu, ie); } /* Send Write-Replace-Warning-Indication * 3GPP TS 36.413 4.3.4.3.5, 3GPP TS 23.041 9.3.39 * static const long asn_VAL_14_SBcAP_id_Send_Write_Replace_Warning_Indication = 24; */ ie = sbcap_alloc_Write_Replace_Warning_Request_IE(24, SBcAP_Criticality_ignore, SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Send_Write_Replace_Warning_Indication); ie->value.choice.Send_Write_Replace_Warning_Indication = SBcAP_Send_Write_Replace_Warning_Indication_true; ASN_SEQUENCE_ADD(as_pdu, ie); return pdu; } /* generate a SBc-AP WRITE-REPLACE WARNING REQUEST from our internal representation */ SBcAP_SBC_AP_PDU_t *sbcap_gen_stop_warning_req(void *ctx, const struct cbc_message *cbcmsg) { const struct smscb_message *smscb = &cbcmsg->msg; SBcAP_SBC_AP_PDU_t *pdu; SBcAP_Stop_Warning_Request_IEs_t *ie; #if 0 A_SEQUENCE_OF(void) *as_warn_area_ecgi = NULL; #endif pdu = sbcap_pdu_alloc(); if (!pdu) return NULL; pdu->present = SBcAP_SBC_AP_PDU_PR_initiatingMessage; pdu->choice.initiatingMessage.procedureCode = SBcAP_ProcedureId_Stop_Warning; pdu->choice.initiatingMessage.criticality = SBcAP_Criticality_reject; pdu->choice.initiatingMessage.value.present = SBcAP_InitiatingMessage__value_PR_Stop_Warning_Request; A_SEQUENCE_OF(void) *as_pdu = (void *)&pdu->choice.initiatingMessage.value.choice.Stop_Warning_Request.protocolIEs.list; /* Message Identifier: * 3GPP TS 36.413 9.2.1.44, 3GPP TS 23.041 9.4.1.3.6 * static const long asn_VAL_1_SBcAP_id_Message_Identifier = 5; */ ie = sbcap_alloc_Stop_Warning_Request_IE(5, SBcAP_Criticality_reject, SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Message_Identifier); ie->value.choice.Message_Identifier.buf = MALLOC(sizeof(uint16_t)); ie->value.choice.Message_Identifier.size = sizeof(uint16_t); ie->value.choice.Message_Identifier.bits_unused = 0; osmo_store16be(smscb->message_id, ie->value.choice.Message_Identifier.buf); ASN_SEQUENCE_ADD(as_pdu, ie); /* Serial Number * 3GPP TS 36.413 9.2.1.45, 3GPP TS 23.041 9.4.1.2.1 * static const long asn_VAL_2_SBcAP_id_Serial_Number = 11; */ ie = sbcap_alloc_Stop_Warning_Request_IE(11, SBcAP_Criticality_reject, SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Serial_Number); ie->value.choice.Serial_Number.buf = MALLOC(sizeof(uint16_t)); ie->value.choice.Serial_Number.size = sizeof(uint16_t); ie->value.choice.Serial_Number.bits_unused = 0; osmo_store16be(smscb->serial_nr, ie->value.choice.Serial_Number.buf); ASN_SEQUENCE_ADD(as_pdu, ie); switch (cbcmsg->scope) { case CBC_MSG_SCOPE_PLMN: break; /* Nothing to be done :*/ #if 0 case CBC_MSG_SCOPE_EUTRAN_CGI: /* Warning Area List * 3GPP TS 36.413 9.2.1.46, 3GPP TS 23.041 9.3.30 * static const long asn_VAL_25_SBcAP_id_Warning_Area_List = 15; */ ie = sbcap_alloc_Stop_Warning_Request_IE(15, SBcAP_Criticality_ignore, SBcAP_Write_Replace_Warning_Request_IEs__value_PR_Warning_Area_List); ASN_SEQUENCE_ADD(as_pdu, ie); as_warn_area_ecgi = (void *)ie->value.choice.Warning_Area_List.choice.cell_ID_List.list; msgb_put_sbcap_cell_list(cbcmsg, as_warn_area_ecgi); break; #endif default: OSMO_ASSERT(0); } return pdu; } /* generate a SBc-AP ERROR INDICATION, 3GPP TS 29.168 4.3.4.2A.1. * rx_pdu can be NULL. */ SBcAP_SBC_AP_PDU_t *sbcap_gen_error_ind(void *ctx, SBcAP_Cause_t cause, SBcAP_SBC_AP_PDU_t *rx_pdu) { SBcAP_SBC_AP_PDU_t *pdu; SBcAP_ErrorIndicationIEs_t *ie; pdu = sbcap_pdu_alloc(); if (!pdu) return NULL; pdu->present = SBcAP_SBC_AP_PDU_PR_initiatingMessage; pdu->choice.initiatingMessage.procedureCode = SBcAP_ProcedureId_Error_Indication; pdu->choice.initiatingMessage.criticality = SBcAP_Criticality_ignore; pdu->choice.initiatingMessage.value.present = SBcAP_InitiatingMessage__value_PR_Error_Indication; A_SEQUENCE_OF(void) *as_pdu = (void *)&pdu->choice.initiatingMessage.value.choice.Error_Indication.protocolIEs.list; /* Cause, Optional: * 3GPP TS 36.413 4.3.4.3.2 * static const long asn_VAL_19_SBcAP_id_Cause = 1; */ ie = sbcap_alloc_Error_Indication_IE(1, SBcAP_Criticality_ignore, SBcAP_ErrorIndicationIEs__value_PR_Cause); ie->value.choice.Cause = cause; ASN_SEQUENCE_ADD(as_pdu, ie); if (rx_pdu) { SBcAP_Criticality_Diagnostics_t *diag_ie; /* Criticality Diagnostics, Optional: * 3GPP TS 36.413 4.3.4.3.3 * static const long asn_VAL_20_SBcAP_id_Criticality_Diagnostics = 2; */ ie = sbcap_alloc_Error_Indication_IE(1, SBcAP_Criticality_ignore, SBcAP_ErrorIndicationIEs__value_PR_Criticality_Diagnostics); diag_ie = &ie->value.choice.Criticality_Diagnostics; diag_ie->procedureCode = MALLOC(sizeof(*diag_ie->procedureCode)); *diag_ie->procedureCode = sbcap_pdu_get_procedure_code(rx_pdu); diag_ie->triggeringMessage = MALLOC(sizeof(*diag_ie->triggeringMessage)); *diag_ie->triggeringMessage = rx_pdu->present; diag_ie->procedureCriticality = MALLOC(sizeof(*diag_ie->procedureCriticality)); *diag_ie->procedureCriticality = sbcap_pdu_get_criticality(rx_pdu); ASN_SEQUENCE_ADD(as_pdu, ie); } return pdu; } static void cci_from_sbcap_ecgi(struct cbc_cell_id *cci, const SBcAP_EUTRAN_CGI_t *eCGI) { cci->id_discr = CBC_CELL_ID_ECGI; cci->u.ecgi.eci = (osmo_load32be(&eCGI->cell_ID.buf[0]) >> 4); osmo_plmn_from_bcd(eCGI->pLMNidentity.buf, &cci->u.ecgi.plmn); } /* Fill a cbc_cell_id from a SBcAP_CellId_Broadcast_List_Item */ void cci_from_sbcap_bcast_cell_id(struct cbc_cell_id *cci, const SBcAP_CellId_Broadcast_List_Item_t *it) { cci_from_sbcap_ecgi(cci, &it->eCGI); } /* Fill a cbc_cell_id from a SBcAP_TAI_t */ void cci_from_sbcap_tai(struct cbc_cell_id *cci, const SBcAP_TAI_t *tai) { cci->id_discr = CBC_CELL_ID_TAI; cci->u.tai.tac = osmo_load16be(tai->tAC.buf); osmo_plmn_from_bcd(tai->pLMNidentity.buf, &cci->u.tai.plmn); }