blob: 353f53f4caa7cd19d77eebcf9edcdd2ec468bbdd [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)
Harald Weltefcbf3472021-02-22 10:05:05 +0100133 return 10 + (secs-10)/2;
Harald Welte07958e42019-05-03 09:39:10 +0200134 if (secs <= 120)
Harald Weltefcbf3472021-02-22 10:05:05 +0100135 return 30 + (secs-30)/5;
Harald Welte07958e42019-05-03 09:39:10 +0200136 if (secs <= 600)
Harald Weltefcbf3472021-02-22 10:05:05 +0100137 return 120 + (secs-120)/10;
Harald Welte07958e42019-05-03 09:39:10 +0200138 if (secs <= 60*60)
Harald Weltefcbf3472021-02-22 10:05:05 +0100139 return 600 + (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{
Harald Welte48f22b02021-02-22 09:55:44 +0100355 int rperiod = encode_wperiod(in->repetition_period);
356 if (in->repetition_period > 120)
357 return -EINVAL;
358 if (rperiod < 0)
359 return -EINVAL;
360 msgb_tv_put(msg, CBSP_IEI_KEEP_ALIVE_REP_PERIOD, rperiod);
Harald Welte07958e42019-05-03 09:39:10 +0200361 return 0;
362}
363
364/* 8.1.3.18b KEEP ALIVE COMPLETE */
365static int cbsp_enc_keep_alive_compl(struct msgb *msg, const struct osmo_cbsp_keep_alive_complete *in)
366{
367 return 0;
368}
369
370/* 8.1.3.19 RESTART */
371static int cbsp_enc_restart(struct msgb *msg, const struct osmo_cbsp_restart *in)
372{
373 msgb_put_cbsp_cell_list(msg, &in->cell_list);
374 msgb_tv_put(msg, CBSP_IEI_BCAST_MSG_TYPE, in->bcast_msg_type);
375 msgb_tv_put(msg, CBSP_IEI_RECOVERY_IND, in->recovery_ind);
376 return 0;
377}
378
379/* 8.1.3.20 FAILURE */
380static int cbsp_enc_failure(struct msgb *msg, const struct osmo_cbsp_failure *in)
381{
382 msgb_put_cbsp_fail_list(msg, &in->fail_list);
383 msgb_tv_put(msg, CBSP_IEI_BCAST_MSG_TYPE, in->bcast_msg_type);
384 return 0;
385}
386
387/* 8.1.3.21 ERROR INDICATION */
388static int cbsp_enc_error_ind(struct msgb *msg, const struct osmo_cbsp_error_ind *in)
389{
390 msgb_tv_put(msg, CBSP_IEI_CAUSE, in->cause);
391 if (in->msg_id)
392 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, *in->msg_id);
393 if (in->new_serial_nr)
394 msgb_tv16_put(msg, CBSP_IEI_NEW_SERIAL_NR, *in->new_serial_nr);
395 if (in->old_serial_nr)
396 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, *in->old_serial_nr);
397 if (in->channel_ind)
398 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
399 return 0;
400}
401
402/*! Encode a CBSP message from the decoded/parsed structure representation to binary PDU.
403 * \param[in] ctx talloc context from which to allocate returned msgb.
404 * \param[in] in decoded CBSP message which is to be encoded. Ownership not transferred.
405 * \return callee-allocated message buffer containing binary CBSP PDU; NULL on error */
406struct msgb *osmo_cbsp_encode(void *ctx, const struct osmo_cbsp_decoded *in)
407{
408 struct msgb *msg = osmo_cbsp_msgb_alloc(ctx, __func__);
409 unsigned int len;
410 int rc;
411
Harald Weltef72155a2019-06-15 23:05:19 +0200412 osmo_cbsp_errstr = NULL;
413
Harald Welte07958e42019-05-03 09:39:10 +0200414 if (!msg)
415 return NULL;
416
417 switch (in->msg_type) {
418 case CBSP_MSGT_WRITE_REPLACE:
419 rc = cbsp_enc_write_repl(msg, &in->u.write_replace);
420 break;
421 case CBSP_MSGT_WRITE_REPLACE_COMPL:
422 rc = cbsp_enc_write_repl_compl(msg, &in->u.write_replace_compl);
423 break;
424 case CBSP_MSGT_WRITE_REPLACE_FAIL:
425 rc = cbsp_enc_write_repl_fail(msg, &in->u.write_replace_fail);
426 break;
427 case CBSP_MSGT_KILL:
428 rc = cbsp_enc_kill(msg, &in->u.kill);
429 break;
430 case CBSP_MSGT_KILL_COMPL:
431 rc = cbsp_enc_kill_compl(msg, &in->u.kill_compl);
432 break;
433 case CBSP_MSGT_KILL_FAIL:
434 rc = cbsp_enc_kill_fail(msg, &in->u.kill_fail);
435 break;
436 case CBSP_MSGT_LOAD_QUERY:
437 rc = cbsp_enc_load_query(msg, &in->u.load_query);
438 break;
439 case CBSP_MSGT_LOAD_QUERY_COMPL:
440 rc = cbsp_enc_load_query_compl(msg, &in->u.load_query_compl);
441 break;
442 case CBSP_MSGT_LOAD_QUERY_FAIL:
443 rc = cbsp_enc_load_query_fail(msg, &in->u.load_query_fail);
444 break;
445 case CBSP_MSGT_MSG_STATUS_QUERY:
446 rc = cbsp_enc_msg_status_query(msg, &in->u.msg_status_query);
447 break;
448 case CBSP_MSGT_MSG_STATUS_QUERY_COMPL:
449 rc = cbsp_enc_msg_status_query_compl(msg, &in->u.msg_status_query_compl);
450 break;
451 case CBSP_MSGT_MSG_STATUS_QUERY_FAIL:
452 rc = cbsp_enc_msg_status_query_fail(msg, &in->u.msg_status_query_fail);
453 break;
454 case CBSP_MSGT_RESET:
455 rc = cbsp_enc_reset(msg, &in->u.reset);
456 break;
457 case CBSP_MSGT_RESET_COMPL:
458 rc = cbsp_enc_reset_compl(msg, &in->u.reset_compl);
459 break;
460 case CBSP_MSGT_RESET_FAIL:
461 rc = cbsp_enc_reset_fail(msg, &in->u.reset_fail);
462 break;
463 case CBSP_MSGT_RESTART:
464 rc = cbsp_enc_restart(msg, &in->u.restart);
465 break;
466 case CBSP_MSGT_FAILURE:
467 rc = cbsp_enc_failure(msg, &in->u.failure);
468 break;
469 case CBSP_MSGT_ERROR_IND:
470 rc = cbsp_enc_error_ind(msg, &in->u.error_ind);
471 break;
472 case CBSP_MSGT_KEEP_ALIVE:
473 rc = cbsp_enc_keep_alive(msg, &in->u.keep_alive);
474 break;
475 case CBSP_MSGT_KEEP_ALIVE_COMPL:
476 rc = cbsp_enc_keep_alive_compl(msg, &in->u.keep_alive_compl);
477 break;
478 case CBSP_MSGT_SET_DRX:
479 case CBSP_MSGT_SET_DRX_COMPL:
480 case CBSP_MSGT_SET_DRX_FAIL:
Harald Weltef72155a2019-06-15 23:05:19 +0200481 osmo_cbsp_errstr = "message type not implemented";
Harald Welte07958e42019-05-03 09:39:10 +0200482 rc = -1;
483 break;
484 default:
Harald Weltef72155a2019-06-15 23:05:19 +0200485 osmo_cbsp_errstr = "message type not known in spec";
Harald Welte07958e42019-05-03 09:39:10 +0200486 rc = -1;
487 break;
488 }
489
490 if (rc < 0) {
491 msgb_free(msg);
492 return NULL;
493 }
494
495 /* push header in front */
496 len = msgb_length(msg);
497 msgb_push_u8(msg, len & 0xff);
498 msgb_push_u8(msg, (len >> 8) & 0xff);
499 msgb_push_u8(msg, (len >> 16) & 0xff);
500 msgb_push_u8(msg, in->msg_type);
501
502 return msg;
503}
504
505/***********************************************************************
506 * IE Decoding
507 ***********************************************************************/
508
509/* 8.2.6 Cell List */
510static int cbsp_decode_cell_list(struct osmo_cbsp_cell_list *cl, void *ctx,
511 const uint8_t *buf, unsigned int len)
512{
513 const uint8_t *cur = buf;
514 int rc;
515
516 cl->id_discr = *cur++;
517
518 while (cur < buf + len) {
519 struct osmo_cbsp_cell_ent *ent = talloc_zero(ctx, struct osmo_cbsp_cell_ent);
520 unsigned int len_remain = len - (cur - buf);
521 OSMO_ASSERT(ent);
522 rc = gsm0808_decode_cell_id_u(&ent->cell_id, cl->id_discr, cur, len_remain);
Harald Weltef72155a2019-06-15 23:05:19 +0200523 if (rc < 0) {
524 osmo_cbsp_errstr = "cell list: error decoding cell_id_union";
Harald Welte07958e42019-05-03 09:39:10 +0200525 return rc;
Harald Weltef72155a2019-06-15 23:05:19 +0200526 }
Harald Weltef2210032019-08-31 21:25:05 +0200527 cur += gsm0808_cell_id_size(cl->id_discr);
Harald Welte07958e42019-05-03 09:39:10 +0200528 llist_add_tail(&ent->list, &cl->list);
529 }
530 return 0;
531}
532
533/* 8.2.11 Failure List (discriminator per entry) */
534static int cbsp_decode_fail_list(struct llist_head *fl, void *ctx,
535 const uint8_t *buf, unsigned int len)
536{
537 const uint8_t *cur = buf;
538 int rc;
539
540 while (cur < buf + len) {
541 struct osmo_cbsp_fail_ent *ent = talloc_zero(ctx, struct osmo_cbsp_fail_ent);
542 unsigned int len_remain = len - (cur - buf);
543 OSMO_ASSERT(ent);
544 ent->id_discr = cur[0];
545 rc = gsm0808_decode_cell_id_u(&ent->cell_id, ent->id_discr, cur+1, len_remain-1);
Harald Weltef72155a2019-06-15 23:05:19 +0200546 if (rc < 0) {
547 osmo_cbsp_errstr = "fail list: error decoding cell_id_union";
Harald Welte07958e42019-05-03 09:39:10 +0200548 return rc;
Harald Weltef72155a2019-06-15 23:05:19 +0200549 }
Harald Weltef2210032019-08-31 21:25:05 +0200550 cur += gsm0808_cell_id_size(ent->id_discr);
Harald Welte07958e42019-05-03 09:39:10 +0200551 ent->cause = *cur++;
552 llist_add_tail(&ent->list, fl);
553 }
554 return 0;
555}
556
557/* 8.2.12 Radio Resource Loading List */
558static int cbsp_decode_loading_list(struct osmo_cbsp_loading_list *ll, void *ctx,
559 const uint8_t *buf, unsigned int len)
560{
561 const uint8_t *cur = buf;
562 int rc;
563
564 ll->id_discr = *cur++;
565 while (cur < buf + len) {
566 struct osmo_cbsp_loading_ent *ent = talloc_zero(ctx, struct osmo_cbsp_loading_ent);
567 unsigned int len_remain = len - (cur - buf);
568 OSMO_ASSERT(ent);
569 rc = gsm0808_decode_cell_id_u(&ent->cell_id, ll->id_discr, cur, len_remain);
Harald Weltef72155a2019-06-15 23:05:19 +0200570 if (rc < 0) {
571 osmo_cbsp_errstr = "load list: error decoding cell_id_union";
Harald Welte07958e42019-05-03 09:39:10 +0200572 return rc;
Harald Weltef72155a2019-06-15 23:05:19 +0200573 }
Harald Weltef2210032019-08-31 21:25:05 +0200574 cur += gsm0808_cell_id_size(ll->id_discr);
Harald Welte07958e42019-05-03 09:39:10 +0200575 if (cur + 2 > buf + len) {
576 talloc_free(ent);
Harald Weltef72155a2019-06-15 23:05:19 +0200577 osmo_cbsp_errstr = "load list: truncated IE";
Harald Welte07958e42019-05-03 09:39:10 +0200578 return -EINVAL;
579 }
580 ent->load[0] = *cur++;
581 ent->load[1] = *cur++;
582 llist_add_tail(&ent->list, &ll->list);
583 }
584 return 0;
585}
586
587/* 8.2.10 Completed List */
588static int cbsp_decode_num_compl_list(struct osmo_cbsp_num_compl_list *cl, void *ctx,
589 const uint8_t *buf, unsigned int len)
590{
591 const uint8_t *cur = buf;
592 int rc;
593
594 cl->id_discr = *cur++;
595 while (cur < buf + len) {
596 struct osmo_cbsp_num_compl_ent *ent = talloc_zero(ctx, struct osmo_cbsp_num_compl_ent);
597 unsigned int len_remain = len - (cur - buf);
598 OSMO_ASSERT(ent);
599 rc = gsm0808_decode_cell_id_u(&ent->cell_id, cl->id_discr, cur, len_remain);
Harald Weltef72155a2019-06-15 23:05:19 +0200600 if (rc < 0) {
601 osmo_cbsp_errstr = "completed list: error decoding cell_id_union";
Harald Welte07958e42019-05-03 09:39:10 +0200602 return rc;
Harald Weltef72155a2019-06-15 23:05:19 +0200603 }
Harald Weltef2210032019-08-31 21:25:05 +0200604 cur += gsm0808_cell_id_size(cl->id_discr);
Harald Welte07958e42019-05-03 09:39:10 +0200605 if (cur + 3 > buf + len) {
606 talloc_free(ent);
Harald Weltef72155a2019-06-15 23:05:19 +0200607 osmo_cbsp_errstr = "completed list: truncated IE";
Harald Welte07958e42019-05-03 09:39:10 +0200608 return -EINVAL;
609 }
610 ent->num_compl = osmo_load16be(cur); cur += 2;
611 ent->num_bcast_info = *cur++;
612 llist_add_tail(&ent->list, &cl->list);
613 }
614 return 0;
615}
616
617/* 8.2.25 */
618static uint32_t decode_wperiod(uint8_t in)
619{
620 if (in == 0x00)
621 return 0xffffffff; /* infinite */
622 if (in <= 10)
623 return in;
624 if (in <= 20)
625 return 10 + (in - 10)*2;
626 if (in <= 38)
627 return 30 + (in - 20)*5;
628 if (in <= 86)
629 return 120 + (in - 38)*10;
630 if (in <= 186)
631 return 600 + (in - 86)*30;
632 else
633 return 0;
634}
635
636
637/***********************************************************************
638 * Message Decoding
639 ***********************************************************************/
640
641/* 8.1.3.1 WRITE REPLACE */
642static int cbsp_dec_write_repl(struct osmo_cbsp_write_replace *out, const struct tlv_parsed *tp,
643 struct msgb *in, void *ctx)
644{
645 unsigned int i;
646
647 /* check for mandatory IEs */
648 if (!TLVP_PRESENT(tp, CBSP_IEI_MSG_ID) ||
649 !TLVP_PRESENT(tp, CBSP_IEI_NEW_SERIAL_NR) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200650 !TLVP_PRESENT(tp, CBSP_IEI_CELL_LIST)) {
651 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200652 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200653 }
Harald Welte07958e42019-05-03 09:39:10 +0200654
655 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
656 out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
657 if (TLVP_PRESENT(tp, CBSP_IEI_OLD_SERIAL_NR)) {
658 out->old_serial_nr = talloc(ctx, uint16_t);
659 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
660 }
661
662 INIT_LLIST_HEAD(&out->cell_list.list);
663 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
664 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
665
666 if (TLVP_PRESENT(tp, CBSP_IEI_CHANNEL_IND)) {
667 uint8_t num_of_pages;
668 INIT_LLIST_HEAD(&out->u.cbs.msg_content);
Harald Weltef72155a2019-06-15 23:05:19 +0200669 if (TLVP_PRESENT(tp, CBSP_IEI_EMERG_IND)) {
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 if (!TLVP_PRESENT(tp, CBSP_IEI_CATEGORY) ||
674 !TLVP_PRESENT(tp, CBSP_IEI_REP_PERIOD) ||
675 !TLVP_PRESENT(tp, CBSP_IEI_NUM_BCAST_REQ) ||
676 !TLVP_PRESENT(tp, CBSP_IEI_NUM_OF_PAGES) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200677 !TLVP_PRESENT(tp, CBSP_IEI_DCS)) {
678 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200679 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200680 }
Harald Welte07958e42019-05-03 09:39:10 +0200681 out->is_cbs = true;
682 out->u.cbs.channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
683 out->u.cbs.category = *TLVP_VAL(tp, CBSP_IEI_CATEGORY);
684 out->u.cbs.rep_period = tlvp_val16be(tp, CBSP_IEI_REP_PERIOD);
685 out->u.cbs.num_bcast_req = tlvp_val16be(tp, CBSP_IEI_NUM_BCAST_REQ);
Harald Welte886e7422021-01-02 22:30:02 +0100686 out->u.cbs.dcs = *TLVP_VAL(tp, CBSP_IEI_DCS);
Harald Welte07958e42019-05-03 09:39:10 +0200687 num_of_pages = *TLVP_VAL(tp, CBSP_IEI_NUM_OF_PAGES);
688 if (num_of_pages < 1)
689 return -EINVAL;
690 /* parse pages */
691 for (i = 0; i < num_of_pages; i++) {
692 const uint8_t *ie = TLVP_VAL(&tp[i], CBSP_IEI_MSG_CONTENT);
693 struct osmo_cbsp_content *page;
Harald Weltef72155a2019-06-15 23:05:19 +0200694 if (!ie) {
695 osmo_cbsp_errstr = "insufficient message content IEs";
Harald Welte07958e42019-05-03 09:39:10 +0200696 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200697 }
Harald Welte07958e42019-05-03 09:39:10 +0200698 page = talloc_zero(ctx, struct osmo_cbsp_content);
699 OSMO_ASSERT(page);
Harald Weltee674c442019-09-01 22:30:58 +0200700 page->user_len = ie[0]; /* length byte before payload */
701 memcpy(page->data, ie+1, sizeof(page->data));
Harald Welte07958e42019-05-03 09:39:10 +0200702 llist_add_tail(&page->list, &out->u.cbs.msg_content);
703 }
704 } else {
705 if (!TLVP_PRES_LEN(tp, CBSP_IEI_EMERG_IND, 1) ||
706 !TLVP_PRES_LEN(tp, CBSP_IEI_WARN_TYPE, 2) ||
707 !TLVP_PRES_LEN(tp, CBSP_IEI_WARN_SEC_INFO, 50) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200708 !TLVP_PRES_LEN(tp, CBSP_IEI_WARNING_PERIOD, 1)) {
709 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200710 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200711 }
Harald Welte07958e42019-05-03 09:39:10 +0200712 out->u.emergency.indicator = *TLVP_VAL(tp, CBSP_IEI_EMERG_IND);
713 out->u.emergency.warning_type = tlvp_val16be(tp, CBSP_IEI_WARN_TYPE);
714 memcpy(&out->u.emergency.warning_sec_info, TLVP_VAL(tp, CBSP_IEI_WARN_SEC_INFO),
715 sizeof(out->u.emergency.warning_sec_info));
716 out->u.emergency.warning_period = decode_wperiod(*TLVP_VAL(tp, CBSP_IEI_WARNING_PERIOD));
717 }
718 return 0;
719}
720
721/* 8.1.3.2 WRITE REPLACE COMPLETE*/
722static int cbsp_dec_write_repl_compl(struct osmo_cbsp_write_replace_complete *out,
723 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
724{
725 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200726 !TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2)) {
727 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200728 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200729 }
Harald Welte07958e42019-05-03 09:39:10 +0200730
731 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
732 out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
733 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
734 out->old_serial_nr = talloc(ctx, uint16_t);
735 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
736 }
737
738 INIT_LLIST_HEAD(&out->num_compl_list.list);
739 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
740 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
741 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
742 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
743 }
744
745 INIT_LLIST_HEAD(&out->cell_list.list);
746 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
747 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
748
749 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
750 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
751 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
752 }
753 return 0;
754}
755
756/* 8.1.3.3 WRITE REPLACE FAILURE */
757static int cbsp_dec_write_repl_fail(struct osmo_cbsp_write_replace_failure *out,
758 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
759{
760 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
761 !TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200762 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
763 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200764 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200765 }
Harald Welte07958e42019-05-03 09:39:10 +0200766
767 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
768 out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
769 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
770 out->old_serial_nr = talloc(ctx, uint16_t);
771 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
772 }
773
774 INIT_LLIST_HEAD(&out->fail_list);
775 cbsp_decode_fail_list(&out->fail_list, ctx,
776 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
777 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
778
779 INIT_LLIST_HEAD(&out->num_compl_list.list);
780 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
781 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
782 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
783 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
784 }
785
786 INIT_LLIST_HEAD(&out->cell_list.list);
787 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
788 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
789 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
790 }
791
792 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
793 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
794 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
795 }
796 return 0;
797}
798
799/* 8.1.3.4 KILL */
800static int cbsp_dec_kill(struct osmo_cbsp_kill *out, const struct tlv_parsed *tp,
801 struct msgb *in, void *ctx)
802{
803 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
804 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200805 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
806 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200807 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200808 }
809
Harald Welte07958e42019-05-03 09:39:10 +0200810 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
811 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
812
813 INIT_LLIST_HEAD(&out->cell_list.list);
814 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
815 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
816
817 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
818 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
819 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
820 }
821 return 0;
822}
823
824/* 8.1.3.5 KILL COMPLETE */
825static int cbsp_dec_kill_compl(struct osmo_cbsp_kill_complete *out, const struct tlv_parsed *tp,
826 struct msgb *in, void *ctx)
827{
828 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
829 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200830 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
831 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200832 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200833 }
Harald Welte07958e42019-05-03 09:39:10 +0200834
835 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
836 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
837
838 INIT_LLIST_HEAD(&out->num_compl_list.list);
839 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
840 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
841 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
842 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
843 }
844
845 INIT_LLIST_HEAD(&out->cell_list.list);
846 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
847 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
848
849 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
850 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
851 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
852 }
853 return 0;
854}
855
856/* 8.1.3.6 KILL FAILURE */
857static int cbsp_dec_kill_fail(struct osmo_cbsp_kill_failure *out, const struct tlv_parsed *tp,
858 struct msgb *in, void *ctx)
859{
860 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
861 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200862 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
863 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200864 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200865 }
Harald Welte07958e42019-05-03 09:39:10 +0200866
867 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
868 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
869
870 INIT_LLIST_HEAD(&out->fail_list);
871 cbsp_decode_fail_list(&out->fail_list, ctx,
872 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
873 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
874
875 INIT_LLIST_HEAD(&out->num_compl_list.list);
876 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
877 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
878 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
879 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
880 }
881
882 INIT_LLIST_HEAD(&out->cell_list.list);
883 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
884 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
885 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
886 }
887
888 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
889 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
890 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
891 }
892 return 0;
893}
894
895/* 8.1.3.7 LOAD QUERY */
896static int cbsp_dec_load_query(struct osmo_cbsp_load_query *out, const struct tlv_parsed *tp,
897 struct msgb *in, void *ctx)
898{
899 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200900 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
901 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200902 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200903 }
Harald Welte07958e42019-05-03 09:39:10 +0200904
905 INIT_LLIST_HEAD(&out->cell_list.list);
906 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
907 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
908
909 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
910 return 0;
911}
912
913/* 8.1.3.8 LOAD QUERY COMPLETE */
914static int cbsp_dec_load_query_compl(struct osmo_cbsp_load_query_complete *out,
915 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
916{
917 if (!TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200918 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
919 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200920 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200921 }
Harald Welte07958e42019-05-03 09:39:10 +0200922
923 INIT_LLIST_HEAD(&out->loading_list.list);
924 cbsp_decode_loading_list(&out->loading_list, ctx,
925 TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
926 TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
927
928 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
929 return 0;
930}
931
932/* 8.1.3.9 LOAD QUERY FAILURE */
933static int cbsp_dec_load_query_fail(struct osmo_cbsp_load_query_failure *out,
934 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
935{
936 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200937 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
938 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200939 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200940 }
Harald Welte07958e42019-05-03 09:39:10 +0200941
942 INIT_LLIST_HEAD(&out->fail_list);
943 cbsp_decode_fail_list(&out->fail_list, ctx,
944 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
945 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
946
947 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
948
949 INIT_LLIST_HEAD(&out->loading_list.list);
950 if (TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6)) {
951 cbsp_decode_loading_list(&out->loading_list, ctx,
952 TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
953 TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
954 }
955 return 0;
956}
957
958/* 8.1.3.10 STATUS QUERY */
959static int cbsp_dec_msg_status_query(struct osmo_cbsp_msg_status_query *out,
960 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
961{
962 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
963 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
964 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200965 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
966 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200967 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200968 }
969
Harald Welte07958e42019-05-03 09:39:10 +0200970 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
971 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
972
973 INIT_LLIST_HEAD(&out->cell_list.list);
974 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
975 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
976
977 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
978 return 0;
979}
980
981/* 8.1.3.11 STATUS QUERY COMPLETE */
982static int cbsp_dec_msg_status_query_compl(struct osmo_cbsp_msg_status_query_complete *out,
983 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
984{
985 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
986 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
987 !TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7) ||
Harald Weltef72155a2019-06-15 23:05:19 +0200988 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
989 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +0200990 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +0200991 }
Harald Welte07958e42019-05-03 09:39:10 +0200992
993 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
994 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
995
996 INIT_LLIST_HEAD(&out->num_compl_list.list);
997 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
998 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
999 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
1000 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
1001 return 0;
1002}
1003
1004/* 8.1.3.12 STATUS QUERY FAILURE */
1005static int cbsp_dec_msg_status_query_fail(struct osmo_cbsp_msg_status_query_failure *out,
1006 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
1007{
1008 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
1009 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
1010 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
Harald Weltef72155a2019-06-15 23:05:19 +02001011 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
1012 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001013 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001014 }
Harald Welte07958e42019-05-03 09:39:10 +02001015
1016 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
1017 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
1018
1019 INIT_LLIST_HEAD(&out->fail_list);
1020 cbsp_decode_fail_list(&out->fail_list, ctx,
1021 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
1022 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
1023
1024 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
1025
1026 INIT_LLIST_HEAD(&out->num_compl_list.list);
1027 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
1028 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
1029 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
1030 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
1031 }
1032 return 0;
1033}
1034
1035/* 8.1.3.16 RESET */
1036static int cbsp_dec_reset(struct osmo_cbsp_reset *out, const struct tlv_parsed *tp,
1037 struct msgb *in, void *ctx)
1038{
Harald Weltef72155a2019-06-15 23:05:19 +02001039 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
1040 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001041 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001042 }
Harald Welte07958e42019-05-03 09:39:10 +02001043
1044 INIT_LLIST_HEAD(&out->cell_list.list);
1045 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1046 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1047 return 0;
1048}
1049
1050/* 8.1.3.17 RESET COMPLETE */
1051static int cbsp_dec_reset_compl(struct osmo_cbsp_reset_complete *out, const struct tlv_parsed *tp,
1052 struct msgb *in, void *ctx)
1053{
Harald Weltef72155a2019-06-15 23:05:19 +02001054 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
1055 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001056 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001057 }
Harald Welte07958e42019-05-03 09:39:10 +02001058
1059 INIT_LLIST_HEAD(&out->cell_list.list);
1060 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1061 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1062 return 0;
1063}
1064
1065/* 8.1.3.18 RESET FAILURE */
1066static int cbsp_dec_reset_fail(struct osmo_cbsp_reset_failure *out, const struct tlv_parsed *tp,
1067 struct msgb *in, void *ctx)
1068{
Harald Weltef72155a2019-06-15 23:05:19 +02001069 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5)) {
1070 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001071 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001072 }
Harald Welte07958e42019-05-03 09:39:10 +02001073
1074 INIT_LLIST_HEAD(&out->fail_list);
1075 cbsp_decode_fail_list(&out->fail_list, ctx,
1076 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
1077 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
1078
1079 INIT_LLIST_HEAD(&out->cell_list.list);
1080 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
1081 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1082 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1083 }
1084 return 0;
1085}
1086
1087/* 8.1.3.18a KEEP ALIVE */
1088static int cbsp_dec_keep_alive(struct osmo_cbsp_keep_alive *out, const struct tlv_parsed *tp,
1089 struct msgb *in, void *ctx)
1090{
Harald Welte48f22b02021-02-22 09:55:44 +01001091 uint8_t rperiod;
Harald Weltef72155a2019-06-15 23:05:19 +02001092 if (!TLVP_PRES_LEN(tp, CBSP_IEI_KEEP_ALIVE_REP_PERIOD, 1)) {
1093 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001094 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001095 }
Harald Welte07958e42019-05-03 09:39:10 +02001096
Harald Welte48f22b02021-02-22 09:55:44 +01001097 rperiod = *TLVP_VAL(tp, CBSP_IEI_KEEP_ALIVE_REP_PERIOD);
1098 out->repetition_period = decode_wperiod(rperiod);
Harald Welte07958e42019-05-03 09:39:10 +02001099 return 0;
1100}
1101
1102/* 8.1.3.18b KEEP ALIVE COMPLETE */
1103static int cbsp_dec_keep_alive_compl(struct osmo_cbsp_keep_alive_complete *out,
1104 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
1105{
1106 return 0;
1107}
1108
1109/* 8.1.3.19 RESTART */
1110static int cbsp_dec_restart(struct osmo_cbsp_restart *out, const struct tlv_parsed *tp,
1111 struct msgb *in, void *ctx)
1112{
1113 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
1114 !TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1) ||
Harald Weltef72155a2019-06-15 23:05:19 +02001115 !TLVP_PRES_LEN(tp, CBSP_IEI_RECOVERY_IND, 1)) {
1116 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001117 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001118 }
Harald Welte07958e42019-05-03 09:39:10 +02001119
1120 INIT_LLIST_HEAD(&out->cell_list.list);
1121 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1122 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1123
1124 out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE);
1125 out->recovery_ind = *TLVP_VAL(tp, CBSP_IEI_RECOVERY_IND);
1126 return 0;
1127}
1128
1129/* 8.1.3.20 FAILURE */
1130static int cbsp_dec_failure(struct osmo_cbsp_failure *out, const struct tlv_parsed *tp,
1131 struct msgb *in, void *ctx)
1132{
1133 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
Harald Weltef72155a2019-06-15 23:05:19 +02001134 !TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1)) {
1135 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001136 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001137 }
Harald Welte07958e42019-05-03 09:39:10 +02001138
1139 INIT_LLIST_HEAD(&out->fail_list);
1140 cbsp_decode_fail_list(&out->fail_list, ctx,
1141 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
1142 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
1143
1144 out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE);
1145 return 0;
1146}
1147
1148/* 8.1.3.21 ERROR INDICATION */
1149static int cbsp_dec_error_ind(struct osmo_cbsp_error_ind *out, const struct tlv_parsed *tp,
1150 struct msgb *in, void *ctx)
1151{
Harald Weltef72155a2019-06-15 23:05:19 +02001152 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CAUSE, 1)) {
1153 osmo_cbsp_errstr = "missing/short mandatory IE";
Harald Welte07958e42019-05-03 09:39:10 +02001154 return -EINVAL;
Harald Weltef72155a2019-06-15 23:05:19 +02001155 }
Harald Welte07958e42019-05-03 09:39:10 +02001156
1157 out->cause = *TLVP_VAL(tp, CBSP_IEI_CAUSE);
1158 if (TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2)) {
1159 out->msg_id = talloc(ctx, uint16_t);
1160 *out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
1161 }
1162 if (TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2)) {
1163 out->new_serial_nr = talloc(ctx, uint16_t);
1164 *out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
1165 }
1166 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
1167 out->old_serial_nr = talloc(ctx, uint16_t);
1168 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
1169 }
1170 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
1171 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
1172 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
1173 }
1174 return 0;
1175}
1176
1177/*! Decode a CBSP message from wire formwat to pased structure.
1178 * \param[in] ctx talloc context from which to allocate decoded output.
1179 * \param[in] in message buffer contiaining binary CBSP message.
1180 * \returns callee-allocated decoded representation of CBSP message; NULL on error */
1181struct osmo_cbsp_decoded *osmo_cbsp_decode(void *ctx, struct msgb *in)
1182{
1183 struct osmo_cbsp_decoded *out = talloc_zero(ctx, struct osmo_cbsp_decoded);
1184 const struct cbsp_header *h = msgb_l1(in);
1185 struct tlv_parsed tp[16]; /* max. number of pages in a given CBS message */
1186 unsigned int len;
1187 int rc;
1188
Harald Weltef72155a2019-06-15 23:05:19 +02001189 osmo_cbsp_errstr = NULL;
1190
Harald Welte07958e42019-05-03 09:39:10 +02001191 if (!out)
1192 return NULL;
1193
1194 if (msgb_l1len(in) < sizeof(*h)) {
1195 goto out_err;
1196 }
1197 len = h->len[0] << 16 | h->len[1] << 8 | h->len[2];
1198
1199 /* discard messages where indicated length is more than we have */
1200 if (len > msgb_l2len(in)) {
1201 goto out_err;
1202 }
1203
1204 /* trim any messages with extra payload at the end */
1205 if (len < msgb_l2len(in))
1206 msgb_trim(in, (in->l2h - in->data) + msgb_l2len(in));
1207 out->msg_type = h->msg_type;
1208
1209 rc = tlv_parse2(tp, ARRAY_SIZE(tp), &cbsp_att_tlvdef, msgb_l2(in), msgb_l2len(in), 0, 0);
1210 if (rc < 0) {
1211 goto out_err;
1212 }
1213
1214 switch (h->msg_type) {
1215 case CBSP_MSGT_WRITE_REPLACE:
1216 rc = cbsp_dec_write_repl(&out->u.write_replace, tp, in, out);
1217 break;
1218 case CBSP_MSGT_WRITE_REPLACE_COMPL:
1219 rc = cbsp_dec_write_repl_compl(&out->u.write_replace_compl, tp, in, out);
1220 break;
1221 case CBSP_MSGT_WRITE_REPLACE_FAIL:
1222 rc = cbsp_dec_write_repl_fail(&out->u.write_replace_fail, tp, in, out);
1223 break;
1224 case CBSP_MSGT_KILL:
1225 rc = cbsp_dec_kill(&out->u.kill, tp, in, out);
1226 break;
1227 case CBSP_MSGT_KILL_COMPL:
1228 rc = cbsp_dec_kill_compl(&out->u.kill_compl, tp, in, out);
1229 break;
1230 case CBSP_MSGT_KILL_FAIL:
1231 rc = cbsp_dec_kill_fail(&out->u.kill_fail, tp, in, out);
1232 break;
1233 case CBSP_MSGT_LOAD_QUERY:
1234 rc = cbsp_dec_load_query(&out->u.load_query, tp, in, out);
1235 break;
1236 case CBSP_MSGT_LOAD_QUERY_COMPL:
1237 rc = cbsp_dec_load_query_compl(&out->u.load_query_compl, tp, in, out);
1238 break;
1239 case CBSP_MSGT_LOAD_QUERY_FAIL:
1240 rc = cbsp_dec_load_query_fail(&out->u.load_query_fail, tp, in, out);
1241 break;
1242 case CBSP_MSGT_MSG_STATUS_QUERY:
1243 rc = cbsp_dec_msg_status_query(&out->u.msg_status_query, tp, in, out);
1244 break;
1245 case CBSP_MSGT_MSG_STATUS_QUERY_COMPL:
1246 rc = cbsp_dec_msg_status_query_compl(&out->u.msg_status_query_compl, tp, in, out);
1247 break;
1248 case CBSP_MSGT_MSG_STATUS_QUERY_FAIL:
1249 rc = cbsp_dec_msg_status_query_fail(&out->u.msg_status_query_fail, tp, in, out);
1250 break;
1251 case CBSP_MSGT_RESET:
1252 rc = cbsp_dec_reset(&out->u.reset, tp, in, out);
1253 break;
1254 case CBSP_MSGT_RESET_COMPL:
1255 rc = cbsp_dec_reset_compl(&out->u.reset_compl, tp, in, out);
1256 break;
1257 case CBSP_MSGT_RESET_FAIL:
1258 rc = cbsp_dec_reset_fail(&out->u.reset_fail, tp, in, out);
1259 break;
1260 case CBSP_MSGT_RESTART:
1261 rc = cbsp_dec_restart(&out->u.restart, tp, in, out);
1262 break;
1263 case CBSP_MSGT_FAILURE:
1264 rc = cbsp_dec_failure(&out->u.failure, tp, in, out);
1265 break;
1266 case CBSP_MSGT_ERROR_IND:
1267 rc = cbsp_dec_error_ind(&out->u.error_ind, tp, in, out);
1268 break;
1269 case CBSP_MSGT_KEEP_ALIVE:
1270 rc = cbsp_dec_keep_alive(&out->u.keep_alive, tp, in, out);
1271 break;
1272 case CBSP_MSGT_KEEP_ALIVE_COMPL:
1273 rc = cbsp_dec_keep_alive_compl(&out->u.keep_alive_compl, tp, in, out);
1274 break;
1275 case CBSP_MSGT_SET_DRX:
1276 case CBSP_MSGT_SET_DRX_COMPL:
1277 case CBSP_MSGT_SET_DRX_FAIL:
Harald Weltef72155a2019-06-15 23:05:19 +02001278 osmo_cbsp_errstr = "message type not implemented";
Harald Welte07958e42019-05-03 09:39:10 +02001279 rc = -1;
1280 break;
1281 default:
Harald Weltef72155a2019-06-15 23:05:19 +02001282 osmo_cbsp_errstr = "message type not known in spec";
Harald Welte07958e42019-05-03 09:39:10 +02001283 rc = -1;
1284 break;
1285 }
1286
1287 if (rc < 0) {
1288 goto out_err;
1289 }
1290
1291 return out;
1292
1293out_err:
1294 talloc_free(out);
1295 return NULL;
1296}
1297
1298/* initialization of 'decoded' structure of given message type */
1299void osmo_cbsp_init_struct(struct osmo_cbsp_decoded *cbsp, enum cbsp_msg_type msg_type)
1300{
1301 memset(cbsp, 0, sizeof(*cbsp));
1302 cbsp->msg_type = msg_type;
1303
1304 switch (msg_type) {
1305 case CBSP_MSGT_WRITE_REPLACE:
1306 INIT_LLIST_HEAD(&cbsp->u.write_replace.cell_list.list);
1307 break;
1308 case CBSP_MSGT_WRITE_REPLACE_COMPL:
1309 INIT_LLIST_HEAD(&cbsp->u.write_replace_compl.num_compl_list.list);
1310 INIT_LLIST_HEAD(&cbsp->u.write_replace_compl.cell_list.list);
1311 break;
1312 case CBSP_MSGT_WRITE_REPLACE_FAIL:
1313 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.fail_list);
1314 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.num_compl_list.list);
1315 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.cell_list.list);
1316 break;
1317 case CBSP_MSGT_KILL:
1318 INIT_LLIST_HEAD(&cbsp->u.kill.cell_list.list);
1319 break;
1320 case CBSP_MSGT_KILL_COMPL:
1321 INIT_LLIST_HEAD(&cbsp->u.kill_compl.num_compl_list.list);
1322 INIT_LLIST_HEAD(&cbsp->u.kill_compl.cell_list.list);
1323 break;
1324 case CBSP_MSGT_KILL_FAIL:
1325 INIT_LLIST_HEAD(&cbsp->u.kill_fail.fail_list);
1326 INIT_LLIST_HEAD(&cbsp->u.kill_fail.num_compl_list.list);
1327 INIT_LLIST_HEAD(&cbsp->u.kill_fail.cell_list.list);
1328 break;
1329 case CBSP_MSGT_LOAD_QUERY:
1330 INIT_LLIST_HEAD(&cbsp->u.load_query.cell_list.list);
1331 break;
1332 case CBSP_MSGT_LOAD_QUERY_COMPL:
1333 INIT_LLIST_HEAD(&cbsp->u.load_query_compl.loading_list.list);
1334 break;
1335 case CBSP_MSGT_LOAD_QUERY_FAIL:
1336 INIT_LLIST_HEAD(&cbsp->u.load_query_fail.fail_list);
1337 break;
1338 case CBSP_MSGT_MSG_STATUS_QUERY:
1339 INIT_LLIST_HEAD(&cbsp->u.msg_status_query.cell_list.list);
1340 break;
1341 case CBSP_MSGT_MSG_STATUS_QUERY_COMPL:
1342 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_compl.num_compl_list.list);
1343 break;
1344 case CBSP_MSGT_MSG_STATUS_QUERY_FAIL:
1345 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_fail.fail_list);
1346 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_fail.num_compl_list.list);
1347 break;
1348 case CBSP_MSGT_RESET:
1349 INIT_LLIST_HEAD(&cbsp->u.reset.cell_list.list);
1350 break;
1351 case CBSP_MSGT_RESET_COMPL:
1352 INIT_LLIST_HEAD(&cbsp->u.reset_compl.cell_list.list);
1353 break;
1354 case CBSP_MSGT_RESET_FAIL:
1355 INIT_LLIST_HEAD(&cbsp->u.reset_fail.fail_list);
1356 INIT_LLIST_HEAD(&cbsp->u.reset_fail.cell_list.list);
1357 break;
1358 case CBSP_MSGT_RESTART:
1359 INIT_LLIST_HEAD(&cbsp->u.restart.cell_list.list);
1360 break;
1361 case CBSP_MSGT_FAILURE:
1362 INIT_LLIST_HEAD(&cbsp->u.failure.fail_list);
1363 break;
1364 default:
1365 break;
1366 }
1367}
1368
1369/*! Dynamically allocate and initialize decoded CBSP structure.
1370 * \param[in] ctx talloc context from which to allocate
1371 * \param[in] msg_type CBSP message type for which to initialize result
1372 * \returns allocated + initialized decoded CBSP structure; NULL on talloc failure */
1373struct osmo_cbsp_decoded *osmo_cbsp_decoded_alloc(void *ctx, enum cbsp_msg_type msg_type)
1374{
1375 struct osmo_cbsp_decoded *cbsp = talloc_zero(ctx, struct osmo_cbsp_decoded);
1376 if (!cbsp)
1377 return NULL;
1378 osmo_cbsp_init_struct(cbsp, msg_type);
1379 return cbsp;
1380}
1381
1382/***********************************************************************
1383 * Message Reception
1384 ***********************************************************************/
1385
1386#ifdef HAVE_SYS_SOCKET_H
1387#include <sys/socket.h>
1388
1389/*! Read one CBSP message from socket fd or store part if still not fully received.
1390 * \param[in] ctx talloc context from which to allocate new msgb.
1391 * \param[in] fd The fd for the socket to read from.
1392 * \param[out] rmsg internally allocated msgb containing a fully received CBSP message.
1393 * \param[inout] tmp_msg internally allocated msgb caching data for not yet fully received message.
1394 *
1395 * Function is designed just like ipa_msg_recv_buffered()
1396 */
1397int osmo_cbsp_recv_buffered(void *ctx, int fd, struct msgb **rmsg, struct msgb **tmp_msg)
1398{
1399 struct msgb *msg = tmp_msg ? *tmp_msg : NULL;
1400 struct cbsp_header *h;
1401 int len, rc;
1402 int needed;
1403
1404 if (!msg) {
1405 msg = osmo_cbsp_msgb_alloc(ctx, __func__);
Harald Weltec30d8be2019-07-21 07:51:33 +02001406 if (!msg)
Harald Welte07958e42019-05-03 09:39:10 +02001407 return -ENOMEM;
Harald Welte07958e42019-05-03 09:39:10 +02001408 msg->l1h = msg->tail;
1409 }
1410
1411 if (msg->l2h == NULL) {
1412 /* first read the [missing part of the] header */
1413 needed = sizeof(*h) - msg->len;
1414 rc = recv(fd, msg->tail, needed, 0);
1415 if (rc == 0)
1416 goto discard_msg;
1417 else if (rc < 0) {
1418 if (errno == EAGAIN || errno == EINTR)
1419 rc = 0;
1420 else {
1421 rc = -errno;
1422 goto discard_msg;
1423 }
1424 }
1425 msgb_put(msg, rc);
1426 if (rc < needed) {
1427 if (msg->len == 0) {
1428 rc = -EAGAIN;
1429 goto discard_msg;
1430 }
1431
1432 if (!tmp_msg) {
1433 rc = -EIO;
1434 goto discard_msg;
1435 }
1436 *tmp_msg = msg;
1437 return -EAGAIN;
1438 }
1439 msg->l2h = msg->tail;
1440 }
1441
1442 h = (struct cbsp_header *) msg->data;
1443 /* then read the length as specified in the header */
1444 len = h->len[0] << 16 | h->len[1] << 8 | h->len[2];
1445
1446 needed = len - msgb_l2len(msg);
1447 if (needed > 0) {
1448 rc = recv(fd, msg->tail, needed, 0);
1449 if (rc == 0)
1450 goto discard_msg;
1451 else if (rc < 0) {
1452 if (errno == EAGAIN || errno == EINTR)
1453 rc = 0;
1454 else {
1455 rc = -errno;
1456 goto discard_msg;
1457 }
1458 }
1459 msgb_put(msg, rc);
1460 /* still not all of payload received? */
1461 if (rc < needed) {
1462 if (!tmp_msg) {
1463 rc = -EIO;
1464 goto discard_msg;
1465 }
1466 *tmp_msg = msg;
1467 return -EAGAIN;
1468 }
1469 }
1470 /* else: complete message received */
Harald Weltefdd71c82021-01-02 20:50:47 +01001471 rc = msgb_length(msg);
Harald Welte07958e42019-05-03 09:39:10 +02001472 if (tmp_msg)
1473 *tmp_msg = NULL;
1474 *rmsg = msg;
1475 return rc;
1476
1477discard_msg:
Harald Welte07958e42019-05-03 09:39:10 +02001478 if (tmp_msg)
1479 *tmp_msg = NULL;
1480 msgb_free(msg);
1481 return rc;
1482}
1483
1484#endif /* HAVE_SYS_SOCKET_H */