diff options
Diffstat (limited to 'src/gsm/cbsp.c')
-rw-r--r-- | src/gsm/cbsp.c | 310 |
1 files changed, 222 insertions, 88 deletions
diff --git a/src/gsm/cbsp.c b/src/gsm/cbsp.c index ccc2df53..a5e58f4c 100644 --- a/src/gsm/cbsp.c +++ b/src/gsm/cbsp.c @@ -1,3 +1,4 @@ +/* Cell Broadcast Service Protocol (CBSP, 3GPP TS 48.049): Message encoding, decoding and reception */ /* * Copyright (C) 2019 Harald Welte <laforge@gnumonks.org> * @@ -14,10 +15,6 @@ * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" @@ -33,7 +30,7 @@ #include <osmocom/gsm/cbsp.h> #include <osmocom/gsm/gsm0808_utils.h> -const __thread char *osmo_cbsp_errstr; +__thread const char *osmo_cbsp_errstr; struct msgb *osmo_cbsp_msgb_alloc(void *ctx, const char *name) { @@ -129,13 +126,13 @@ static int encode_wperiod(uint32_t secs) if (secs <= 10) return secs; if (secs <= 30) - return (secs-10)/2; + return 10 + (secs-10)/2; if (secs <= 120) - return (secs-30)/5; + return 30 + (secs-30)/5; if (secs <= 600) - return (secs-120)/10; + return 120 + (secs-120)/10; if (secs <= 60*60) - return (secs-600)/30; + return 600 + (secs-600)/30; osmo_cbsp_errstr = "warning period out of range"; return -1; } @@ -176,12 +173,15 @@ static int cbsp_enc_write_repl(struct msgb *msg, const struct osmo_cbsp_write_re } } else { int wperiod = encode_wperiod(in->u.emergency.warning_period); + uint8_t *cur; if (wperiod < 0) return -EINVAL; msgb_tv_put(msg, CBSP_IEI_EMERG_IND, in->u.emergency.indicator); msgb_tv16_put(msg, CBSP_IEI_WARN_TYPE, in->u.emergency.warning_type); - msgb_tlv_put(msg, CBSP_IEI_WARN_SEC_INFO, sizeof(in->u.emergency.warning_sec_info), - in->u.emergency.warning_sec_info); + /* Tag + fixed length value! */ + msgb_put_u8(msg, CBSP_IEI_WARN_SEC_INFO); + cur = msgb_put(msg, sizeof(in->u.emergency.warning_sec_info)); + memcpy(cur, in->u.emergency.warning_sec_info, sizeof(in->u.emergency.warning_sec_info)); msgb_tv_put(msg, CBSP_IEI_WARNING_PERIOD, wperiod); } return 0; @@ -348,7 +348,12 @@ static int cbsp_enc_reset_fail(struct msgb *msg, const struct osmo_cbsp_reset_fa /* 8.1.3.18a KEEP ALIVE */ static int cbsp_enc_keep_alive(struct msgb *msg, const struct osmo_cbsp_keep_alive *in) { - msgb_tv_put(msg, CBSP_IEI_KEEP_ALIVE_REP_PERIOD, in->repetition_period); + int rperiod = encode_wperiod(in->repetition_period); + if (in->repetition_period > 120) + return -EINVAL; + if (rperiod < 0) + return -EINVAL; + msgb_tv_put(msg, CBSP_IEI_KEEP_ALIVE_REP_PERIOD, rperiod); return 0; } @@ -532,8 +537,8 @@ static int cbsp_decode_fail_list(struct llist_head *fl, void *ctx, struct osmo_cbsp_fail_ent *ent = talloc_zero(ctx, struct osmo_cbsp_fail_ent); unsigned int len_remain = len - (cur - buf); OSMO_ASSERT(ent); - ent->id_discr = cur[0]; - rc = gsm0808_decode_cell_id_u(&ent->cell_id, ent->id_discr, cur+1, len_remain-1); + ent->id_discr = *cur++; + rc = gsm0808_decode_cell_id_u(&ent->cell_id, ent->id_discr, cur, len_remain-1); if (rc < 0) { osmo_cbsp_errstr = "fail list: error decoding cell_id_union"; return rc; @@ -634,6 +639,7 @@ static int cbsp_dec_write_repl(struct osmo_cbsp_write_replace *out, const struct struct msgb *in, void *ctx) { unsigned int i; + int rc; /* check for mandatory IEs */ if (!TLVP_PRESENT(tp, CBSP_IEI_MSG_ID) || @@ -651,8 +657,10 @@ static int cbsp_dec_write_repl(struct osmo_cbsp_write_replace *out, const struct } INIT_LLIST_HEAD(&out->cell_list.list); - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; if (TLVP_PRESENT(tp, CBSP_IEI_CHANNEL_IND)) { uint8_t num_of_pages; @@ -674,6 +682,7 @@ static int cbsp_dec_write_repl(struct osmo_cbsp_write_replace *out, const struct out->u.cbs.category = *TLVP_VAL(tp, CBSP_IEI_CATEGORY); out->u.cbs.rep_period = tlvp_val16be(tp, CBSP_IEI_REP_PERIOD); out->u.cbs.num_bcast_req = tlvp_val16be(tp, CBSP_IEI_NUM_BCAST_REQ); + out->u.cbs.dcs = *TLVP_VAL(tp, CBSP_IEI_DCS); num_of_pages = *TLVP_VAL(tp, CBSP_IEI_NUM_OF_PAGES); if (num_of_pages < 1) return -EINVAL; @@ -712,6 +721,8 @@ static int cbsp_dec_write_repl(struct osmo_cbsp_write_replace *out, const struct static int cbsp_dec_write_repl_compl(struct osmo_cbsp_write_replace_complete *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2)) { osmo_cbsp_errstr = "missing/short mandatory IE"; @@ -727,14 +738,18 @@ static int cbsp_dec_write_repl_compl(struct osmo_cbsp_write_replace_complete *ou INIT_LLIST_HEAD(&out->num_compl_list.list); if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) { - cbsp_decode_num_compl_list(&out->num_compl_list, ctx, - TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), - TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx, + TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), + TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + if (rc < 0) + return rc; } INIT_LLIST_HEAD(&out->cell_list.list); - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) { out->channel_ind = talloc(ctx, enum cbsp_channel_ind); @@ -747,6 +762,8 @@ static int cbsp_dec_write_repl_compl(struct osmo_cbsp_write_replace_complete *ou static int cbsp_dec_write_repl_fail(struct osmo_cbsp_write_replace_failure *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) { @@ -762,21 +779,27 @@ static int cbsp_dec_write_repl_fail(struct osmo_cbsp_write_replace_failure *out, } INIT_LLIST_HEAD(&out->fail_list); - cbsp_decode_fail_list(&out->fail_list, ctx, - TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), - TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + rc = cbsp_decode_fail_list(&out->fail_list, ctx, + TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), + TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + if (rc < 0) + return rc; INIT_LLIST_HEAD(&out->num_compl_list.list); if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) { - cbsp_decode_num_compl_list(&out->num_compl_list, ctx, - TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), - TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx, + TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), + TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + if (rc < 0) + return rc; } INIT_LLIST_HEAD(&out->cell_list.list); if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) { - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; } if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) { @@ -790,6 +813,8 @@ static int cbsp_dec_write_repl_fail(struct osmo_cbsp_write_replace_failure *out, static int cbsp_dec_kill(struct osmo_cbsp_kill *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) { @@ -801,8 +826,10 @@ static int cbsp_dec_kill(struct osmo_cbsp_kill *out, const struct tlv_parsed *tp out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR); INIT_LLIST_HEAD(&out->cell_list.list); - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) { out->channel_ind = talloc(ctx, enum cbsp_channel_ind); @@ -815,6 +842,8 @@ static int cbsp_dec_kill(struct osmo_cbsp_kill *out, const struct tlv_parsed *tp static int cbsp_dec_kill_compl(struct osmo_cbsp_kill_complete *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) { @@ -827,14 +856,18 @@ static int cbsp_dec_kill_compl(struct osmo_cbsp_kill_complete *out, const struct INIT_LLIST_HEAD(&out->num_compl_list.list); if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) { - cbsp_decode_num_compl_list(&out->num_compl_list, ctx, - TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), - TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx, + TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), + TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + if (rc < 0) + return rc; } INIT_LLIST_HEAD(&out->cell_list.list); - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) { out->channel_ind = talloc(ctx, enum cbsp_channel_ind); @@ -847,6 +880,8 @@ static int cbsp_dec_kill_compl(struct osmo_cbsp_kill_complete *out, const struct static int cbsp_dec_kill_fail(struct osmo_cbsp_kill_failure *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) { @@ -858,21 +893,27 @@ static int cbsp_dec_kill_fail(struct osmo_cbsp_kill_failure *out, const struct t out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR); INIT_LLIST_HEAD(&out->fail_list); - cbsp_decode_fail_list(&out->fail_list, ctx, - TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), - TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + rc = cbsp_decode_fail_list(&out->fail_list, ctx, + TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), + TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + if (rc < 0) + return rc; INIT_LLIST_HEAD(&out->num_compl_list.list); if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) { - cbsp_decode_num_compl_list(&out->num_compl_list, ctx, - TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), - TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx, + TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), + TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + if (rc < 0) + return rc; } INIT_LLIST_HEAD(&out->cell_list.list); if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) { - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; } if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) { @@ -886,6 +927,8 @@ static int cbsp_dec_kill_fail(struct osmo_cbsp_kill_failure *out, const struct t static int cbsp_dec_load_query(struct osmo_cbsp_load_query *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) || !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) { osmo_cbsp_errstr = "missing/short mandatory IE"; @@ -893,8 +936,10 @@ static int cbsp_dec_load_query(struct osmo_cbsp_load_query *out, const struct tl } INIT_LLIST_HEAD(&out->cell_list.list); - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND); return 0; @@ -904,6 +949,8 @@ static int cbsp_dec_load_query(struct osmo_cbsp_load_query *out, const struct tl static int cbsp_dec_load_query_compl(struct osmo_cbsp_load_query_complete *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6) || !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) { osmo_cbsp_errstr = "missing/short mandatory IE"; @@ -911,9 +958,11 @@ static int cbsp_dec_load_query_compl(struct osmo_cbsp_load_query_complete *out, } INIT_LLIST_HEAD(&out->loading_list.list); - cbsp_decode_loading_list(&out->loading_list, ctx, - TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST), - TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST)); + rc = cbsp_decode_loading_list(&out->loading_list, ctx, + TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST), + TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST)); + if (rc < 0) + return rc; out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND); return 0; @@ -923,6 +972,8 @@ static int cbsp_dec_load_query_compl(struct osmo_cbsp_load_query_complete *out, static int cbsp_dec_load_query_fail(struct osmo_cbsp_load_query_failure *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) || !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) { osmo_cbsp_errstr = "missing/short mandatory IE"; @@ -930,25 +981,29 @@ static int cbsp_dec_load_query_fail(struct osmo_cbsp_load_query_failure *out, } INIT_LLIST_HEAD(&out->fail_list); - cbsp_decode_fail_list(&out->fail_list, ctx, - TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), - TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + rc = cbsp_decode_fail_list(&out->fail_list, ctx, + TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), + TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + if (rc < 0) + return rc; out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND); INIT_LLIST_HEAD(&out->loading_list.list); if (TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6)) { - cbsp_decode_loading_list(&out->loading_list, ctx, - TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST), - TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST)); + rc = cbsp_decode_loading_list(&out->loading_list, ctx, + TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST), + TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST)); } - return 0; + return rc; } /* 8.1.3.10 STATUS QUERY */ static int cbsp_dec_msg_status_query(struct osmo_cbsp_msg_status_query *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) || @@ -961,8 +1016,10 @@ static int cbsp_dec_msg_status_query(struct osmo_cbsp_msg_status_query *out, out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR); INIT_LLIST_HEAD(&out->cell_list.list); - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND); return 0; @@ -972,6 +1029,8 @@ static int cbsp_dec_msg_status_query(struct osmo_cbsp_msg_status_query *out, static int cbsp_dec_msg_status_query_compl(struct osmo_cbsp_msg_status_query_complete *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7) || @@ -984,9 +1043,11 @@ static int cbsp_dec_msg_status_query_compl(struct osmo_cbsp_msg_status_query_com out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR); INIT_LLIST_HEAD(&out->num_compl_list.list); - cbsp_decode_num_compl_list(&out->num_compl_list, ctx, - TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), - TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx, + TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), + TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + if (rc < 0) + return rc; out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND); return 0; } @@ -995,6 +1056,8 @@ static int cbsp_dec_msg_status_query_compl(struct osmo_cbsp_msg_status_query_com static int cbsp_dec_msg_status_query_fail(struct osmo_cbsp_msg_status_query_failure *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) || !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) || @@ -1007,17 +1070,21 @@ static int cbsp_dec_msg_status_query_fail(struct osmo_cbsp_msg_status_query_fail out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR); INIT_LLIST_HEAD(&out->fail_list); - cbsp_decode_fail_list(&out->fail_list, ctx, - TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), - TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + rc = cbsp_decode_fail_list(&out->fail_list, ctx, + TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), + TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + if (rc < 0) + return rc; out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND); INIT_LLIST_HEAD(&out->num_compl_list.list); if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) { - cbsp_decode_num_compl_list(&out->num_compl_list, ctx, - TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), - TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx, + TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST), + TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST)); + if (rc < 0) + return rc; } return 0; } @@ -1026,14 +1093,19 @@ static int cbsp_dec_msg_status_query_fail(struct osmo_cbsp_msg_status_query_fail static int cbsp_dec_reset(struct osmo_cbsp_reset *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) { osmo_cbsp_errstr = "missing/short mandatory IE"; return -EINVAL; } INIT_LLIST_HEAD(&out->cell_list.list); - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; + return 0; } @@ -1041,14 +1113,19 @@ static int cbsp_dec_reset(struct osmo_cbsp_reset *out, const struct tlv_parsed * static int cbsp_dec_reset_compl(struct osmo_cbsp_reset_complete *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) { osmo_cbsp_errstr = "missing/short mandatory IE"; return -EINVAL; } INIT_LLIST_HEAD(&out->cell_list.list); - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; + return 0; } @@ -1056,20 +1133,26 @@ static int cbsp_dec_reset_compl(struct osmo_cbsp_reset_complete *out, const stru static int cbsp_dec_reset_fail(struct osmo_cbsp_reset_failure *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) { osmo_cbsp_errstr = "missing/short mandatory IE"; return -EINVAL; } INIT_LLIST_HEAD(&out->fail_list); - cbsp_decode_fail_list(&out->fail_list, ctx, - TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), - TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + rc = cbsp_decode_fail_list(&out->fail_list, ctx, + TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), + TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + if (rc < 0) + return rc; INIT_LLIST_HEAD(&out->cell_list.list); if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) { - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; } return 0; } @@ -1078,12 +1161,14 @@ static int cbsp_dec_reset_fail(struct osmo_cbsp_reset_failure *out, const struct static int cbsp_dec_keep_alive(struct osmo_cbsp_keep_alive *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + uint8_t rperiod; if (!TLVP_PRES_LEN(tp, CBSP_IEI_KEEP_ALIVE_REP_PERIOD, 1)) { osmo_cbsp_errstr = "missing/short mandatory IE"; return -EINVAL; } - out->repetition_period = *TLVP_VAL(tp, CBSP_IEI_KEEP_ALIVE_REP_PERIOD); + rperiod = *TLVP_VAL(tp, CBSP_IEI_KEEP_ALIVE_REP_PERIOD); + out->repetition_period = decode_wperiod(rperiod); return 0; } @@ -1098,6 +1183,8 @@ static int cbsp_dec_keep_alive_compl(struct osmo_cbsp_keep_alive_complete *out, static int cbsp_dec_restart(struct osmo_cbsp_restart *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) || !TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1) || !TLVP_PRES_LEN(tp, CBSP_IEI_RECOVERY_IND, 1)) { @@ -1106,8 +1193,10 @@ static int cbsp_dec_restart(struct osmo_cbsp_restart *out, const struct tlv_pars } INIT_LLIST_HEAD(&out->cell_list.list); - cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), - TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST), + TLVP_LEN(tp, CBSP_IEI_CELL_LIST)); + if (rc < 0) + return rc; out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE); out->recovery_ind = *TLVP_VAL(tp, CBSP_IEI_RECOVERY_IND); @@ -1118,6 +1207,8 @@ static int cbsp_dec_restart(struct osmo_cbsp_restart *out, const struct tlv_pars static int cbsp_dec_failure(struct osmo_cbsp_failure *out, const struct tlv_parsed *tp, struct msgb *in, void *ctx) { + int rc; + if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) || !TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1)) { osmo_cbsp_errstr = "missing/short mandatory IE"; @@ -1125,9 +1216,11 @@ static int cbsp_dec_failure(struct osmo_cbsp_failure *out, const struct tlv_pars } INIT_LLIST_HEAD(&out->fail_list); - cbsp_decode_fail_list(&out->fail_list, ctx, - TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), - TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + rc = cbsp_decode_fail_list(&out->fail_list, ctx, + TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST), + TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST)); + if (rc < 0) + return rc; out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE); return 0; @@ -1168,6 +1261,7 @@ static int cbsp_dec_error_ind(struct osmo_cbsp_error_ind *out, const struct tlv_ * \returns callee-allocated decoded representation of CBSP message; NULL on error */ struct osmo_cbsp_decoded *osmo_cbsp_decode(void *ctx, struct msgb *in) { + OSMO_ASSERT(in->l1h != NULL && in->l2h != NULL); struct osmo_cbsp_decoded *out = talloc_zero(ctx, struct osmo_cbsp_decoded); const struct cbsp_header *h = msgb_l1(in); struct tlv_parsed tp[16]; /* max. number of pages in a given CBS message */ @@ -1433,6 +1527,10 @@ int osmo_cbsp_recv_buffered(void *ctx, int fd, struct msgb **rmsg, struct msgb * needed = len - msgb_l2len(msg); if (needed > 0) { + if (needed > msgb_tailroom(msg)) { + rc = -ENOMEM; + goto discard_msg; + } rc = recv(fd, msg->tail, needed, 0); if (rc == 0) goto discard_msg; @@ -1456,12 +1554,7 @@ int osmo_cbsp_recv_buffered(void *ctx, int fd, struct msgb **rmsg, struct msgb * } } /* else: complete message received */ - rc = msgb_l2len(msg); - if (rc == 0) { - /* drop empty message */ - rc = -EAGAIN; - goto discard_msg; - } + rc = msgb_length(msg); if (tmp_msg) *tmp_msg = NULL; *rmsg = msg; @@ -1474,4 +1567,45 @@ discard_msg: return rc; } +/*! call-back function to segment the data at message boundaries. + * Returns the size of the next message. If it returns -EAGAIN or a value larger than msgb_length() (message + * is incomplete), the caller (e.g. osmo_io) has to wait for more data to be read. */ +int osmo_cbsp_segmentation_cb(struct msgb *msg) +{ + const struct cbsp_header *h; + int len; + + if (msgb_length(msg) < sizeof(*h)) + return -EAGAIN; + + h = (const struct cbsp_header *) msg->data; + msg->l1h = msg->data; + msg->l2h = msg->data + sizeof(*h); + /* then read the length as specified in the header */ + len = h->len[0] << 16 | h->len[1] << 8 | h->len[2]; + + return sizeof(*h) + len; +} + +/*! value_string[] for enum osmo_cbsp_cause. */ +const struct value_string osmo_cbsp_cause_names[] = { + { OSMO_CBSP_CAUSE_PARAM_NOT_RECOGNISED, "Parameter-not-recognised" }, + { OSMO_CBSP_CAUSE_PARAM_VALUE_INVALID, "Parameter-value-invalid" }, + { OSMO_CBSP_CAUSE_MSG_REF_NOT_IDENTIFIED, "Message-reference-not-identified" }, + { OSMO_CBSP_CAUSE_CELL_ID_NOT_VALID, "Cell-identity-not-valid" }, + { OSMO_CBSP_CAUSE_UNRECOGNISED_MESSAGE, "Unrecognised-message" }, + { OSMO_CBSP_CAUSE_MISSING_MANDATORY_ELEMENT, "Missing-mandatory-element" }, + { OSMO_CBSP_CAUSE_BSC_CAPACITY_EXCEEDED, "BSC-capacity-exceeded" }, + { OSMO_CBSP_CAUSE_CELL_MEMORY_EXCEEDED, "Cell-memory-exceeded" }, + { OSMO_CBSP_CAUSE_BSC_MEMORY_EXCEEDED, "BSC-memory-exceeded" }, + { OSMO_CBSP_CAUSE_CELL_BROADCAST_NOT_SUPPORTED, "Cell-broadcast-not-supported" }, + { OSMO_CBSP_CAUSE_CELL_BROADCAST_NOT_OPERATIONAL, "Cell-broadcast-not-operational" }, + { OSMO_CBSP_CAUSE_INCOMPATIBLE_DRX_PARAM, "Incompatible-DRX-parameter:"}, + { OSMO_CBSP_CAUSE_EXT_CHAN_NOT_SUPPORTED, "Extended-channel-not-supported"}, + { OSMO_CBSP_CAUSE_MSG_REF_ALREADY_USED, "Message-reference-already-used"}, + { OSMO_CBSP_CAUSE_UNSPECIFIED_ERROR, "Unspecified-error"}, + { OSMO_CBSP_CAUSE_LAI_OR_LAC_NOT_VALID, "LAI-or-LAC-not-valid"}, + { 0, NULL } +}; + #endif /* HAVE_SYS_SOCKET_H */ |