blob: 2c7a7b723dcf70ac45f77f948ceeb10c88baea67 [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.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23
24#include "config.h"
25
26#include <errno.h>
27
28#include <sys/types.h>
29
30#include <osmocom/core/linuxlist.h>
31#include <osmocom/core/msgb.h>
32
33#include <osmocom/gsm/tlv.h>
34#include <osmocom/gsm/cbsp.h>
35#include <osmocom/gsm/gsm0808_utils.h>
36
Harald Weltef72155a2019-06-15 23:05:19 +020037const __thread char *osmo_cbsp_errstr;
38
Harald Welte07958e42019-05-03 09:39:10 +020039struct msgb *osmo_cbsp_msgb_alloc(void *ctx, const char *name)
40{
41 /* make the messages rather large as the cell lists can be long! */
42 return msgb_alloc_headroom_c(ctx, 65535, 16, name);
43}
44
45/***********************************************************************
46 * IE Encoding
47 ***********************************************************************/
48
49/* 8.2.6 Cell List */
50static void msgb_put_cbsp_cell_list(struct msgb *msg, const struct osmo_cbsp_cell_list *cl)
51{
52 const struct osmo_cbsp_cell_ent *ent;
53 uint8_t *lenptr;
54
55 /* put tag; reserve space for length; put discriminator */
56 msgb_put_u8(msg, CBSP_IEI_CELL_LIST);
57 lenptr = msgb_put(msg, sizeof(uint16_t));
58 msgb_put_u8(msg, cl->id_discr);
59 /* put list elements */
60 llist_for_each_entry(ent, &cl->list, list) {
61 gsm0808_msgb_put_cell_id_u(msg, cl->id_discr, &ent->cell_id);
62 }
63 /* update IE length */
64 osmo_store16be(msg->tail - (lenptr+2), lenptr);
65}
66
67/* 8.2.11 Failure List (discriminator per entry) */
68static void msgb_put_cbsp_fail_list(struct msgb *msg, const struct llist_head *fl)
69{
70 const struct osmo_cbsp_fail_ent *ent;
71 uint8_t *lenptr;
72
73 /* put tag; reserve space for length; put discriminator */
74 msgb_put_u8(msg, CBSP_IEI_FAILURE_LIST);
75 lenptr = msgb_put(msg, sizeof(uint16_t));
76 /* put list elements */
77 llist_for_each_entry(ent, fl, list) {
78 msgb_put_u8(msg, ent->id_discr);
79 gsm0808_msgb_put_cell_id_u(msg, ent->id_discr, &ent->cell_id);
80 msgb_put_u8(msg, ent->cause);
81 }
82 /* update IE length */
83 osmo_store16be(msg->tail - (lenptr+2), lenptr);
84}
85
86/* 8.2.12 Radio Resource Loading List */
87static void msgb_put_cbsp_loading_list(struct msgb *msg, const struct osmo_cbsp_loading_list *ll)
88{
89 const struct osmo_cbsp_loading_ent *ent;
90 uint8_t *lenptr;
91
92 /* put tag; reserve space for length; put discriminator */
93 msgb_put_u8(msg, CBSP_IEI_RR_LOADING_LIST);
94 lenptr = msgb_put(msg, sizeof(uint16_t));
95 msgb_put_u8(msg, ll->id_discr);
96 /* put list elements */
97 llist_for_each_entry(ent, &ll->list, list) {
98 gsm0808_msgb_put_cell_id_u(msg, ll->id_discr, &ent->cell_id);
99 msgb_put_u8(msg, ent->load[0]);
100 msgb_put_u8(msg, ent->load[1]);
101 }
102 /* update IE length */
103 osmo_store16be(msg->tail - (lenptr+2), lenptr);
104}
105
106/* 8.2.10 Completed List */
107static void msgb_put_cbsp_num_compl_list(struct msgb *msg, const struct osmo_cbsp_num_compl_list *cl)
108{
109 const struct osmo_cbsp_num_compl_ent *ent;
110 uint8_t *lenptr;
111
112 /* put tag; reserve space for length; put discriminator */
113 msgb_put_u8(msg, CBSP_IEI_NUM_BCAST_COMPL_LIST);
114 lenptr = msgb_put(msg, sizeof(uint16_t));
115 msgb_put_u8(msg, cl->id_discr);
116 /* put list elements */
117 llist_for_each_entry(ent, &cl->list, list) {
118 gsm0808_msgb_put_cell_id_u(msg, cl->id_discr, &ent->cell_id);
119 msgb_put_u16(msg, ent->num_compl);
120 msgb_put_u8(msg, ent->num_bcast_info);
121 }
122 /* update IE length */
123 osmo_store16be(msg->tail - (lenptr+2), lenptr);
124}
125
126static int encode_wperiod(uint32_t secs)
127{
128 if (secs == 0xffffffff)
129 return 0; /* infinite */
130 if (secs <= 10)
131 return secs;
132 if (secs <= 30)
133 return (secs-10)/2;
134 if (secs <= 120)
135 return (secs-30)/5;
136 if (secs <= 600)
137 return (secs-120)/10;
138 if (secs <= 60*60)
139 return (secs-600)/30;
Harald Weltef72155a2019-06-15 23:05:19 +0200140 osmo_cbsp_errstr = "warning period out of range";
Harald Welte07958e42019-05-03 09:39:10 +0200141 return -1;
142}
143
144/***********************************************************************
145 * Message Encoding
146 ***********************************************************************/
147
148/* 8.1.3.1 WRITE REPLACE */
149static int cbsp_enc_write_repl(struct msgb *msg, const struct osmo_cbsp_write_replace *in)
150{
151 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
152 msgb_tv16_put(msg, CBSP_IEI_NEW_SERIAL_NR, in->new_serial_nr);
153 if (in->old_serial_nr)
154 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, *in->old_serial_nr);
155 msgb_put_cbsp_cell_list(msg, &in->cell_list);
156 if (in->is_cbs) {
157 int num_of_pages = llist_count(&in->u.cbs.msg_content);
158 struct osmo_cbsp_content *ce;
Harald Weltef72155a2019-06-15 23:05:19 +0200159 if (num_of_pages == 0 || num_of_pages > 15) {
160 osmo_cbsp_errstr = "invalid number of pages";
Harald Welte07958e42019-05-03 09:39:10 +0200161 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200162 }
Harald Welte07958e42019-05-03 09:39:10 +0200163 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->u.cbs.channel_ind);
164 msgb_tv_put(msg, CBSP_IEI_CATEGORY, in->u.cbs.category);
165 msgb_tv16_put(msg, CBSP_IEI_REP_PERIOD, in->u.cbs.rep_period);
166 msgb_tv16_put(msg, CBSP_IEI_NUM_BCAST_REQ, in->u.cbs.num_bcast_req);
167 msgb_tv_put(msg, CBSP_IEI_NUM_OF_PAGES, num_of_pages);
168 msgb_tv_put(msg, CBSP_IEI_DCS, in->u.cbs.dcs);
169 llist_for_each_entry(ce, &in->u.cbs.msg_content, list) {
170 uint8_t *out;
171 /* cannot use msgb_tlv_put() as 'len' isn't actually the length of
172 * the data field */
173 msgb_put_u8(msg, CBSP_IEI_MSG_CONTENT);
174 msgb_put_u8(msg, ce->user_len);
175 out = msgb_put(msg, sizeof(ce->data));
176 memcpy(out, ce->data, sizeof(ce->data));
177 }
178 } else {
179 int wperiod = encode_wperiod(in->u.emergency.warning_period);
Harald Welte64b94072021-01-02 20:46:51 +0100180 uint8_t *cur;
Harald Welte07958e42019-05-03 09:39:10 +0200181 if (wperiod < 0)
182 return -EINVAL;
183 msgb_tv_put(msg, CBSP_IEI_EMERG_IND, in->u.emergency.indicator);
184 msgb_tv16_put(msg, CBSP_IEI_WARN_TYPE, in->u.emergency.warning_type);
Harald Welte64b94072021-01-02 20:46:51 +0100185 /* Tag + fixed length value! */
186 msgb_put_u8(msg, CBSP_IEI_WARN_SEC_INFO);
187 cur = msgb_put(msg, sizeof(in->u.emergency.warning_sec_info));
188 memcpy(cur, in->u.emergency.warning_sec_info, sizeof(in->u.emergency.warning_sec_info));
Harald Welte07958e42019-05-03 09:39:10 +0200189 msgb_tv_put(msg, CBSP_IEI_WARNING_PERIOD, wperiod);
190 }
191 return 0;
192}
193
194/* 8.1.3.2 WRITE REPLACE COMPLETE*/
195static int cbsp_enc_write_repl_compl(struct msgb *msg, const struct osmo_cbsp_write_replace_complete *in)
196{
197 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
198 msgb_tv16_put(msg, CBSP_IEI_NEW_SERIAL_NR, in->new_serial_nr);
199 if (in->old_serial_nr)
200 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, *in->old_serial_nr);
201
202 if (!llist_empty(&in->num_compl_list.list))
203 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
204 if (!llist_empty(&in->cell_list.list))
205 msgb_put_cbsp_cell_list(msg, &in->cell_list);
206 if (in->channel_ind)
207 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
208 return 0;
209}
210
211/* 8.1.3.3 WRITE REPLACE FAILURE */
212static int cbsp_enc_write_repl_fail(struct msgb *msg, const struct osmo_cbsp_write_replace_failure *in)
213{
214 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
215 msgb_tv16_put(msg, CBSP_IEI_NEW_SERIAL_NR, in->new_serial_nr);
216 if (in->old_serial_nr)
217 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, *in->old_serial_nr);
218
219 msgb_put_cbsp_fail_list(msg, &in->fail_list);
220 if (!llist_empty(&in->num_compl_list.list))
221 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
222 if (!llist_empty(&in->cell_list.list))
223 msgb_put_cbsp_cell_list(msg, &in->cell_list);
224 if (in->channel_ind)
225 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
226 return 0;
227}
228
229/* 8.1.3.4 KILL */
230static int cbsp_enc_kill(struct msgb *msg, const struct osmo_cbsp_kill *in)
231{
232 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
233 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
234 msgb_put_cbsp_cell_list(msg, &in->cell_list);
235 if (in->channel_ind)
236 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
237 return 0;
238}
239
240/* 8.1.3.5 KILL COMPLETE */
241static int cbsp_enc_kill_compl(struct msgb *msg, const struct osmo_cbsp_kill_complete *in)
242{
243 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
244 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
245 if (!llist_empty(&in->num_compl_list.list))
246 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
247 if (!llist_empty(&in->cell_list.list))
248 msgb_put_cbsp_cell_list(msg, &in->cell_list);
249 if (in->channel_ind)
250 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
251 return 0;
252}
253
254/* 8.1.3.6 KILL FAILURE */
255static int cbsp_enc_kill_fail(struct msgb *msg, const struct osmo_cbsp_kill_failure *in)
256{
257 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
258 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
259 msgb_put_cbsp_fail_list(msg, &in->fail_list);
260 if (!llist_empty(&in->num_compl_list.list))
261 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
262 if (!llist_empty(&in->cell_list.list))
263 msgb_put_cbsp_cell_list(msg, &in->cell_list);
264 if (in->channel_ind)
265 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
266 return 0;
267}
268
269/* 8.1.3.7 LOAD QUERY */
270static int cbsp_enc_load_query(struct msgb *msg, const struct osmo_cbsp_load_query *in)
271{
272 msgb_put_cbsp_cell_list(msg, &in->cell_list);
273 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
274 return 0;
275}
276
277/* 8.1.3.8 LOAD QUERY COMPLETE */
278static int cbsp_enc_load_query_compl(struct msgb *msg, const struct osmo_cbsp_load_query_complete *in)
279{
280 msgb_put_cbsp_loading_list(msg, &in->loading_list);
281 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
282 return 0;
283}
284
285/* 8.1.3.9 LOAD QUERY FAILURE */
286static int cbsp_enc_load_query_fail(struct msgb *msg, const struct osmo_cbsp_load_query_failure *in)
287{
288 msgb_put_cbsp_fail_list(msg, &in->fail_list);
289 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
290 if (!llist_empty(&in->loading_list.list))
291 msgb_put_cbsp_loading_list(msg, &in->loading_list);
292 return 0;
293}
294
295/* 8.1.3.10 STATUS QUERY */
296static int cbsp_enc_msg_status_query(struct msgb *msg, const struct osmo_cbsp_msg_status_query *in)
297{
298 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
299 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
300 msgb_put_cbsp_cell_list(msg, &in->cell_list);
301 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
302 return 0;
303}
304
305/* 8.1.3.11 STATUS QUERY COMPLETE */
306static int cbsp_enc_msg_status_query_compl(struct msgb *msg,
307 const struct osmo_cbsp_msg_status_query_complete *in)
308{
309 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
310 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
311 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
312 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
313 return 0;
314}
315
316/* 8.1.3.12 STATUS QUERY FAILURE */
317static int cbsp_enc_msg_status_query_fail(struct msgb *msg,
318 const struct osmo_cbsp_msg_status_query_failure *in)
319{
320 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
321 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
322 msgb_put_cbsp_fail_list(msg, &in->fail_list);
323 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
324 if (!llist_empty(&in->num_compl_list.list))
325 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
326 return 0;
327}
328
329/* 8.1.3.16 RESET */
330static int cbsp_enc_reset(struct msgb *msg, const struct osmo_cbsp_reset *in)
331{
332 msgb_put_cbsp_cell_list(msg, &in->cell_list);
333 return 0;
334}
335
336/* 8.1.3.17 RESET COMPLETE */
337static int cbsp_enc_reset_compl(struct msgb *msg, const struct osmo_cbsp_reset_complete *in)
338{
339 msgb_put_cbsp_cell_list(msg, &in->cell_list);
340 return 0;
341}
342
343/* 8.1.3.18 RESET FAILURE */
344static int cbsp_enc_reset_fail(struct msgb *msg, const struct osmo_cbsp_reset_failure *in)
345{
346 msgb_put_cbsp_fail_list(msg, &in->fail_list);
347 if (!llist_empty(&in->cell_list.list))
348 msgb_put_cbsp_cell_list(msg, &in->cell_list);
349 return 0;
350}
351
352/* 8.1.3.18a KEEP ALIVE */
353static int cbsp_enc_keep_alive(struct msgb *msg, const struct osmo_cbsp_keep_alive *in)
354{
355 msgb_tv_put(msg, CBSP_IEI_KEEP_ALIVE_REP_PERIOD, in->repetition_period);
356 return 0;
357}
358
359/* 8.1.3.18b KEEP ALIVE COMPLETE */
360static int cbsp_enc_keep_alive_compl(struct msgb *msg, const struct osmo_cbsp_keep_alive_complete *in)
361{
362 return 0;
363}
364
365/* 8.1.3.19 RESTART */
366static int cbsp_enc_restart(struct msgb *msg, const struct osmo_cbsp_restart *in)
367{
368 msgb_put_cbsp_cell_list(msg, &in->cell_list);
369 msgb_tv_put(msg, CBSP_IEI_BCAST_MSG_TYPE, in->bcast_msg_type);
370 msgb_tv_put(msg, CBSP_IEI_RECOVERY_IND, in->recovery_ind);
371 return 0;
372}
373
374/* 8.1.3.20 FAILURE */
375static int cbsp_enc_failure(struct msgb *msg, const struct osmo_cbsp_failure *in)
376{
377 msgb_put_cbsp_fail_list(msg, &in->fail_list);
378 msgb_tv_put(msg, CBSP_IEI_BCAST_MSG_TYPE, in->bcast_msg_type);
379 return 0;
380}
381
382/* 8.1.3.21 ERROR INDICATION */
383static int cbsp_enc_error_ind(struct msgb *msg, const struct osmo_cbsp_error_ind *in)
384{
385 msgb_tv_put(msg, CBSP_IEI_CAUSE, in->cause);
386 if (in->msg_id)
387 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, *in->msg_id);
388 if (in->new_serial_nr)
389 msgb_tv16_put(msg, CBSP_IEI_NEW_SERIAL_NR, *in->new_serial_nr);
390 if (in->old_serial_nr)
391 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, *in->old_serial_nr);
392 if (in->channel_ind)
393 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
394 return 0;
395}
396
397/*! Encode a CBSP message from the decoded/parsed structure representation to binary PDU.
398 * \param[in] ctx talloc context from which to allocate returned msgb.
399 * \param[in] in decoded CBSP message which is to be encoded. Ownership not transferred.
400 * \return callee-allocated message buffer containing binary CBSP PDU; NULL on error */
401struct msgb *osmo_cbsp_encode(void *ctx, const struct osmo_cbsp_decoded *in)
402{
403 struct msgb *msg = osmo_cbsp_msgb_alloc(ctx, __func__);
404 unsigned int len;
405 int rc;
406
Harald Weltef72155a2019-06-15 23:05:19 +0200407 osmo_cbsp_errstr = NULL;
408
Harald Welte07958e42019-05-03 09:39:10 +0200409 if (!msg)
410 return NULL;
411
412 switch (in->msg_type) {
413 case CBSP_MSGT_WRITE_REPLACE:
414 rc = cbsp_enc_write_repl(msg, &in->u.write_replace);
415 break;
416 case CBSP_MSGT_WRITE_REPLACE_COMPL:
417 rc = cbsp_enc_write_repl_compl(msg, &in->u.write_replace_compl);
418 break;
419 case CBSP_MSGT_WRITE_REPLACE_FAIL:
420 rc = cbsp_enc_write_repl_fail(msg, &in->u.write_replace_fail);
421 break;
422 case CBSP_MSGT_KILL:
423 rc = cbsp_enc_kill(msg, &in->u.kill);
424 break;
425 case CBSP_MSGT_KILL_COMPL:
426 rc = cbsp_enc_kill_compl(msg, &in->u.kill_compl);
427 break;
428 case CBSP_MSGT_KILL_FAIL:
429 rc = cbsp_enc_kill_fail(msg, &in->u.kill_fail);
430 break;
431 case CBSP_MSGT_LOAD_QUERY:
432 rc = cbsp_enc_load_query(msg, &in->u.load_query);
433 break;
434 case CBSP_MSGT_LOAD_QUERY_COMPL:
435 rc = cbsp_enc_load_query_compl(msg, &in->u.load_query_compl);
436 break;
437 case CBSP_MSGT_LOAD_QUERY_FAIL:
438 rc = cbsp_enc_load_query_fail(msg, &in->u.load_query_fail);
439 break;
440 case CBSP_MSGT_MSG_STATUS_QUERY:
441 rc = cbsp_enc_msg_status_query(msg, &in->u.msg_status_query);
442 break;
443 case CBSP_MSGT_MSG_STATUS_QUERY_COMPL:
444 rc = cbsp_enc_msg_status_query_compl(msg, &in->u.msg_status_query_compl);
445 break;
446 case CBSP_MSGT_MSG_STATUS_QUERY_FAIL:
447 rc = cbsp_enc_msg_status_query_fail(msg, &in->u.msg_status_query_fail);
448 break;
449 case CBSP_MSGT_RESET:
450 rc = cbsp_enc_reset(msg, &in->u.reset);
451 break;
452 case CBSP_MSGT_RESET_COMPL:
453 rc = cbsp_enc_reset_compl(msg, &in->u.reset_compl);
454 break;
455 case CBSP_MSGT_RESET_FAIL:
456 rc = cbsp_enc_reset_fail(msg, &in->u.reset_fail);
457 break;
458 case CBSP_MSGT_RESTART:
459 rc = cbsp_enc_restart(msg, &in->u.restart);
460 break;
461 case CBSP_MSGT_FAILURE:
462 rc = cbsp_enc_failure(msg, &in->u.failure);
463 break;
464 case CBSP_MSGT_ERROR_IND:
465 rc = cbsp_enc_error_ind(msg, &in->u.error_ind);
466 break;
467 case CBSP_MSGT_KEEP_ALIVE:
468 rc = cbsp_enc_keep_alive(msg, &in->u.keep_alive);
469 break;
470 case CBSP_MSGT_KEEP_ALIVE_COMPL:
471 rc = cbsp_enc_keep_alive_compl(msg, &in->u.keep_alive_compl);
472 break;
473 case CBSP_MSGT_SET_DRX:
474 case CBSP_MSGT_SET_DRX_COMPL:
475 case CBSP_MSGT_SET_DRX_FAIL:
Harald Weltef72155a2019-06-15 23:05:19 +0200476 osmo_cbsp_errstr = "message type not implemented";
Harald Welte07958e42019-05-03 09:39:10 +0200477 rc = -1;
478 break;
479 default:
Harald Weltef72155a2019-06-15 23:05:19 +0200480 osmo_cbsp_errstr = "message type not known in spec";
Harald Welte07958e42019-05-03 09:39:10 +0200481 rc = -1;
482 break;
483 }
484
485 if (rc < 0) {
486 msgb_free(msg);
487 return NULL;
488 }
489
490 /* push header in front */
491 len = msgb_length(msg);
492 msgb_push_u8(msg, len & 0xff);
493 msgb_push_u8(msg, (len >> 8) & 0xff);
494 msgb_push_u8(msg, (len >> 16) & 0xff);
495 msgb_push_u8(msg, in->msg_type);
496
497 return msg;
498}
499
500/***********************************************************************
501 * IE Decoding
502 ***********************************************************************/
503
504/* 8.2.6 Cell List */
505static int cbsp_decode_cell_list(struct osmo_cbsp_cell_list *cl, void *ctx,
506 const uint8_t *buf, unsigned int len)
507{
508 const uint8_t *cur = buf;
509 int rc;
510
511 cl->id_discr = *cur++;
512
513 while (cur < buf + len) {
514 struct osmo_cbsp_cell_ent *ent = talloc_zero(ctx, struct osmo_cbsp_cell_ent);
515 unsigned int len_remain = len - (cur - buf);
516 OSMO_ASSERT(ent);
517 rc = gsm0808_decode_cell_id_u(&ent->cell_id, cl->id_discr, cur, len_remain);
Harald Weltef72155a2019-06-15 23:05:19 +0200518 if (rc < 0) {
519 osmo_cbsp_errstr = "cell list: error decoding cell_id_union";
Harald Welte07958e42019-05-03 09:39:10 +0200520 return rc;
Harald Weltef72155a2019-06-15 23:05:19 +0200521 }
Harald Weltef2210032019-08-31 21:25:05 +0200522 cur += gsm0808_cell_id_size(cl->id_discr);
Harald Welte07958e42019-05-03 09:39:10 +0200523 llist_add_tail(&ent->list, &cl->list);
524 }
525 return 0;
526}
527
528/* 8.2.11 Failure List (discriminator per entry) */
529static int cbsp_decode_fail_list(struct llist_head *fl, void *ctx,
530 const uint8_t *buf, unsigned int len)
531{
532 const uint8_t *cur = buf;
533 int rc;
534
535 while (cur < buf + len) {
536 struct osmo_cbsp_fail_ent *ent = talloc_zero(ctx, struct osmo_cbsp_fail_ent);
537 unsigned int len_remain = len - (cur - buf);
538 OSMO_ASSERT(ent);
539 ent->id_discr = cur[0];
540 rc = gsm0808_decode_cell_id_u(&ent->cell_id, ent->id_discr, cur+1, len_remain-1);
Harald Weltef72155a2019-06-15 23:05:19 +0200541 if (rc < 0) {
542 osmo_cbsp_errstr = "fail list: error decoding cell_id_union";
Harald Welte07958e42019-05-03 09:39:10 +0200543 return rc;
Harald Weltef72155a2019-06-15 23:05:19 +0200544 }
Harald Weltef2210032019-08-31 21:25:05 +0200545 cur += gsm0808_cell_id_size(ent->id_discr);
Harald Welte07958e42019-05-03 09:39:10 +0200546 ent->cause = *cur++;
547 llist_add_tail(&ent->list, fl);
548 }
549 return 0;
550}
551
552/* 8.2.12 Radio Resource Loading List */
553static int cbsp_decode_loading_list(struct osmo_cbsp_loading_list *ll, void *ctx,
554 const uint8_t *buf, unsigned int len)
555{
556 const uint8_t *cur = buf;
557 int rc;
558
559 ll->id_discr = *cur++;
560 while (cur < buf + len) {
561 struct osmo_cbsp_loading_ent *ent = talloc_zero(ctx, struct osmo_cbsp_loading_ent);
562 unsigned int len_remain = len - (cur - buf);
563 OSMO_ASSERT(ent);
564 rc = gsm0808_decode_cell_id_u(&ent->cell_id, ll->id_discr, cur, len_remain);
Harald Weltef72155a2019-06-15 23:05:19 +0200565 if (rc < 0) {
566 osmo_cbsp_errstr = "load list: error decoding cell_id_union";
Harald Welte07958e42019-05-03 09:39:10 +0200567 return rc;
Harald Weltef72155a2019-06-15 23:05:19 +0200568 }
Harald Weltef2210032019-08-31 21:25:05 +0200569 cur += gsm0808_cell_id_size(ll->id_discr);
Harald Welte07958e42019-05-03 09:39:10 +0200570 if (cur + 2 > buf + len) {
571 talloc_free(ent);
Harald Weltef72155a2019-06-15 23:05:19 +0200572 osmo_cbsp_errstr = "load list: truncated IE";
Harald Welte07958e42019-05-03 09:39:10 +0200573 return -EINVAL;
574 }
575 ent->load[0] = *cur++;
576 ent->load[1] = *cur++;
577 llist_add_tail(&ent->list, &ll->list);
578 }
579 return 0;
580}
581
582/* 8.2.10 Completed List */
583static int cbsp_decode_num_compl_list(struct osmo_cbsp_num_compl_list *cl, void *ctx,
584 const uint8_t *buf, unsigned int len)
585{
586 const uint8_t *cur = buf;
587 int rc;
588
589 cl->id_discr = *cur++;
590 while (cur < buf + len) {
591 struct osmo_cbsp_num_compl_ent *ent = talloc_zero(ctx, struct osmo_cbsp_num_compl_ent);
592 unsigned int len_remain = len - (cur - buf);
593 OSMO_ASSERT(ent);
594 rc = gsm0808_decode_cell_id_u(&ent->cell_id, cl->id_discr, cur, len_remain);
Harald Weltef72155a2019-06-15 23:05:19 +0200595 if (rc < 0) {
596 osmo_cbsp_errstr = "completed list: error decoding cell_id_union";
Harald Welte07958e42019-05-03 09:39:10 +0200597 return rc;
Harald Weltef72155a2019-06-15 23:05:19 +0200598 }
Harald Weltef2210032019-08-31 21:25:05 +0200599 cur += gsm0808_cell_id_size(cl->id_discr);
Harald Welte07958e42019-05-03 09:39:10 +0200600 if (cur + 3 > buf + len) {
601 talloc_free(ent);
Harald Weltef72155a2019-06-15 23:05:19 +0200602 osmo_cbsp_errstr = "completed list: truncated IE";
Harald Welte07958e42019-05-03 09:39:10 +0200603 return -EINVAL;
604 }
605 ent->num_compl = osmo_load16be(cur); cur += 2;
606 ent->num_bcast_info = *cur++;
607 llist_add_tail(&ent->list, &cl->list);
608 }
609 return 0;
610}
611
612/* 8.2.25 */
613static uint32_t decode_wperiod(uint8_t in)
614{
615 if (in == 0x00)
616 return 0xffffffff; /* infinite */
617 if (in <= 10)
618 return in;
619 if (in <= 20)
620 return 10 + (in - 10)*2;
621 if (in <= 38)
622 return 30 + (in - 20)*5;
623 if (in <= 86)
624 return 120 + (in - 38)*10;
625 if (in <= 186)
626 return 600 + (in - 86)*30;
627 else
628 return 0;
629}
630
631
632/***********************************************************************
633 * Message Decoding
634 ***********************************************************************/
635
636/* 8.1.3.1 WRITE REPLACE */
637static int cbsp_dec_write_repl(struct osmo_cbsp_write_replace *out, const struct tlv_parsed *tp,
638 struct msgb *in, void *ctx)
639{
640 unsigned int i;
641
642 /* check for mandatory IEs */
643 if (!TLVP_PRESENT(tp, CBSP_IEI_MSG_ID) ||
644 !TLVP_PRESENT(tp, CBSP_IEI_NEW_SERIAL_NR) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200645 !TLVP_PRESENT(tp, CBSP_IEI_CELL_LIST)) {
646 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200647 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200648 }
Harald Welte07958e42019-05-03 09:39:10 +0200649
650 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
651 out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
652 if (TLVP_PRESENT(tp, CBSP_IEI_OLD_SERIAL_NR)) {
653 out->old_serial_nr = talloc(ctx, uint16_t);
654 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
655 }
656
657 INIT_LLIST_HEAD(&out->cell_list.list);
658 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
659 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
660
661 if (TLVP_PRESENT(tp, CBSP_IEI_CHANNEL_IND)) {
662 uint8_t num_of_pages;
663 INIT_LLIST_HEAD(&out->u.cbs.msg_content);
Harald Weltef72155a2019-06-15 23:05:19 +0200664 if (TLVP_PRESENT(tp, CBSP_IEI_EMERG_IND)) {
665 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200666 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200667 }
Harald Welte07958e42019-05-03 09:39:10 +0200668 if (!TLVP_PRESENT(tp, CBSP_IEI_CATEGORY) ||
669 !TLVP_PRESENT(tp, CBSP_IEI_REP_PERIOD) ||
670 !TLVP_PRESENT(tp, CBSP_IEI_NUM_BCAST_REQ) ||
671 !TLVP_PRESENT(tp, CBSP_IEI_NUM_OF_PAGES) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200672 !TLVP_PRESENT(tp, CBSP_IEI_DCS)) {
673 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200674 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200675 }
Harald Welte07958e42019-05-03 09:39:10 +0200676 out->is_cbs = true;
677 out->u.cbs.channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
678 out->u.cbs.category = *TLVP_VAL(tp, CBSP_IEI_CATEGORY);
679 out->u.cbs.rep_period = tlvp_val16be(tp, CBSP_IEI_REP_PERIOD);
680 out->u.cbs.num_bcast_req = tlvp_val16be(tp, CBSP_IEI_NUM_BCAST_REQ);
Harald Welte886e7422021-01-02 22:30:02 +0100681 out->u.cbs.dcs = *TLVP_VAL(tp, CBSP_IEI_DCS);
Harald Welte07958e42019-05-03 09:39:10 +0200682 num_of_pages = *TLVP_VAL(tp, CBSP_IEI_NUM_OF_PAGES);
683 if (num_of_pages < 1)
684 return -EINVAL;
685 /* parse pages */
686 for (i = 0; i < num_of_pages; i++) {
687 const uint8_t *ie = TLVP_VAL(&tp[i], CBSP_IEI_MSG_CONTENT);
688 struct osmo_cbsp_content *page;
Harald Weltef72155a2019-06-15 23:05:19 +0200689 if (!ie) {
690 osmo_cbsp_errstr = "insufficient message content IEs";
Harald Welte07958e42019-05-03 09:39:10 +0200691 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200692 }
Harald Welte07958e42019-05-03 09:39:10 +0200693 page = talloc_zero(ctx, struct osmo_cbsp_content);
694 OSMO_ASSERT(page);
Harald Weltee674c442019-09-01 22:30:58 +0200695 page->user_len = ie[0]; /* length byte before payload */
696 memcpy(page->data, ie+1, sizeof(page->data));
Harald Welte07958e42019-05-03 09:39:10 +0200697 llist_add_tail(&page->list, &out->u.cbs.msg_content);
698 }
699 } else {
700 if (!TLVP_PRES_LEN(tp, CBSP_IEI_EMERG_IND, 1) ||
701 !TLVP_PRES_LEN(tp, CBSP_IEI_WARN_TYPE, 2) ||
702 !TLVP_PRES_LEN(tp, CBSP_IEI_WARN_SEC_INFO, 50) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200703 !TLVP_PRES_LEN(tp, CBSP_IEI_WARNING_PERIOD, 1)) {
704 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200705 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200706 }
Harald Welte07958e42019-05-03 09:39:10 +0200707 out->u.emergency.indicator = *TLVP_VAL(tp, CBSP_IEI_EMERG_IND);
708 out->u.emergency.warning_type = tlvp_val16be(tp, CBSP_IEI_WARN_TYPE);
709 memcpy(&out->u.emergency.warning_sec_info, TLVP_VAL(tp, CBSP_IEI_WARN_SEC_INFO),
710 sizeof(out->u.emergency.warning_sec_info));
711 out->u.emergency.warning_period = decode_wperiod(*TLVP_VAL(tp, CBSP_IEI_WARNING_PERIOD));
712 }
713 return 0;
714}
715
716/* 8.1.3.2 WRITE REPLACE COMPLETE*/
717static int cbsp_dec_write_repl_compl(struct osmo_cbsp_write_replace_complete *out,
718 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
719{
720 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200721 !TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2)) {
722 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200723 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200724 }
Harald Welte07958e42019-05-03 09:39:10 +0200725
726 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
727 out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
728 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
729 out->old_serial_nr = talloc(ctx, uint16_t);
730 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
731 }
732
733 INIT_LLIST_HEAD(&out->num_compl_list.list);
734 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
735 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
736 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
737 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
738 }
739
740 INIT_LLIST_HEAD(&out->cell_list.list);
741 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
742 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
743
744 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
745 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
746 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
747 }
748 return 0;
749}
750
751/* 8.1.3.3 WRITE REPLACE FAILURE */
752static int cbsp_dec_write_repl_fail(struct osmo_cbsp_write_replace_failure *out,
753 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
754{
755 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
756 !TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200757 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
758 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200759 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200760 }
Harald Welte07958e42019-05-03 09:39:10 +0200761
762 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
763 out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
764 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
765 out->old_serial_nr = talloc(ctx, uint16_t);
766 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
767 }
768
769 INIT_LLIST_HEAD(&out->fail_list);
770 cbsp_decode_fail_list(&out->fail_list, ctx,
771 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
772 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
773
774 INIT_LLIST_HEAD(&out->num_compl_list.list);
775 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
776 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
777 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
778 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
779 }
780
781 INIT_LLIST_HEAD(&out->cell_list.list);
782 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
783 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
784 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
785 }
786
787 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
788 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
789 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
790 }
791 return 0;
792}
793
794/* 8.1.3.4 KILL */
795static int cbsp_dec_kill(struct osmo_cbsp_kill *out, const struct tlv_parsed *tp,
796 struct msgb *in, void *ctx)
797{
798 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
799 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200800 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
801 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200802 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200803 }
804
Harald Welte07958e42019-05-03 09:39:10 +0200805 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
806 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
807
808 INIT_LLIST_HEAD(&out->cell_list.list);
809 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
810 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
811
812 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
813 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
814 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
815 }
816 return 0;
817}
818
819/* 8.1.3.5 KILL COMPLETE */
820static int cbsp_dec_kill_compl(struct osmo_cbsp_kill_complete *out, const struct tlv_parsed *tp,
821 struct msgb *in, void *ctx)
822{
823 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
824 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200825 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
826 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200827 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200828 }
Harald Welte07958e42019-05-03 09:39:10 +0200829
830 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
831 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
832
833 INIT_LLIST_HEAD(&out->num_compl_list.list);
834 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
835 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
836 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
837 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
838 }
839
840 INIT_LLIST_HEAD(&out->cell_list.list);
841 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
842 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
843
844 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
845 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
846 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
847 }
848 return 0;
849}
850
851/* 8.1.3.6 KILL FAILURE */
852static int cbsp_dec_kill_fail(struct osmo_cbsp_kill_failure *out, const struct tlv_parsed *tp,
853 struct msgb *in, void *ctx)
854{
855 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
856 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200857 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
858 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200859 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200860 }
Harald Welte07958e42019-05-03 09:39:10 +0200861
862 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
863 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
864
865 INIT_LLIST_HEAD(&out->fail_list);
866 cbsp_decode_fail_list(&out->fail_list, ctx,
867 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
868 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
869
870 INIT_LLIST_HEAD(&out->num_compl_list.list);
871 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
872 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
873 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
874 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
875 }
876
877 INIT_LLIST_HEAD(&out->cell_list.list);
878 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
879 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
880 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
881 }
882
883 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
884 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
885 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
886 }
887 return 0;
888}
889
890/* 8.1.3.7 LOAD QUERY */
891static int cbsp_dec_load_query(struct osmo_cbsp_load_query *out, const struct tlv_parsed *tp,
892 struct msgb *in, void *ctx)
893{
894 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200895 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
896 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200897 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200898 }
Harald Welte07958e42019-05-03 09:39:10 +0200899
900 INIT_LLIST_HEAD(&out->cell_list.list);
901 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
902 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
903
904 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
905 return 0;
906}
907
908/* 8.1.3.8 LOAD QUERY COMPLETE */
909static int cbsp_dec_load_query_compl(struct osmo_cbsp_load_query_complete *out,
910 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
911{
912 if (!TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200913 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
914 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200915 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200916 }
Harald Welte07958e42019-05-03 09:39:10 +0200917
918 INIT_LLIST_HEAD(&out->loading_list.list);
919 cbsp_decode_loading_list(&out->loading_list, ctx,
920 TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
921 TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
922
923 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
924 return 0;
925}
926
927/* 8.1.3.9 LOAD QUERY FAILURE */
928static int cbsp_dec_load_query_fail(struct osmo_cbsp_load_query_failure *out,
929 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
930{
931 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200932 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
933 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200934 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200935 }
Harald Welte07958e42019-05-03 09:39:10 +0200936
937 INIT_LLIST_HEAD(&out->fail_list);
938 cbsp_decode_fail_list(&out->fail_list, ctx,
939 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
940 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
941
942 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
943
944 INIT_LLIST_HEAD(&out->loading_list.list);
945 if (TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6)) {
946 cbsp_decode_loading_list(&out->loading_list, ctx,
947 TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
948 TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
949 }
950 return 0;
951}
952
953/* 8.1.3.10 STATUS QUERY */
954static int cbsp_dec_msg_status_query(struct osmo_cbsp_msg_status_query *out,
955 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
956{
957 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
958 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
959 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200960 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
961 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200962 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200963 }
964
Harald Welte07958e42019-05-03 09:39:10 +0200965 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
966 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
967
968 INIT_LLIST_HEAD(&out->cell_list.list);
969 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
970 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
971
972 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
973 return 0;
974}
975
976/* 8.1.3.11 STATUS QUERY COMPLETE */
977static int cbsp_dec_msg_status_query_compl(struct osmo_cbsp_msg_status_query_complete *out,
978 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
979{
980 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
981 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
982 !TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200983 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
984 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200985 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200986 }
Harald Welte07958e42019-05-03 09:39:10 +0200987
988 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
989 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
990
991 INIT_LLIST_HEAD(&out->num_compl_list.list);
992 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
993 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
994 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
995 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
996 return 0;
997}
998
999/* 8.1.3.12 STATUS QUERY FAILURE */
1000static int cbsp_dec_msg_status_query_fail(struct osmo_cbsp_msg_status_query_failure *out,
1001 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
1002{
1003 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
1004 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
1005 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
Harald Weltef72155a2019-06-15 23:05:19 +02001006 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
1007 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001008 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001009 }
Harald Welte07958e42019-05-03 09:39:10 +02001010
1011 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
1012 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
1013
1014 INIT_LLIST_HEAD(&out->fail_list);
1015 cbsp_decode_fail_list(&out->fail_list, ctx,
1016 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
1017 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
1018
1019 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
1020
1021 INIT_LLIST_HEAD(&out->num_compl_list.list);
1022 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
1023 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
1024 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
1025 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
1026 }
1027 return 0;
1028}
1029
1030/* 8.1.3.16 RESET */
1031static int cbsp_dec_reset(struct osmo_cbsp_reset *out, const struct tlv_parsed *tp,
1032 struct msgb *in, void *ctx)
1033{
Harald Weltef72155a2019-06-15 23:05:19 +02001034 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
1035 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001036 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001037 }
Harald Welte07958e42019-05-03 09:39:10 +02001038
1039 INIT_LLIST_HEAD(&out->cell_list.list);
1040 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1041 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1042 return 0;
1043}
1044
1045/* 8.1.3.17 RESET COMPLETE */
1046static int cbsp_dec_reset_compl(struct osmo_cbsp_reset_complete *out, const struct tlv_parsed *tp,
1047 struct msgb *in, void *ctx)
1048{
Harald Weltef72155a2019-06-15 23:05:19 +02001049 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
1050 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001051 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001052 }
Harald Welte07958e42019-05-03 09:39:10 +02001053
1054 INIT_LLIST_HEAD(&out->cell_list.list);
1055 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1056 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1057 return 0;
1058}
1059
1060/* 8.1.3.18 RESET FAILURE */
1061static int cbsp_dec_reset_fail(struct osmo_cbsp_reset_failure *out, const struct tlv_parsed *tp,
1062 struct msgb *in, void *ctx)
1063{
Harald Weltef72155a2019-06-15 23:05:19 +02001064 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
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 INIT_LLIST_HEAD(&out->fail_list);
1070 cbsp_decode_fail_list(&out->fail_list, ctx,
1071 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
1072 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
1073
1074 INIT_LLIST_HEAD(&out->cell_list.list);
1075 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
1076 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1077 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1078 }
1079 return 0;
1080}
1081
1082/* 8.1.3.18a KEEP ALIVE */
1083static int cbsp_dec_keep_alive(struct osmo_cbsp_keep_alive *out, const struct tlv_parsed *tp,
1084 struct msgb *in, void *ctx)
1085{
Harald Weltef72155a2019-06-15 23:05:19 +02001086 if (!TLVP_PRES_LEN(tp, CBSP_IEI_KEEP_ALIVE_REP_PERIOD, 1)) {
1087 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001088 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001089 }
Harald Welte07958e42019-05-03 09:39:10 +02001090
1091 out->repetition_period = *TLVP_VAL(tp, CBSP_IEI_KEEP_ALIVE_REP_PERIOD);
1092 return 0;
1093}
1094
1095/* 8.1.3.18b KEEP ALIVE COMPLETE */
1096static int cbsp_dec_keep_alive_compl(struct osmo_cbsp_keep_alive_complete *out,
1097 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
1098{
1099 return 0;
1100}
1101
1102/* 8.1.3.19 RESTART */
1103static int cbsp_dec_restart(struct osmo_cbsp_restart *out, const struct tlv_parsed *tp,
1104 struct msgb *in, void *ctx)
1105{
1106 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
1107 !TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1) ||
Harald Weltef72155a2019-06-15 23:05:19 +02001108 !TLVP_PRES_LEN(tp, CBSP_IEI_RECOVERY_IND, 1)) {
1109 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001110 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001111 }
Harald Welte07958e42019-05-03 09:39:10 +02001112
1113 INIT_LLIST_HEAD(&out->cell_list.list);
1114 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1115 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1116
1117 out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE);
1118 out->recovery_ind = *TLVP_VAL(tp, CBSP_IEI_RECOVERY_IND);
1119 return 0;
1120}
1121
1122/* 8.1.3.20 FAILURE */
1123static int cbsp_dec_failure(struct osmo_cbsp_failure *out, const struct tlv_parsed *tp,
1124 struct msgb *in, void *ctx)
1125{
1126 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
Harald Weltef72155a2019-06-15 23:05:19 +02001127 !TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1)) {
1128 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001129 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001130 }
Harald Welte07958e42019-05-03 09:39:10 +02001131
1132 INIT_LLIST_HEAD(&out->fail_list);
1133 cbsp_decode_fail_list(&out->fail_list, ctx,
1134 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
1135 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
1136
1137 out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE);
1138 return 0;
1139}
1140
1141/* 8.1.3.21 ERROR INDICATION */
1142static int cbsp_dec_error_ind(struct osmo_cbsp_error_ind *out, const struct tlv_parsed *tp,
1143 struct msgb *in, void *ctx)
1144{
Harald Weltef72155a2019-06-15 23:05:19 +02001145 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CAUSE, 1)) {
1146 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001147 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001148 }
Harald Welte07958e42019-05-03 09:39:10 +02001149
1150 out->cause = *TLVP_VAL(tp, CBSP_IEI_CAUSE);
1151 if (TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2)) {
1152 out->msg_id = talloc(ctx, uint16_t);
1153 *out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
1154 }
1155 if (TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2)) {
1156 out->new_serial_nr = talloc(ctx, uint16_t);
1157 *out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
1158 }
1159 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
1160 out->old_serial_nr = talloc(ctx, uint16_t);
1161 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
1162 }
1163 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
1164 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
1165 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
1166 }
1167 return 0;
1168}
1169
1170/*! Decode a CBSP message from wire formwat to pased structure.
1171 * \param[in] ctx talloc context from which to allocate decoded output.
1172 * \param[in] in message buffer contiaining binary CBSP message.
1173 * \returns callee-allocated decoded representation of CBSP message; NULL on error */
1174struct osmo_cbsp_decoded *osmo_cbsp_decode(void *ctx, struct msgb *in)
1175{
1176 struct osmo_cbsp_decoded *out = talloc_zero(ctx, struct osmo_cbsp_decoded);
1177 const struct cbsp_header *h = msgb_l1(in);
1178 struct tlv_parsed tp[16]; /* max. number of pages in a given CBS message */
1179 unsigned int len;
1180 int rc;
1181
Harald Weltef72155a2019-06-15 23:05:19 +02001182 osmo_cbsp_errstr = NULL;
1183
Harald Welte07958e42019-05-03 09:39:10 +02001184 if (!out)
1185 return NULL;
1186
1187 if (msgb_l1len(in) < sizeof(*h)) {
1188 goto out_err;
1189 }
1190 len = h->len[0] << 16 | h->len[1] << 8 | h->len[2];
1191
1192 /* discard messages where indicated length is more than we have */
1193 if (len > msgb_l2len(in)) {
1194 goto out_err;
1195 }
1196
1197 /* trim any messages with extra payload at the end */
1198 if (len < msgb_l2len(in))
1199 msgb_trim(in, (in->l2h - in->data) + msgb_l2len(in));
1200 out->msg_type = h->msg_type;
1201
1202 rc = tlv_parse2(tp, ARRAY_SIZE(tp), &cbsp_att_tlvdef, msgb_l2(in), msgb_l2len(in), 0, 0);
1203 if (rc < 0) {
1204 goto out_err;
1205 }
1206
1207 switch (h->msg_type) {
1208 case CBSP_MSGT_WRITE_REPLACE:
1209 rc = cbsp_dec_write_repl(&out->u.write_replace, tp, in, out);
1210 break;
1211 case CBSP_MSGT_WRITE_REPLACE_COMPL:
1212 rc = cbsp_dec_write_repl_compl(&out->u.write_replace_compl, tp, in, out);
1213 break;
1214 case CBSP_MSGT_WRITE_REPLACE_FAIL:
1215 rc = cbsp_dec_write_repl_fail(&out->u.write_replace_fail, tp, in, out);
1216 break;
1217 case CBSP_MSGT_KILL:
1218 rc = cbsp_dec_kill(&out->u.kill, tp, in, out);
1219 break;
1220 case CBSP_MSGT_KILL_COMPL:
1221 rc = cbsp_dec_kill_compl(&out->u.kill_compl, tp, in, out);
1222 break;
1223 case CBSP_MSGT_KILL_FAIL:
1224 rc = cbsp_dec_kill_fail(&out->u.kill_fail, tp, in, out);
1225 break;
1226 case CBSP_MSGT_LOAD_QUERY:
1227 rc = cbsp_dec_load_query(&out->u.load_query, tp, in, out);
1228 break;
1229 case CBSP_MSGT_LOAD_QUERY_COMPL:
1230 rc = cbsp_dec_load_query_compl(&out->u.load_query_compl, tp, in, out);
1231 break;
1232 case CBSP_MSGT_LOAD_QUERY_FAIL:
1233 rc = cbsp_dec_load_query_fail(&out->u.load_query_fail, tp, in, out);
1234 break;
1235 case CBSP_MSGT_MSG_STATUS_QUERY:
1236 rc = cbsp_dec_msg_status_query(&out->u.msg_status_query, tp, in, out);
1237 break;
1238 case CBSP_MSGT_MSG_STATUS_QUERY_COMPL:
1239 rc = cbsp_dec_msg_status_query_compl(&out->u.msg_status_query_compl, tp, in, out);
1240 break;
1241 case CBSP_MSGT_MSG_STATUS_QUERY_FAIL:
1242 rc = cbsp_dec_msg_status_query_fail(&out->u.msg_status_query_fail, tp, in, out);
1243 break;
1244 case CBSP_MSGT_RESET:
1245 rc = cbsp_dec_reset(&out->u.reset, tp, in, out);
1246 break;
1247 case CBSP_MSGT_RESET_COMPL:
1248 rc = cbsp_dec_reset_compl(&out->u.reset_compl, tp, in, out);
1249 break;
1250 case CBSP_MSGT_RESET_FAIL:
1251 rc = cbsp_dec_reset_fail(&out->u.reset_fail, tp, in, out);
1252 break;
1253 case CBSP_MSGT_RESTART:
1254 rc = cbsp_dec_restart(&out->u.restart, tp, in, out);
1255 break;
1256 case CBSP_MSGT_FAILURE:
1257 rc = cbsp_dec_failure(&out->u.failure, tp, in, out);
1258 break;
1259 case CBSP_MSGT_ERROR_IND:
1260 rc = cbsp_dec_error_ind(&out->u.error_ind, tp, in, out);
1261 break;
1262 case CBSP_MSGT_KEEP_ALIVE:
1263 rc = cbsp_dec_keep_alive(&out->u.keep_alive, tp, in, out);
1264 break;
1265 case CBSP_MSGT_KEEP_ALIVE_COMPL:
1266 rc = cbsp_dec_keep_alive_compl(&out->u.keep_alive_compl, tp, in, out);
1267 break;
1268 case CBSP_MSGT_SET_DRX:
1269 case CBSP_MSGT_SET_DRX_COMPL:
1270 case CBSP_MSGT_SET_DRX_FAIL:
Harald Weltef72155a2019-06-15 23:05:19 +02001271 osmo_cbsp_errstr = "message type not implemented";
Harald Welte07958e42019-05-03 09:39:10 +02001272 rc = -1;
1273 break;
1274 default:
Harald Weltef72155a2019-06-15 23:05:19 +02001275 osmo_cbsp_errstr = "message type not known in spec";
Harald Welte07958e42019-05-03 09:39:10 +02001276 rc = -1;
1277 break;
1278 }
1279
1280 if (rc < 0) {
1281 goto out_err;
1282 }
1283
1284 return out;
1285
1286out_err:
1287 talloc_free(out);
1288 return NULL;
1289}
1290
1291/* initialization of 'decoded' structure of given message type */
1292void osmo_cbsp_init_struct(struct osmo_cbsp_decoded *cbsp, enum cbsp_msg_type msg_type)
1293{
1294 memset(cbsp, 0, sizeof(*cbsp));
1295 cbsp->msg_type = msg_type;
1296
1297 switch (msg_type) {
1298 case CBSP_MSGT_WRITE_REPLACE:
1299 INIT_LLIST_HEAD(&cbsp->u.write_replace.cell_list.list);
1300 break;
1301 case CBSP_MSGT_WRITE_REPLACE_COMPL:
1302 INIT_LLIST_HEAD(&cbsp->u.write_replace_compl.num_compl_list.list);
1303 INIT_LLIST_HEAD(&cbsp->u.write_replace_compl.cell_list.list);
1304 break;
1305 case CBSP_MSGT_WRITE_REPLACE_FAIL:
1306 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.fail_list);
1307 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.num_compl_list.list);
1308 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.cell_list.list);
1309 break;
1310 case CBSP_MSGT_KILL:
1311 INIT_LLIST_HEAD(&cbsp->u.kill.cell_list.list);
1312 break;
1313 case CBSP_MSGT_KILL_COMPL:
1314 INIT_LLIST_HEAD(&cbsp->u.kill_compl.num_compl_list.list);
1315 INIT_LLIST_HEAD(&cbsp->u.kill_compl.cell_list.list);
1316 break;
1317 case CBSP_MSGT_KILL_FAIL:
1318 INIT_LLIST_HEAD(&cbsp->u.kill_fail.fail_list);
1319 INIT_LLIST_HEAD(&cbsp->u.kill_fail.num_compl_list.list);
1320 INIT_LLIST_HEAD(&cbsp->u.kill_fail.cell_list.list);
1321 break;
1322 case CBSP_MSGT_LOAD_QUERY:
1323 INIT_LLIST_HEAD(&cbsp->u.load_query.cell_list.list);
1324 break;
1325 case CBSP_MSGT_LOAD_QUERY_COMPL:
1326 INIT_LLIST_HEAD(&cbsp->u.load_query_compl.loading_list.list);
1327 break;
1328 case CBSP_MSGT_LOAD_QUERY_FAIL:
1329 INIT_LLIST_HEAD(&cbsp->u.load_query_fail.fail_list);
1330 break;
1331 case CBSP_MSGT_MSG_STATUS_QUERY:
1332 INIT_LLIST_HEAD(&cbsp->u.msg_status_query.cell_list.list);
1333 break;
1334 case CBSP_MSGT_MSG_STATUS_QUERY_COMPL:
1335 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_compl.num_compl_list.list);
1336 break;
1337 case CBSP_MSGT_MSG_STATUS_QUERY_FAIL:
1338 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_fail.fail_list);
1339 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_fail.num_compl_list.list);
1340 break;
1341 case CBSP_MSGT_RESET:
1342 INIT_LLIST_HEAD(&cbsp->u.reset.cell_list.list);
1343 break;
1344 case CBSP_MSGT_RESET_COMPL:
1345 INIT_LLIST_HEAD(&cbsp->u.reset_compl.cell_list.list);
1346 break;
1347 case CBSP_MSGT_RESET_FAIL:
1348 INIT_LLIST_HEAD(&cbsp->u.reset_fail.fail_list);
1349 INIT_LLIST_HEAD(&cbsp->u.reset_fail.cell_list.list);
1350 break;
1351 case CBSP_MSGT_RESTART:
1352 INIT_LLIST_HEAD(&cbsp->u.restart.cell_list.list);
1353 break;
1354 case CBSP_MSGT_FAILURE:
1355 INIT_LLIST_HEAD(&cbsp->u.failure.fail_list);
1356 break;
1357 default:
1358 break;
1359 }
1360}
1361
1362/*! Dynamically allocate and initialize decoded CBSP structure.
1363 * \param[in] ctx talloc context from which to allocate
1364 * \param[in] msg_type CBSP message type for which to initialize result
1365 * \returns allocated + initialized decoded CBSP structure; NULL on talloc failure */
1366struct osmo_cbsp_decoded *osmo_cbsp_decoded_alloc(void *ctx, enum cbsp_msg_type msg_type)
1367{
1368 struct osmo_cbsp_decoded *cbsp = talloc_zero(ctx, struct osmo_cbsp_decoded);
1369 if (!cbsp)
1370 return NULL;
1371 osmo_cbsp_init_struct(cbsp, msg_type);
1372 return cbsp;
1373}
1374
1375/***********************************************************************
1376 * Message Reception
1377 ***********************************************************************/
1378
1379#ifdef HAVE_SYS_SOCKET_H
1380#include <sys/socket.h>
1381
1382/*! Read one CBSP message from socket fd or store part if still not fully received.
1383 * \param[in] ctx talloc context from which to allocate new msgb.
1384 * \param[in] fd The fd for the socket to read from.
1385 * \param[out] rmsg internally allocated msgb containing a fully received CBSP message.
1386 * \param[inout] tmp_msg internally allocated msgb caching data for not yet fully received message.
1387 *
1388 * Function is designed just like ipa_msg_recv_buffered()
1389 */
1390int osmo_cbsp_recv_buffered(void *ctx, int fd, struct msgb **rmsg, struct msgb **tmp_msg)
1391{
1392 struct msgb *msg = tmp_msg ? *tmp_msg : NULL;
1393 struct cbsp_header *h;
1394 int len, rc;
1395 int needed;
1396
1397 if (!msg) {
1398 msg = osmo_cbsp_msgb_alloc(ctx, __func__);
Harald Weltec30d8be2019-07-21 07:51:33 +02001399 if (!msg)
Harald Welte07958e42019-05-03 09:39:10 +02001400 return -ENOMEM;
Harald Welte07958e42019-05-03 09:39:10 +02001401 msg->l1h = msg->tail;
1402 }
1403
1404 if (msg->l2h == NULL) {
1405 /* first read the [missing part of the] header */
1406 needed = sizeof(*h) - msg->len;
1407 rc = recv(fd, msg->tail, needed, 0);
1408 if (rc == 0)
1409 goto discard_msg;
1410 else if (rc < 0) {
1411 if (errno == EAGAIN || errno == EINTR)
1412 rc = 0;
1413 else {
1414 rc = -errno;
1415 goto discard_msg;
1416 }
1417 }
1418 msgb_put(msg, rc);
1419 if (rc < needed) {
1420 if (msg->len == 0) {
1421 rc = -EAGAIN;
1422 goto discard_msg;
1423 }
1424
1425 if (!tmp_msg) {
1426 rc = -EIO;
1427 goto discard_msg;
1428 }
1429 *tmp_msg = msg;
1430 return -EAGAIN;
1431 }
1432 msg->l2h = msg->tail;
1433 }
1434
1435 h = (struct cbsp_header *) msg->data;
1436 /* then read the length as specified in the header */
1437 len = h->len[0] << 16 | h->len[1] << 8 | h->len[2];
1438
1439 needed = len - msgb_l2len(msg);
1440 if (needed > 0) {
1441 rc = recv(fd, msg->tail, needed, 0);
1442 if (rc == 0)
1443 goto discard_msg;
1444 else if (rc < 0) {
1445 if (errno == EAGAIN || errno == EINTR)
1446 rc = 0;
1447 else {
1448 rc = -errno;
1449 goto discard_msg;
1450 }
1451 }
1452 msgb_put(msg, rc);
1453 /* still not all of payload received? */
1454 if (rc < needed) {
1455 if (!tmp_msg) {
1456 rc = -EIO;
1457 goto discard_msg;
1458 }
1459 *tmp_msg = msg;
1460 return -EAGAIN;
1461 }
1462 }
1463 /* else: complete message received */
Harald Weltefdd71c82021-01-02 20:50:47 +01001464 rc = msgb_length(msg);
Harald Welte07958e42019-05-03 09:39:10 +02001465 if (tmp_msg)
1466 *tmp_msg = NULL;
1467 *rmsg = msg;
1468 return rc;
1469
1470discard_msg:
Harald Welte07958e42019-05-03 09:39:10 +02001471 if (tmp_msg)
1472 *tmp_msg = NULL;
1473 msgb_free(msg);
1474 return rc;
1475}
1476
1477#endif /* HAVE_SYS_SOCKET_H */