blob: 6ddef33f0477a0bf59f8c0a8f22e2a6688b45c1b [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);
681 num_of_pages = *TLVP_VAL(tp, CBSP_IEI_NUM_OF_PAGES);
682 if (num_of_pages < 1)
683 return -EINVAL;
684 /* parse pages */
685 for (i = 0; i < num_of_pages; i++) {
686 const uint8_t *ie = TLVP_VAL(&tp[i], CBSP_IEI_MSG_CONTENT);
687 struct osmo_cbsp_content *page;
Harald Weltef72155a2019-06-15 23:05:19 +0200688 if (!ie) {
689 osmo_cbsp_errstr = "insufficient message content IEs";
Harald Welte07958e42019-05-03 09:39:10 +0200690 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200691 }
Harald Welte07958e42019-05-03 09:39:10 +0200692 page = talloc_zero(ctx, struct osmo_cbsp_content);
693 OSMO_ASSERT(page);
Harald Weltee674c442019-09-01 22:30:58 +0200694 page->user_len = ie[0]; /* length byte before payload */
695 memcpy(page->data, ie+1, sizeof(page->data));
Harald Welte07958e42019-05-03 09:39:10 +0200696 llist_add_tail(&page->list, &out->u.cbs.msg_content);
697 }
698 } else {
699 if (!TLVP_PRES_LEN(tp, CBSP_IEI_EMERG_IND, 1) ||
700 !TLVP_PRES_LEN(tp, CBSP_IEI_WARN_TYPE, 2) ||
701 !TLVP_PRES_LEN(tp, CBSP_IEI_WARN_SEC_INFO, 50) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200702 !TLVP_PRES_LEN(tp, CBSP_IEI_WARNING_PERIOD, 1)) {
703 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200704 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200705 }
Harald Welte07958e42019-05-03 09:39:10 +0200706 out->u.emergency.indicator = *TLVP_VAL(tp, CBSP_IEI_EMERG_IND);
707 out->u.emergency.warning_type = tlvp_val16be(tp, CBSP_IEI_WARN_TYPE);
708 memcpy(&out->u.emergency.warning_sec_info, TLVP_VAL(tp, CBSP_IEI_WARN_SEC_INFO),
709 sizeof(out->u.emergency.warning_sec_info));
710 out->u.emergency.warning_period = decode_wperiod(*TLVP_VAL(tp, CBSP_IEI_WARNING_PERIOD));
711 }
712 return 0;
713}
714
715/* 8.1.3.2 WRITE REPLACE COMPLETE*/
716static int cbsp_dec_write_repl_compl(struct osmo_cbsp_write_replace_complete *out,
717 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
718{
719 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200720 !TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2)) {
721 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200722 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200723 }
Harald Welte07958e42019-05-03 09:39:10 +0200724
725 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
726 out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
727 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
728 out->old_serial_nr = talloc(ctx, uint16_t);
729 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
730 }
731
732 INIT_LLIST_HEAD(&out->num_compl_list.list);
733 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
734 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
735 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
736 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
737 }
738
739 INIT_LLIST_HEAD(&out->cell_list.list);
740 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
741 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
742
743 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
744 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
745 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
746 }
747 return 0;
748}
749
750/* 8.1.3.3 WRITE REPLACE FAILURE */
751static int cbsp_dec_write_repl_fail(struct osmo_cbsp_write_replace_failure *out,
752 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
753{
754 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
755 !TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200756 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
757 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200758 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200759 }
Harald Welte07958e42019-05-03 09:39:10 +0200760
761 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
762 out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
763 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
764 out->old_serial_nr = talloc(ctx, uint16_t);
765 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
766 }
767
768 INIT_LLIST_HEAD(&out->fail_list);
769 cbsp_decode_fail_list(&out->fail_list, ctx,
770 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
771 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
772
773 INIT_LLIST_HEAD(&out->num_compl_list.list);
774 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
775 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
776 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
777 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
778 }
779
780 INIT_LLIST_HEAD(&out->cell_list.list);
781 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
782 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
783 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
784 }
785
786 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
787 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
788 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
789 }
790 return 0;
791}
792
793/* 8.1.3.4 KILL */
794static int cbsp_dec_kill(struct osmo_cbsp_kill *out, const struct tlv_parsed *tp,
795 struct msgb *in, void *ctx)
796{
797 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
798 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200799 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
800 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200801 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200802 }
803
Harald Welte07958e42019-05-03 09:39:10 +0200804 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
805 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
806
807 INIT_LLIST_HEAD(&out->cell_list.list);
808 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
809 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
810
811 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
812 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
813 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
814 }
815 return 0;
816}
817
818/* 8.1.3.5 KILL COMPLETE */
819static int cbsp_dec_kill_compl(struct osmo_cbsp_kill_complete *out, const struct tlv_parsed *tp,
820 struct msgb *in, void *ctx)
821{
822 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
823 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200824 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
825 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200826 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200827 }
Harald Welte07958e42019-05-03 09:39:10 +0200828
829 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
830 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
831
832 INIT_LLIST_HEAD(&out->num_compl_list.list);
833 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
834 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
835 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
836 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
837 }
838
839 INIT_LLIST_HEAD(&out->cell_list.list);
840 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
841 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
842
843 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
844 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
845 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
846 }
847 return 0;
848}
849
850/* 8.1.3.6 KILL FAILURE */
851static int cbsp_dec_kill_fail(struct osmo_cbsp_kill_failure *out, const struct tlv_parsed *tp,
852 struct msgb *in, void *ctx)
853{
854 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
855 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200856 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
857 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200858 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200859 }
Harald Welte07958e42019-05-03 09:39:10 +0200860
861 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
862 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
863
864 INIT_LLIST_HEAD(&out->fail_list);
865 cbsp_decode_fail_list(&out->fail_list, ctx,
866 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
867 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
868
869 INIT_LLIST_HEAD(&out->num_compl_list.list);
870 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
871 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
872 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
873 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
874 }
875
876 INIT_LLIST_HEAD(&out->cell_list.list);
877 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
878 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
879 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
880 }
881
882 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
883 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
884 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
885 }
886 return 0;
887}
888
889/* 8.1.3.7 LOAD QUERY */
890static int cbsp_dec_load_query(struct osmo_cbsp_load_query *out, const struct tlv_parsed *tp,
891 struct msgb *in, void *ctx)
892{
893 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200894 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
895 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200896 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200897 }
Harald Welte07958e42019-05-03 09:39:10 +0200898
899 INIT_LLIST_HEAD(&out->cell_list.list);
900 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
901 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
902
903 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
904 return 0;
905}
906
907/* 8.1.3.8 LOAD QUERY COMPLETE */
908static int cbsp_dec_load_query_compl(struct osmo_cbsp_load_query_complete *out,
909 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
910{
911 if (!TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200912 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
913 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200914 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200915 }
Harald Welte07958e42019-05-03 09:39:10 +0200916
917 INIT_LLIST_HEAD(&out->loading_list.list);
918 cbsp_decode_loading_list(&out->loading_list, ctx,
919 TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
920 TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
921
922 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
923 return 0;
924}
925
926/* 8.1.3.9 LOAD QUERY FAILURE */
927static int cbsp_dec_load_query_fail(struct osmo_cbsp_load_query_failure *out,
928 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
929{
930 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200931 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
932 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200933 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200934 }
Harald Welte07958e42019-05-03 09:39:10 +0200935
936 INIT_LLIST_HEAD(&out->fail_list);
937 cbsp_decode_fail_list(&out->fail_list, ctx,
938 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
939 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
940
941 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
942
943 INIT_LLIST_HEAD(&out->loading_list.list);
944 if (TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6)) {
945 cbsp_decode_loading_list(&out->loading_list, ctx,
946 TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
947 TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
948 }
949 return 0;
950}
951
952/* 8.1.3.10 STATUS QUERY */
953static int cbsp_dec_msg_status_query(struct osmo_cbsp_msg_status_query *out,
954 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
955{
956 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
957 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
958 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200959 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
960 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200961 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200962 }
963
Harald Welte07958e42019-05-03 09:39:10 +0200964 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
965 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
966
967 INIT_LLIST_HEAD(&out->cell_list.list);
968 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
969 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
970
971 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
972 return 0;
973}
974
975/* 8.1.3.11 STATUS QUERY COMPLETE */
976static int cbsp_dec_msg_status_query_compl(struct osmo_cbsp_msg_status_query_complete *out,
977 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
978{
979 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
980 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
981 !TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200982 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
983 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200984 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200985 }
Harald Welte07958e42019-05-03 09:39:10 +0200986
987 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
988 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
989
990 INIT_LLIST_HEAD(&out->num_compl_list.list);
991 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
992 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
993 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
994 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
995 return 0;
996}
997
998/* 8.1.3.12 STATUS QUERY FAILURE */
999static int cbsp_dec_msg_status_query_fail(struct osmo_cbsp_msg_status_query_failure *out,
1000 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
1001{
1002 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
1003 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
1004 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
Harald Weltef72155a2019-06-15 23:05:19 +02001005 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
1006 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001007 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001008 }
Harald Welte07958e42019-05-03 09:39:10 +02001009
1010 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
1011 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
1012
1013 INIT_LLIST_HEAD(&out->fail_list);
1014 cbsp_decode_fail_list(&out->fail_list, ctx,
1015 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
1016 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
1017
1018 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
1019
1020 INIT_LLIST_HEAD(&out->num_compl_list.list);
1021 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
1022 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
1023 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
1024 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
1025 }
1026 return 0;
1027}
1028
1029/* 8.1.3.16 RESET */
1030static int cbsp_dec_reset(struct osmo_cbsp_reset *out, const struct tlv_parsed *tp,
1031 struct msgb *in, void *ctx)
1032{
Harald Weltef72155a2019-06-15 23:05:19 +02001033 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
1034 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001035 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001036 }
Harald Welte07958e42019-05-03 09:39:10 +02001037
1038 INIT_LLIST_HEAD(&out->cell_list.list);
1039 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1040 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1041 return 0;
1042}
1043
1044/* 8.1.3.17 RESET COMPLETE */
1045static int cbsp_dec_reset_compl(struct osmo_cbsp_reset_complete *out, const struct tlv_parsed *tp,
1046 struct msgb *in, void *ctx)
1047{
Harald Weltef72155a2019-06-15 23:05:19 +02001048 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
1049 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001050 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001051 }
Harald Welte07958e42019-05-03 09:39:10 +02001052
1053 INIT_LLIST_HEAD(&out->cell_list.list);
1054 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1055 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1056 return 0;
1057}
1058
1059/* 8.1.3.18 RESET FAILURE */
1060static int cbsp_dec_reset_fail(struct osmo_cbsp_reset_failure *out, const struct tlv_parsed *tp,
1061 struct msgb *in, void *ctx)
1062{
Harald Weltef72155a2019-06-15 23:05:19 +02001063 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
1064 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001065 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001066 }
Harald Welte07958e42019-05-03 09:39:10 +02001067
1068 INIT_LLIST_HEAD(&out->fail_list);
1069 cbsp_decode_fail_list(&out->fail_list, ctx,
1070 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
1071 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
1072
1073 INIT_LLIST_HEAD(&out->cell_list.list);
1074 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
1075 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1076 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1077 }
1078 return 0;
1079}
1080
1081/* 8.1.3.18a KEEP ALIVE */
1082static int cbsp_dec_keep_alive(struct osmo_cbsp_keep_alive *out, const struct tlv_parsed *tp,
1083 struct msgb *in, void *ctx)
1084{
Harald Weltef72155a2019-06-15 23:05:19 +02001085 if (!TLVP_PRES_LEN(tp, CBSP_IEI_KEEP_ALIVE_REP_PERIOD, 1)) {
1086 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001087 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001088 }
Harald Welte07958e42019-05-03 09:39:10 +02001089
1090 out->repetition_period = *TLVP_VAL(tp, CBSP_IEI_KEEP_ALIVE_REP_PERIOD);
1091 return 0;
1092}
1093
1094/* 8.1.3.18b KEEP ALIVE COMPLETE */
1095static int cbsp_dec_keep_alive_compl(struct osmo_cbsp_keep_alive_complete *out,
1096 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
1097{
1098 return 0;
1099}
1100
1101/* 8.1.3.19 RESTART */
1102static int cbsp_dec_restart(struct osmo_cbsp_restart *out, const struct tlv_parsed *tp,
1103 struct msgb *in, void *ctx)
1104{
1105 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
1106 !TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1) ||
Harald Weltef72155a2019-06-15 23:05:19 +02001107 !TLVP_PRES_LEN(tp, CBSP_IEI_RECOVERY_IND, 1)) {
1108 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001109 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001110 }
Harald Welte07958e42019-05-03 09:39:10 +02001111
1112 INIT_LLIST_HEAD(&out->cell_list.list);
1113 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1114 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1115
1116 out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE);
1117 out->recovery_ind = *TLVP_VAL(tp, CBSP_IEI_RECOVERY_IND);
1118 return 0;
1119}
1120
1121/* 8.1.3.20 FAILURE */
1122static int cbsp_dec_failure(struct osmo_cbsp_failure *out, const struct tlv_parsed *tp,
1123 struct msgb *in, void *ctx)
1124{
1125 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
Harald Weltef72155a2019-06-15 23:05:19 +02001126 !TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1)) {
1127 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001128 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001129 }
Harald Welte07958e42019-05-03 09:39:10 +02001130
1131 INIT_LLIST_HEAD(&out->fail_list);
1132 cbsp_decode_fail_list(&out->fail_list, ctx,
1133 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
1134 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
1135
1136 out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE);
1137 return 0;
1138}
1139
1140/* 8.1.3.21 ERROR INDICATION */
1141static int cbsp_dec_error_ind(struct osmo_cbsp_error_ind *out, const struct tlv_parsed *tp,
1142 struct msgb *in, void *ctx)
1143{
Harald Weltef72155a2019-06-15 23:05:19 +02001144 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CAUSE, 1)) {
1145 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001146 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001147 }
Harald Welte07958e42019-05-03 09:39:10 +02001148
1149 out->cause = *TLVP_VAL(tp, CBSP_IEI_CAUSE);
1150 if (TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2)) {
1151 out->msg_id = talloc(ctx, uint16_t);
1152 *out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
1153 }
1154 if (TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2)) {
1155 out->new_serial_nr = talloc(ctx, uint16_t);
1156 *out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
1157 }
1158 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
1159 out->old_serial_nr = talloc(ctx, uint16_t);
1160 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
1161 }
1162 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
1163 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
1164 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
1165 }
1166 return 0;
1167}
1168
1169/*! Decode a CBSP message from wire formwat to pased structure.
1170 * \param[in] ctx talloc context from which to allocate decoded output.
1171 * \param[in] in message buffer contiaining binary CBSP message.
1172 * \returns callee-allocated decoded representation of CBSP message; NULL on error */
1173struct osmo_cbsp_decoded *osmo_cbsp_decode(void *ctx, struct msgb *in)
1174{
1175 struct osmo_cbsp_decoded *out = talloc_zero(ctx, struct osmo_cbsp_decoded);
1176 const struct cbsp_header *h = msgb_l1(in);
1177 struct tlv_parsed tp[16]; /* max. number of pages in a given CBS message */
1178 unsigned int len;
1179 int rc;
1180
Harald Weltef72155a2019-06-15 23:05:19 +02001181 osmo_cbsp_errstr = NULL;
1182
Harald Welte07958e42019-05-03 09:39:10 +02001183 if (!out)
1184 return NULL;
1185
1186 if (msgb_l1len(in) < sizeof(*h)) {
1187 goto out_err;
1188 }
1189 len = h->len[0] << 16 | h->len[1] << 8 | h->len[2];
1190
1191 /* discard messages where indicated length is more than we have */
1192 if (len > msgb_l2len(in)) {
1193 goto out_err;
1194 }
1195
1196 /* trim any messages with extra payload at the end */
1197 if (len < msgb_l2len(in))
1198 msgb_trim(in, (in->l2h - in->data) + msgb_l2len(in));
1199 out->msg_type = h->msg_type;
1200
1201 rc = tlv_parse2(tp, ARRAY_SIZE(tp), &cbsp_att_tlvdef, msgb_l2(in), msgb_l2len(in), 0, 0);
1202 if (rc < 0) {
1203 goto out_err;
1204 }
1205
1206 switch (h->msg_type) {
1207 case CBSP_MSGT_WRITE_REPLACE:
1208 rc = cbsp_dec_write_repl(&out->u.write_replace, tp, in, out);
1209 break;
1210 case CBSP_MSGT_WRITE_REPLACE_COMPL:
1211 rc = cbsp_dec_write_repl_compl(&out->u.write_replace_compl, tp, in, out);
1212 break;
1213 case CBSP_MSGT_WRITE_REPLACE_FAIL:
1214 rc = cbsp_dec_write_repl_fail(&out->u.write_replace_fail, tp, in, out);
1215 break;
1216 case CBSP_MSGT_KILL:
1217 rc = cbsp_dec_kill(&out->u.kill, tp, in, out);
1218 break;
1219 case CBSP_MSGT_KILL_COMPL:
1220 rc = cbsp_dec_kill_compl(&out->u.kill_compl, tp, in, out);
1221 break;
1222 case CBSP_MSGT_KILL_FAIL:
1223 rc = cbsp_dec_kill_fail(&out->u.kill_fail, tp, in, out);
1224 break;
1225 case CBSP_MSGT_LOAD_QUERY:
1226 rc = cbsp_dec_load_query(&out->u.load_query, tp, in, out);
1227 break;
1228 case CBSP_MSGT_LOAD_QUERY_COMPL:
1229 rc = cbsp_dec_load_query_compl(&out->u.load_query_compl, tp, in, out);
1230 break;
1231 case CBSP_MSGT_LOAD_QUERY_FAIL:
1232 rc = cbsp_dec_load_query_fail(&out->u.load_query_fail, tp, in, out);
1233 break;
1234 case CBSP_MSGT_MSG_STATUS_QUERY:
1235 rc = cbsp_dec_msg_status_query(&out->u.msg_status_query, tp, in, out);
1236 break;
1237 case CBSP_MSGT_MSG_STATUS_QUERY_COMPL:
1238 rc = cbsp_dec_msg_status_query_compl(&out->u.msg_status_query_compl, tp, in, out);
1239 break;
1240 case CBSP_MSGT_MSG_STATUS_QUERY_FAIL:
1241 rc = cbsp_dec_msg_status_query_fail(&out->u.msg_status_query_fail, tp, in, out);
1242 break;
1243 case CBSP_MSGT_RESET:
1244 rc = cbsp_dec_reset(&out->u.reset, tp, in, out);
1245 break;
1246 case CBSP_MSGT_RESET_COMPL:
1247 rc = cbsp_dec_reset_compl(&out->u.reset_compl, tp, in, out);
1248 break;
1249 case CBSP_MSGT_RESET_FAIL:
1250 rc = cbsp_dec_reset_fail(&out->u.reset_fail, tp, in, out);
1251 break;
1252 case CBSP_MSGT_RESTART:
1253 rc = cbsp_dec_restart(&out->u.restart, tp, in, out);
1254 break;
1255 case CBSP_MSGT_FAILURE:
1256 rc = cbsp_dec_failure(&out->u.failure, tp, in, out);
1257 break;
1258 case CBSP_MSGT_ERROR_IND:
1259 rc = cbsp_dec_error_ind(&out->u.error_ind, tp, in, out);
1260 break;
1261 case CBSP_MSGT_KEEP_ALIVE:
1262 rc = cbsp_dec_keep_alive(&out->u.keep_alive, tp, in, out);
1263 break;
1264 case CBSP_MSGT_KEEP_ALIVE_COMPL:
1265 rc = cbsp_dec_keep_alive_compl(&out->u.keep_alive_compl, tp, in, out);
1266 break;
1267 case CBSP_MSGT_SET_DRX:
1268 case CBSP_MSGT_SET_DRX_COMPL:
1269 case CBSP_MSGT_SET_DRX_FAIL:
Harald Weltef72155a2019-06-15 23:05:19 +02001270 osmo_cbsp_errstr = "message type not implemented";
Harald Welte07958e42019-05-03 09:39:10 +02001271 rc = -1;
1272 break;
1273 default:
Harald Weltef72155a2019-06-15 23:05:19 +02001274 osmo_cbsp_errstr = "message type not known in spec";
Harald Welte07958e42019-05-03 09:39:10 +02001275 rc = -1;
1276 break;
1277 }
1278
1279 if (rc < 0) {
1280 goto out_err;
1281 }
1282
1283 return out;
1284
1285out_err:
1286 talloc_free(out);
1287 return NULL;
1288}
1289
1290/* initialization of 'decoded' structure of given message type */
1291void osmo_cbsp_init_struct(struct osmo_cbsp_decoded *cbsp, enum cbsp_msg_type msg_type)
1292{
1293 memset(cbsp, 0, sizeof(*cbsp));
1294 cbsp->msg_type = msg_type;
1295
1296 switch (msg_type) {
1297 case CBSP_MSGT_WRITE_REPLACE:
1298 INIT_LLIST_HEAD(&cbsp->u.write_replace.cell_list.list);
1299 break;
1300 case CBSP_MSGT_WRITE_REPLACE_COMPL:
1301 INIT_LLIST_HEAD(&cbsp->u.write_replace_compl.num_compl_list.list);
1302 INIT_LLIST_HEAD(&cbsp->u.write_replace_compl.cell_list.list);
1303 break;
1304 case CBSP_MSGT_WRITE_REPLACE_FAIL:
1305 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.fail_list);
1306 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.num_compl_list.list);
1307 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.cell_list.list);
1308 break;
1309 case CBSP_MSGT_KILL:
1310 INIT_LLIST_HEAD(&cbsp->u.kill.cell_list.list);
1311 break;
1312 case CBSP_MSGT_KILL_COMPL:
1313 INIT_LLIST_HEAD(&cbsp->u.kill_compl.num_compl_list.list);
1314 INIT_LLIST_HEAD(&cbsp->u.kill_compl.cell_list.list);
1315 break;
1316 case CBSP_MSGT_KILL_FAIL:
1317 INIT_LLIST_HEAD(&cbsp->u.kill_fail.fail_list);
1318 INIT_LLIST_HEAD(&cbsp->u.kill_fail.num_compl_list.list);
1319 INIT_LLIST_HEAD(&cbsp->u.kill_fail.cell_list.list);
1320 break;
1321 case CBSP_MSGT_LOAD_QUERY:
1322 INIT_LLIST_HEAD(&cbsp->u.load_query.cell_list.list);
1323 break;
1324 case CBSP_MSGT_LOAD_QUERY_COMPL:
1325 INIT_LLIST_HEAD(&cbsp->u.load_query_compl.loading_list.list);
1326 break;
1327 case CBSP_MSGT_LOAD_QUERY_FAIL:
1328 INIT_LLIST_HEAD(&cbsp->u.load_query_fail.fail_list);
1329 break;
1330 case CBSP_MSGT_MSG_STATUS_QUERY:
1331 INIT_LLIST_HEAD(&cbsp->u.msg_status_query.cell_list.list);
1332 break;
1333 case CBSP_MSGT_MSG_STATUS_QUERY_COMPL:
1334 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_compl.num_compl_list.list);
1335 break;
1336 case CBSP_MSGT_MSG_STATUS_QUERY_FAIL:
1337 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_fail.fail_list);
1338 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_fail.num_compl_list.list);
1339 break;
1340 case CBSP_MSGT_RESET:
1341 INIT_LLIST_HEAD(&cbsp->u.reset.cell_list.list);
1342 break;
1343 case CBSP_MSGT_RESET_COMPL:
1344 INIT_LLIST_HEAD(&cbsp->u.reset_compl.cell_list.list);
1345 break;
1346 case CBSP_MSGT_RESET_FAIL:
1347 INIT_LLIST_HEAD(&cbsp->u.reset_fail.fail_list);
1348 INIT_LLIST_HEAD(&cbsp->u.reset_fail.cell_list.list);
1349 break;
1350 case CBSP_MSGT_RESTART:
1351 INIT_LLIST_HEAD(&cbsp->u.restart.cell_list.list);
1352 break;
1353 case CBSP_MSGT_FAILURE:
1354 INIT_LLIST_HEAD(&cbsp->u.failure.fail_list);
1355 break;
1356 default:
1357 break;
1358 }
1359}
1360
1361/*! Dynamically allocate and initialize decoded CBSP structure.
1362 * \param[in] ctx talloc context from which to allocate
1363 * \param[in] msg_type CBSP message type for which to initialize result
1364 * \returns allocated + initialized decoded CBSP structure; NULL on talloc failure */
1365struct osmo_cbsp_decoded *osmo_cbsp_decoded_alloc(void *ctx, enum cbsp_msg_type msg_type)
1366{
1367 struct osmo_cbsp_decoded *cbsp = talloc_zero(ctx, struct osmo_cbsp_decoded);
1368 if (!cbsp)
1369 return NULL;
1370 osmo_cbsp_init_struct(cbsp, msg_type);
1371 return cbsp;
1372}
1373
1374/***********************************************************************
1375 * Message Reception
1376 ***********************************************************************/
1377
1378#ifdef HAVE_SYS_SOCKET_H
1379#include <sys/socket.h>
1380
1381/*! Read one CBSP message from socket fd or store part if still not fully received.
1382 * \param[in] ctx talloc context from which to allocate new msgb.
1383 * \param[in] fd The fd for the socket to read from.
1384 * \param[out] rmsg internally allocated msgb containing a fully received CBSP message.
1385 * \param[inout] tmp_msg internally allocated msgb caching data for not yet fully received message.
1386 *
1387 * Function is designed just like ipa_msg_recv_buffered()
1388 */
1389int osmo_cbsp_recv_buffered(void *ctx, int fd, struct msgb **rmsg, struct msgb **tmp_msg)
1390{
1391 struct msgb *msg = tmp_msg ? *tmp_msg : NULL;
1392 struct cbsp_header *h;
1393 int len, rc;
1394 int needed;
1395
1396 if (!msg) {
1397 msg = osmo_cbsp_msgb_alloc(ctx, __func__);
Harald Weltec30d8be2019-07-21 07:51:33 +02001398 if (!msg)
Harald Welte07958e42019-05-03 09:39:10 +02001399 return -ENOMEM;
Harald Welte07958e42019-05-03 09:39:10 +02001400 msg->l1h = msg->tail;
1401 }
1402
1403 if (msg->l2h == NULL) {
1404 /* first read the [missing part of the] header */
1405 needed = sizeof(*h) - msg->len;
1406 rc = recv(fd, msg->tail, needed, 0);
1407 if (rc == 0)
1408 goto discard_msg;
1409 else if (rc < 0) {
1410 if (errno == EAGAIN || errno == EINTR)
1411 rc = 0;
1412 else {
1413 rc = -errno;
1414 goto discard_msg;
1415 }
1416 }
1417 msgb_put(msg, rc);
1418 if (rc < needed) {
1419 if (msg->len == 0) {
1420 rc = -EAGAIN;
1421 goto discard_msg;
1422 }
1423
1424 if (!tmp_msg) {
1425 rc = -EIO;
1426 goto discard_msg;
1427 }
1428 *tmp_msg = msg;
1429 return -EAGAIN;
1430 }
1431 msg->l2h = msg->tail;
1432 }
1433
1434 h = (struct cbsp_header *) msg->data;
1435 /* then read the length as specified in the header */
1436 len = h->len[0] << 16 | h->len[1] << 8 | h->len[2];
1437
1438 needed = len - msgb_l2len(msg);
1439 if (needed > 0) {
1440 rc = recv(fd, msg->tail, needed, 0);
1441 if (rc == 0)
1442 goto discard_msg;
1443 else if (rc < 0) {
1444 if (errno == EAGAIN || errno == EINTR)
1445 rc = 0;
1446 else {
1447 rc = -errno;
1448 goto discard_msg;
1449 }
1450 }
1451 msgb_put(msg, rc);
1452 /* still not all of payload received? */
1453 if (rc < needed) {
1454 if (!tmp_msg) {
1455 rc = -EIO;
1456 goto discard_msg;
1457 }
1458 *tmp_msg = msg;
1459 return -EAGAIN;
1460 }
1461 }
1462 /* else: complete message received */
1463 rc = msgb_l2len(msg);
1464 if (rc == 0) {
1465 /* drop empty message */
1466 rc = -EAGAIN;
1467 goto discard_msg;
1468 }
1469 if (tmp_msg)
1470 *tmp_msg = NULL;
1471 *rmsg = msg;
1472 return rc;
1473
1474discard_msg:
Harald Welte07958e42019-05-03 09:39:10 +02001475 if (tmp_msg)
1476 *tmp_msg = NULL;
1477 msgb_free(msg);
1478 return rc;
1479}
1480
1481#endif /* HAVE_SYS_SOCKET_H */