blob: a31517bcc7f9ba581dd8c78aef8c956a08b37a78 [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);
540 ent->id_discr = cur[0];
541 rc = gsm0808_decode_cell_id_u(&ent->cell_id, ent->id_discr, cur+1, 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;
642
643 /* check for mandatory IEs */
644 if (!TLVP_PRESENT(tp, CBSP_IEI_MSG_ID) ||
645 !TLVP_PRESENT(tp, CBSP_IEI_NEW_SERIAL_NR) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200646 !TLVP_PRESENT(tp, CBSP_IEI_CELL_LIST)) {
647 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200648 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200649 }
Harald Welte07958e42019-05-03 09:39:10 +0200650
651 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
652 out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
653 if (TLVP_PRESENT(tp, CBSP_IEI_OLD_SERIAL_NR)) {
654 out->old_serial_nr = talloc(ctx, uint16_t);
655 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
656 }
657
658 INIT_LLIST_HEAD(&out->cell_list.list);
659 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
660 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
661
662 if (TLVP_PRESENT(tp, CBSP_IEI_CHANNEL_IND)) {
663 uint8_t num_of_pages;
664 INIT_LLIST_HEAD(&out->u.cbs.msg_content);
Harald Weltef72155a2019-06-15 23:05:19 +0200665 if (TLVP_PRESENT(tp, CBSP_IEI_EMERG_IND)) {
666 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200667 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200668 }
Harald Welte07958e42019-05-03 09:39:10 +0200669 if (!TLVP_PRESENT(tp, CBSP_IEI_CATEGORY) ||
670 !TLVP_PRESENT(tp, CBSP_IEI_REP_PERIOD) ||
671 !TLVP_PRESENT(tp, CBSP_IEI_NUM_BCAST_REQ) ||
672 !TLVP_PRESENT(tp, CBSP_IEI_NUM_OF_PAGES) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200673 !TLVP_PRESENT(tp, CBSP_IEI_DCS)) {
674 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200675 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200676 }
Harald Welte07958e42019-05-03 09:39:10 +0200677 out->is_cbs = true;
678 out->u.cbs.channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
679 out->u.cbs.category = *TLVP_VAL(tp, CBSP_IEI_CATEGORY);
680 out->u.cbs.rep_period = tlvp_val16be(tp, CBSP_IEI_REP_PERIOD);
681 out->u.cbs.num_bcast_req = tlvp_val16be(tp, CBSP_IEI_NUM_BCAST_REQ);
Harald Welte886e7422021-01-02 22:30:02 +0100682 out->u.cbs.dcs = *TLVP_VAL(tp, CBSP_IEI_DCS);
Harald Welte07958e42019-05-03 09:39:10 +0200683 num_of_pages = *TLVP_VAL(tp, CBSP_IEI_NUM_OF_PAGES);
684 if (num_of_pages < 1)
685 return -EINVAL;
686 /* parse pages */
687 for (i = 0; i < num_of_pages; i++) {
688 const uint8_t *ie = TLVP_VAL(&tp[i], CBSP_IEI_MSG_CONTENT);
689 struct osmo_cbsp_content *page;
Harald Weltef72155a2019-06-15 23:05:19 +0200690 if (!ie) {
691 osmo_cbsp_errstr = "insufficient message content IEs";
Harald Welte07958e42019-05-03 09:39:10 +0200692 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200693 }
Harald Welte07958e42019-05-03 09:39:10 +0200694 page = talloc_zero(ctx, struct osmo_cbsp_content);
695 OSMO_ASSERT(page);
Harald Weltee674c442019-09-01 22:30:58 +0200696 page->user_len = ie[0]; /* length byte before payload */
697 memcpy(page->data, ie+1, sizeof(page->data));
Harald Welte07958e42019-05-03 09:39:10 +0200698 llist_add_tail(&page->list, &out->u.cbs.msg_content);
699 }
700 } else {
701 if (!TLVP_PRES_LEN(tp, CBSP_IEI_EMERG_IND, 1) ||
702 !TLVP_PRES_LEN(tp, CBSP_IEI_WARN_TYPE, 2) ||
703 !TLVP_PRES_LEN(tp, CBSP_IEI_WARN_SEC_INFO, 50) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200704 !TLVP_PRES_LEN(tp, CBSP_IEI_WARNING_PERIOD, 1)) {
705 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200706 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200707 }
Harald Welte07958e42019-05-03 09:39:10 +0200708 out->u.emergency.indicator = *TLVP_VAL(tp, CBSP_IEI_EMERG_IND);
709 out->u.emergency.warning_type = tlvp_val16be(tp, CBSP_IEI_WARN_TYPE);
710 memcpy(&out->u.emergency.warning_sec_info, TLVP_VAL(tp, CBSP_IEI_WARN_SEC_INFO),
711 sizeof(out->u.emergency.warning_sec_info));
712 out->u.emergency.warning_period = decode_wperiod(*TLVP_VAL(tp, CBSP_IEI_WARNING_PERIOD));
713 }
714 return 0;
715}
716
717/* 8.1.3.2 WRITE REPLACE COMPLETE*/
718static int cbsp_dec_write_repl_compl(struct osmo_cbsp_write_replace_complete *out,
719 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
720{
721 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200722 !TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2)) {
723 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200724 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200725 }
Harald Welte07958e42019-05-03 09:39:10 +0200726
727 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
728 out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
729 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
730 out->old_serial_nr = talloc(ctx, uint16_t);
731 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
732 }
733
734 INIT_LLIST_HEAD(&out->num_compl_list.list);
735 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
736 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
737 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
738 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
739 }
740
741 INIT_LLIST_HEAD(&out->cell_list.list);
742 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
743 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
744
745 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
746 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
747 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
748 }
749 return 0;
750}
751
752/* 8.1.3.3 WRITE REPLACE FAILURE */
753static int cbsp_dec_write_repl_fail(struct osmo_cbsp_write_replace_failure *out,
754 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
755{
756 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
757 !TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200758 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
759 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200760 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200761 }
Harald Welte07958e42019-05-03 09:39:10 +0200762
763 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
764 out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
765 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
766 out->old_serial_nr = talloc(ctx, uint16_t);
767 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
768 }
769
770 INIT_LLIST_HEAD(&out->fail_list);
771 cbsp_decode_fail_list(&out->fail_list, ctx,
772 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
773 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
774
775 INIT_LLIST_HEAD(&out->num_compl_list.list);
776 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
777 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
778 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
779 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
780 }
781
782 INIT_LLIST_HEAD(&out->cell_list.list);
783 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
784 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
785 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
786 }
787
788 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
789 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
790 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
791 }
792 return 0;
793}
794
795/* 8.1.3.4 KILL */
796static int cbsp_dec_kill(struct osmo_cbsp_kill *out, const struct tlv_parsed *tp,
797 struct msgb *in, void *ctx)
798{
799 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
800 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200801 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
802 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200803 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200804 }
805
Harald Welte07958e42019-05-03 09:39:10 +0200806 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
807 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
808
809 INIT_LLIST_HEAD(&out->cell_list.list);
810 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
811 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
812
813 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
814 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
815 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
816 }
817 return 0;
818}
819
820/* 8.1.3.5 KILL COMPLETE */
821static int cbsp_dec_kill_compl(struct osmo_cbsp_kill_complete *out, const struct tlv_parsed *tp,
822 struct msgb *in, void *ctx)
823{
824 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
825 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200826 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
827 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200828 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200829 }
Harald Welte07958e42019-05-03 09:39:10 +0200830
831 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
832 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
833
834 INIT_LLIST_HEAD(&out->num_compl_list.list);
835 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
836 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
837 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
838 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
839 }
840
841 INIT_LLIST_HEAD(&out->cell_list.list);
842 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
843 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
844
845 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
846 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
847 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
848 }
849 return 0;
850}
851
852/* 8.1.3.6 KILL FAILURE */
853static int cbsp_dec_kill_fail(struct osmo_cbsp_kill_failure *out, const struct tlv_parsed *tp,
854 struct msgb *in, void *ctx)
855{
856 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
857 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200858 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
859 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200860 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200861 }
Harald Welte07958e42019-05-03 09:39:10 +0200862
863 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
864 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
865
866 INIT_LLIST_HEAD(&out->fail_list);
867 cbsp_decode_fail_list(&out->fail_list, ctx,
868 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
869 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
870
871 INIT_LLIST_HEAD(&out->num_compl_list.list);
872 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
873 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
874 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
875 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
876 }
877
878 INIT_LLIST_HEAD(&out->cell_list.list);
879 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
880 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
881 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
882 }
883
884 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
885 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
886 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
887 }
888 return 0;
889}
890
891/* 8.1.3.7 LOAD QUERY */
892static int cbsp_dec_load_query(struct osmo_cbsp_load_query *out, const struct tlv_parsed *tp,
893 struct msgb *in, void *ctx)
894{
895 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200896 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
897 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200898 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200899 }
Harald Welte07958e42019-05-03 09:39:10 +0200900
901 INIT_LLIST_HEAD(&out->cell_list.list);
902 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
903 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
904
905 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
906 return 0;
907}
908
909/* 8.1.3.8 LOAD QUERY COMPLETE */
910static int cbsp_dec_load_query_compl(struct osmo_cbsp_load_query_complete *out,
911 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
912{
913 if (!TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200914 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
915 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200916 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200917 }
Harald Welte07958e42019-05-03 09:39:10 +0200918
919 INIT_LLIST_HEAD(&out->loading_list.list);
920 cbsp_decode_loading_list(&out->loading_list, ctx,
921 TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
922 TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
923
924 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
925 return 0;
926}
927
928/* 8.1.3.9 LOAD QUERY FAILURE */
929static int cbsp_dec_load_query_fail(struct osmo_cbsp_load_query_failure *out,
930 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
931{
932 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
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->fail_list);
939 cbsp_decode_fail_list(&out->fail_list, ctx,
940 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
941 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
942
943 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
944
945 INIT_LLIST_HEAD(&out->loading_list.list);
946 if (TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6)) {
947 cbsp_decode_loading_list(&out->loading_list, ctx,
948 TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
949 TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
950 }
951 return 0;
952}
953
954/* 8.1.3.10 STATUS QUERY */
955static int cbsp_dec_msg_status_query(struct osmo_cbsp_msg_status_query *out,
956 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
957{
958 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
959 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
960 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200961 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
962 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200963 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200964 }
965
Harald Welte07958e42019-05-03 09:39:10 +0200966 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
967 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
968
969 INIT_LLIST_HEAD(&out->cell_list.list);
970 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
971 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
972
973 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
974 return 0;
975}
976
977/* 8.1.3.11 STATUS QUERY COMPLETE */
978static int cbsp_dec_msg_status_query_compl(struct osmo_cbsp_msg_status_query_complete *out,
979 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
980{
981 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
982 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
983 !TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200984 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
985 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200986 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200987 }
Harald Welte07958e42019-05-03 09:39:10 +0200988
989 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
990 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
991
992 INIT_LLIST_HEAD(&out->num_compl_list.list);
993 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
994 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
995 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
996 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
997 return 0;
998}
999
1000/* 8.1.3.12 STATUS QUERY FAILURE */
1001static int cbsp_dec_msg_status_query_fail(struct osmo_cbsp_msg_status_query_failure *out,
1002 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
1003{
1004 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
1005 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
1006 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
Harald Weltef72155a2019-06-15 23:05:19 +02001007 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
1008 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001009 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001010 }
Harald Welte07958e42019-05-03 09:39:10 +02001011
1012 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
1013 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
1014
1015 INIT_LLIST_HEAD(&out->fail_list);
1016 cbsp_decode_fail_list(&out->fail_list, ctx,
1017 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
1018 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
1019
1020 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
1021
1022 INIT_LLIST_HEAD(&out->num_compl_list.list);
1023 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
1024 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
1025 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
1026 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
1027 }
1028 return 0;
1029}
1030
1031/* 8.1.3.16 RESET */
1032static int cbsp_dec_reset(struct osmo_cbsp_reset *out, const struct tlv_parsed *tp,
1033 struct msgb *in, void *ctx)
1034{
Harald Weltef72155a2019-06-15 23:05:19 +02001035 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
1036 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001037 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001038 }
Harald Welte07958e42019-05-03 09:39:10 +02001039
1040 INIT_LLIST_HEAD(&out->cell_list.list);
1041 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1042 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1043 return 0;
1044}
1045
1046/* 8.1.3.17 RESET COMPLETE */
1047static int cbsp_dec_reset_compl(struct osmo_cbsp_reset_complete *out, const struct tlv_parsed *tp,
1048 struct msgb *in, void *ctx)
1049{
Harald Weltef72155a2019-06-15 23:05:19 +02001050 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
1051 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001052 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001053 }
Harald Welte07958e42019-05-03 09:39:10 +02001054
1055 INIT_LLIST_HEAD(&out->cell_list.list);
1056 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1057 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1058 return 0;
1059}
1060
1061/* 8.1.3.18 RESET FAILURE */
1062static int cbsp_dec_reset_fail(struct osmo_cbsp_reset_failure *out, const struct tlv_parsed *tp,
1063 struct msgb *in, void *ctx)
1064{
Harald Weltef72155a2019-06-15 23:05:19 +02001065 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
1066 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001067 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001068 }
Harald Welte07958e42019-05-03 09:39:10 +02001069
1070 INIT_LLIST_HEAD(&out->fail_list);
1071 cbsp_decode_fail_list(&out->fail_list, ctx,
1072 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
1073 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
1074
1075 INIT_LLIST_HEAD(&out->cell_list.list);
1076 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
1077 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1078 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1079 }
1080 return 0;
1081}
1082
1083/* 8.1.3.18a KEEP ALIVE */
1084static int cbsp_dec_keep_alive(struct osmo_cbsp_keep_alive *out, const struct tlv_parsed *tp,
1085 struct msgb *in, void *ctx)
1086{
Harald Welte48f22b02021-02-22 09:55:44 +01001087 uint8_t rperiod;
Harald Weltef72155a2019-06-15 23:05:19 +02001088 if (!TLVP_PRES_LEN(tp, CBSP_IEI_KEEP_ALIVE_REP_PERIOD, 1)) {
1089 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001090 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001091 }
Harald Welte07958e42019-05-03 09:39:10 +02001092
Harald Welte48f22b02021-02-22 09:55:44 +01001093 rperiod = *TLVP_VAL(tp, CBSP_IEI_KEEP_ALIVE_REP_PERIOD);
1094 out->repetition_period = decode_wperiod(rperiod);
Harald Welte07958e42019-05-03 09:39:10 +02001095 return 0;
1096}
1097
1098/* 8.1.3.18b KEEP ALIVE COMPLETE */
1099static int cbsp_dec_keep_alive_compl(struct osmo_cbsp_keep_alive_complete *out,
1100 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
1101{
1102 return 0;
1103}
1104
1105/* 8.1.3.19 RESTART */
1106static int cbsp_dec_restart(struct osmo_cbsp_restart *out, const struct tlv_parsed *tp,
1107 struct msgb *in, void *ctx)
1108{
1109 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
1110 !TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1) ||
Harald Weltef72155a2019-06-15 23:05:19 +02001111 !TLVP_PRES_LEN(tp, CBSP_IEI_RECOVERY_IND, 1)) {
1112 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001113 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001114 }
Harald Welte07958e42019-05-03 09:39:10 +02001115
1116 INIT_LLIST_HEAD(&out->cell_list.list);
1117 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1118 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1119
1120 out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE);
1121 out->recovery_ind = *TLVP_VAL(tp, CBSP_IEI_RECOVERY_IND);
1122 return 0;
1123}
1124
1125/* 8.1.3.20 FAILURE */
1126static int cbsp_dec_failure(struct osmo_cbsp_failure *out, const struct tlv_parsed *tp,
1127 struct msgb *in, void *ctx)
1128{
1129 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
Harald Weltef72155a2019-06-15 23:05:19 +02001130 !TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1)) {
1131 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001132 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001133 }
Harald Welte07958e42019-05-03 09:39:10 +02001134
1135 INIT_LLIST_HEAD(&out->fail_list);
1136 cbsp_decode_fail_list(&out->fail_list, ctx,
1137 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
1138 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
1139
1140 out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE);
1141 return 0;
1142}
1143
1144/* 8.1.3.21 ERROR INDICATION */
1145static int cbsp_dec_error_ind(struct osmo_cbsp_error_ind *out, const struct tlv_parsed *tp,
1146 struct msgb *in, void *ctx)
1147{
Harald Weltef72155a2019-06-15 23:05:19 +02001148 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CAUSE, 1)) {
1149 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001150 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001151 }
Harald Welte07958e42019-05-03 09:39:10 +02001152
1153 out->cause = *TLVP_VAL(tp, CBSP_IEI_CAUSE);
1154 if (TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2)) {
1155 out->msg_id = talloc(ctx, uint16_t);
1156 *out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
1157 }
1158 if (TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2)) {
1159 out->new_serial_nr = talloc(ctx, uint16_t);
1160 *out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
1161 }
1162 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
1163 out->old_serial_nr = talloc(ctx, uint16_t);
1164 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
1165 }
1166 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
1167 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
1168 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
1169 }
1170 return 0;
1171}
1172
1173/*! Decode a CBSP message from wire formwat to pased structure.
1174 * \param[in] ctx talloc context from which to allocate decoded output.
1175 * \param[in] in message buffer contiaining binary CBSP message.
1176 * \returns callee-allocated decoded representation of CBSP message; NULL on error */
1177struct osmo_cbsp_decoded *osmo_cbsp_decode(void *ctx, struct msgb *in)
1178{
1179 struct osmo_cbsp_decoded *out = talloc_zero(ctx, struct osmo_cbsp_decoded);
1180 const struct cbsp_header *h = msgb_l1(in);
1181 struct tlv_parsed tp[16]; /* max. number of pages in a given CBS message */
1182 unsigned int len;
1183 int rc;
1184
Harald Weltef72155a2019-06-15 23:05:19 +02001185 osmo_cbsp_errstr = NULL;
1186
Harald Welte07958e42019-05-03 09:39:10 +02001187 if (!out)
1188 return NULL;
1189
1190 if (msgb_l1len(in) < sizeof(*h)) {
1191 goto out_err;
1192 }
1193 len = h->len[0] << 16 | h->len[1] << 8 | h->len[2];
1194
1195 /* discard messages where indicated length is more than we have */
1196 if (len > msgb_l2len(in)) {
1197 goto out_err;
1198 }
1199
1200 /* trim any messages with extra payload at the end */
1201 if (len < msgb_l2len(in))
1202 msgb_trim(in, (in->l2h - in->data) + msgb_l2len(in));
1203 out->msg_type = h->msg_type;
1204
1205 rc = tlv_parse2(tp, ARRAY_SIZE(tp), &cbsp_att_tlvdef, msgb_l2(in), msgb_l2len(in), 0, 0);
1206 if (rc < 0) {
1207 goto out_err;
1208 }
1209
1210 switch (h->msg_type) {
1211 case CBSP_MSGT_WRITE_REPLACE:
1212 rc = cbsp_dec_write_repl(&out->u.write_replace, tp, in, out);
1213 break;
1214 case CBSP_MSGT_WRITE_REPLACE_COMPL:
1215 rc = cbsp_dec_write_repl_compl(&out->u.write_replace_compl, tp, in, out);
1216 break;
1217 case CBSP_MSGT_WRITE_REPLACE_FAIL:
1218 rc = cbsp_dec_write_repl_fail(&out->u.write_replace_fail, tp, in, out);
1219 break;
1220 case CBSP_MSGT_KILL:
1221 rc = cbsp_dec_kill(&out->u.kill, tp, in, out);
1222 break;
1223 case CBSP_MSGT_KILL_COMPL:
1224 rc = cbsp_dec_kill_compl(&out->u.kill_compl, tp, in, out);
1225 break;
1226 case CBSP_MSGT_KILL_FAIL:
1227 rc = cbsp_dec_kill_fail(&out->u.kill_fail, tp, in, out);
1228 break;
1229 case CBSP_MSGT_LOAD_QUERY:
1230 rc = cbsp_dec_load_query(&out->u.load_query, tp, in, out);
1231 break;
1232 case CBSP_MSGT_LOAD_QUERY_COMPL:
1233 rc = cbsp_dec_load_query_compl(&out->u.load_query_compl, tp, in, out);
1234 break;
1235 case CBSP_MSGT_LOAD_QUERY_FAIL:
1236 rc = cbsp_dec_load_query_fail(&out->u.load_query_fail, tp, in, out);
1237 break;
1238 case CBSP_MSGT_MSG_STATUS_QUERY:
1239 rc = cbsp_dec_msg_status_query(&out->u.msg_status_query, tp, in, out);
1240 break;
1241 case CBSP_MSGT_MSG_STATUS_QUERY_COMPL:
1242 rc = cbsp_dec_msg_status_query_compl(&out->u.msg_status_query_compl, tp, in, out);
1243 break;
1244 case CBSP_MSGT_MSG_STATUS_QUERY_FAIL:
1245 rc = cbsp_dec_msg_status_query_fail(&out->u.msg_status_query_fail, tp, in, out);
1246 break;
1247 case CBSP_MSGT_RESET:
1248 rc = cbsp_dec_reset(&out->u.reset, tp, in, out);
1249 break;
1250 case CBSP_MSGT_RESET_COMPL:
1251 rc = cbsp_dec_reset_compl(&out->u.reset_compl, tp, in, out);
1252 break;
1253 case CBSP_MSGT_RESET_FAIL:
1254 rc = cbsp_dec_reset_fail(&out->u.reset_fail, tp, in, out);
1255 break;
1256 case CBSP_MSGT_RESTART:
1257 rc = cbsp_dec_restart(&out->u.restart, tp, in, out);
1258 break;
1259 case CBSP_MSGT_FAILURE:
1260 rc = cbsp_dec_failure(&out->u.failure, tp, in, out);
1261 break;
1262 case CBSP_MSGT_ERROR_IND:
1263 rc = cbsp_dec_error_ind(&out->u.error_ind, tp, in, out);
1264 break;
1265 case CBSP_MSGT_KEEP_ALIVE:
1266 rc = cbsp_dec_keep_alive(&out->u.keep_alive, tp, in, out);
1267 break;
1268 case CBSP_MSGT_KEEP_ALIVE_COMPL:
1269 rc = cbsp_dec_keep_alive_compl(&out->u.keep_alive_compl, tp, in, out);
1270 break;
1271 case CBSP_MSGT_SET_DRX:
1272 case CBSP_MSGT_SET_DRX_COMPL:
1273 case CBSP_MSGT_SET_DRX_FAIL:
Harald Weltef72155a2019-06-15 23:05:19 +02001274 osmo_cbsp_errstr = "message type not implemented";
Harald Welte07958e42019-05-03 09:39:10 +02001275 rc = -1;
1276 break;
1277 default:
Harald Weltef72155a2019-06-15 23:05:19 +02001278 osmo_cbsp_errstr = "message type not known in spec";
Harald Welte07958e42019-05-03 09:39:10 +02001279 rc = -1;
1280 break;
1281 }
1282
1283 if (rc < 0) {
1284 goto out_err;
1285 }
1286
1287 return out;
1288
1289out_err:
1290 talloc_free(out);
1291 return NULL;
1292}
1293
1294/* initialization of 'decoded' structure of given message type */
1295void osmo_cbsp_init_struct(struct osmo_cbsp_decoded *cbsp, enum cbsp_msg_type msg_type)
1296{
1297 memset(cbsp, 0, sizeof(*cbsp));
1298 cbsp->msg_type = msg_type;
1299
1300 switch (msg_type) {
1301 case CBSP_MSGT_WRITE_REPLACE:
1302 INIT_LLIST_HEAD(&cbsp->u.write_replace.cell_list.list);
1303 break;
1304 case CBSP_MSGT_WRITE_REPLACE_COMPL:
1305 INIT_LLIST_HEAD(&cbsp->u.write_replace_compl.num_compl_list.list);
1306 INIT_LLIST_HEAD(&cbsp->u.write_replace_compl.cell_list.list);
1307 break;
1308 case CBSP_MSGT_WRITE_REPLACE_FAIL:
1309 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.fail_list);
1310 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.num_compl_list.list);
1311 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.cell_list.list);
1312 break;
1313 case CBSP_MSGT_KILL:
1314 INIT_LLIST_HEAD(&cbsp->u.kill.cell_list.list);
1315 break;
1316 case CBSP_MSGT_KILL_COMPL:
1317 INIT_LLIST_HEAD(&cbsp->u.kill_compl.num_compl_list.list);
1318 INIT_LLIST_HEAD(&cbsp->u.kill_compl.cell_list.list);
1319 break;
1320 case CBSP_MSGT_KILL_FAIL:
1321 INIT_LLIST_HEAD(&cbsp->u.kill_fail.fail_list);
1322 INIT_LLIST_HEAD(&cbsp->u.kill_fail.num_compl_list.list);
1323 INIT_LLIST_HEAD(&cbsp->u.kill_fail.cell_list.list);
1324 break;
1325 case CBSP_MSGT_LOAD_QUERY:
1326 INIT_LLIST_HEAD(&cbsp->u.load_query.cell_list.list);
1327 break;
1328 case CBSP_MSGT_LOAD_QUERY_COMPL:
1329 INIT_LLIST_HEAD(&cbsp->u.load_query_compl.loading_list.list);
1330 break;
1331 case CBSP_MSGT_LOAD_QUERY_FAIL:
1332 INIT_LLIST_HEAD(&cbsp->u.load_query_fail.fail_list);
1333 break;
1334 case CBSP_MSGT_MSG_STATUS_QUERY:
1335 INIT_LLIST_HEAD(&cbsp->u.msg_status_query.cell_list.list);
1336 break;
1337 case CBSP_MSGT_MSG_STATUS_QUERY_COMPL:
1338 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_compl.num_compl_list.list);
1339 break;
1340 case CBSP_MSGT_MSG_STATUS_QUERY_FAIL:
1341 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_fail.fail_list);
1342 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_fail.num_compl_list.list);
1343 break;
1344 case CBSP_MSGT_RESET:
1345 INIT_LLIST_HEAD(&cbsp->u.reset.cell_list.list);
1346 break;
1347 case CBSP_MSGT_RESET_COMPL:
1348 INIT_LLIST_HEAD(&cbsp->u.reset_compl.cell_list.list);
1349 break;
1350 case CBSP_MSGT_RESET_FAIL:
1351 INIT_LLIST_HEAD(&cbsp->u.reset_fail.fail_list);
1352 INIT_LLIST_HEAD(&cbsp->u.reset_fail.cell_list.list);
1353 break;
1354 case CBSP_MSGT_RESTART:
1355 INIT_LLIST_HEAD(&cbsp->u.restart.cell_list.list);
1356 break;
1357 case CBSP_MSGT_FAILURE:
1358 INIT_LLIST_HEAD(&cbsp->u.failure.fail_list);
1359 break;
1360 default:
1361 break;
1362 }
1363}
1364
1365/*! Dynamically allocate and initialize decoded CBSP structure.
1366 * \param[in] ctx talloc context from which to allocate
1367 * \param[in] msg_type CBSP message type for which to initialize result
1368 * \returns allocated + initialized decoded CBSP structure; NULL on talloc failure */
1369struct osmo_cbsp_decoded *osmo_cbsp_decoded_alloc(void *ctx, enum cbsp_msg_type msg_type)
1370{
1371 struct osmo_cbsp_decoded *cbsp = talloc_zero(ctx, struct osmo_cbsp_decoded);
1372 if (!cbsp)
1373 return NULL;
1374 osmo_cbsp_init_struct(cbsp, msg_type);
1375 return cbsp;
1376}
1377
1378/***********************************************************************
1379 * Message Reception
1380 ***********************************************************************/
1381
1382#ifdef HAVE_SYS_SOCKET_H
1383#include <sys/socket.h>
1384
1385/*! Read one CBSP message from socket fd or store part if still not fully received.
1386 * \param[in] ctx talloc context from which to allocate new msgb.
1387 * \param[in] fd The fd for the socket to read from.
1388 * \param[out] rmsg internally allocated msgb containing a fully received CBSP message.
1389 * \param[inout] tmp_msg internally allocated msgb caching data for not yet fully received message.
1390 *
1391 * Function is designed just like ipa_msg_recv_buffered()
1392 */
1393int osmo_cbsp_recv_buffered(void *ctx, int fd, struct msgb **rmsg, struct msgb **tmp_msg)
1394{
1395 struct msgb *msg = tmp_msg ? *tmp_msg : NULL;
1396 struct cbsp_header *h;
1397 int len, rc;
1398 int needed;
1399
1400 if (!msg) {
1401 msg = osmo_cbsp_msgb_alloc(ctx, __func__);
Harald Weltec30d8be2019-07-21 07:51:33 +02001402 if (!msg)
Harald Welte07958e42019-05-03 09:39:10 +02001403 return -ENOMEM;
Harald Welte07958e42019-05-03 09:39:10 +02001404 msg->l1h = msg->tail;
1405 }
1406
1407 if (msg->l2h == NULL) {
1408 /* first read the [missing part of the] header */
1409 needed = sizeof(*h) - msg->len;
1410 rc = recv(fd, msg->tail, needed, 0);
1411 if (rc == 0)
1412 goto discard_msg;
1413 else if (rc < 0) {
1414 if (errno == EAGAIN || errno == EINTR)
1415 rc = 0;
1416 else {
1417 rc = -errno;
1418 goto discard_msg;
1419 }
1420 }
1421 msgb_put(msg, rc);
1422 if (rc < needed) {
1423 if (msg->len == 0) {
1424 rc = -EAGAIN;
1425 goto discard_msg;
1426 }
1427
1428 if (!tmp_msg) {
1429 rc = -EIO;
1430 goto discard_msg;
1431 }
1432 *tmp_msg = msg;
1433 return -EAGAIN;
1434 }
1435 msg->l2h = msg->tail;
1436 }
1437
1438 h = (struct cbsp_header *) msg->data;
1439 /* then read the length as specified in the header */
1440 len = h->len[0] << 16 | h->len[1] << 8 | h->len[2];
1441
1442 needed = len - msgb_l2len(msg);
1443 if (needed > 0) {
Pau Espin Pedrol8f577fb2022-06-29 17:55:54 +02001444 if (needed > msgb_tailroom(msg)) {
1445 rc = -ENOMEM;
1446 goto discard_msg;
1447 }
Harald Welte07958e42019-05-03 09:39:10 +02001448 rc = recv(fd, msg->tail, needed, 0);
1449 if (rc == 0)
1450 goto discard_msg;
1451 else if (rc < 0) {
1452 if (errno == EAGAIN || errno == EINTR)
1453 rc = 0;
1454 else {
1455 rc = -errno;
1456 goto discard_msg;
1457 }
1458 }
1459 msgb_put(msg, rc);
1460 /* still not all of payload received? */
1461 if (rc < needed) {
1462 if (!tmp_msg) {
1463 rc = -EIO;
1464 goto discard_msg;
1465 }
1466 *tmp_msg = msg;
1467 return -EAGAIN;
1468 }
1469 }
1470 /* else: complete message received */
Harald Weltefdd71c82021-01-02 20:50:47 +01001471 rc = msgb_length(msg);
Harald Welte07958e42019-05-03 09:39:10 +02001472 if (tmp_msg)
1473 *tmp_msg = NULL;
1474 *rmsg = msg;
1475 return rc;
1476
1477discard_msg:
Harald Welte07958e42019-05-03 09:39:10 +02001478 if (tmp_msg)
1479 *tmp_msg = NULL;
1480 msgb_free(msg);
1481 return rc;
1482}
1483
Pau Espin Pedrol0cdd0ab2022-06-09 18:13:39 +02001484/*! value_string[] for enum osmo_cbsp_cause. */
1485const struct value_string osmo_cbsp_cause_names[] = {
1486 { OSMO_CBSP_CAUSE_PARAM_NOT_RECOGNISED, "Parameter-not-recognised" },
1487 { OSMO_CBSP_CAUSE_PARAM_VALUE_INVALID, "Parameter-value-invalid" },
1488 { OSMO_CBSP_CAUSE_MSG_REF_NOT_IDENTIFIED, "Message-reference-not-identified" },
1489 { OSMO_CBSP_CAUSE_CELL_ID_NOT_VALID, "Cell-identity-not-valid" },
1490 { OSMO_CBSP_CAUSE_UNRECOGNISED_MESSAGE, "Unrecognised-message" },
1491 { OSMO_CBSP_CAUSE_MISSING_MANDATORY_ELEMENT, "Missing-mandatory-element" },
1492 { OSMO_CBSP_CAUSE_BSC_CAPACITY_EXCEEDED, "BSC-capacity-exceeded" },
1493 { OSMO_CBSP_CAUSE_CELL_MEMORY_EXCEEDED, "Cell-memory-exceeded" },
1494 { OSMO_CBSP_CAUSE_BSC_MEMORY_EXCEEDED, "BSC-memory-exceeded" },
1495 { OSMO_CBSP_CAUSE_CELL_BROADCAST_NOT_SUPPORTED, "Cell-broadcast-not-supported" },
1496 { OSMO_CBSP_CAUSE_CELL_BROADCAST_NOT_OPERATIONAL, "Cell-broadcast-not-operational" },
1497 { OSMO_CBSP_CAUSE_INCOMPATIBLE_DRX_PARAM, "Incompatible-DRX-parameter:"},
1498 { OSMO_CBSP_CAUSE_EXT_CHAN_NOT_SUPPORTED, "Extended-channel-not-supported"},
1499 { OSMO_CBSP_CAUSE_MSG_REF_ALREADY_USED, "Message-reference-already-used"},
1500 { OSMO_CBSP_CAUSE_UNSPECIFIED_ERROR, "Unspecified-error"},
1501 { OSMO_CBSP_CAUSE_LAI_OR_LAC_NOT_VALID, "LAI-or-LAC-not-valid"},
1502 { 0, NULL }
1503};
1504
Harald Welte07958e42019-05-03 09:39:10 +02001505#endif /* HAVE_SYS_SOCKET_H */