/* ip.access nanoBTS specific code, OML attribute table generator */ /* (C) 2016-2023 by sysmocom s.f.m.c. GmbH * All Rights Reserved * * Author: Philipp Maier * * 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 const struct tlv_definition ipacc_eie_tlv_def = { .def = { /* TODO: add more values from enum ipac_eie */ [NM_IPAC_EIE_FREQ_BANDS] = { TLV_TYPE_TL16V }, [NM_IPAC_EIE_MAX_TA] = { TLV_TYPE_TL16V }, [NM_IPAC_EIE_CIPH_ALGOS] = { TLV_TYPE_TL16V }, [NM_IPAC_EIE_CHAN_TYPES] = { TLV_TYPE_TL16V }, [NM_IPAC_EIE_CHAN_MODES] = { TLV_TYPE_TL16V }, [NM_IPAC_EIE_GPRS_CODING] = { TLV_TYPE_TL16V }, [NM_IPAC_EIE_RTP_FEATURES] = { TLV_TYPE_TL16V }, [NM_IPAC_EIE_RSL_FEATURES] = { TLV_TYPE_TL16V }, } }; static void ipacc_parse_supp_flags(const struct abis_om_fom_hdr *foh, const struct tlv_parsed *tp, const enum ipac_eie tag, struct ipacc_supp_feat *feat, const struct value_string *flags, const char *text) { const struct tlv_p_entry *e; feat->present = false; feat->val = 0; if ((e = TLVP_GET(tp, tag)) == NULL) return; for (unsigned int i = 0; i < OSMO_MIN(e->len, sizeof(feat->val)); i++) feat->val |= e->val[i] << (i * 8); feat->present = true; if (flags == NULL) { LOGPFOH(DNM, LOGL_INFO, foh, "%s: %u\n", text, feat->val); return; } for (const struct value_string *vs = flags; vs->value && vs->str; vs++) { if (feat->val & vs->value) LOGPFOH(DNM, LOGL_INFO, foh, "%s '%s' is supported\n", text, vs->str); } } /* Parse ip.access Supported Features IE */ int ipacc_parse_supp_features(struct gsm_bts *bts, const struct abis_om_fom_hdr *foh, const uint8_t *data, uint16_t data_len) { struct gsm_abis_mo *mo; struct tlv_parsed tp; if (tlv_parse(&tp, &ipacc_eie_tlv_def, data, data_len, 0, 0) < 0) { LOGPFOH(DNM, LOGL_ERROR, foh, "%s(): tlv_parse failed\n", __func__); return -EINVAL; } /* store the flags to the respective MO state */ mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst); if (mo == NULL) { LOGPFOH(DNM, LOGL_ERROR, foh, "%s(): gsm_objclass2mo() failed\n", __func__); return -ENODEV; } switch (mo->obj_class) { case NM_OC_BTS: ipacc_parse_supp_flags(foh, &tp, NM_IPAC_EIE_MAX_TA, &mo->ipaccess.max_ta, NULL, "Max Timing Advance"); break; case NM_OC_RADIO_CARRIER: ipacc_parse_supp_flags(foh, &tp, NM_IPAC_EIE_FREQ_BANDS, &mo->ipaccess.freq_bands, abis_nm_ipacc_freq_band_desc, "Freq. band"); break; case NM_OC_BASEB_TRANSC: ipacc_parse_supp_flags(foh, &tp, NM_IPAC_EIE_CIPH_ALGOS, &mo->ipaccess.ciph_algos, abis_nm_ipacc_ciph_algo_desc, "Ciphering algorithm"); ipacc_parse_supp_flags(foh, &tp, NM_IPAC_EIE_CHAN_TYPES, &mo->ipaccess.chan_types, abis_nm_ipacc_chant_desc, "Channel type"); ipacc_parse_supp_flags(foh, &tp, NM_IPAC_EIE_CHAN_MODES, &mo->ipaccess.chan_modes, abis_nm_ipacc_chanm_desc, "Channel mode"); ipacc_parse_supp_flags(foh, &tp, NM_IPAC_EIE_RTP_FEATURES, &mo->ipaccess.rtp_features, abis_nm_ipacc_rtp_feat_desc, "RTP Feature"); ipacc_parse_supp_flags(foh, &tp, NM_IPAC_EIE_RSL_FEATURES, &mo->ipaccess.rsl_features, abis_nm_ipacc_rsl_feat_desc, "RSL Feature"); break; case NM_OC_GPRS_CELL: ipacc_parse_supp_flags(foh, &tp, NM_IPAC_EIE_GPRS_CODING, &mo->ipaccess.gprs_coding, abis_nm_ipacc_gprs_coding_desc, "GPRS Coding Scheme"); break; default: LOGPFOH(DNM, LOGL_NOTICE, foh, "Unhandled NM_ATT_IPACC_SUPP_FEATURES IE: %s\n", osmo_hexdump(data, data_len)); return -ENOTSUP; } return 0; } /* 3GPP TS 52.021 section 8.6.1 Set BTS Attributes */ struct msgb *nanobts_gen_set_bts_attr(struct gsm_bts *bts) { struct msgb *msgb; uint8_t buf[256]; int rlt; msgb = msgb_alloc(1024, __func__); if (!msgb) return NULL; /* Interference level Boundaries: 0 .. X5 (3GPP TS 52.021 sec 9.4.25) */ msgb_tv_fixed_put(msgb, NM_ATT_INTERF_BOUND, sizeof(bts->interf_meas_params_cfg.bounds_dbm), &bts->interf_meas_params_cfg.bounds_dbm[0]); /* Intave: Interference Averaging period (3GPP TS 52.021 sec 9.4.24) */ msgb_tv_put(msgb, NM_ATT_INTAVE_PARAM, bts->interf_meas_params_cfg.avg_period); /* Connection Failure Criterion (3GPP TS 52.021 sec 9.4.14) */ rlt = gsm_bts_get_radio_link_timeout(bts); if (rlt == -1) { /* Osmocom extension: Use infinite radio link timeout */ buf[0] = 0xFF; buf[1] = 0x00; } else { /* conn fail based on SACCH error rate */ buf[0] = 0x01; buf[1] = rlt; } msgb_tl16v_put(msgb, NM_ATT_CONN_FAIL_CRIT, 2, buf); /* T200 (3GPP TS 52.021 sec 9.4.53) */ memcpy(buf, "\x1e\x24\x24\xa8\x34\x21\xa8", 7); msgb_tv_fixed_put(msgb, NM_ATT_T200, 7, buf); /* Max Timing Advance (3GPP TS 52.021 sec 9.4.31) */ msgb_tv_put(msgb, NM_ATT_MAX_TA, 0x3f); /* Overload Period (3GPP TS 52.021 sec 9.4.39), seconds */ memcpy(buf, "\x00\x01\x0a", 3); msgb_tv_fixed_put(msgb, NM_ATT_OVERL_PERIOD, 3, buf); /* CCCH Load Threshold (3GPP TS 12.21 sec 9.4.12), percent */ msgb_tv_put(msgb, NM_ATT_CCCH_L_T, bts->ccch_load_ind_thresh); /* CCCH Load Indication Period (3GPP TS 12.21 sec 9.4.11), seconds */ msgb_tv_put(msgb, NM_ATT_CCCH_L_I_P, bts->ccch_load_ind_period); /* RACH Busy Threshold (3GPP TS 12.21 sec 9.4.44), -dBm */ buf[0] = 90; /* -90 dBm as default "busy" threshold */ if (bts->rach_b_thresh != -1) buf[0] = bts->rach_b_thresh & 0xff; msgb_tv_put(msgb, NM_ATT_RACH_B_THRESH, buf[0]); /* RACH Load Averaging Slots (3GPP TS 12.21 sec 9.4.45), 1000 slots */ buf[0] = 0x03; buf[1] = 0xe8; if (bts->rach_ldavg_slots != -1) { buf[0] = (bts->rach_ldavg_slots >> 8) & 0x0f; buf[1] = bts->rach_ldavg_slots & 0xff; } msgb_tv_fixed_put(msgb, NM_ATT_LDAVG_SLOTS, 2, buf); /* BTS Air Timer (3GPP TS 12.21 sec 9.4.10), 10 milliseconds */ msgb_tv_put(msgb, NM_ATT_BTS_AIR_TIMER, osmo_tdef_get(bts->network->T_defs, 3105, OSMO_TDEF_MS, -1)/10); /* NY1 (3GPP TS 12.21 sec 9.4.37), number of retransmissions of physical config */ gsm_bts_check_ny1(bts); msgb_tv_put(msgb, NM_ATT_NY1, osmo_tdef_get(bts->network->T_defs, -3105, OSMO_TDEF_CUSTOM, -1)); /* BCCH ARFCN (3GPP TS 12.21 sec 9.4.8) */ buf[0] = (bts->c0->arfcn >> 8) & 0x0f; buf[1] = bts->c0->arfcn & 0xff; msgb_tv_fixed_put(msgb, NM_ATT_BCCH_ARFCN, 2, buf); /* BSIC (3GPP TS 12.21 sec 9.4.9) */ msgb_tv_put(msgb, NM_ATT_BSIC, bts->bsic); abis_nm_ipaccess_cgi(buf, bts); msgb_tl16v_put(msgb, NM_ATT_IPACC_CGI, 7, buf); return msgb; } struct msgb *nanobts_gen_set_nse_attr(struct gsm_bts_sm *bts_sm) { struct msgb *msgb; uint8_t buf[2]; struct abis_nm_ipacc_att_ns_cfg ns_cfg; struct abis_nm_ipacc_att_bssgp_cfg bssgp_cfg; struct gsm_bts *bts = gsm_bts_sm_get_bts(bts_sm); msgb = msgb_alloc(1024, __func__); if (!msgb) return NULL; /* NSEI 925 */ buf[0] = bts_sm->gprs.nse.nsei >> 8; buf[1] = bts_sm->gprs.nse.nsei & 0xff; msgb_tl16v_put(msgb, NM_ATT_IPACC_NSEI, 2, buf); osmo_static_assert(ARRAY_SIZE(bts_sm->gprs.nse.timer) == 7, nse_timer_array_wrong_size); ns_cfg = (struct abis_nm_ipacc_att_ns_cfg){ .un_blocking_timer = bts_sm->gprs.nse.timer[0], .un_blocking_retries = bts_sm->gprs.nse.timer[1], .reset_timer = bts_sm->gprs.nse.timer[2], .reset_retries = bts_sm->gprs.nse.timer[3], .test_timer = bts_sm->gprs.nse.timer[4], .alive_timer = bts_sm->gprs.nse.timer[5], .alive_retries = bts_sm->gprs.nse.timer[6], }; msgb_tl16v_put(msgb, NM_ATT_IPACC_NS_CFG, sizeof(ns_cfg), (const uint8_t *)&ns_cfg); osmo_static_assert(ARRAY_SIZE(bts->gprs.cell.timer) == 11, cell_timer_array_wrong_size); bssgp_cfg = (struct abis_nm_ipacc_att_bssgp_cfg){ .t1_s = bts->gprs.cell.timer[0], .t1_blocking_retries = bts->gprs.cell.timer[1], .t1_unblocking_retries = bts->gprs.cell.timer[2], .t2_s = bts->gprs.cell.timer[3], .t2_retries = bts->gprs.cell.timer[4], .t3_100ms = bts->gprs.cell.timer[5], .t3_retries = bts->gprs.cell.timer[6], .t4_100ms = bts->gprs.cell.timer[7], .t4_retries = bts->gprs.cell.timer[8], .t5_s = bts->gprs.cell.timer[9], .t5_retries = bts->gprs.cell.timer[10], }; msgb_tl16v_put(msgb, NM_ATT_IPACC_BSSGP_CFG, sizeof(bssgp_cfg), (const uint8_t *)&bssgp_cfg); return msgb; } struct msgb *nanobts_gen_set_cell_attr(struct gsm_bts *bts) { const struct gsm_gprs_cell *cell = &bts->gprs.cell; const struct gprs_rlc_cfg *rlcc = &cell->rlc_cfg; struct msgb *msgb; uint8_t buf[2]; msgb = msgb_alloc(1024, __func__); if (!msgb) return NULL; /* routing area code */ buf[0] = bts->gprs.rac; msgb_tl16v_put(msgb, NM_ATT_IPACC_RAC, 1, buf); buf[0] = rlcc->paging.repeat_time / 50; /* units of 50ms */ buf[1] = rlcc->paging.repeat_count; msgb_tl16v_put(msgb, NM_ATT_IPACC_GPRS_PAGING_CFG, 2, buf); /* BVCI 925 */ buf[0] = cell->bvci >> 8; buf[1] = cell->bvci & 0xff; msgb_tl16v_put(msgb, NM_ATT_IPACC_BVCI, 2, buf); /* all timers in seconds, unless otherwise stated */ const struct abis_nm_ipacc_att_rlc_cfg rlc_cfg = { .t3142 = rlcc->parameter[RLC_T3142], .t3169 = rlcc->parameter[RLC_T3169], .t3191 = rlcc->parameter[RLC_T3191], .t3193_10ms = rlcc->parameter[RLC_T3193], .t3195 = rlcc->parameter[RLC_T3195], .n3101 = rlcc->parameter[RLC_N3101], .n3103 = rlcc->parameter[RLC_N3103], .n3105 = rlcc->parameter[RLC_N3105], .rlc_cv_countdown = rlcc->parameter[CV_COUNTDOWN], }; msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG, sizeof(rlc_cfg), (const uint8_t *)&rlc_cfg); switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: if (cell->mo.ipaccess.obj_version < 4) break; /* fall-through */ case GSM_BTS_TYPE_OSMOBTS: /* CS1..CS4 flags encoded in the first octet */ buf[0] = rlcc->cs_mask & 0x0f; /* MCS1..MSC8 flags encoded in the second octet */ buf[1] = 0x00; if (bts->gprs.mode == BTS_GPRS_EGPRS) { /* MSC9 is special and also goes to the first octet */ if (rlcc->cs_mask & (1 << GPRS_MCS9)) buf[0] |= (1 << 7); buf[1] = (rlcc->cs_mask >> 4) & 0xff; } msgb_tl16v_put(msgb, NM_ATT_IPACC_CODING_SCHEMES, 2, buf); break; default: break; } switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: if (cell->mo.ipaccess.obj_version < 20) break; /* fall-through */ case GSM_BTS_TYPE_OSMOBTS: { const struct abis_nm_ipacc_att_rlc_cfg_2 rlc_cfg_2 = { .t_dl_tbf_ext_10ms = htons(rlcc->parameter[T_DL_TBF_EXT] / 10), .t_ul_tbf_ext_10ms = htons(rlcc->parameter[T_UL_TBF_EXT] / 10), .initial_cs = rlcc->initial_cs, }; msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG_2, sizeof(rlc_cfg_2), (const uint8_t *)&rlc_cfg_2); break; } default: break; } switch (bts->type) { case GSM_BTS_TYPE_NANOBTS: if (cell->mo.ipaccess.obj_version < 30) break; /* fall-through */ case GSM_BTS_TYPE_OSMOBTS: { const struct abis_nm_ipacc_att_rlc_cfg_3 rlc_cfg_3 = { .initial_mcs = rlcc->initial_mcs, }; msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG_3, sizeof(rlc_cfg_3), (const uint8_t *)&rlc_cfg_3); break; } default: break; } return msgb; } struct msgb *nanobts_gen_set_nsvc_attr(struct gsm_gprs_nsvc *nsvc) { struct msgb *msgb; uint8_t buf[256]; msgb = msgb_alloc(1024, __func__); if (!msgb) return NULL; /* 925 */ buf[0] = nsvc->nsvci >> 8; buf[1] = nsvc->nsvci & 0xff; msgb_tl16v_put(msgb, NM_ATT_IPACC_NSVCI, 2, buf); switch (nsvc->remote.u.sa.sa_family) { case AF_INET6: /* all fields are encoded in network byte order */ /* protocol family */ buf[0] = OSMO_NSVC_ADDR_IPV6; /* padding */ buf[1] = 0x00; /* local udp port */ osmo_store16be(nsvc->local_port, &buf[2]); /* remote udp port */ memcpy(&buf[4], &nsvc->remote.u.sin6.sin6_port, sizeof(uint16_t)); /* remote ip address */ memcpy(&buf[6], &nsvc->remote.u.sin6.sin6_addr, sizeof(struct in6_addr)); msgb_tl16v_put(msgb, NM_ATT_OSMO_NS_LINK_CFG, 6 + sizeof(struct in6_addr), buf); break; case AF_INET: /* remote udp port */ memcpy(&buf[0], &nsvc->remote.u.sin.sin_port, sizeof(uint16_t)); /* remote ip address */ memcpy(&buf[2], &nsvc->remote.u.sin.sin_addr, sizeof(struct in_addr)); /* local udp port */ osmo_store16be(nsvc->local_port, &buf[6]); msgb_tl16v_put(msgb, NM_ATT_IPACC_NS_LINK_CFG, 8, buf); break; default: break; } return msgb; } struct msgb *nanobts_gen_set_radio_attr(struct gsm_bts *bts, struct gsm_bts_trx *trx) { struct msgb *msgb; uint8_t buf[256]; msgb = msgb_alloc(1024, __func__); if (!msgb) return NULL; /* number of -2dB reduction steps / Pn */ msgb_tv_put(msgb, NM_ATT_RF_MAXPOWR_R, trx->max_power_red / 2); buf[0] = trx->arfcn >> 8; buf[1] = trx->arfcn & 0xff; msgb_tl16v_put(msgb, NM_ATT_ARFCN_LIST, 2, buf); return msgb; }