blob: 7411056fbd07c2c0588e66c543472f67de6c25da [file] [log] [blame]
Neels Hofmeyrb19b5332020-08-25 13:34:04 +02001/* Cell Broadcast Service Protocol (CBSP, 3GPP TS 48.049): Message encoding, decoding and reception */
Harald Welte07958e42019-05-03 09:39:10 +02002/*
3 * Copyright (C) 2019 Harald Welte <laforge@gnumonks.org>
4 *
5 * All Rights Reserved
6 *
7 * SPDX-License-Identifier: GPL-2.0+
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
Harald Welte07958e42019-05-03 09:39:10 +020018 */
19
20#include "config.h"
21
22#include <errno.h>
23
24#include <sys/types.h>
25
26#include <osmocom/core/linuxlist.h>
27#include <osmocom/core/msgb.h>
28
29#include <osmocom/gsm/tlv.h>
30#include <osmocom/gsm/cbsp.h>
31#include <osmocom/gsm/gsm0808_utils.h>
32
Harald Weltef72155a2019-06-15 23:05:19 +020033const __thread char *osmo_cbsp_errstr;
34
Harald Welte07958e42019-05-03 09:39:10 +020035struct msgb *osmo_cbsp_msgb_alloc(void *ctx, const char *name)
36{
37 /* make the messages rather large as the cell lists can be long! */
38 return msgb_alloc_headroom_c(ctx, 65535, 16, name);
39}
40
41/***********************************************************************
42 * IE Encoding
43 ***********************************************************************/
44
45/* 8.2.6 Cell List */
46static void msgb_put_cbsp_cell_list(struct msgb *msg, const struct osmo_cbsp_cell_list *cl)
47{
48 const struct osmo_cbsp_cell_ent *ent;
49 uint8_t *lenptr;
50
51 /* put tag; reserve space for length; put discriminator */
52 msgb_put_u8(msg, CBSP_IEI_CELL_LIST);
53 lenptr = msgb_put(msg, sizeof(uint16_t));
54 msgb_put_u8(msg, cl->id_discr);
55 /* put list elements */
56 llist_for_each_entry(ent, &cl->list, list) {
57 gsm0808_msgb_put_cell_id_u(msg, cl->id_discr, &ent->cell_id);
58 }
59 /* update IE length */
60 osmo_store16be(msg->tail - (lenptr+2), lenptr);
61}
62
63/* 8.2.11 Failure List (discriminator per entry) */
64static void msgb_put_cbsp_fail_list(struct msgb *msg, const struct llist_head *fl)
65{
66 const struct osmo_cbsp_fail_ent *ent;
67 uint8_t *lenptr;
68
69 /* put tag; reserve space for length; put discriminator */
70 msgb_put_u8(msg, CBSP_IEI_FAILURE_LIST);
71 lenptr = msgb_put(msg, sizeof(uint16_t));
72 /* put list elements */
73 llist_for_each_entry(ent, fl, list) {
74 msgb_put_u8(msg, ent->id_discr);
75 gsm0808_msgb_put_cell_id_u(msg, ent->id_discr, &ent->cell_id);
76 msgb_put_u8(msg, ent->cause);
77 }
78 /* update IE length */
79 osmo_store16be(msg->tail - (lenptr+2), lenptr);
80}
81
82/* 8.2.12 Radio Resource Loading List */
83static void msgb_put_cbsp_loading_list(struct msgb *msg, const struct osmo_cbsp_loading_list *ll)
84{
85 const struct osmo_cbsp_loading_ent *ent;
86 uint8_t *lenptr;
87
88 /* put tag; reserve space for length; put discriminator */
89 msgb_put_u8(msg, CBSP_IEI_RR_LOADING_LIST);
90 lenptr = msgb_put(msg, sizeof(uint16_t));
91 msgb_put_u8(msg, ll->id_discr);
92 /* put list elements */
93 llist_for_each_entry(ent, &ll->list, list) {
94 gsm0808_msgb_put_cell_id_u(msg, ll->id_discr, &ent->cell_id);
95 msgb_put_u8(msg, ent->load[0]);
96 msgb_put_u8(msg, ent->load[1]);
97 }
98 /* update IE length */
99 osmo_store16be(msg->tail - (lenptr+2), lenptr);
100}
101
102/* 8.2.10 Completed List */
103static void msgb_put_cbsp_num_compl_list(struct msgb *msg, const struct osmo_cbsp_num_compl_list *cl)
104{
105 const struct osmo_cbsp_num_compl_ent *ent;
106 uint8_t *lenptr;
107
108 /* put tag; reserve space for length; put discriminator */
109 msgb_put_u8(msg, CBSP_IEI_NUM_BCAST_COMPL_LIST);
110 lenptr = msgb_put(msg, sizeof(uint16_t));
111 msgb_put_u8(msg, cl->id_discr);
112 /* put list elements */
113 llist_for_each_entry(ent, &cl->list, list) {
114 gsm0808_msgb_put_cell_id_u(msg, cl->id_discr, &ent->cell_id);
115 msgb_put_u16(msg, ent->num_compl);
116 msgb_put_u8(msg, ent->num_bcast_info);
117 }
118 /* update IE length */
119 osmo_store16be(msg->tail - (lenptr+2), lenptr);
120}
121
122static int encode_wperiod(uint32_t secs)
123{
124 if (secs == 0xffffffff)
125 return 0; /* infinite */
126 if (secs <= 10)
127 return secs;
128 if (secs <= 30)
Harald Weltefcbf3472021-02-22 10:05:05 +0100129 return 10 + (secs-10)/2;
Harald Welte07958e42019-05-03 09:39:10 +0200130 if (secs <= 120)
Harald Weltefcbf3472021-02-22 10:05:05 +0100131 return 30 + (secs-30)/5;
Harald Welte07958e42019-05-03 09:39:10 +0200132 if (secs <= 600)
Harald Weltefcbf3472021-02-22 10:05:05 +0100133 return 120 + (secs-120)/10;
Harald Welte07958e42019-05-03 09:39:10 +0200134 if (secs <= 60*60)
Harald Weltefcbf3472021-02-22 10:05:05 +0100135 return 600 + (secs-600)/30;
Harald Weltef72155a2019-06-15 23:05:19 +0200136 osmo_cbsp_errstr = "warning period out of range";
Harald Welte07958e42019-05-03 09:39:10 +0200137 return -1;
138}
139
140/***********************************************************************
141 * Message Encoding
142 ***********************************************************************/
143
144/* 8.1.3.1 WRITE REPLACE */
145static int cbsp_enc_write_repl(struct msgb *msg, const struct osmo_cbsp_write_replace *in)
146{
147 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
148 msgb_tv16_put(msg, CBSP_IEI_NEW_SERIAL_NR, in->new_serial_nr);
149 if (in->old_serial_nr)
150 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, *in->old_serial_nr);
151 msgb_put_cbsp_cell_list(msg, &in->cell_list);
152 if (in->is_cbs) {
153 int num_of_pages = llist_count(&in->u.cbs.msg_content);
154 struct osmo_cbsp_content *ce;
Harald Weltef72155a2019-06-15 23:05:19 +0200155 if (num_of_pages == 0 || num_of_pages > 15) {
156 osmo_cbsp_errstr = "invalid number of pages";
Harald Welte07958e42019-05-03 09:39:10 +0200157 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200158 }
Harald Welte07958e42019-05-03 09:39:10 +0200159 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->u.cbs.channel_ind);
160 msgb_tv_put(msg, CBSP_IEI_CATEGORY, in->u.cbs.category);
161 msgb_tv16_put(msg, CBSP_IEI_REP_PERIOD, in->u.cbs.rep_period);
162 msgb_tv16_put(msg, CBSP_IEI_NUM_BCAST_REQ, in->u.cbs.num_bcast_req);
163 msgb_tv_put(msg, CBSP_IEI_NUM_OF_PAGES, num_of_pages);
164 msgb_tv_put(msg, CBSP_IEI_DCS, in->u.cbs.dcs);
165 llist_for_each_entry(ce, &in->u.cbs.msg_content, list) {
166 uint8_t *out;
167 /* cannot use msgb_tlv_put() as 'len' isn't actually the length of
168 * the data field */
169 msgb_put_u8(msg, CBSP_IEI_MSG_CONTENT);
170 msgb_put_u8(msg, ce->user_len);
171 out = msgb_put(msg, sizeof(ce->data));
172 memcpy(out, ce->data, sizeof(ce->data));
173 }
174 } else {
175 int wperiod = encode_wperiod(in->u.emergency.warning_period);
Harald Welte64b94072021-01-02 20:46:51 +0100176 uint8_t *cur;
Harald Welte07958e42019-05-03 09:39:10 +0200177 if (wperiod < 0)
178 return -EINVAL;
179 msgb_tv_put(msg, CBSP_IEI_EMERG_IND, in->u.emergency.indicator);
180 msgb_tv16_put(msg, CBSP_IEI_WARN_TYPE, in->u.emergency.warning_type);
Harald Welte64b94072021-01-02 20:46:51 +0100181 /* Tag + fixed length value! */
182 msgb_put_u8(msg, CBSP_IEI_WARN_SEC_INFO);
183 cur = msgb_put(msg, sizeof(in->u.emergency.warning_sec_info));
184 memcpy(cur, in->u.emergency.warning_sec_info, sizeof(in->u.emergency.warning_sec_info));
Harald Welte07958e42019-05-03 09:39:10 +0200185 msgb_tv_put(msg, CBSP_IEI_WARNING_PERIOD, wperiod);
186 }
187 return 0;
188}
189
190/* 8.1.3.2 WRITE REPLACE COMPLETE*/
191static int cbsp_enc_write_repl_compl(struct msgb *msg, const struct osmo_cbsp_write_replace_complete *in)
192{
193 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
194 msgb_tv16_put(msg, CBSP_IEI_NEW_SERIAL_NR, in->new_serial_nr);
195 if (in->old_serial_nr)
196 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, *in->old_serial_nr);
197
198 if (!llist_empty(&in->num_compl_list.list))
199 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
200 if (!llist_empty(&in->cell_list.list))
201 msgb_put_cbsp_cell_list(msg, &in->cell_list);
202 if (in->channel_ind)
203 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
204 return 0;
205}
206
207/* 8.1.3.3 WRITE REPLACE FAILURE */
208static int cbsp_enc_write_repl_fail(struct msgb *msg, const struct osmo_cbsp_write_replace_failure *in)
209{
210 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
211 msgb_tv16_put(msg, CBSP_IEI_NEW_SERIAL_NR, in->new_serial_nr);
212 if (in->old_serial_nr)
213 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, *in->old_serial_nr);
214
215 msgb_put_cbsp_fail_list(msg, &in->fail_list);
216 if (!llist_empty(&in->num_compl_list.list))
217 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
218 if (!llist_empty(&in->cell_list.list))
219 msgb_put_cbsp_cell_list(msg, &in->cell_list);
220 if (in->channel_ind)
221 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
222 return 0;
223}
224
225/* 8.1.3.4 KILL */
226static int cbsp_enc_kill(struct msgb *msg, const struct osmo_cbsp_kill *in)
227{
228 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
229 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
230 msgb_put_cbsp_cell_list(msg, &in->cell_list);
231 if (in->channel_ind)
232 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
233 return 0;
234}
235
236/* 8.1.3.5 KILL COMPLETE */
237static int cbsp_enc_kill_compl(struct msgb *msg, const struct osmo_cbsp_kill_complete *in)
238{
239 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
240 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
241 if (!llist_empty(&in->num_compl_list.list))
242 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
243 if (!llist_empty(&in->cell_list.list))
244 msgb_put_cbsp_cell_list(msg, &in->cell_list);
245 if (in->channel_ind)
246 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
247 return 0;
248}
249
250/* 8.1.3.6 KILL FAILURE */
251static int cbsp_enc_kill_fail(struct msgb *msg, const struct osmo_cbsp_kill_failure *in)
252{
253 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
254 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
255 msgb_put_cbsp_fail_list(msg, &in->fail_list);
256 if (!llist_empty(&in->num_compl_list.list))
257 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
258 if (!llist_empty(&in->cell_list.list))
259 msgb_put_cbsp_cell_list(msg, &in->cell_list);
260 if (in->channel_ind)
261 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
262 return 0;
263}
264
265/* 8.1.3.7 LOAD QUERY */
266static int cbsp_enc_load_query(struct msgb *msg, const struct osmo_cbsp_load_query *in)
267{
268 msgb_put_cbsp_cell_list(msg, &in->cell_list);
269 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
270 return 0;
271}
272
273/* 8.1.3.8 LOAD QUERY COMPLETE */
274static int cbsp_enc_load_query_compl(struct msgb *msg, const struct osmo_cbsp_load_query_complete *in)
275{
276 msgb_put_cbsp_loading_list(msg, &in->loading_list);
277 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
278 return 0;
279}
280
281/* 8.1.3.9 LOAD QUERY FAILURE */
282static int cbsp_enc_load_query_fail(struct msgb *msg, const struct osmo_cbsp_load_query_failure *in)
283{
284 msgb_put_cbsp_fail_list(msg, &in->fail_list);
285 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
286 if (!llist_empty(&in->loading_list.list))
287 msgb_put_cbsp_loading_list(msg, &in->loading_list);
288 return 0;
289}
290
291/* 8.1.3.10 STATUS QUERY */
292static int cbsp_enc_msg_status_query(struct msgb *msg, const struct osmo_cbsp_msg_status_query *in)
293{
294 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
295 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
296 msgb_put_cbsp_cell_list(msg, &in->cell_list);
297 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
298 return 0;
299}
300
301/* 8.1.3.11 STATUS QUERY COMPLETE */
302static int cbsp_enc_msg_status_query_compl(struct msgb *msg,
303 const struct osmo_cbsp_msg_status_query_complete *in)
304{
305 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
306 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
307 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
308 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
309 return 0;
310}
311
312/* 8.1.3.12 STATUS QUERY FAILURE */
313static int cbsp_enc_msg_status_query_fail(struct msgb *msg,
314 const struct osmo_cbsp_msg_status_query_failure *in)
315{
316 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
317 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
318 msgb_put_cbsp_fail_list(msg, &in->fail_list);
319 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
320 if (!llist_empty(&in->num_compl_list.list))
321 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
322 return 0;
323}
324
325/* 8.1.3.16 RESET */
326static int cbsp_enc_reset(struct msgb *msg, const struct osmo_cbsp_reset *in)
327{
328 msgb_put_cbsp_cell_list(msg, &in->cell_list);
329 return 0;
330}
331
332/* 8.1.3.17 RESET COMPLETE */
333static int cbsp_enc_reset_compl(struct msgb *msg, const struct osmo_cbsp_reset_complete *in)
334{
335 msgb_put_cbsp_cell_list(msg, &in->cell_list);
336 return 0;
337}
338
339/* 8.1.3.18 RESET FAILURE */
340static int cbsp_enc_reset_fail(struct msgb *msg, const struct osmo_cbsp_reset_failure *in)
341{
342 msgb_put_cbsp_fail_list(msg, &in->fail_list);
343 if (!llist_empty(&in->cell_list.list))
344 msgb_put_cbsp_cell_list(msg, &in->cell_list);
345 return 0;
346}
347
348/* 8.1.3.18a KEEP ALIVE */
349static int cbsp_enc_keep_alive(struct msgb *msg, const struct osmo_cbsp_keep_alive *in)
350{
Harald Welte48f22b02021-02-22 09:55:44 +0100351 int rperiod = encode_wperiod(in->repetition_period);
352 if (in->repetition_period > 120)
353 return -EINVAL;
354 if (rperiod < 0)
355 return -EINVAL;
356 msgb_tv_put(msg, CBSP_IEI_KEEP_ALIVE_REP_PERIOD, rperiod);
Harald Welte07958e42019-05-03 09:39:10 +0200357 return 0;
358}
359
360/* 8.1.3.18b KEEP ALIVE COMPLETE */
361static int cbsp_enc_keep_alive_compl(struct msgb *msg, const struct osmo_cbsp_keep_alive_complete *in)
362{
363 return 0;
364}
365
366/* 8.1.3.19 RESTART */
367static int cbsp_enc_restart(struct msgb *msg, const struct osmo_cbsp_restart *in)
368{
369 msgb_put_cbsp_cell_list(msg, &in->cell_list);
370 msgb_tv_put(msg, CBSP_IEI_BCAST_MSG_TYPE, in->bcast_msg_type);
371 msgb_tv_put(msg, CBSP_IEI_RECOVERY_IND, in->recovery_ind);
372 return 0;
373}
374
375/* 8.1.3.20 FAILURE */
376static int cbsp_enc_failure(struct msgb *msg, const struct osmo_cbsp_failure *in)
377{
378 msgb_put_cbsp_fail_list(msg, &in->fail_list);
379 msgb_tv_put(msg, CBSP_IEI_BCAST_MSG_TYPE, in->bcast_msg_type);
380 return 0;
381}
382
383/* 8.1.3.21 ERROR INDICATION */
384static int cbsp_enc_error_ind(struct msgb *msg, const struct osmo_cbsp_error_ind *in)
385{
386 msgb_tv_put(msg, CBSP_IEI_CAUSE, in->cause);
387 if (in->msg_id)
388 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, *in->msg_id);
389 if (in->new_serial_nr)
390 msgb_tv16_put(msg, CBSP_IEI_NEW_SERIAL_NR, *in->new_serial_nr);
391 if (in->old_serial_nr)
392 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, *in->old_serial_nr);
393 if (in->channel_ind)
394 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
395 return 0;
396}
397
398/*! Encode a CBSP message from the decoded/parsed structure representation to binary PDU.
399 * \param[in] ctx talloc context from which to allocate returned msgb.
400 * \param[in] in decoded CBSP message which is to be encoded. Ownership not transferred.
401 * \return callee-allocated message buffer containing binary CBSP PDU; NULL on error */
402struct msgb *osmo_cbsp_encode(void *ctx, const struct osmo_cbsp_decoded *in)
403{
404 struct msgb *msg = osmo_cbsp_msgb_alloc(ctx, __func__);
405 unsigned int len;
406 int rc;
407
Harald Weltef72155a2019-06-15 23:05:19 +0200408 osmo_cbsp_errstr = NULL;
409
Harald Welte07958e42019-05-03 09:39:10 +0200410 if (!msg)
411 return NULL;
412
413 switch (in->msg_type) {
414 case CBSP_MSGT_WRITE_REPLACE:
415 rc = cbsp_enc_write_repl(msg, &in->u.write_replace);
416 break;
417 case CBSP_MSGT_WRITE_REPLACE_COMPL:
418 rc = cbsp_enc_write_repl_compl(msg, &in->u.write_replace_compl);
419 break;
420 case CBSP_MSGT_WRITE_REPLACE_FAIL:
421 rc = cbsp_enc_write_repl_fail(msg, &in->u.write_replace_fail);
422 break;
423 case CBSP_MSGT_KILL:
424 rc = cbsp_enc_kill(msg, &in->u.kill);
425 break;
426 case CBSP_MSGT_KILL_COMPL:
427 rc = cbsp_enc_kill_compl(msg, &in->u.kill_compl);
428 break;
429 case CBSP_MSGT_KILL_FAIL:
430 rc = cbsp_enc_kill_fail(msg, &in->u.kill_fail);
431 break;
432 case CBSP_MSGT_LOAD_QUERY:
433 rc = cbsp_enc_load_query(msg, &in->u.load_query);
434 break;
435 case CBSP_MSGT_LOAD_QUERY_COMPL:
436 rc = cbsp_enc_load_query_compl(msg, &in->u.load_query_compl);
437 break;
438 case CBSP_MSGT_LOAD_QUERY_FAIL:
439 rc = cbsp_enc_load_query_fail(msg, &in->u.load_query_fail);
440 break;
441 case CBSP_MSGT_MSG_STATUS_QUERY:
442 rc = cbsp_enc_msg_status_query(msg, &in->u.msg_status_query);
443 break;
444 case CBSP_MSGT_MSG_STATUS_QUERY_COMPL:
445 rc = cbsp_enc_msg_status_query_compl(msg, &in->u.msg_status_query_compl);
446 break;
447 case CBSP_MSGT_MSG_STATUS_QUERY_FAIL:
448 rc = cbsp_enc_msg_status_query_fail(msg, &in->u.msg_status_query_fail);
449 break;
450 case CBSP_MSGT_RESET:
451 rc = cbsp_enc_reset(msg, &in->u.reset);
452 break;
453 case CBSP_MSGT_RESET_COMPL:
454 rc = cbsp_enc_reset_compl(msg, &in->u.reset_compl);
455 break;
456 case CBSP_MSGT_RESET_FAIL:
457 rc = cbsp_enc_reset_fail(msg, &in->u.reset_fail);
458 break;
459 case CBSP_MSGT_RESTART:
460 rc = cbsp_enc_restart(msg, &in->u.restart);
461 break;
462 case CBSP_MSGT_FAILURE:
463 rc = cbsp_enc_failure(msg, &in->u.failure);
464 break;
465 case CBSP_MSGT_ERROR_IND:
466 rc = cbsp_enc_error_ind(msg, &in->u.error_ind);
467 break;
468 case CBSP_MSGT_KEEP_ALIVE:
469 rc = cbsp_enc_keep_alive(msg, &in->u.keep_alive);
470 break;
471 case CBSP_MSGT_KEEP_ALIVE_COMPL:
472 rc = cbsp_enc_keep_alive_compl(msg, &in->u.keep_alive_compl);
473 break;
474 case CBSP_MSGT_SET_DRX:
475 case CBSP_MSGT_SET_DRX_COMPL:
476 case CBSP_MSGT_SET_DRX_FAIL:
Harald Weltef72155a2019-06-15 23:05:19 +0200477 osmo_cbsp_errstr = "message type not implemented";
Harald Welte07958e42019-05-03 09:39:10 +0200478 rc = -1;
479 break;
480 default:
Harald Weltef72155a2019-06-15 23:05:19 +0200481 osmo_cbsp_errstr = "message type not known in spec";
Harald Welte07958e42019-05-03 09:39:10 +0200482 rc = -1;
483 break;
484 }
485
486 if (rc < 0) {
487 msgb_free(msg);
488 return NULL;
489 }
490
491 /* push header in front */
492 len = msgb_length(msg);
493 msgb_push_u8(msg, len & 0xff);
494 msgb_push_u8(msg, (len >> 8) & 0xff);
495 msgb_push_u8(msg, (len >> 16) & 0xff);
496 msgb_push_u8(msg, in->msg_type);
497
498 return msg;
499}
500
501/***********************************************************************
502 * IE Decoding
503 ***********************************************************************/
504
505/* 8.2.6 Cell List */
506static int cbsp_decode_cell_list(struct osmo_cbsp_cell_list *cl, void *ctx,
507 const uint8_t *buf, unsigned int len)
508{
509 const uint8_t *cur = buf;
510 int rc;
511
512 cl->id_discr = *cur++;
513
514 while (cur < buf + len) {
515 struct osmo_cbsp_cell_ent *ent = talloc_zero(ctx, struct osmo_cbsp_cell_ent);
516 unsigned int len_remain = len - (cur - buf);
517 OSMO_ASSERT(ent);
518 rc = gsm0808_decode_cell_id_u(&ent->cell_id, cl->id_discr, cur, len_remain);
Harald Weltef72155a2019-06-15 23:05:19 +0200519 if (rc < 0) {
520 osmo_cbsp_errstr = "cell list: error decoding cell_id_union";
Harald Welte07958e42019-05-03 09:39:10 +0200521 return rc;
Harald Weltef72155a2019-06-15 23:05:19 +0200522 }
Harald Weltef2210032019-08-31 21:25:05 +0200523 cur += gsm0808_cell_id_size(cl->id_discr);
Harald Welte07958e42019-05-03 09:39:10 +0200524 llist_add_tail(&ent->list, &cl->list);
525 }
526 return 0;
527}
528
529/* 8.2.11 Failure List (discriminator per entry) */
530static int cbsp_decode_fail_list(struct llist_head *fl, void *ctx,
531 const uint8_t *buf, unsigned int len)
532{
533 const uint8_t *cur = buf;
534 int rc;
535
536 while (cur < buf + len) {
537 struct osmo_cbsp_fail_ent *ent = talloc_zero(ctx, struct osmo_cbsp_fail_ent);
538 unsigned int len_remain = len - (cur - buf);
539 OSMO_ASSERT(ent);
Pau Espin Pedrol9eb698a2022-08-05 13:58:17 +0200540 ent->id_discr = *cur++;
541 rc = gsm0808_decode_cell_id_u(&ent->cell_id, ent->id_discr, cur, len_remain-1);
Harald Weltef72155a2019-06-15 23:05:19 +0200542 if (rc < 0) {
543 osmo_cbsp_errstr = "fail list: error decoding cell_id_union";
Harald Welte07958e42019-05-03 09:39:10 +0200544 return rc;
Harald Weltef72155a2019-06-15 23:05:19 +0200545 }
Harald Weltef2210032019-08-31 21:25:05 +0200546 cur += gsm0808_cell_id_size(ent->id_discr);
Harald Welte07958e42019-05-03 09:39:10 +0200547 ent->cause = *cur++;
548 llist_add_tail(&ent->list, fl);
549 }
550 return 0;
551}
552
553/* 8.2.12 Radio Resource Loading List */
554static int cbsp_decode_loading_list(struct osmo_cbsp_loading_list *ll, void *ctx,
555 const uint8_t *buf, unsigned int len)
556{
557 const uint8_t *cur = buf;
558 int rc;
559
560 ll->id_discr = *cur++;
561 while (cur < buf + len) {
562 struct osmo_cbsp_loading_ent *ent = talloc_zero(ctx, struct osmo_cbsp_loading_ent);
563 unsigned int len_remain = len - (cur - buf);
564 OSMO_ASSERT(ent);
565 rc = gsm0808_decode_cell_id_u(&ent->cell_id, ll->id_discr, cur, len_remain);
Harald Weltef72155a2019-06-15 23:05:19 +0200566 if (rc < 0) {
567 osmo_cbsp_errstr = "load list: error decoding cell_id_union";
Harald Welte07958e42019-05-03 09:39:10 +0200568 return rc;
Harald Weltef72155a2019-06-15 23:05:19 +0200569 }
Harald Weltef2210032019-08-31 21:25:05 +0200570 cur += gsm0808_cell_id_size(ll->id_discr);
Harald Welte07958e42019-05-03 09:39:10 +0200571 if (cur + 2 > buf + len) {
572 talloc_free(ent);
Harald Weltef72155a2019-06-15 23:05:19 +0200573 osmo_cbsp_errstr = "load list: truncated IE";
Harald Welte07958e42019-05-03 09:39:10 +0200574 return -EINVAL;
575 }
576 ent->load[0] = *cur++;
577 ent->load[1] = *cur++;
578 llist_add_tail(&ent->list, &ll->list);
579 }
580 return 0;
581}
582
583/* 8.2.10 Completed List */
584static int cbsp_decode_num_compl_list(struct osmo_cbsp_num_compl_list *cl, void *ctx,
585 const uint8_t *buf, unsigned int len)
586{
587 const uint8_t *cur = buf;
588 int rc;
589
590 cl->id_discr = *cur++;
591 while (cur < buf + len) {
592 struct osmo_cbsp_num_compl_ent *ent = talloc_zero(ctx, struct osmo_cbsp_num_compl_ent);
593 unsigned int len_remain = len - (cur - buf);
594 OSMO_ASSERT(ent);
595 rc = gsm0808_decode_cell_id_u(&ent->cell_id, cl->id_discr, cur, len_remain);
Harald Weltef72155a2019-06-15 23:05:19 +0200596 if (rc < 0) {
597 osmo_cbsp_errstr = "completed list: error decoding cell_id_union";
Harald Welte07958e42019-05-03 09:39:10 +0200598 return rc;
Harald Weltef72155a2019-06-15 23:05:19 +0200599 }
Harald Weltef2210032019-08-31 21:25:05 +0200600 cur += gsm0808_cell_id_size(cl->id_discr);
Harald Welte07958e42019-05-03 09:39:10 +0200601 if (cur + 3 > buf + len) {
602 talloc_free(ent);
Harald Weltef72155a2019-06-15 23:05:19 +0200603 osmo_cbsp_errstr = "completed list: truncated IE";
Harald Welte07958e42019-05-03 09:39:10 +0200604 return -EINVAL;
605 }
606 ent->num_compl = osmo_load16be(cur); cur += 2;
607 ent->num_bcast_info = *cur++;
608 llist_add_tail(&ent->list, &cl->list);
609 }
610 return 0;
611}
612
613/* 8.2.25 */
614static uint32_t decode_wperiod(uint8_t in)
615{
616 if (in == 0x00)
617 return 0xffffffff; /* infinite */
618 if (in <= 10)
619 return in;
620 if (in <= 20)
621 return 10 + (in - 10)*2;
622 if (in <= 38)
623 return 30 + (in - 20)*5;
624 if (in <= 86)
625 return 120 + (in - 38)*10;
626 if (in <= 186)
627 return 600 + (in - 86)*30;
628 else
629 return 0;
630}
631
632
633/***********************************************************************
634 * Message Decoding
635 ***********************************************************************/
636
637/* 8.1.3.1 WRITE REPLACE */
638static int cbsp_dec_write_repl(struct osmo_cbsp_write_replace *out, const struct tlv_parsed *tp,
639 struct msgb *in, void *ctx)
640{
641 unsigned int i;
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200642 int rc;
Harald Welte07958e42019-05-03 09:39:10 +0200643
644 /* check for mandatory IEs */
645 if (!TLVP_PRESENT(tp, CBSP_IEI_MSG_ID) ||
646 !TLVP_PRESENT(tp, CBSP_IEI_NEW_SERIAL_NR) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200647 !TLVP_PRESENT(tp, CBSP_IEI_CELL_LIST)) {
648 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200649 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200650 }
Harald Welte07958e42019-05-03 09:39:10 +0200651
652 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
653 out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
654 if (TLVP_PRESENT(tp, CBSP_IEI_OLD_SERIAL_NR)) {
655 out->old_serial_nr = talloc(ctx, uint16_t);
656 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
657 }
658
659 INIT_LLIST_HEAD(&out->cell_list.list);
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200660 rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
661 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
662 if (rc < 0)
663 return rc;
Harald Welte07958e42019-05-03 09:39:10 +0200664
665 if (TLVP_PRESENT(tp, CBSP_IEI_CHANNEL_IND)) {
666 uint8_t num_of_pages;
667 INIT_LLIST_HEAD(&out->u.cbs.msg_content);
Harald Weltef72155a2019-06-15 23:05:19 +0200668 if (TLVP_PRESENT(tp, CBSP_IEI_EMERG_IND)) {
669 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200670 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200671 }
Harald Welte07958e42019-05-03 09:39:10 +0200672 if (!TLVP_PRESENT(tp, CBSP_IEI_CATEGORY) ||
673 !TLVP_PRESENT(tp, CBSP_IEI_REP_PERIOD) ||
674 !TLVP_PRESENT(tp, CBSP_IEI_NUM_BCAST_REQ) ||
675 !TLVP_PRESENT(tp, CBSP_IEI_NUM_OF_PAGES) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200676 !TLVP_PRESENT(tp, CBSP_IEI_DCS)) {
677 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200678 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200679 }
Harald Welte07958e42019-05-03 09:39:10 +0200680 out->is_cbs = true;
681 out->u.cbs.channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
682 out->u.cbs.category = *TLVP_VAL(tp, CBSP_IEI_CATEGORY);
683 out->u.cbs.rep_period = tlvp_val16be(tp, CBSP_IEI_REP_PERIOD);
684 out->u.cbs.num_bcast_req = tlvp_val16be(tp, CBSP_IEI_NUM_BCAST_REQ);
Harald Welte886e7422021-01-02 22:30:02 +0100685 out->u.cbs.dcs = *TLVP_VAL(tp, CBSP_IEI_DCS);
Harald Welte07958e42019-05-03 09:39:10 +0200686 num_of_pages = *TLVP_VAL(tp, CBSP_IEI_NUM_OF_PAGES);
687 if (num_of_pages < 1)
688 return -EINVAL;
689 /* parse pages */
690 for (i = 0; i < num_of_pages; i++) {
691 const uint8_t *ie = TLVP_VAL(&tp[i], CBSP_IEI_MSG_CONTENT);
692 struct osmo_cbsp_content *page;
Harald Weltef72155a2019-06-15 23:05:19 +0200693 if (!ie) {
694 osmo_cbsp_errstr = "insufficient message content IEs";
Harald Welte07958e42019-05-03 09:39:10 +0200695 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200696 }
Harald Welte07958e42019-05-03 09:39:10 +0200697 page = talloc_zero(ctx, struct osmo_cbsp_content);
698 OSMO_ASSERT(page);
Harald Weltee674c442019-09-01 22:30:58 +0200699 page->user_len = ie[0]; /* length byte before payload */
700 memcpy(page->data, ie+1, sizeof(page->data));
Harald Welte07958e42019-05-03 09:39:10 +0200701 llist_add_tail(&page->list, &out->u.cbs.msg_content);
702 }
703 } else {
704 if (!TLVP_PRES_LEN(tp, CBSP_IEI_EMERG_IND, 1) ||
705 !TLVP_PRES_LEN(tp, CBSP_IEI_WARN_TYPE, 2) ||
706 !TLVP_PRES_LEN(tp, CBSP_IEI_WARN_SEC_INFO, 50) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200707 !TLVP_PRES_LEN(tp, CBSP_IEI_WARNING_PERIOD, 1)) {
708 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200709 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200710 }
Harald Welte07958e42019-05-03 09:39:10 +0200711 out->u.emergency.indicator = *TLVP_VAL(tp, CBSP_IEI_EMERG_IND);
712 out->u.emergency.warning_type = tlvp_val16be(tp, CBSP_IEI_WARN_TYPE);
713 memcpy(&out->u.emergency.warning_sec_info, TLVP_VAL(tp, CBSP_IEI_WARN_SEC_INFO),
714 sizeof(out->u.emergency.warning_sec_info));
715 out->u.emergency.warning_period = decode_wperiod(*TLVP_VAL(tp, CBSP_IEI_WARNING_PERIOD));
716 }
717 return 0;
718}
719
720/* 8.1.3.2 WRITE REPLACE COMPLETE*/
721static int cbsp_dec_write_repl_compl(struct osmo_cbsp_write_replace_complete *out,
722 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
723{
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200724 int rc;
725
Harald Welte07958e42019-05-03 09:39:10 +0200726 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200727 !TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2)) {
728 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200729 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200730 }
Harald Welte07958e42019-05-03 09:39:10 +0200731
732 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
733 out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
734 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
735 out->old_serial_nr = talloc(ctx, uint16_t);
736 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
737 }
738
739 INIT_LLIST_HEAD(&out->num_compl_list.list);
740 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200741 rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
742 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
743 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
744 if (rc < 0)
745 return rc;
Harald Welte07958e42019-05-03 09:39:10 +0200746 }
747
748 INIT_LLIST_HEAD(&out->cell_list.list);
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200749 rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
750 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
751 if (rc < 0)
752 return rc;
Harald Welte07958e42019-05-03 09:39:10 +0200753
754 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
755 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
756 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
757 }
758 return 0;
759}
760
761/* 8.1.3.3 WRITE REPLACE FAILURE */
762static int cbsp_dec_write_repl_fail(struct osmo_cbsp_write_replace_failure *out,
763 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
764{
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200765 int rc;
766
Harald Welte07958e42019-05-03 09:39:10 +0200767 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
768 !TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200769 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
770 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200771 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200772 }
Harald Welte07958e42019-05-03 09:39:10 +0200773
774 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
775 out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
776 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
777 out->old_serial_nr = talloc(ctx, uint16_t);
778 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
779 }
780
781 INIT_LLIST_HEAD(&out->fail_list);
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200782 rc = cbsp_decode_fail_list(&out->fail_list, ctx,
783 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
784 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
785 if (rc < 0)
786 return rc;
Harald Welte07958e42019-05-03 09:39:10 +0200787
788 INIT_LLIST_HEAD(&out->num_compl_list.list);
789 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200790 rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
791 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
792 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
793 if (rc < 0)
794 return rc;
Harald Welte07958e42019-05-03 09:39:10 +0200795 }
796
797 INIT_LLIST_HEAD(&out->cell_list.list);
798 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200799 rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
800 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
801 if (rc < 0)
802 return rc;
Harald Welte07958e42019-05-03 09:39:10 +0200803 }
804
805 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
806 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
807 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
808 }
809 return 0;
810}
811
812/* 8.1.3.4 KILL */
813static int cbsp_dec_kill(struct osmo_cbsp_kill *out, const struct tlv_parsed *tp,
814 struct msgb *in, void *ctx)
815{
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200816 int rc;
817
Harald Welte07958e42019-05-03 09:39:10 +0200818 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
819 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200820 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
821 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200822 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200823 }
824
Harald Welte07958e42019-05-03 09:39:10 +0200825 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
826 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
827
828 INIT_LLIST_HEAD(&out->cell_list.list);
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200829 rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
830 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
831 if (rc < 0)
832 return rc;
Harald Welte07958e42019-05-03 09:39:10 +0200833
834 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
835 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
836 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
837 }
838 return 0;
839}
840
841/* 8.1.3.5 KILL COMPLETE */
842static int cbsp_dec_kill_compl(struct osmo_cbsp_kill_complete *out, const struct tlv_parsed *tp,
843 struct msgb *in, void *ctx)
844{
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200845 int rc;
846
Harald Welte07958e42019-05-03 09:39:10 +0200847 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
848 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200849 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
850 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200851 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200852 }
Harald Welte07958e42019-05-03 09:39:10 +0200853
854 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
855 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
856
857 INIT_LLIST_HEAD(&out->num_compl_list.list);
858 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200859 rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
860 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
861 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
862 if (rc < 0)
863 return rc;
Harald Welte07958e42019-05-03 09:39:10 +0200864 }
865
866 INIT_LLIST_HEAD(&out->cell_list.list);
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200867 rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
868 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
869 if (rc < 0)
870 return rc;
Harald Welte07958e42019-05-03 09:39:10 +0200871
872 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
873 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
874 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
875 }
876 return 0;
877}
878
879/* 8.1.3.6 KILL FAILURE */
880static int cbsp_dec_kill_fail(struct osmo_cbsp_kill_failure *out, const struct tlv_parsed *tp,
881 struct msgb *in, void *ctx)
882{
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200883 int rc;
884
Harald Welte07958e42019-05-03 09:39:10 +0200885 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
886 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200887 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
888 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200889 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200890 }
Harald Welte07958e42019-05-03 09:39:10 +0200891
892 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
893 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
894
895 INIT_LLIST_HEAD(&out->fail_list);
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200896 rc = cbsp_decode_fail_list(&out->fail_list, ctx,
897 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
898 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
899 if (rc < 0)
900 return rc;
Harald Welte07958e42019-05-03 09:39:10 +0200901
902 INIT_LLIST_HEAD(&out->num_compl_list.list);
903 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200904 rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
905 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
906 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
907 if (rc < 0)
908 return rc;
Harald Welte07958e42019-05-03 09:39:10 +0200909 }
910
911 INIT_LLIST_HEAD(&out->cell_list.list);
912 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200913 rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
914 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
915 if (rc < 0)
916 return rc;
Harald Welte07958e42019-05-03 09:39:10 +0200917 }
918
919 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
920 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
921 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
922 }
923 return 0;
924}
925
926/* 8.1.3.7 LOAD QUERY */
927static int cbsp_dec_load_query(struct osmo_cbsp_load_query *out, const struct tlv_parsed *tp,
928 struct msgb *in, void *ctx)
929{
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200930 int rc;
931
Harald Welte07958e42019-05-03 09:39:10 +0200932 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200933 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
934 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200935 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200936 }
Harald Welte07958e42019-05-03 09:39:10 +0200937
938 INIT_LLIST_HEAD(&out->cell_list.list);
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200939 rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
940 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
941 if (rc < 0)
942 return rc;
Harald Welte07958e42019-05-03 09:39:10 +0200943
944 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
945 return 0;
946}
947
948/* 8.1.3.8 LOAD QUERY COMPLETE */
949static int cbsp_dec_load_query_compl(struct osmo_cbsp_load_query_complete *out,
950 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
951{
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200952 int rc;
953
Harald Welte07958e42019-05-03 09:39:10 +0200954 if (!TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200955 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
956 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200957 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200958 }
Harald Welte07958e42019-05-03 09:39:10 +0200959
960 INIT_LLIST_HEAD(&out->loading_list.list);
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200961 rc = cbsp_decode_loading_list(&out->loading_list, ctx,
962 TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
963 TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
964 if (rc < 0)
965 return rc;
Harald Welte07958e42019-05-03 09:39:10 +0200966
967 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
968 return 0;
969}
970
971/* 8.1.3.9 LOAD QUERY FAILURE */
972static int cbsp_dec_load_query_fail(struct osmo_cbsp_load_query_failure *out,
973 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
974{
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200975 int rc;
976
Harald Welte07958e42019-05-03 09:39:10 +0200977 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200978 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
979 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200980 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200981 }
Harald Welte07958e42019-05-03 09:39:10 +0200982
983 INIT_LLIST_HEAD(&out->fail_list);
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200984 rc = cbsp_decode_fail_list(&out->fail_list, ctx,
985 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
986 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
987 if (rc < 0)
988 return rc;
Harald Welte07958e42019-05-03 09:39:10 +0200989
990 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
991
992 INIT_LLIST_HEAD(&out->loading_list.list);
993 if (TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6)) {
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200994 rc = cbsp_decode_loading_list(&out->loading_list, ctx,
995 TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
996 TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
Harald Welte07958e42019-05-03 09:39:10 +0200997 }
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +0200998 return rc;
Harald Welte07958e42019-05-03 09:39:10 +0200999}
1000
1001/* 8.1.3.10 STATUS QUERY */
1002static int cbsp_dec_msg_status_query(struct osmo_cbsp_msg_status_query *out,
1003 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
1004{
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +02001005 int rc;
1006
Harald Welte07958e42019-05-03 09:39:10 +02001007 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
1008 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
1009 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
Harald Weltef72155a2019-06-15 23:05:19 +02001010 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
1011 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001012 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001013 }
1014
Harald Welte07958e42019-05-03 09:39:10 +02001015 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
1016 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
1017
1018 INIT_LLIST_HEAD(&out->cell_list.list);
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +02001019 rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1020 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1021 if (rc < 0)
1022 return rc;
Harald Welte07958e42019-05-03 09:39:10 +02001023
1024 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
1025 return 0;
1026}
1027
1028/* 8.1.3.11 STATUS QUERY COMPLETE */
1029static int cbsp_dec_msg_status_query_compl(struct osmo_cbsp_msg_status_query_complete *out,
1030 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
1031{
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +02001032 int rc;
1033
Harald Welte07958e42019-05-03 09:39:10 +02001034 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
1035 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
1036 !TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7) ||
Harald Weltef72155a2019-06-15 23:05:19 +02001037 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
1038 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001039 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001040 }
Harald Welte07958e42019-05-03 09:39:10 +02001041
1042 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
1043 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
1044
1045 INIT_LLIST_HEAD(&out->num_compl_list.list);
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +02001046 rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
1047 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
1048 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
1049 if (rc < 0)
1050 return rc;
Harald Welte07958e42019-05-03 09:39:10 +02001051 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
1052 return 0;
1053}
1054
1055/* 8.1.3.12 STATUS QUERY FAILURE */
1056static int cbsp_dec_msg_status_query_fail(struct osmo_cbsp_msg_status_query_failure *out,
1057 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
1058{
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +02001059 int rc;
1060
Harald Welte07958e42019-05-03 09:39:10 +02001061 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
1062 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
1063 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
Harald Weltef72155a2019-06-15 23:05:19 +02001064 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
1065 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001066 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001067 }
Harald Welte07958e42019-05-03 09:39:10 +02001068
1069 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
1070 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
1071
1072 INIT_LLIST_HEAD(&out->fail_list);
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +02001073 rc = cbsp_decode_fail_list(&out->fail_list, ctx,
1074 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
1075 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
1076 if (rc < 0)
1077 return rc;
Harald Welte07958e42019-05-03 09:39:10 +02001078
1079 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
1080
1081 INIT_LLIST_HEAD(&out->num_compl_list.list);
1082 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +02001083 rc = cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
1084 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
1085 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
1086 if (rc < 0)
1087 return rc;
Harald Welte07958e42019-05-03 09:39:10 +02001088 }
1089 return 0;
1090}
1091
1092/* 8.1.3.16 RESET */
1093static int cbsp_dec_reset(struct osmo_cbsp_reset *out, const struct tlv_parsed *tp,
1094 struct msgb *in, void *ctx)
1095{
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +02001096 int rc;
1097
Harald Weltef72155a2019-06-15 23:05:19 +02001098 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
1099 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001100 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001101 }
Harald Welte07958e42019-05-03 09:39:10 +02001102
1103 INIT_LLIST_HEAD(&out->cell_list.list);
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +02001104 rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1105 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1106 if (rc < 0)
1107 return rc;
1108
Harald Welte07958e42019-05-03 09:39:10 +02001109 return 0;
1110}
1111
1112/* 8.1.3.17 RESET COMPLETE */
1113static int cbsp_dec_reset_compl(struct osmo_cbsp_reset_complete *out, const struct tlv_parsed *tp,
1114 struct msgb *in, void *ctx)
1115{
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +02001116 int rc;
1117
Harald Weltef72155a2019-06-15 23:05:19 +02001118 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
1119 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001120 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001121 }
Harald Welte07958e42019-05-03 09:39:10 +02001122
1123 INIT_LLIST_HEAD(&out->cell_list.list);
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +02001124 rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1125 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1126 if (rc < 0)
1127 return rc;
1128
Harald Welte07958e42019-05-03 09:39:10 +02001129 return 0;
1130}
1131
1132/* 8.1.3.18 RESET FAILURE */
1133static int cbsp_dec_reset_fail(struct osmo_cbsp_reset_failure *out, const struct tlv_parsed *tp,
1134 struct msgb *in, void *ctx)
1135{
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +02001136 int rc;
1137
Harald Weltef72155a2019-06-15 23:05:19 +02001138 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
1139 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001140 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001141 }
Harald Welte07958e42019-05-03 09:39:10 +02001142
1143 INIT_LLIST_HEAD(&out->fail_list);
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +02001144 rc = cbsp_decode_fail_list(&out->fail_list, ctx,
1145 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
1146 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
1147 if (rc < 0)
1148 return rc;
Harald Welte07958e42019-05-03 09:39:10 +02001149
1150 INIT_LLIST_HEAD(&out->cell_list.list);
1151 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +02001152 rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1153 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1154 if (rc < 0)
1155 return rc;
Harald Welte07958e42019-05-03 09:39:10 +02001156 }
1157 return 0;
1158}
1159
1160/* 8.1.3.18a KEEP ALIVE */
1161static int cbsp_dec_keep_alive(struct osmo_cbsp_keep_alive *out, const struct tlv_parsed *tp,
1162 struct msgb *in, void *ctx)
1163{
Harald Welte48f22b02021-02-22 09:55:44 +01001164 uint8_t rperiod;
Harald Weltef72155a2019-06-15 23:05:19 +02001165 if (!TLVP_PRES_LEN(tp, CBSP_IEI_KEEP_ALIVE_REP_PERIOD, 1)) {
1166 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001167 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001168 }
Harald Welte07958e42019-05-03 09:39:10 +02001169
Harald Welte48f22b02021-02-22 09:55:44 +01001170 rperiod = *TLVP_VAL(tp, CBSP_IEI_KEEP_ALIVE_REP_PERIOD);
1171 out->repetition_period = decode_wperiod(rperiod);
Harald Welte07958e42019-05-03 09:39:10 +02001172 return 0;
1173}
1174
1175/* 8.1.3.18b KEEP ALIVE COMPLETE */
1176static int cbsp_dec_keep_alive_compl(struct osmo_cbsp_keep_alive_complete *out,
1177 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
1178{
1179 return 0;
1180}
1181
1182/* 8.1.3.19 RESTART */
1183static int cbsp_dec_restart(struct osmo_cbsp_restart *out, const struct tlv_parsed *tp,
1184 struct msgb *in, void *ctx)
1185{
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +02001186 int rc;
1187
Harald Welte07958e42019-05-03 09:39:10 +02001188 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
1189 !TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1) ||
Harald Weltef72155a2019-06-15 23:05:19 +02001190 !TLVP_PRES_LEN(tp, CBSP_IEI_RECOVERY_IND, 1)) {
1191 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001192 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001193 }
Harald Welte07958e42019-05-03 09:39:10 +02001194
1195 INIT_LLIST_HEAD(&out->cell_list.list);
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +02001196 rc = cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1197 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1198 if (rc < 0)
1199 return rc;
Harald Welte07958e42019-05-03 09:39:10 +02001200
1201 out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE);
1202 out->recovery_ind = *TLVP_VAL(tp, CBSP_IEI_RECOVERY_IND);
1203 return 0;
1204}
1205
1206/* 8.1.3.20 FAILURE */
1207static int cbsp_dec_failure(struct osmo_cbsp_failure *out, const struct tlv_parsed *tp,
1208 struct msgb *in, void *ctx)
1209{
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +02001210 int rc;
1211
Harald Welte07958e42019-05-03 09:39:10 +02001212 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
Harald Weltef72155a2019-06-15 23:05:19 +02001213 !TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1)) {
1214 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001215 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001216 }
Harald Welte07958e42019-05-03 09:39:10 +02001217
1218 INIT_LLIST_HEAD(&out->fail_list);
Pau Espin Pedrolfdb8f812022-08-04 17:55:31 +02001219 rc = cbsp_decode_fail_list(&out->fail_list, ctx,
1220 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
1221 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
1222 if (rc < 0)
1223 return rc;
Harald Welte07958e42019-05-03 09:39:10 +02001224
1225 out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE);
1226 return 0;
1227}
1228
1229/* 8.1.3.21 ERROR INDICATION */
1230static int cbsp_dec_error_ind(struct osmo_cbsp_error_ind *out, const struct tlv_parsed *tp,
1231 struct msgb *in, void *ctx)
1232{
Harald Weltef72155a2019-06-15 23:05:19 +02001233 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CAUSE, 1)) {
1234 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001235 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001236 }
Harald Welte07958e42019-05-03 09:39:10 +02001237
1238 out->cause = *TLVP_VAL(tp, CBSP_IEI_CAUSE);
1239 if (TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2)) {
1240 out->msg_id = talloc(ctx, uint16_t);
1241 *out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
1242 }
1243 if (TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2)) {
1244 out->new_serial_nr = talloc(ctx, uint16_t);
1245 *out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
1246 }
1247 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
1248 out->old_serial_nr = talloc(ctx, uint16_t);
1249 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
1250 }
1251 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
1252 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
1253 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
1254 }
1255 return 0;
1256}
1257
1258/*! Decode a CBSP message from wire formwat to pased structure.
1259 * \param[in] ctx talloc context from which to allocate decoded output.
1260 * \param[in] in message buffer contiaining binary CBSP message.
1261 * \returns callee-allocated decoded representation of CBSP message; NULL on error */
1262struct osmo_cbsp_decoded *osmo_cbsp_decode(void *ctx, struct msgb *in)
1263{
Pau Espin Pedrol5563bc92022-08-05 14:20:32 +02001264 OSMO_ASSERT(in->l1h != NULL && in->l2h != NULL);
Harald Welte07958e42019-05-03 09:39:10 +02001265 struct osmo_cbsp_decoded *out = talloc_zero(ctx, struct osmo_cbsp_decoded);
1266 const struct cbsp_header *h = msgb_l1(in);
1267 struct tlv_parsed tp[16]; /* max. number of pages in a given CBS message */
1268 unsigned int len;
1269 int rc;
1270
Harald Weltef72155a2019-06-15 23:05:19 +02001271 osmo_cbsp_errstr = NULL;
1272
Harald Welte07958e42019-05-03 09:39:10 +02001273 if (!out)
1274 return NULL;
1275
1276 if (msgb_l1len(in) < sizeof(*h)) {
1277 goto out_err;
1278 }
1279 len = h->len[0] << 16 | h->len[1] << 8 | h->len[2];
1280
1281 /* discard messages where indicated length is more than we have */
1282 if (len > msgb_l2len(in)) {
1283 goto out_err;
1284 }
1285
1286 /* trim any messages with extra payload at the end */
1287 if (len < msgb_l2len(in))
1288 msgb_trim(in, (in->l2h - in->data) + msgb_l2len(in));
1289 out->msg_type = h->msg_type;
1290
1291 rc = tlv_parse2(tp, ARRAY_SIZE(tp), &cbsp_att_tlvdef, msgb_l2(in), msgb_l2len(in), 0, 0);
1292 if (rc < 0) {
1293 goto out_err;
1294 }
1295
1296 switch (h->msg_type) {
1297 case CBSP_MSGT_WRITE_REPLACE:
1298 rc = cbsp_dec_write_repl(&out->u.write_replace, tp, in, out);
1299 break;
1300 case CBSP_MSGT_WRITE_REPLACE_COMPL:
1301 rc = cbsp_dec_write_repl_compl(&out->u.write_replace_compl, tp, in, out);
1302 break;
1303 case CBSP_MSGT_WRITE_REPLACE_FAIL:
1304 rc = cbsp_dec_write_repl_fail(&out->u.write_replace_fail, tp, in, out);
1305 break;
1306 case CBSP_MSGT_KILL:
1307 rc = cbsp_dec_kill(&out->u.kill, tp, in, out);
1308 break;
1309 case CBSP_MSGT_KILL_COMPL:
1310 rc = cbsp_dec_kill_compl(&out->u.kill_compl, tp, in, out);
1311 break;
1312 case CBSP_MSGT_KILL_FAIL:
1313 rc = cbsp_dec_kill_fail(&out->u.kill_fail, tp, in, out);
1314 break;
1315 case CBSP_MSGT_LOAD_QUERY:
1316 rc = cbsp_dec_load_query(&out->u.load_query, tp, in, out);
1317 break;
1318 case CBSP_MSGT_LOAD_QUERY_COMPL:
1319 rc = cbsp_dec_load_query_compl(&out->u.load_query_compl, tp, in, out);
1320 break;
1321 case CBSP_MSGT_LOAD_QUERY_FAIL:
1322 rc = cbsp_dec_load_query_fail(&out->u.load_query_fail, tp, in, out);
1323 break;
1324 case CBSP_MSGT_MSG_STATUS_QUERY:
1325 rc = cbsp_dec_msg_status_query(&out->u.msg_status_query, tp, in, out);
1326 break;
1327 case CBSP_MSGT_MSG_STATUS_QUERY_COMPL:
1328 rc = cbsp_dec_msg_status_query_compl(&out->u.msg_status_query_compl, tp, in, out);
1329 break;
1330 case CBSP_MSGT_MSG_STATUS_QUERY_FAIL:
1331 rc = cbsp_dec_msg_status_query_fail(&out->u.msg_status_query_fail, tp, in, out);
1332 break;
1333 case CBSP_MSGT_RESET:
1334 rc = cbsp_dec_reset(&out->u.reset, tp, in, out);
1335 break;
1336 case CBSP_MSGT_RESET_COMPL:
1337 rc = cbsp_dec_reset_compl(&out->u.reset_compl, tp, in, out);
1338 break;
1339 case CBSP_MSGT_RESET_FAIL:
1340 rc = cbsp_dec_reset_fail(&out->u.reset_fail, tp, in, out);
1341 break;
1342 case CBSP_MSGT_RESTART:
1343 rc = cbsp_dec_restart(&out->u.restart, tp, in, out);
1344 break;
1345 case CBSP_MSGT_FAILURE:
1346 rc = cbsp_dec_failure(&out->u.failure, tp, in, out);
1347 break;
1348 case CBSP_MSGT_ERROR_IND:
1349 rc = cbsp_dec_error_ind(&out->u.error_ind, tp, in, out);
1350 break;
1351 case CBSP_MSGT_KEEP_ALIVE:
1352 rc = cbsp_dec_keep_alive(&out->u.keep_alive, tp, in, out);
1353 break;
1354 case CBSP_MSGT_KEEP_ALIVE_COMPL:
1355 rc = cbsp_dec_keep_alive_compl(&out->u.keep_alive_compl, tp, in, out);
1356 break;
1357 case CBSP_MSGT_SET_DRX:
1358 case CBSP_MSGT_SET_DRX_COMPL:
1359 case CBSP_MSGT_SET_DRX_FAIL:
Harald Weltef72155a2019-06-15 23:05:19 +02001360 osmo_cbsp_errstr = "message type not implemented";
Harald Welte07958e42019-05-03 09:39:10 +02001361 rc = -1;
1362 break;
1363 default:
Harald Weltef72155a2019-06-15 23:05:19 +02001364 osmo_cbsp_errstr = "message type not known in spec";
Harald Welte07958e42019-05-03 09:39:10 +02001365 rc = -1;
1366 break;
1367 }
1368
1369 if (rc < 0) {
1370 goto out_err;
1371 }
1372
1373 return out;
1374
1375out_err:
1376 talloc_free(out);
1377 return NULL;
1378}
1379
1380/* initialization of 'decoded' structure of given message type */
1381void osmo_cbsp_init_struct(struct osmo_cbsp_decoded *cbsp, enum cbsp_msg_type msg_type)
1382{
1383 memset(cbsp, 0, sizeof(*cbsp));
1384 cbsp->msg_type = msg_type;
1385
1386 switch (msg_type) {
1387 case CBSP_MSGT_WRITE_REPLACE:
1388 INIT_LLIST_HEAD(&cbsp->u.write_replace.cell_list.list);
1389 break;
1390 case CBSP_MSGT_WRITE_REPLACE_COMPL:
1391 INIT_LLIST_HEAD(&cbsp->u.write_replace_compl.num_compl_list.list);
1392 INIT_LLIST_HEAD(&cbsp->u.write_replace_compl.cell_list.list);
1393 break;
1394 case CBSP_MSGT_WRITE_REPLACE_FAIL:
1395 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.fail_list);
1396 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.num_compl_list.list);
1397 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.cell_list.list);
1398 break;
1399 case CBSP_MSGT_KILL:
1400 INIT_LLIST_HEAD(&cbsp->u.kill.cell_list.list);
1401 break;
1402 case CBSP_MSGT_KILL_COMPL:
1403 INIT_LLIST_HEAD(&cbsp->u.kill_compl.num_compl_list.list);
1404 INIT_LLIST_HEAD(&cbsp->u.kill_compl.cell_list.list);
1405 break;
1406 case CBSP_MSGT_KILL_FAIL:
1407 INIT_LLIST_HEAD(&cbsp->u.kill_fail.fail_list);
1408 INIT_LLIST_HEAD(&cbsp->u.kill_fail.num_compl_list.list);
1409 INIT_LLIST_HEAD(&cbsp->u.kill_fail.cell_list.list);
1410 break;
1411 case CBSP_MSGT_LOAD_QUERY:
1412 INIT_LLIST_HEAD(&cbsp->u.load_query.cell_list.list);
1413 break;
1414 case CBSP_MSGT_LOAD_QUERY_COMPL:
1415 INIT_LLIST_HEAD(&cbsp->u.load_query_compl.loading_list.list);
1416 break;
1417 case CBSP_MSGT_LOAD_QUERY_FAIL:
1418 INIT_LLIST_HEAD(&cbsp->u.load_query_fail.fail_list);
1419 break;
1420 case CBSP_MSGT_MSG_STATUS_QUERY:
1421 INIT_LLIST_HEAD(&cbsp->u.msg_status_query.cell_list.list);
1422 break;
1423 case CBSP_MSGT_MSG_STATUS_QUERY_COMPL:
1424 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_compl.num_compl_list.list);
1425 break;
1426 case CBSP_MSGT_MSG_STATUS_QUERY_FAIL:
1427 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_fail.fail_list);
1428 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_fail.num_compl_list.list);
1429 break;
1430 case CBSP_MSGT_RESET:
1431 INIT_LLIST_HEAD(&cbsp->u.reset.cell_list.list);
1432 break;
1433 case CBSP_MSGT_RESET_COMPL:
1434 INIT_LLIST_HEAD(&cbsp->u.reset_compl.cell_list.list);
1435 break;
1436 case CBSP_MSGT_RESET_FAIL:
1437 INIT_LLIST_HEAD(&cbsp->u.reset_fail.fail_list);
1438 INIT_LLIST_HEAD(&cbsp->u.reset_fail.cell_list.list);
1439 break;
1440 case CBSP_MSGT_RESTART:
1441 INIT_LLIST_HEAD(&cbsp->u.restart.cell_list.list);
1442 break;
1443 case CBSP_MSGT_FAILURE:
1444 INIT_LLIST_HEAD(&cbsp->u.failure.fail_list);
1445 break;
1446 default:
1447 break;
1448 }
1449}
1450
1451/*! Dynamically allocate and initialize decoded CBSP structure.
1452 * \param[in] ctx talloc context from which to allocate
1453 * \param[in] msg_type CBSP message type for which to initialize result
1454 * \returns allocated + initialized decoded CBSP structure; NULL on talloc failure */
1455struct osmo_cbsp_decoded *osmo_cbsp_decoded_alloc(void *ctx, enum cbsp_msg_type msg_type)
1456{
1457 struct osmo_cbsp_decoded *cbsp = talloc_zero(ctx, struct osmo_cbsp_decoded);
1458 if (!cbsp)
1459 return NULL;
1460 osmo_cbsp_init_struct(cbsp, msg_type);
1461 return cbsp;
1462}
1463
1464/***********************************************************************
1465 * Message Reception
1466 ***********************************************************************/
1467
1468#ifdef HAVE_SYS_SOCKET_H
1469#include <sys/socket.h>
1470
1471/*! Read one CBSP message from socket fd or store part if still not fully received.
1472 * \param[in] ctx talloc context from which to allocate new msgb.
1473 * \param[in] fd The fd for the socket to read from.
1474 * \param[out] rmsg internally allocated msgb containing a fully received CBSP message.
1475 * \param[inout] tmp_msg internally allocated msgb caching data for not yet fully received message.
1476 *
1477 * Function is designed just like ipa_msg_recv_buffered()
1478 */
1479int osmo_cbsp_recv_buffered(void *ctx, int fd, struct msgb **rmsg, struct msgb **tmp_msg)
1480{
1481 struct msgb *msg = tmp_msg ? *tmp_msg : NULL;
1482 struct cbsp_header *h;
1483 int len, rc;
1484 int needed;
1485
1486 if (!msg) {
1487 msg = osmo_cbsp_msgb_alloc(ctx, __func__);
Harald Weltec30d8be2019-07-21 07:51:33 +02001488 if (!msg)
Harald Welte07958e42019-05-03 09:39:10 +02001489 return -ENOMEM;
Harald Welte07958e42019-05-03 09:39:10 +02001490 msg->l1h = msg->tail;
1491 }
1492
1493 if (msg->l2h == NULL) {
1494 /* first read the [missing part of the] header */
1495 needed = sizeof(*h) - msg->len;
1496 rc = recv(fd, msg->tail, needed, 0);
1497 if (rc == 0)
1498 goto discard_msg;
1499 else if (rc < 0) {
1500 if (errno == EAGAIN || errno == EINTR)
1501 rc = 0;
1502 else {
1503 rc = -errno;
1504 goto discard_msg;
1505 }
1506 }
1507 msgb_put(msg, rc);
1508 if (rc < needed) {
1509 if (msg->len == 0) {
1510 rc = -EAGAIN;
1511 goto discard_msg;
1512 }
1513
1514 if (!tmp_msg) {
1515 rc = -EIO;
1516 goto discard_msg;
1517 }
1518 *tmp_msg = msg;
1519 return -EAGAIN;
1520 }
1521 msg->l2h = msg->tail;
1522 }
1523
1524 h = (struct cbsp_header *) msg->data;
1525 /* then read the length as specified in the header */
1526 len = h->len[0] << 16 | h->len[1] << 8 | h->len[2];
1527
1528 needed = len - msgb_l2len(msg);
1529 if (needed > 0) {
Pau Espin Pedrol8f577fb2022-06-29 17:55:54 +02001530 if (needed > msgb_tailroom(msg)) {
1531 rc = -ENOMEM;
1532 goto discard_msg;
1533 }
Harald Welte07958e42019-05-03 09:39:10 +02001534 rc = recv(fd, msg->tail, needed, 0);
1535 if (rc == 0)
1536 goto discard_msg;
1537 else if (rc < 0) {
1538 if (errno == EAGAIN || errno == EINTR)
1539 rc = 0;
1540 else {
1541 rc = -errno;
1542 goto discard_msg;
1543 }
1544 }
1545 msgb_put(msg, rc);
1546 /* still not all of payload received? */
1547 if (rc < needed) {
1548 if (!tmp_msg) {
1549 rc = -EIO;
1550 goto discard_msg;
1551 }
1552 *tmp_msg = msg;
1553 return -EAGAIN;
1554 }
1555 }
1556 /* else: complete message received */
Harald Weltefdd71c82021-01-02 20:50:47 +01001557 rc = msgb_length(msg);
Harald Welte07958e42019-05-03 09:39:10 +02001558 if (tmp_msg)
1559 *tmp_msg = NULL;
1560 *rmsg = msg;
1561 return rc;
1562
1563discard_msg:
Harald Welte07958e42019-05-03 09:39:10 +02001564 if (tmp_msg)
1565 *tmp_msg = NULL;
1566 msgb_free(msg);
1567 return rc;
1568}
1569
Pau Espin Pedrol0cdd0ab2022-06-09 18:13:39 +02001570/*! value_string[] for enum osmo_cbsp_cause. */
1571const struct value_string osmo_cbsp_cause_names[] = {
1572 { OSMO_CBSP_CAUSE_PARAM_NOT_RECOGNISED, "Parameter-not-recognised" },
1573 { OSMO_CBSP_CAUSE_PARAM_VALUE_INVALID, "Parameter-value-invalid" },
1574 { OSMO_CBSP_CAUSE_MSG_REF_NOT_IDENTIFIED, "Message-reference-not-identified" },
1575 { OSMO_CBSP_CAUSE_CELL_ID_NOT_VALID, "Cell-identity-not-valid" },
1576 { OSMO_CBSP_CAUSE_UNRECOGNISED_MESSAGE, "Unrecognised-message" },
1577 { OSMO_CBSP_CAUSE_MISSING_MANDATORY_ELEMENT, "Missing-mandatory-element" },
1578 { OSMO_CBSP_CAUSE_BSC_CAPACITY_EXCEEDED, "BSC-capacity-exceeded" },
1579 { OSMO_CBSP_CAUSE_CELL_MEMORY_EXCEEDED, "Cell-memory-exceeded" },
1580 { OSMO_CBSP_CAUSE_BSC_MEMORY_EXCEEDED, "BSC-memory-exceeded" },
1581 { OSMO_CBSP_CAUSE_CELL_BROADCAST_NOT_SUPPORTED, "Cell-broadcast-not-supported" },
1582 { OSMO_CBSP_CAUSE_CELL_BROADCAST_NOT_OPERATIONAL, "Cell-broadcast-not-operational" },
1583 { OSMO_CBSP_CAUSE_INCOMPATIBLE_DRX_PARAM, "Incompatible-DRX-parameter:"},
1584 { OSMO_CBSP_CAUSE_EXT_CHAN_NOT_SUPPORTED, "Extended-channel-not-supported"},
1585 { OSMO_CBSP_CAUSE_MSG_REF_ALREADY_USED, "Message-reference-already-used"},
1586 { OSMO_CBSP_CAUSE_UNSPECIFIED_ERROR, "Unspecified-error"},
1587 { OSMO_CBSP_CAUSE_LAI_OR_LAC_NOT_VALID, "LAI-or-LAC-not-valid"},
1588 { 0, NULL }
1589};
1590
Harald Welte07958e42019-05-03 09:39:10 +02001591#endif /* HAVE_SYS_SOCKET_H */