aboutsummaryrefslogtreecommitdiffstats
path: root/src/gsm/cbsp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gsm/cbsp.c')
-rw-r--r--src/gsm/cbsp.c310
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 */