blob: b89358d4ce8ce1b71e1e0b8582a56ce66571f3ab [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);
180 if (wperiod < 0)
181 return -EINVAL;
182 msgb_tv_put(msg, CBSP_IEI_EMERG_IND, in->u.emergency.indicator);
183 msgb_tv16_put(msg, CBSP_IEI_WARN_TYPE, in->u.emergency.warning_type);
184 msgb_tlv_put(msg, CBSP_IEI_WARN_SEC_INFO, sizeof(in->u.emergency.warning_sec_info),
185 in->u.emergency.warning_sec_info);
186 msgb_tv_put(msg, CBSP_IEI_WARNING_PERIOD, wperiod);
187 }
188 return 0;
189}
190
191/* 8.1.3.2 WRITE REPLACE COMPLETE*/
192static int cbsp_enc_write_repl_compl(struct msgb *msg, const struct osmo_cbsp_write_replace_complete *in)
193{
194 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
195 msgb_tv16_put(msg, CBSP_IEI_NEW_SERIAL_NR, in->new_serial_nr);
196 if (in->old_serial_nr)
197 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, *in->old_serial_nr);
198
199 if (!llist_empty(&in->num_compl_list.list))
200 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
201 if (!llist_empty(&in->cell_list.list))
202 msgb_put_cbsp_cell_list(msg, &in->cell_list);
203 if (in->channel_ind)
204 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
205 return 0;
206}
207
208/* 8.1.3.3 WRITE REPLACE FAILURE */
209static int cbsp_enc_write_repl_fail(struct msgb *msg, const struct osmo_cbsp_write_replace_failure *in)
210{
211 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
212 msgb_tv16_put(msg, CBSP_IEI_NEW_SERIAL_NR, in->new_serial_nr);
213 if (in->old_serial_nr)
214 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, *in->old_serial_nr);
215
216 msgb_put_cbsp_fail_list(msg, &in->fail_list);
217 if (!llist_empty(&in->num_compl_list.list))
218 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
219 if (!llist_empty(&in->cell_list.list))
220 msgb_put_cbsp_cell_list(msg, &in->cell_list);
221 if (in->channel_ind)
222 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
223 return 0;
224}
225
226/* 8.1.3.4 KILL */
227static int cbsp_enc_kill(struct msgb *msg, const struct osmo_cbsp_kill *in)
228{
229 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
230 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
231 msgb_put_cbsp_cell_list(msg, &in->cell_list);
232 if (in->channel_ind)
233 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
234 return 0;
235}
236
237/* 8.1.3.5 KILL COMPLETE */
238static int cbsp_enc_kill_compl(struct msgb *msg, const struct osmo_cbsp_kill_complete *in)
239{
240 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
241 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
242 if (!llist_empty(&in->num_compl_list.list))
243 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
244 if (!llist_empty(&in->cell_list.list))
245 msgb_put_cbsp_cell_list(msg, &in->cell_list);
246 if (in->channel_ind)
247 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
248 return 0;
249}
250
251/* 8.1.3.6 KILL FAILURE */
252static int cbsp_enc_kill_fail(struct msgb *msg, const struct osmo_cbsp_kill_failure *in)
253{
254 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
255 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
256 msgb_put_cbsp_fail_list(msg, &in->fail_list);
257 if (!llist_empty(&in->num_compl_list.list))
258 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
259 if (!llist_empty(&in->cell_list.list))
260 msgb_put_cbsp_cell_list(msg, &in->cell_list);
261 if (in->channel_ind)
262 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
263 return 0;
264}
265
266/* 8.1.3.7 LOAD QUERY */
267static int cbsp_enc_load_query(struct msgb *msg, const struct osmo_cbsp_load_query *in)
268{
269 msgb_put_cbsp_cell_list(msg, &in->cell_list);
270 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
271 return 0;
272}
273
274/* 8.1.3.8 LOAD QUERY COMPLETE */
275static int cbsp_enc_load_query_compl(struct msgb *msg, const struct osmo_cbsp_load_query_complete *in)
276{
277 msgb_put_cbsp_loading_list(msg, &in->loading_list);
278 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
279 return 0;
280}
281
282/* 8.1.3.9 LOAD QUERY FAILURE */
283static int cbsp_enc_load_query_fail(struct msgb *msg, const struct osmo_cbsp_load_query_failure *in)
284{
285 msgb_put_cbsp_fail_list(msg, &in->fail_list);
286 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
287 if (!llist_empty(&in->loading_list.list))
288 msgb_put_cbsp_loading_list(msg, &in->loading_list);
289 return 0;
290}
291
292/* 8.1.3.10 STATUS QUERY */
293static int cbsp_enc_msg_status_query(struct msgb *msg, const struct osmo_cbsp_msg_status_query *in)
294{
295 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
296 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
297 msgb_put_cbsp_cell_list(msg, &in->cell_list);
298 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
299 return 0;
300}
301
302/* 8.1.3.11 STATUS QUERY COMPLETE */
303static int cbsp_enc_msg_status_query_compl(struct msgb *msg,
304 const struct osmo_cbsp_msg_status_query_complete *in)
305{
306 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
307 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
308 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
309 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
310 return 0;
311}
312
313/* 8.1.3.12 STATUS QUERY FAILURE */
314static int cbsp_enc_msg_status_query_fail(struct msgb *msg,
315 const struct osmo_cbsp_msg_status_query_failure *in)
316{
317 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
318 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
319 msgb_put_cbsp_fail_list(msg, &in->fail_list);
320 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
321 if (!llist_empty(&in->num_compl_list.list))
322 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
323 return 0;
324}
325
326/* 8.1.3.16 RESET */
327static int cbsp_enc_reset(struct msgb *msg, const struct osmo_cbsp_reset *in)
328{
329 msgb_put_cbsp_cell_list(msg, &in->cell_list);
330 return 0;
331}
332
333/* 8.1.3.17 RESET COMPLETE */
334static int cbsp_enc_reset_compl(struct msgb *msg, const struct osmo_cbsp_reset_complete *in)
335{
336 msgb_put_cbsp_cell_list(msg, &in->cell_list);
337 return 0;
338}
339
340/* 8.1.3.18 RESET FAILURE */
341static int cbsp_enc_reset_fail(struct msgb *msg, const struct osmo_cbsp_reset_failure *in)
342{
343 msgb_put_cbsp_fail_list(msg, &in->fail_list);
344 if (!llist_empty(&in->cell_list.list))
345 msgb_put_cbsp_cell_list(msg, &in->cell_list);
346 return 0;
347}
348
349/* 8.1.3.18a KEEP ALIVE */
350static int cbsp_enc_keep_alive(struct msgb *msg, const struct osmo_cbsp_keep_alive *in)
351{
352 msgb_tv_put(msg, CBSP_IEI_KEEP_ALIVE_REP_PERIOD, in->repetition_period);
353 return 0;
354}
355
356/* 8.1.3.18b KEEP ALIVE COMPLETE */
357static int cbsp_enc_keep_alive_compl(struct msgb *msg, const struct osmo_cbsp_keep_alive_complete *in)
358{
359 return 0;
360}
361
362/* 8.1.3.19 RESTART */
363static int cbsp_enc_restart(struct msgb *msg, const struct osmo_cbsp_restart *in)
364{
365 msgb_put_cbsp_cell_list(msg, &in->cell_list);
366 msgb_tv_put(msg, CBSP_IEI_BCAST_MSG_TYPE, in->bcast_msg_type);
367 msgb_tv_put(msg, CBSP_IEI_RECOVERY_IND, in->recovery_ind);
368 return 0;
369}
370
371/* 8.1.3.20 FAILURE */
372static int cbsp_enc_failure(struct msgb *msg, const struct osmo_cbsp_failure *in)
373{
374 msgb_put_cbsp_fail_list(msg, &in->fail_list);
375 msgb_tv_put(msg, CBSP_IEI_BCAST_MSG_TYPE, in->bcast_msg_type);
376 return 0;
377}
378
379/* 8.1.3.21 ERROR INDICATION */
380static int cbsp_enc_error_ind(struct msgb *msg, const struct osmo_cbsp_error_ind *in)
381{
382 msgb_tv_put(msg, CBSP_IEI_CAUSE, in->cause);
383 if (in->msg_id)
384 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, *in->msg_id);
385 if (in->new_serial_nr)
386 msgb_tv16_put(msg, CBSP_IEI_NEW_SERIAL_NR, *in->new_serial_nr);
387 if (in->old_serial_nr)
388 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, *in->old_serial_nr);
389 if (in->channel_ind)
390 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
391 return 0;
392}
393
394/*! Encode a CBSP message from the decoded/parsed structure representation to binary PDU.
395 * \param[in] ctx talloc context from which to allocate returned msgb.
396 * \param[in] in decoded CBSP message which is to be encoded. Ownership not transferred.
397 * \return callee-allocated message buffer containing binary CBSP PDU; NULL on error */
398struct msgb *osmo_cbsp_encode(void *ctx, const struct osmo_cbsp_decoded *in)
399{
400 struct msgb *msg = osmo_cbsp_msgb_alloc(ctx, __func__);
401 unsigned int len;
402 int rc;
403
Harald Weltef72155a2019-06-15 23:05:19 +0200404 osmo_cbsp_errstr = NULL;
405
Harald Welte07958e42019-05-03 09:39:10 +0200406 if (!msg)
407 return NULL;
408
409 switch (in->msg_type) {
410 case CBSP_MSGT_WRITE_REPLACE:
411 rc = cbsp_enc_write_repl(msg, &in->u.write_replace);
412 break;
413 case CBSP_MSGT_WRITE_REPLACE_COMPL:
414 rc = cbsp_enc_write_repl_compl(msg, &in->u.write_replace_compl);
415 break;
416 case CBSP_MSGT_WRITE_REPLACE_FAIL:
417 rc = cbsp_enc_write_repl_fail(msg, &in->u.write_replace_fail);
418 break;
419 case CBSP_MSGT_KILL:
420 rc = cbsp_enc_kill(msg, &in->u.kill);
421 break;
422 case CBSP_MSGT_KILL_COMPL:
423 rc = cbsp_enc_kill_compl(msg, &in->u.kill_compl);
424 break;
425 case CBSP_MSGT_KILL_FAIL:
426 rc = cbsp_enc_kill_fail(msg, &in->u.kill_fail);
427 break;
428 case CBSP_MSGT_LOAD_QUERY:
429 rc = cbsp_enc_load_query(msg, &in->u.load_query);
430 break;
431 case CBSP_MSGT_LOAD_QUERY_COMPL:
432 rc = cbsp_enc_load_query_compl(msg, &in->u.load_query_compl);
433 break;
434 case CBSP_MSGT_LOAD_QUERY_FAIL:
435 rc = cbsp_enc_load_query_fail(msg, &in->u.load_query_fail);
436 break;
437 case CBSP_MSGT_MSG_STATUS_QUERY:
438 rc = cbsp_enc_msg_status_query(msg, &in->u.msg_status_query);
439 break;
440 case CBSP_MSGT_MSG_STATUS_QUERY_COMPL:
441 rc = cbsp_enc_msg_status_query_compl(msg, &in->u.msg_status_query_compl);
442 break;
443 case CBSP_MSGT_MSG_STATUS_QUERY_FAIL:
444 rc = cbsp_enc_msg_status_query_fail(msg, &in->u.msg_status_query_fail);
445 break;
446 case CBSP_MSGT_RESET:
447 rc = cbsp_enc_reset(msg, &in->u.reset);
448 break;
449 case CBSP_MSGT_RESET_COMPL:
450 rc = cbsp_enc_reset_compl(msg, &in->u.reset_compl);
451 break;
452 case CBSP_MSGT_RESET_FAIL:
453 rc = cbsp_enc_reset_fail(msg, &in->u.reset_fail);
454 break;
455 case CBSP_MSGT_RESTART:
456 rc = cbsp_enc_restart(msg, &in->u.restart);
457 break;
458 case CBSP_MSGT_FAILURE:
459 rc = cbsp_enc_failure(msg, &in->u.failure);
460 break;
461 case CBSP_MSGT_ERROR_IND:
462 rc = cbsp_enc_error_ind(msg, &in->u.error_ind);
463 break;
464 case CBSP_MSGT_KEEP_ALIVE:
465 rc = cbsp_enc_keep_alive(msg, &in->u.keep_alive);
466 break;
467 case CBSP_MSGT_KEEP_ALIVE_COMPL:
468 rc = cbsp_enc_keep_alive_compl(msg, &in->u.keep_alive_compl);
469 break;
470 case CBSP_MSGT_SET_DRX:
471 case CBSP_MSGT_SET_DRX_COMPL:
472 case CBSP_MSGT_SET_DRX_FAIL:
Harald Weltef72155a2019-06-15 23:05:19 +0200473 osmo_cbsp_errstr = "message type not implemented";
Harald Welte07958e42019-05-03 09:39:10 +0200474 rc = -1;
475 break;
476 default:
Harald Weltef72155a2019-06-15 23:05:19 +0200477 osmo_cbsp_errstr = "message type not known in spec";
Harald Welte07958e42019-05-03 09:39:10 +0200478 rc = -1;
479 break;
480 }
481
482 if (rc < 0) {
483 msgb_free(msg);
484 return NULL;
485 }
486
487 /* push header in front */
488 len = msgb_length(msg);
489 msgb_push_u8(msg, len & 0xff);
490 msgb_push_u8(msg, (len >> 8) & 0xff);
491 msgb_push_u8(msg, (len >> 16) & 0xff);
492 msgb_push_u8(msg, in->msg_type);
493
494 return msg;
495}
496
497/***********************************************************************
498 * IE Decoding
499 ***********************************************************************/
500
501/* 8.2.6 Cell List */
502static int cbsp_decode_cell_list(struct osmo_cbsp_cell_list *cl, void *ctx,
503 const uint8_t *buf, unsigned int len)
504{
505 const uint8_t *cur = buf;
506 int rc;
507
508 cl->id_discr = *cur++;
509
510 while (cur < buf + len) {
511 struct osmo_cbsp_cell_ent *ent = talloc_zero(ctx, struct osmo_cbsp_cell_ent);
512 unsigned int len_remain = len - (cur - buf);
513 OSMO_ASSERT(ent);
514 rc = gsm0808_decode_cell_id_u(&ent->cell_id, cl->id_discr, cur, len_remain);
Harald Weltef72155a2019-06-15 23:05:19 +0200515 if (rc < 0) {
516 osmo_cbsp_errstr = "cell list: error decoding cell_id_union";
Harald Welte07958e42019-05-03 09:39:10 +0200517 return rc;
Harald Weltef72155a2019-06-15 23:05:19 +0200518 }
Harald Weltef2210032019-08-31 21:25:05 +0200519 cur += gsm0808_cell_id_size(cl->id_discr);
Harald Welte07958e42019-05-03 09:39:10 +0200520 llist_add_tail(&ent->list, &cl->list);
521 }
522 return 0;
523}
524
525/* 8.2.11 Failure List (discriminator per entry) */
526static int cbsp_decode_fail_list(struct llist_head *fl, void *ctx,
527 const uint8_t *buf, unsigned int len)
528{
529 const uint8_t *cur = buf;
530 int rc;
531
532 while (cur < buf + len) {
533 struct osmo_cbsp_fail_ent *ent = talloc_zero(ctx, struct osmo_cbsp_fail_ent);
534 unsigned int len_remain = len - (cur - buf);
535 OSMO_ASSERT(ent);
536 ent->id_discr = cur[0];
537 rc = gsm0808_decode_cell_id_u(&ent->cell_id, ent->id_discr, cur+1, len_remain-1);
Harald Weltef72155a2019-06-15 23:05:19 +0200538 if (rc < 0) {
539 osmo_cbsp_errstr = "fail list: error decoding cell_id_union";
Harald Welte07958e42019-05-03 09:39:10 +0200540 return rc;
Harald Weltef72155a2019-06-15 23:05:19 +0200541 }
Harald Weltef2210032019-08-31 21:25:05 +0200542 cur += gsm0808_cell_id_size(ent->id_discr);
Harald Welte07958e42019-05-03 09:39:10 +0200543 ent->cause = *cur++;
544 llist_add_tail(&ent->list, fl);
545 }
546 return 0;
547}
548
549/* 8.2.12 Radio Resource Loading List */
550static int cbsp_decode_loading_list(struct osmo_cbsp_loading_list *ll, void *ctx,
551 const uint8_t *buf, unsigned int len)
552{
553 const uint8_t *cur = buf;
554 int rc;
555
556 ll->id_discr = *cur++;
557 while (cur < buf + len) {
558 struct osmo_cbsp_loading_ent *ent = talloc_zero(ctx, struct osmo_cbsp_loading_ent);
559 unsigned int len_remain = len - (cur - buf);
560 OSMO_ASSERT(ent);
561 rc = gsm0808_decode_cell_id_u(&ent->cell_id, ll->id_discr, cur, len_remain);
Harald Weltef72155a2019-06-15 23:05:19 +0200562 if (rc < 0) {
563 osmo_cbsp_errstr = "load list: error decoding cell_id_union";
Harald Welte07958e42019-05-03 09:39:10 +0200564 return rc;
Harald Weltef72155a2019-06-15 23:05:19 +0200565 }
Harald Weltef2210032019-08-31 21:25:05 +0200566 cur += gsm0808_cell_id_size(ll->id_discr);
Harald Welte07958e42019-05-03 09:39:10 +0200567 if (cur + 2 > buf + len) {
568 talloc_free(ent);
Harald Weltef72155a2019-06-15 23:05:19 +0200569 osmo_cbsp_errstr = "load list: truncated IE";
Harald Welte07958e42019-05-03 09:39:10 +0200570 return -EINVAL;
571 }
572 ent->load[0] = *cur++;
573 ent->load[1] = *cur++;
574 llist_add_tail(&ent->list, &ll->list);
575 }
576 return 0;
577}
578
579/* 8.2.10 Completed List */
580static int cbsp_decode_num_compl_list(struct osmo_cbsp_num_compl_list *cl, void *ctx,
581 const uint8_t *buf, unsigned int len)
582{
583 const uint8_t *cur = buf;
584 int rc;
585
586 cl->id_discr = *cur++;
587 while (cur < buf + len) {
588 struct osmo_cbsp_num_compl_ent *ent = talloc_zero(ctx, struct osmo_cbsp_num_compl_ent);
589 unsigned int len_remain = len - (cur - buf);
590 OSMO_ASSERT(ent);
591 rc = gsm0808_decode_cell_id_u(&ent->cell_id, cl->id_discr, cur, len_remain);
Harald Weltef72155a2019-06-15 23:05:19 +0200592 if (rc < 0) {
593 osmo_cbsp_errstr = "completed list: error decoding cell_id_union";
Harald Welte07958e42019-05-03 09:39:10 +0200594 return rc;
Harald Weltef72155a2019-06-15 23:05:19 +0200595 }
Harald Weltef2210032019-08-31 21:25:05 +0200596 cur += gsm0808_cell_id_size(cl->id_discr);
Harald Welte07958e42019-05-03 09:39:10 +0200597 if (cur + 3 > buf + len) {
598 talloc_free(ent);
Harald Weltef72155a2019-06-15 23:05:19 +0200599 osmo_cbsp_errstr = "completed list: truncated IE";
Harald Welte07958e42019-05-03 09:39:10 +0200600 return -EINVAL;
601 }
602 ent->num_compl = osmo_load16be(cur); cur += 2;
603 ent->num_bcast_info = *cur++;
604 llist_add_tail(&ent->list, &cl->list);
605 }
606 return 0;
607}
608
609/* 8.2.25 */
610static uint32_t decode_wperiod(uint8_t in)
611{
612 if (in == 0x00)
613 return 0xffffffff; /* infinite */
614 if (in <= 10)
615 return in;
616 if (in <= 20)
617 return 10 + (in - 10)*2;
618 if (in <= 38)
619 return 30 + (in - 20)*5;
620 if (in <= 86)
621 return 120 + (in - 38)*10;
622 if (in <= 186)
623 return 600 + (in - 86)*30;
624 else
625 return 0;
626}
627
628
629/***********************************************************************
630 * Message Decoding
631 ***********************************************************************/
632
633/* 8.1.3.1 WRITE REPLACE */
634static int cbsp_dec_write_repl(struct osmo_cbsp_write_replace *out, const struct tlv_parsed *tp,
635 struct msgb *in, void *ctx)
636{
637 unsigned int i;
638
639 /* check for mandatory IEs */
640 if (!TLVP_PRESENT(tp, CBSP_IEI_MSG_ID) ||
641 !TLVP_PRESENT(tp, CBSP_IEI_NEW_SERIAL_NR) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200642 !TLVP_PRESENT(tp, CBSP_IEI_CELL_LIST)) {
643 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200644 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200645 }
Harald Welte07958e42019-05-03 09:39:10 +0200646
647 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
648 out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
649 if (TLVP_PRESENT(tp, CBSP_IEI_OLD_SERIAL_NR)) {
650 out->old_serial_nr = talloc(ctx, uint16_t);
651 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
652 }
653
654 INIT_LLIST_HEAD(&out->cell_list.list);
655 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
656 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
657
658 if (TLVP_PRESENT(tp, CBSP_IEI_CHANNEL_IND)) {
659 uint8_t num_of_pages;
660 INIT_LLIST_HEAD(&out->u.cbs.msg_content);
Harald Weltef72155a2019-06-15 23:05:19 +0200661 if (TLVP_PRESENT(tp, CBSP_IEI_EMERG_IND)) {
662 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200663 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200664 }
Harald Welte07958e42019-05-03 09:39:10 +0200665 if (!TLVP_PRESENT(tp, CBSP_IEI_CATEGORY) ||
666 !TLVP_PRESENT(tp, CBSP_IEI_REP_PERIOD) ||
667 !TLVP_PRESENT(tp, CBSP_IEI_NUM_BCAST_REQ) ||
668 !TLVP_PRESENT(tp, CBSP_IEI_NUM_OF_PAGES) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200669 !TLVP_PRESENT(tp, CBSP_IEI_DCS)) {
670 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200671 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200672 }
Harald Welte07958e42019-05-03 09:39:10 +0200673 out->is_cbs = true;
674 out->u.cbs.channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
675 out->u.cbs.category = *TLVP_VAL(tp, CBSP_IEI_CATEGORY);
676 out->u.cbs.rep_period = tlvp_val16be(tp, CBSP_IEI_REP_PERIOD);
677 out->u.cbs.num_bcast_req = tlvp_val16be(tp, CBSP_IEI_NUM_BCAST_REQ);
678 num_of_pages = *TLVP_VAL(tp, CBSP_IEI_NUM_OF_PAGES);
679 if (num_of_pages < 1)
680 return -EINVAL;
681 /* parse pages */
682 for (i = 0; i < num_of_pages; i++) {
683 const uint8_t *ie = TLVP_VAL(&tp[i], CBSP_IEI_MSG_CONTENT);
684 struct osmo_cbsp_content *page;
Harald Weltef72155a2019-06-15 23:05:19 +0200685 if (!ie) {
686 osmo_cbsp_errstr = "insufficient message content IEs";
Harald Welte07958e42019-05-03 09:39:10 +0200687 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200688 }
Harald Welte07958e42019-05-03 09:39:10 +0200689 page = talloc_zero(ctx, struct osmo_cbsp_content);
690 OSMO_ASSERT(page);
Harald Weltee674c442019-09-01 22:30:58 +0200691 page->user_len = ie[0]; /* length byte before payload */
692 memcpy(page->data, ie+1, sizeof(page->data));
Harald Welte07958e42019-05-03 09:39:10 +0200693 llist_add_tail(&page->list, &out->u.cbs.msg_content);
694 }
695 } else {
696 if (!TLVP_PRES_LEN(tp, CBSP_IEI_EMERG_IND, 1) ||
697 !TLVP_PRES_LEN(tp, CBSP_IEI_WARN_TYPE, 2) ||
698 !TLVP_PRES_LEN(tp, CBSP_IEI_WARN_SEC_INFO, 50) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200699 !TLVP_PRES_LEN(tp, CBSP_IEI_WARNING_PERIOD, 1)) {
700 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200701 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200702 }
Harald Welte07958e42019-05-03 09:39:10 +0200703 out->u.emergency.indicator = *TLVP_VAL(tp, CBSP_IEI_EMERG_IND);
704 out->u.emergency.warning_type = tlvp_val16be(tp, CBSP_IEI_WARN_TYPE);
705 memcpy(&out->u.emergency.warning_sec_info, TLVP_VAL(tp, CBSP_IEI_WARN_SEC_INFO),
706 sizeof(out->u.emergency.warning_sec_info));
707 out->u.emergency.warning_period = decode_wperiod(*TLVP_VAL(tp, CBSP_IEI_WARNING_PERIOD));
708 }
709 return 0;
710}
711
712/* 8.1.3.2 WRITE REPLACE COMPLETE*/
713static int cbsp_dec_write_repl_compl(struct osmo_cbsp_write_replace_complete *out,
714 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
715{
716 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200717 !TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2)) {
718 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200719 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200720 }
Harald Welte07958e42019-05-03 09:39:10 +0200721
722 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
723 out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
724 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
725 out->old_serial_nr = talloc(ctx, uint16_t);
726 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
727 }
728
729 INIT_LLIST_HEAD(&out->num_compl_list.list);
730 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
731 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
732 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
733 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
734 }
735
736 INIT_LLIST_HEAD(&out->cell_list.list);
737 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
738 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
739
740 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
741 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
742 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
743 }
744 return 0;
745}
746
747/* 8.1.3.3 WRITE REPLACE FAILURE */
748static int cbsp_dec_write_repl_fail(struct osmo_cbsp_write_replace_failure *out,
749 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
750{
751 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
752 !TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200753 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
754 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200755 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200756 }
Harald Welte07958e42019-05-03 09:39:10 +0200757
758 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
759 out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
760 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
761 out->old_serial_nr = talloc(ctx, uint16_t);
762 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
763 }
764
765 INIT_LLIST_HEAD(&out->fail_list);
766 cbsp_decode_fail_list(&out->fail_list, ctx,
767 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
768 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
769
770 INIT_LLIST_HEAD(&out->num_compl_list.list);
771 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
772 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
773 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
774 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
775 }
776
777 INIT_LLIST_HEAD(&out->cell_list.list);
778 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
779 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
780 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
781 }
782
783 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
784 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
785 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
786 }
787 return 0;
788}
789
790/* 8.1.3.4 KILL */
791static int cbsp_dec_kill(struct osmo_cbsp_kill *out, const struct tlv_parsed *tp,
792 struct msgb *in, void *ctx)
793{
794 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
795 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200796 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
797 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200798 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200799 }
800
Harald Welte07958e42019-05-03 09:39:10 +0200801 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
802 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
803
804 INIT_LLIST_HEAD(&out->cell_list.list);
805 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
806 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
807
808 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
809 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
810 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
811 }
812 return 0;
813}
814
815/* 8.1.3.5 KILL COMPLETE */
816static int cbsp_dec_kill_compl(struct osmo_cbsp_kill_complete *out, const struct tlv_parsed *tp,
817 struct msgb *in, void *ctx)
818{
819 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
820 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200821 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
822 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200823 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200824 }
Harald Welte07958e42019-05-03 09:39:10 +0200825
826 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
827 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
828
829 INIT_LLIST_HEAD(&out->num_compl_list.list);
830 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
831 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
832 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
833 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
834 }
835
836 INIT_LLIST_HEAD(&out->cell_list.list);
837 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
838 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
839
840 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
841 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
842 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
843 }
844 return 0;
845}
846
847/* 8.1.3.6 KILL FAILURE */
848static int cbsp_dec_kill_fail(struct osmo_cbsp_kill_failure *out, const struct tlv_parsed *tp,
849 struct msgb *in, void *ctx)
850{
851 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
852 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200853 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
854 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200855 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200856 }
Harald Welte07958e42019-05-03 09:39:10 +0200857
858 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
859 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
860
861 INIT_LLIST_HEAD(&out->fail_list);
862 cbsp_decode_fail_list(&out->fail_list, ctx,
863 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
864 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
865
866 INIT_LLIST_HEAD(&out->num_compl_list.list);
867 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
868 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
869 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
870 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
871 }
872
873 INIT_LLIST_HEAD(&out->cell_list.list);
874 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
875 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
876 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
877 }
878
879 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
880 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
881 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
882 }
883 return 0;
884}
885
886/* 8.1.3.7 LOAD QUERY */
887static int cbsp_dec_load_query(struct osmo_cbsp_load_query *out, const struct tlv_parsed *tp,
888 struct msgb *in, void *ctx)
889{
890 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200891 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
892 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200893 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200894 }
Harald Welte07958e42019-05-03 09:39:10 +0200895
896 INIT_LLIST_HEAD(&out->cell_list.list);
897 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
898 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
899
900 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
901 return 0;
902}
903
904/* 8.1.3.8 LOAD QUERY COMPLETE */
905static int cbsp_dec_load_query_compl(struct osmo_cbsp_load_query_complete *out,
906 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
907{
908 if (!TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200909 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
910 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200911 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200912 }
Harald Welte07958e42019-05-03 09:39:10 +0200913
914 INIT_LLIST_HEAD(&out->loading_list.list);
915 cbsp_decode_loading_list(&out->loading_list, ctx,
916 TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
917 TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
918
919 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
920 return 0;
921}
922
923/* 8.1.3.9 LOAD QUERY FAILURE */
924static int cbsp_dec_load_query_fail(struct osmo_cbsp_load_query_failure *out,
925 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
926{
927 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200928 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
929 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200930 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200931 }
Harald Welte07958e42019-05-03 09:39:10 +0200932
933 INIT_LLIST_HEAD(&out->fail_list);
934 cbsp_decode_fail_list(&out->fail_list, ctx,
935 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
936 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
937
938 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
939
940 INIT_LLIST_HEAD(&out->loading_list.list);
941 if (TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6)) {
942 cbsp_decode_loading_list(&out->loading_list, ctx,
943 TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
944 TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
945 }
946 return 0;
947}
948
949/* 8.1.3.10 STATUS QUERY */
950static int cbsp_dec_msg_status_query(struct osmo_cbsp_msg_status_query *out,
951 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
952{
953 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
954 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
955 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200956 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
957 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200958 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200959 }
960
Harald Welte07958e42019-05-03 09:39:10 +0200961 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
962 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
963
964 INIT_LLIST_HEAD(&out->cell_list.list);
965 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
966 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
967
968 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
969 return 0;
970}
971
972/* 8.1.3.11 STATUS QUERY COMPLETE */
973static int cbsp_dec_msg_status_query_compl(struct osmo_cbsp_msg_status_query_complete *out,
974 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
975{
976 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
977 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
978 !TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200979 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
980 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200981 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200982 }
Harald Welte07958e42019-05-03 09:39:10 +0200983
984 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
985 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
986
987 INIT_LLIST_HEAD(&out->num_compl_list.list);
988 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
989 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
990 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
991 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
992 return 0;
993}
994
995/* 8.1.3.12 STATUS QUERY FAILURE */
996static int cbsp_dec_msg_status_query_fail(struct osmo_cbsp_msg_status_query_failure *out,
997 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
998{
999 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
1000 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
1001 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
Harald Weltef72155a2019-06-15 23:05:19 +02001002 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
1003 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001004 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001005 }
Harald Welte07958e42019-05-03 09:39:10 +02001006
1007 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
1008 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
1009
1010 INIT_LLIST_HEAD(&out->fail_list);
1011 cbsp_decode_fail_list(&out->fail_list, ctx,
1012 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
1013 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
1014
1015 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
1016
1017 INIT_LLIST_HEAD(&out->num_compl_list.list);
1018 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
1019 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
1020 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
1021 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
1022 }
1023 return 0;
1024}
1025
1026/* 8.1.3.16 RESET */
1027static int cbsp_dec_reset(struct osmo_cbsp_reset *out, const struct tlv_parsed *tp,
1028 struct msgb *in, void *ctx)
1029{
Harald Weltef72155a2019-06-15 23:05:19 +02001030 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
1031 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001032 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001033 }
Harald Welte07958e42019-05-03 09:39:10 +02001034
1035 INIT_LLIST_HEAD(&out->cell_list.list);
1036 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1037 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1038 return 0;
1039}
1040
1041/* 8.1.3.17 RESET COMPLETE */
1042static int cbsp_dec_reset_compl(struct osmo_cbsp_reset_complete *out, const struct tlv_parsed *tp,
1043 struct msgb *in, void *ctx)
1044{
Harald Weltef72155a2019-06-15 23:05:19 +02001045 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
1046 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001047 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001048 }
Harald Welte07958e42019-05-03 09:39:10 +02001049
1050 INIT_LLIST_HEAD(&out->cell_list.list);
1051 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1052 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1053 return 0;
1054}
1055
1056/* 8.1.3.18 RESET FAILURE */
1057static int cbsp_dec_reset_fail(struct osmo_cbsp_reset_failure *out, const struct tlv_parsed *tp,
1058 struct msgb *in, void *ctx)
1059{
Harald Weltef72155a2019-06-15 23:05:19 +02001060 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
1061 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001062 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001063 }
Harald Welte07958e42019-05-03 09:39:10 +02001064
1065 INIT_LLIST_HEAD(&out->fail_list);
1066 cbsp_decode_fail_list(&out->fail_list, ctx,
1067 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
1068 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
1069
1070 INIT_LLIST_HEAD(&out->cell_list.list);
1071 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
1072 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1073 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1074 }
1075 return 0;
1076}
1077
1078/* 8.1.3.18a KEEP ALIVE */
1079static int cbsp_dec_keep_alive(struct osmo_cbsp_keep_alive *out, const struct tlv_parsed *tp,
1080 struct msgb *in, void *ctx)
1081{
Harald Weltef72155a2019-06-15 23:05:19 +02001082 if (!TLVP_PRES_LEN(tp, CBSP_IEI_KEEP_ALIVE_REP_PERIOD, 1)) {
1083 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001084 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001085 }
Harald Welte07958e42019-05-03 09:39:10 +02001086
1087 out->repetition_period = *TLVP_VAL(tp, CBSP_IEI_KEEP_ALIVE_REP_PERIOD);
1088 return 0;
1089}
1090
1091/* 8.1.3.18b KEEP ALIVE COMPLETE */
1092static int cbsp_dec_keep_alive_compl(struct osmo_cbsp_keep_alive_complete *out,
1093 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
1094{
1095 return 0;
1096}
1097
1098/* 8.1.3.19 RESTART */
1099static int cbsp_dec_restart(struct osmo_cbsp_restart *out, const struct tlv_parsed *tp,
1100 struct msgb *in, void *ctx)
1101{
1102 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
1103 !TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1) ||
Harald Weltef72155a2019-06-15 23:05:19 +02001104 !TLVP_PRES_LEN(tp, CBSP_IEI_RECOVERY_IND, 1)) {
1105 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001106 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001107 }
Harald Welte07958e42019-05-03 09:39:10 +02001108
1109 INIT_LLIST_HEAD(&out->cell_list.list);
1110 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1111 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1112
1113 out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE);
1114 out->recovery_ind = *TLVP_VAL(tp, CBSP_IEI_RECOVERY_IND);
1115 return 0;
1116}
1117
1118/* 8.1.3.20 FAILURE */
1119static int cbsp_dec_failure(struct osmo_cbsp_failure *out, const struct tlv_parsed *tp,
1120 struct msgb *in, void *ctx)
1121{
1122 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
Harald Weltef72155a2019-06-15 23:05:19 +02001123 !TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1)) {
1124 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001125 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001126 }
Harald Welte07958e42019-05-03 09:39:10 +02001127
1128 INIT_LLIST_HEAD(&out->fail_list);
1129 cbsp_decode_fail_list(&out->fail_list, ctx,
1130 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
1131 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
1132
1133 out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE);
1134 return 0;
1135}
1136
1137/* 8.1.3.21 ERROR INDICATION */
1138static int cbsp_dec_error_ind(struct osmo_cbsp_error_ind *out, const struct tlv_parsed *tp,
1139 struct msgb *in, void *ctx)
1140{
Harald Weltef72155a2019-06-15 23:05:19 +02001141 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CAUSE, 1)) {
1142 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001143 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001144 }
Harald Welte07958e42019-05-03 09:39:10 +02001145
1146 out->cause = *TLVP_VAL(tp, CBSP_IEI_CAUSE);
1147 if (TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2)) {
1148 out->msg_id = talloc(ctx, uint16_t);
1149 *out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
1150 }
1151 if (TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2)) {
1152 out->new_serial_nr = talloc(ctx, uint16_t);
1153 *out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
1154 }
1155 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
1156 out->old_serial_nr = talloc(ctx, uint16_t);
1157 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
1158 }
1159 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
1160 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
1161 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
1162 }
1163 return 0;
1164}
1165
1166/*! Decode a CBSP message from wire formwat to pased structure.
1167 * \param[in] ctx talloc context from which to allocate decoded output.
1168 * \param[in] in message buffer contiaining binary CBSP message.
1169 * \returns callee-allocated decoded representation of CBSP message; NULL on error */
1170struct osmo_cbsp_decoded *osmo_cbsp_decode(void *ctx, struct msgb *in)
1171{
1172 struct osmo_cbsp_decoded *out = talloc_zero(ctx, struct osmo_cbsp_decoded);
1173 const struct cbsp_header *h = msgb_l1(in);
1174 struct tlv_parsed tp[16]; /* max. number of pages in a given CBS message */
1175 unsigned int len;
1176 int rc;
1177
Harald Weltef72155a2019-06-15 23:05:19 +02001178 osmo_cbsp_errstr = NULL;
1179
Harald Welte07958e42019-05-03 09:39:10 +02001180 if (!out)
1181 return NULL;
1182
1183 if (msgb_l1len(in) < sizeof(*h)) {
1184 goto out_err;
1185 }
1186 len = h->len[0] << 16 | h->len[1] << 8 | h->len[2];
1187
1188 /* discard messages where indicated length is more than we have */
1189 if (len > msgb_l2len(in)) {
1190 goto out_err;
1191 }
1192
1193 /* trim any messages with extra payload at the end */
1194 if (len < msgb_l2len(in))
1195 msgb_trim(in, (in->l2h - in->data) + msgb_l2len(in));
1196 out->msg_type = h->msg_type;
1197
1198 rc = tlv_parse2(tp, ARRAY_SIZE(tp), &cbsp_att_tlvdef, msgb_l2(in), msgb_l2len(in), 0, 0);
1199 if (rc < 0) {
1200 goto out_err;
1201 }
1202
1203 switch (h->msg_type) {
1204 case CBSP_MSGT_WRITE_REPLACE:
1205 rc = cbsp_dec_write_repl(&out->u.write_replace, tp, in, out);
1206 break;
1207 case CBSP_MSGT_WRITE_REPLACE_COMPL:
1208 rc = cbsp_dec_write_repl_compl(&out->u.write_replace_compl, tp, in, out);
1209 break;
1210 case CBSP_MSGT_WRITE_REPLACE_FAIL:
1211 rc = cbsp_dec_write_repl_fail(&out->u.write_replace_fail, tp, in, out);
1212 break;
1213 case CBSP_MSGT_KILL:
1214 rc = cbsp_dec_kill(&out->u.kill, tp, in, out);
1215 break;
1216 case CBSP_MSGT_KILL_COMPL:
1217 rc = cbsp_dec_kill_compl(&out->u.kill_compl, tp, in, out);
1218 break;
1219 case CBSP_MSGT_KILL_FAIL:
1220 rc = cbsp_dec_kill_fail(&out->u.kill_fail, tp, in, out);
1221 break;
1222 case CBSP_MSGT_LOAD_QUERY:
1223 rc = cbsp_dec_load_query(&out->u.load_query, tp, in, out);
1224 break;
1225 case CBSP_MSGT_LOAD_QUERY_COMPL:
1226 rc = cbsp_dec_load_query_compl(&out->u.load_query_compl, tp, in, out);
1227 break;
1228 case CBSP_MSGT_LOAD_QUERY_FAIL:
1229 rc = cbsp_dec_load_query_fail(&out->u.load_query_fail, tp, in, out);
1230 break;
1231 case CBSP_MSGT_MSG_STATUS_QUERY:
1232 rc = cbsp_dec_msg_status_query(&out->u.msg_status_query, tp, in, out);
1233 break;
1234 case CBSP_MSGT_MSG_STATUS_QUERY_COMPL:
1235 rc = cbsp_dec_msg_status_query_compl(&out->u.msg_status_query_compl, tp, in, out);
1236 break;
1237 case CBSP_MSGT_MSG_STATUS_QUERY_FAIL:
1238 rc = cbsp_dec_msg_status_query_fail(&out->u.msg_status_query_fail, tp, in, out);
1239 break;
1240 case CBSP_MSGT_RESET:
1241 rc = cbsp_dec_reset(&out->u.reset, tp, in, out);
1242 break;
1243 case CBSP_MSGT_RESET_COMPL:
1244 rc = cbsp_dec_reset_compl(&out->u.reset_compl, tp, in, out);
1245 break;
1246 case CBSP_MSGT_RESET_FAIL:
1247 rc = cbsp_dec_reset_fail(&out->u.reset_fail, tp, in, out);
1248 break;
1249 case CBSP_MSGT_RESTART:
1250 rc = cbsp_dec_restart(&out->u.restart, tp, in, out);
1251 break;
1252 case CBSP_MSGT_FAILURE:
1253 rc = cbsp_dec_failure(&out->u.failure, tp, in, out);
1254 break;
1255 case CBSP_MSGT_ERROR_IND:
1256 rc = cbsp_dec_error_ind(&out->u.error_ind, tp, in, out);
1257 break;
1258 case CBSP_MSGT_KEEP_ALIVE:
1259 rc = cbsp_dec_keep_alive(&out->u.keep_alive, tp, in, out);
1260 break;
1261 case CBSP_MSGT_KEEP_ALIVE_COMPL:
1262 rc = cbsp_dec_keep_alive_compl(&out->u.keep_alive_compl, tp, in, out);
1263 break;
1264 case CBSP_MSGT_SET_DRX:
1265 case CBSP_MSGT_SET_DRX_COMPL:
1266 case CBSP_MSGT_SET_DRX_FAIL:
Harald Weltef72155a2019-06-15 23:05:19 +02001267 osmo_cbsp_errstr = "message type not implemented";
Harald Welte07958e42019-05-03 09:39:10 +02001268 rc = -1;
1269 break;
1270 default:
Harald Weltef72155a2019-06-15 23:05:19 +02001271 osmo_cbsp_errstr = "message type not known in spec";
Harald Welte07958e42019-05-03 09:39:10 +02001272 rc = -1;
1273 break;
1274 }
1275
1276 if (rc < 0) {
1277 goto out_err;
1278 }
1279
1280 return out;
1281
1282out_err:
1283 talloc_free(out);
1284 return NULL;
1285}
1286
1287/* initialization of 'decoded' structure of given message type */
1288void osmo_cbsp_init_struct(struct osmo_cbsp_decoded *cbsp, enum cbsp_msg_type msg_type)
1289{
1290 memset(cbsp, 0, sizeof(*cbsp));
1291 cbsp->msg_type = msg_type;
1292
1293 switch (msg_type) {
1294 case CBSP_MSGT_WRITE_REPLACE:
1295 INIT_LLIST_HEAD(&cbsp->u.write_replace.cell_list.list);
1296 break;
1297 case CBSP_MSGT_WRITE_REPLACE_COMPL:
1298 INIT_LLIST_HEAD(&cbsp->u.write_replace_compl.num_compl_list.list);
1299 INIT_LLIST_HEAD(&cbsp->u.write_replace_compl.cell_list.list);
1300 break;
1301 case CBSP_MSGT_WRITE_REPLACE_FAIL:
1302 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.fail_list);
1303 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.num_compl_list.list);
1304 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.cell_list.list);
1305 break;
1306 case CBSP_MSGT_KILL:
1307 INIT_LLIST_HEAD(&cbsp->u.kill.cell_list.list);
1308 break;
1309 case CBSP_MSGT_KILL_COMPL:
1310 INIT_LLIST_HEAD(&cbsp->u.kill_compl.num_compl_list.list);
1311 INIT_LLIST_HEAD(&cbsp->u.kill_compl.cell_list.list);
1312 break;
1313 case CBSP_MSGT_KILL_FAIL:
1314 INIT_LLIST_HEAD(&cbsp->u.kill_fail.fail_list);
1315 INIT_LLIST_HEAD(&cbsp->u.kill_fail.num_compl_list.list);
1316 INIT_LLIST_HEAD(&cbsp->u.kill_fail.cell_list.list);
1317 break;
1318 case CBSP_MSGT_LOAD_QUERY:
1319 INIT_LLIST_HEAD(&cbsp->u.load_query.cell_list.list);
1320 break;
1321 case CBSP_MSGT_LOAD_QUERY_COMPL:
1322 INIT_LLIST_HEAD(&cbsp->u.load_query_compl.loading_list.list);
1323 break;
1324 case CBSP_MSGT_LOAD_QUERY_FAIL:
1325 INIT_LLIST_HEAD(&cbsp->u.load_query_fail.fail_list);
1326 break;
1327 case CBSP_MSGT_MSG_STATUS_QUERY:
1328 INIT_LLIST_HEAD(&cbsp->u.msg_status_query.cell_list.list);
1329 break;
1330 case CBSP_MSGT_MSG_STATUS_QUERY_COMPL:
1331 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_compl.num_compl_list.list);
1332 break;
1333 case CBSP_MSGT_MSG_STATUS_QUERY_FAIL:
1334 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_fail.fail_list);
1335 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_fail.num_compl_list.list);
1336 break;
1337 case CBSP_MSGT_RESET:
1338 INIT_LLIST_HEAD(&cbsp->u.reset.cell_list.list);
1339 break;
1340 case CBSP_MSGT_RESET_COMPL:
1341 INIT_LLIST_HEAD(&cbsp->u.reset_compl.cell_list.list);
1342 break;
1343 case CBSP_MSGT_RESET_FAIL:
1344 INIT_LLIST_HEAD(&cbsp->u.reset_fail.fail_list);
1345 INIT_LLIST_HEAD(&cbsp->u.reset_fail.cell_list.list);
1346 break;
1347 case CBSP_MSGT_RESTART:
1348 INIT_LLIST_HEAD(&cbsp->u.restart.cell_list.list);
1349 break;
1350 case CBSP_MSGT_FAILURE:
1351 INIT_LLIST_HEAD(&cbsp->u.failure.fail_list);
1352 break;
1353 default:
1354 break;
1355 }
1356}
1357
1358/*! Dynamically allocate and initialize decoded CBSP structure.
1359 * \param[in] ctx talloc context from which to allocate
1360 * \param[in] msg_type CBSP message type for which to initialize result
1361 * \returns allocated + initialized decoded CBSP structure; NULL on talloc failure */
1362struct osmo_cbsp_decoded *osmo_cbsp_decoded_alloc(void *ctx, enum cbsp_msg_type msg_type)
1363{
1364 struct osmo_cbsp_decoded *cbsp = talloc_zero(ctx, struct osmo_cbsp_decoded);
1365 if (!cbsp)
1366 return NULL;
1367 osmo_cbsp_init_struct(cbsp, msg_type);
1368 return cbsp;
1369}
1370
1371/***********************************************************************
1372 * Message Reception
1373 ***********************************************************************/
1374
1375#ifdef HAVE_SYS_SOCKET_H
1376#include <sys/socket.h>
1377
1378/*! Read one CBSP message from socket fd or store part if still not fully received.
1379 * \param[in] ctx talloc context from which to allocate new msgb.
1380 * \param[in] fd The fd for the socket to read from.
1381 * \param[out] rmsg internally allocated msgb containing a fully received CBSP message.
1382 * \param[inout] tmp_msg internally allocated msgb caching data for not yet fully received message.
1383 *
1384 * Function is designed just like ipa_msg_recv_buffered()
1385 */
1386int osmo_cbsp_recv_buffered(void *ctx, int fd, struct msgb **rmsg, struct msgb **tmp_msg)
1387{
1388 struct msgb *msg = tmp_msg ? *tmp_msg : NULL;
1389 struct cbsp_header *h;
1390 int len, rc;
1391 int needed;
1392
1393 if (!msg) {
1394 msg = osmo_cbsp_msgb_alloc(ctx, __func__);
Harald Weltec30d8be2019-07-21 07:51:33 +02001395 if (!msg)
Harald Welte07958e42019-05-03 09:39:10 +02001396 return -ENOMEM;
Harald Welte07958e42019-05-03 09:39:10 +02001397 msg->l1h = msg->tail;
1398 }
1399
1400 if (msg->l2h == NULL) {
1401 /* first read the [missing part of the] header */
1402 needed = sizeof(*h) - msg->len;
1403 rc = recv(fd, msg->tail, needed, 0);
1404 if (rc == 0)
1405 goto discard_msg;
1406 else if (rc < 0) {
1407 if (errno == EAGAIN || errno == EINTR)
1408 rc = 0;
1409 else {
1410 rc = -errno;
1411 goto discard_msg;
1412 }
1413 }
1414 msgb_put(msg, rc);
1415 if (rc < needed) {
1416 if (msg->len == 0) {
1417 rc = -EAGAIN;
1418 goto discard_msg;
1419 }
1420
1421 if (!tmp_msg) {
1422 rc = -EIO;
1423 goto discard_msg;
1424 }
1425 *tmp_msg = msg;
1426 return -EAGAIN;
1427 }
1428 msg->l2h = msg->tail;
1429 }
1430
1431 h = (struct cbsp_header *) msg->data;
1432 /* then read the length as specified in the header */
1433 len = h->len[0] << 16 | h->len[1] << 8 | h->len[2];
1434
1435 needed = len - msgb_l2len(msg);
1436 if (needed > 0) {
1437 rc = recv(fd, msg->tail, needed, 0);
1438 if (rc == 0)
1439 goto discard_msg;
1440 else if (rc < 0) {
1441 if (errno == EAGAIN || errno == EINTR)
1442 rc = 0;
1443 else {
1444 rc = -errno;
1445 goto discard_msg;
1446 }
1447 }
1448 msgb_put(msg, rc);
1449 /* still not all of payload received? */
1450 if (rc < needed) {
1451 if (!tmp_msg) {
1452 rc = -EIO;
1453 goto discard_msg;
1454 }
1455 *tmp_msg = msg;
1456 return -EAGAIN;
1457 }
1458 }
1459 /* else: complete message received */
1460 rc = msgb_l2len(msg);
1461 if (rc == 0) {
1462 /* drop empty message */
1463 rc = -EAGAIN;
1464 goto discard_msg;
1465 }
1466 if (tmp_msg)
1467 *tmp_msg = NULL;
1468 *rmsg = msg;
1469 return rc;
1470
1471discard_msg:
Harald Welte07958e42019-05-03 09:39:10 +02001472 if (tmp_msg)
1473 *tmp_msg = NULL;
1474 msgb_free(msg);
1475 return rc;
1476}
1477
1478#endif /* HAVE_SYS_SOCKET_H */