blob: a891c52aa53be91c456c66ba745991adef751070 [file] [log] [blame]
Harald Welte07958e42019-05-03 09:39:10 +02001/*
2 * Copyright (C) 2019 Harald Welte <laforge@gnumonks.org>
3 *
4 * All Rights Reserved
5 *
6 * SPDX-License-Identifier: GPL-2.0+
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23#include "config.h"
24
25#include <errno.h>
26
27#include <sys/types.h>
28
29#include <osmocom/core/linuxlist.h>
30#include <osmocom/core/msgb.h>
31
32#include <osmocom/gsm/tlv.h>
33#include <osmocom/gsm/cbsp.h>
34#include <osmocom/gsm/gsm0808_utils.h>
35
36struct msgb *osmo_cbsp_msgb_alloc(void *ctx, const char *name)
37{
38 /* make the messages rather large as the cell lists can be long! */
39 return msgb_alloc_headroom_c(ctx, 65535, 16, name);
40}
41
42/***********************************************************************
43 * IE Encoding
44 ***********************************************************************/
45
46/* 8.2.6 Cell List */
47static void msgb_put_cbsp_cell_list(struct msgb *msg, const struct osmo_cbsp_cell_list *cl)
48{
49 const struct osmo_cbsp_cell_ent *ent;
50 uint8_t *lenptr;
51
52 /* put tag; reserve space for length; put discriminator */
53 msgb_put_u8(msg, CBSP_IEI_CELL_LIST);
54 lenptr = msgb_put(msg, sizeof(uint16_t));
55 msgb_put_u8(msg, cl->id_discr);
56 /* put list elements */
57 llist_for_each_entry(ent, &cl->list, list) {
58 gsm0808_msgb_put_cell_id_u(msg, cl->id_discr, &ent->cell_id);
59 }
60 /* update IE length */
61 osmo_store16be(msg->tail - (lenptr+2), lenptr);
62}
63
64/* 8.2.11 Failure List (discriminator per entry) */
65static void msgb_put_cbsp_fail_list(struct msgb *msg, const struct llist_head *fl)
66{
67 const struct osmo_cbsp_fail_ent *ent;
68 uint8_t *lenptr;
69
70 /* put tag; reserve space for length; put discriminator */
71 msgb_put_u8(msg, CBSP_IEI_FAILURE_LIST);
72 lenptr = msgb_put(msg, sizeof(uint16_t));
73 /* put list elements */
74 llist_for_each_entry(ent, fl, list) {
75 msgb_put_u8(msg, ent->id_discr);
76 gsm0808_msgb_put_cell_id_u(msg, ent->id_discr, &ent->cell_id);
77 msgb_put_u8(msg, ent->cause);
78 }
79 /* update IE length */
80 osmo_store16be(msg->tail - (lenptr+2), lenptr);
81}
82
83/* 8.2.12 Radio Resource Loading List */
84static void msgb_put_cbsp_loading_list(struct msgb *msg, const struct osmo_cbsp_loading_list *ll)
85{
86 const struct osmo_cbsp_loading_ent *ent;
87 uint8_t *lenptr;
88
89 /* put tag; reserve space for length; put discriminator */
90 msgb_put_u8(msg, CBSP_IEI_RR_LOADING_LIST);
91 lenptr = msgb_put(msg, sizeof(uint16_t));
92 msgb_put_u8(msg, ll->id_discr);
93 /* put list elements */
94 llist_for_each_entry(ent, &ll->list, list) {
95 gsm0808_msgb_put_cell_id_u(msg, ll->id_discr, &ent->cell_id);
96 msgb_put_u8(msg, ent->load[0]);
97 msgb_put_u8(msg, ent->load[1]);
98 }
99 /* update IE length */
100 osmo_store16be(msg->tail - (lenptr+2), lenptr);
101}
102
103/* 8.2.10 Completed List */
104static void msgb_put_cbsp_num_compl_list(struct msgb *msg, const struct osmo_cbsp_num_compl_list *cl)
105{
106 const struct osmo_cbsp_num_compl_ent *ent;
107 uint8_t *lenptr;
108
109 /* put tag; reserve space for length; put discriminator */
110 msgb_put_u8(msg, CBSP_IEI_NUM_BCAST_COMPL_LIST);
111 lenptr = msgb_put(msg, sizeof(uint16_t));
112 msgb_put_u8(msg, cl->id_discr);
113 /* put list elements */
114 llist_for_each_entry(ent, &cl->list, list) {
115 gsm0808_msgb_put_cell_id_u(msg, cl->id_discr, &ent->cell_id);
116 msgb_put_u16(msg, ent->num_compl);
117 msgb_put_u8(msg, ent->num_bcast_info);
118 }
119 /* update IE length */
120 osmo_store16be(msg->tail - (lenptr+2), lenptr);
121}
122
123static int encode_wperiod(uint32_t secs)
124{
125 if (secs == 0xffffffff)
126 return 0; /* infinite */
127 if (secs <= 10)
128 return secs;
129 if (secs <= 30)
130 return (secs-10)/2;
131 if (secs <= 120)
132 return (secs-30)/5;
133 if (secs <= 600)
134 return (secs-120)/10;
135 if (secs <= 60*60)
136 return (secs-600)/30;
137 return -1;
138}
139
140/***********************************************************************
141 * Message Encoding
142 ***********************************************************************/
143
144/* 8.1.3.1 WRITE REPLACE */
145static int cbsp_enc_write_repl(struct msgb *msg, const struct osmo_cbsp_write_replace *in)
146{
147 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
148 msgb_tv16_put(msg, CBSP_IEI_NEW_SERIAL_NR, in->new_serial_nr);
149 if (in->old_serial_nr)
150 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, *in->old_serial_nr);
151 msgb_put_cbsp_cell_list(msg, &in->cell_list);
152 if (in->is_cbs) {
153 int num_of_pages = llist_count(&in->u.cbs.msg_content);
154 struct osmo_cbsp_content *ce;
155 if (num_of_pages == 0 || num_of_pages > 15)
156 return -EINVAL;
157 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->u.cbs.channel_ind);
158 msgb_tv_put(msg, CBSP_IEI_CATEGORY, in->u.cbs.category);
159 msgb_tv16_put(msg, CBSP_IEI_REP_PERIOD, in->u.cbs.rep_period);
160 msgb_tv16_put(msg, CBSP_IEI_NUM_BCAST_REQ, in->u.cbs.num_bcast_req);
161 msgb_tv_put(msg, CBSP_IEI_NUM_OF_PAGES, num_of_pages);
162 msgb_tv_put(msg, CBSP_IEI_DCS, in->u.cbs.dcs);
163 llist_for_each_entry(ce, &in->u.cbs.msg_content, list) {
164 uint8_t *out;
165 /* cannot use msgb_tlv_put() as 'len' isn't actually the length of
166 * the data field */
167 msgb_put_u8(msg, CBSP_IEI_MSG_CONTENT);
168 msgb_put_u8(msg, ce->user_len);
169 out = msgb_put(msg, sizeof(ce->data));
170 memcpy(out, ce->data, sizeof(ce->data));
171 }
172 } else {
173 int wperiod = encode_wperiod(in->u.emergency.warning_period);
174 if (wperiod < 0)
175 return -EINVAL;
176 msgb_tv_put(msg, CBSP_IEI_EMERG_IND, in->u.emergency.indicator);
177 msgb_tv16_put(msg, CBSP_IEI_WARN_TYPE, in->u.emergency.warning_type);
178 msgb_tlv_put(msg, CBSP_IEI_WARN_SEC_INFO, sizeof(in->u.emergency.warning_sec_info),
179 in->u.emergency.warning_sec_info);
180 msgb_tv_put(msg, CBSP_IEI_WARNING_PERIOD, wperiod);
181 }
182 return 0;
183}
184
185/* 8.1.3.2 WRITE REPLACE COMPLETE*/
186static int cbsp_enc_write_repl_compl(struct msgb *msg, const struct osmo_cbsp_write_replace_complete *in)
187{
188 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
189 msgb_tv16_put(msg, CBSP_IEI_NEW_SERIAL_NR, in->new_serial_nr);
190 if (in->old_serial_nr)
191 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, *in->old_serial_nr);
192
193 if (!llist_empty(&in->num_compl_list.list))
194 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
195 if (!llist_empty(&in->cell_list.list))
196 msgb_put_cbsp_cell_list(msg, &in->cell_list);
197 if (in->channel_ind)
198 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
199 return 0;
200}
201
202/* 8.1.3.3 WRITE REPLACE FAILURE */
203static int cbsp_enc_write_repl_fail(struct msgb *msg, const struct osmo_cbsp_write_replace_failure *in)
204{
205 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
206 msgb_tv16_put(msg, CBSP_IEI_NEW_SERIAL_NR, in->new_serial_nr);
207 if (in->old_serial_nr)
208 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, *in->old_serial_nr);
209
210 msgb_put_cbsp_fail_list(msg, &in->fail_list);
211 if (!llist_empty(&in->num_compl_list.list))
212 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
213 if (!llist_empty(&in->cell_list.list))
214 msgb_put_cbsp_cell_list(msg, &in->cell_list);
215 if (in->channel_ind)
216 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
217 return 0;
218}
219
220/* 8.1.3.4 KILL */
221static int cbsp_enc_kill(struct msgb *msg, const struct osmo_cbsp_kill *in)
222{
223 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
224 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
225 msgb_put_cbsp_cell_list(msg, &in->cell_list);
226 if (in->channel_ind)
227 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
228 return 0;
229}
230
231/* 8.1.3.5 KILL COMPLETE */
232static int cbsp_enc_kill_compl(struct msgb *msg, const struct osmo_cbsp_kill_complete *in)
233{
234 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
235 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
236 if (!llist_empty(&in->num_compl_list.list))
237 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
238 if (!llist_empty(&in->cell_list.list))
239 msgb_put_cbsp_cell_list(msg, &in->cell_list);
240 if (in->channel_ind)
241 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
242 return 0;
243}
244
245/* 8.1.3.6 KILL FAILURE */
246static int cbsp_enc_kill_fail(struct msgb *msg, const struct osmo_cbsp_kill_failure *in)
247{
248 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
249 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
250 msgb_put_cbsp_fail_list(msg, &in->fail_list);
251 if (!llist_empty(&in->num_compl_list.list))
252 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
253 if (!llist_empty(&in->cell_list.list))
254 msgb_put_cbsp_cell_list(msg, &in->cell_list);
255 if (in->channel_ind)
256 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
257 return 0;
258}
259
260/* 8.1.3.7 LOAD QUERY */
261static int cbsp_enc_load_query(struct msgb *msg, const struct osmo_cbsp_load_query *in)
262{
263 msgb_put_cbsp_cell_list(msg, &in->cell_list);
264 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
265 return 0;
266}
267
268/* 8.1.3.8 LOAD QUERY COMPLETE */
269static int cbsp_enc_load_query_compl(struct msgb *msg, const struct osmo_cbsp_load_query_complete *in)
270{
271 msgb_put_cbsp_loading_list(msg, &in->loading_list);
272 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
273 return 0;
274}
275
276/* 8.1.3.9 LOAD QUERY FAILURE */
277static int cbsp_enc_load_query_fail(struct msgb *msg, const struct osmo_cbsp_load_query_failure *in)
278{
279 msgb_put_cbsp_fail_list(msg, &in->fail_list);
280 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
281 if (!llist_empty(&in->loading_list.list))
282 msgb_put_cbsp_loading_list(msg, &in->loading_list);
283 return 0;
284}
285
286/* 8.1.3.10 STATUS QUERY */
287static int cbsp_enc_msg_status_query(struct msgb *msg, const struct osmo_cbsp_msg_status_query *in)
288{
289 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
290 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
291 msgb_put_cbsp_cell_list(msg, &in->cell_list);
292 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
293 return 0;
294}
295
296/* 8.1.3.11 STATUS QUERY COMPLETE */
297static int cbsp_enc_msg_status_query_compl(struct msgb *msg,
298 const struct osmo_cbsp_msg_status_query_complete *in)
299{
300 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
301 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
302 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
303 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
304 return 0;
305}
306
307/* 8.1.3.12 STATUS QUERY FAILURE */
308static int cbsp_enc_msg_status_query_fail(struct msgb *msg,
309 const struct osmo_cbsp_msg_status_query_failure *in)
310{
311 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, in->msg_id);
312 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, in->old_serial_nr);
313 msgb_put_cbsp_fail_list(msg, &in->fail_list);
314 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, in->channel_ind);
315 if (!llist_empty(&in->num_compl_list.list))
316 msgb_put_cbsp_num_compl_list(msg, &in->num_compl_list);
317 return 0;
318}
319
320/* 8.1.3.16 RESET */
321static int cbsp_enc_reset(struct msgb *msg, const struct osmo_cbsp_reset *in)
322{
323 msgb_put_cbsp_cell_list(msg, &in->cell_list);
324 return 0;
325}
326
327/* 8.1.3.17 RESET COMPLETE */
328static int cbsp_enc_reset_compl(struct msgb *msg, const struct osmo_cbsp_reset_complete *in)
329{
330 msgb_put_cbsp_cell_list(msg, &in->cell_list);
331 return 0;
332}
333
334/* 8.1.3.18 RESET FAILURE */
335static int cbsp_enc_reset_fail(struct msgb *msg, const struct osmo_cbsp_reset_failure *in)
336{
337 msgb_put_cbsp_fail_list(msg, &in->fail_list);
338 if (!llist_empty(&in->cell_list.list))
339 msgb_put_cbsp_cell_list(msg, &in->cell_list);
340 return 0;
341}
342
343/* 8.1.3.18a KEEP ALIVE */
344static int cbsp_enc_keep_alive(struct msgb *msg, const struct osmo_cbsp_keep_alive *in)
345{
346 msgb_tv_put(msg, CBSP_IEI_KEEP_ALIVE_REP_PERIOD, in->repetition_period);
347 return 0;
348}
349
350/* 8.1.3.18b KEEP ALIVE COMPLETE */
351static int cbsp_enc_keep_alive_compl(struct msgb *msg, const struct osmo_cbsp_keep_alive_complete *in)
352{
353 return 0;
354}
355
356/* 8.1.3.19 RESTART */
357static int cbsp_enc_restart(struct msgb *msg, const struct osmo_cbsp_restart *in)
358{
359 msgb_put_cbsp_cell_list(msg, &in->cell_list);
360 msgb_tv_put(msg, CBSP_IEI_BCAST_MSG_TYPE, in->bcast_msg_type);
361 msgb_tv_put(msg, CBSP_IEI_RECOVERY_IND, in->recovery_ind);
362 return 0;
363}
364
365/* 8.1.3.20 FAILURE */
366static int cbsp_enc_failure(struct msgb *msg, const struct osmo_cbsp_failure *in)
367{
368 msgb_put_cbsp_fail_list(msg, &in->fail_list);
369 msgb_tv_put(msg, CBSP_IEI_BCAST_MSG_TYPE, in->bcast_msg_type);
370 return 0;
371}
372
373/* 8.1.3.21 ERROR INDICATION */
374static int cbsp_enc_error_ind(struct msgb *msg, const struct osmo_cbsp_error_ind *in)
375{
376 msgb_tv_put(msg, CBSP_IEI_CAUSE, in->cause);
377 if (in->msg_id)
378 msgb_tv16_put(msg, CBSP_IEI_MSG_ID, *in->msg_id);
379 if (in->new_serial_nr)
380 msgb_tv16_put(msg, CBSP_IEI_NEW_SERIAL_NR, *in->new_serial_nr);
381 if (in->old_serial_nr)
382 msgb_tv16_put(msg, CBSP_IEI_OLD_SERIAL_NR, *in->old_serial_nr);
383 if (in->channel_ind)
384 msgb_tv_put(msg, CBSP_IEI_CHANNEL_IND, *in->channel_ind);
385 return 0;
386}
387
388/*! Encode a CBSP message from the decoded/parsed structure representation to binary PDU.
389 * \param[in] ctx talloc context from which to allocate returned msgb.
390 * \param[in] in decoded CBSP message which is to be encoded. Ownership not transferred.
391 * \return callee-allocated message buffer containing binary CBSP PDU; NULL on error */
392struct msgb *osmo_cbsp_encode(void *ctx, const struct osmo_cbsp_decoded *in)
393{
394 struct msgb *msg = osmo_cbsp_msgb_alloc(ctx, __func__);
395 unsigned int len;
396 int rc;
397
398 if (!msg)
399 return NULL;
400
401 switch (in->msg_type) {
402 case CBSP_MSGT_WRITE_REPLACE:
403 rc = cbsp_enc_write_repl(msg, &in->u.write_replace);
404 break;
405 case CBSP_MSGT_WRITE_REPLACE_COMPL:
406 rc = cbsp_enc_write_repl_compl(msg, &in->u.write_replace_compl);
407 break;
408 case CBSP_MSGT_WRITE_REPLACE_FAIL:
409 rc = cbsp_enc_write_repl_fail(msg, &in->u.write_replace_fail);
410 break;
411 case CBSP_MSGT_KILL:
412 rc = cbsp_enc_kill(msg, &in->u.kill);
413 break;
414 case CBSP_MSGT_KILL_COMPL:
415 rc = cbsp_enc_kill_compl(msg, &in->u.kill_compl);
416 break;
417 case CBSP_MSGT_KILL_FAIL:
418 rc = cbsp_enc_kill_fail(msg, &in->u.kill_fail);
419 break;
420 case CBSP_MSGT_LOAD_QUERY:
421 rc = cbsp_enc_load_query(msg, &in->u.load_query);
422 break;
423 case CBSP_MSGT_LOAD_QUERY_COMPL:
424 rc = cbsp_enc_load_query_compl(msg, &in->u.load_query_compl);
425 break;
426 case CBSP_MSGT_LOAD_QUERY_FAIL:
427 rc = cbsp_enc_load_query_fail(msg, &in->u.load_query_fail);
428 break;
429 case CBSP_MSGT_MSG_STATUS_QUERY:
430 rc = cbsp_enc_msg_status_query(msg, &in->u.msg_status_query);
431 break;
432 case CBSP_MSGT_MSG_STATUS_QUERY_COMPL:
433 rc = cbsp_enc_msg_status_query_compl(msg, &in->u.msg_status_query_compl);
434 break;
435 case CBSP_MSGT_MSG_STATUS_QUERY_FAIL:
436 rc = cbsp_enc_msg_status_query_fail(msg, &in->u.msg_status_query_fail);
437 break;
438 case CBSP_MSGT_RESET:
439 rc = cbsp_enc_reset(msg, &in->u.reset);
440 break;
441 case CBSP_MSGT_RESET_COMPL:
442 rc = cbsp_enc_reset_compl(msg, &in->u.reset_compl);
443 break;
444 case CBSP_MSGT_RESET_FAIL:
445 rc = cbsp_enc_reset_fail(msg, &in->u.reset_fail);
446 break;
447 case CBSP_MSGT_RESTART:
448 rc = cbsp_enc_restart(msg, &in->u.restart);
449 break;
450 case CBSP_MSGT_FAILURE:
451 rc = cbsp_enc_failure(msg, &in->u.failure);
452 break;
453 case CBSP_MSGT_ERROR_IND:
454 rc = cbsp_enc_error_ind(msg, &in->u.error_ind);
455 break;
456 case CBSP_MSGT_KEEP_ALIVE:
457 rc = cbsp_enc_keep_alive(msg, &in->u.keep_alive);
458 break;
459 case CBSP_MSGT_KEEP_ALIVE_COMPL:
460 rc = cbsp_enc_keep_alive_compl(msg, &in->u.keep_alive_compl);
461 break;
462 case CBSP_MSGT_SET_DRX:
463 case CBSP_MSGT_SET_DRX_COMPL:
464 case CBSP_MSGT_SET_DRX_FAIL:
465 rc = -1;
466 break;
467 default:
468 rc = -1;
469 break;
470 }
471
472 if (rc < 0) {
473 msgb_free(msg);
474 return NULL;
475 }
476
477 /* push header in front */
478 len = msgb_length(msg);
479 msgb_push_u8(msg, len & 0xff);
480 msgb_push_u8(msg, (len >> 8) & 0xff);
481 msgb_push_u8(msg, (len >> 16) & 0xff);
482 msgb_push_u8(msg, in->msg_type);
483
484 return msg;
485}
486
487/***********************************************************************
488 * IE Decoding
489 ***********************************************************************/
490
491/* 8.2.6 Cell List */
492static int cbsp_decode_cell_list(struct osmo_cbsp_cell_list *cl, void *ctx,
493 const uint8_t *buf, unsigned int len)
494{
495 const uint8_t *cur = buf;
496 int rc;
497
498 cl->id_discr = *cur++;
499
500 while (cur < buf + len) {
501 struct osmo_cbsp_cell_ent *ent = talloc_zero(ctx, struct osmo_cbsp_cell_ent);
502 unsigned int len_remain = len - (cur - buf);
503 OSMO_ASSERT(ent);
504 rc = gsm0808_decode_cell_id_u(&ent->cell_id, cl->id_discr, cur, len_remain);
505 if (rc < 0)
506 return rc;
507 cur += rc;
508 llist_add_tail(&ent->list, &cl->list);
509 }
510 return 0;
511}
512
513/* 8.2.11 Failure List (discriminator per entry) */
514static int cbsp_decode_fail_list(struct llist_head *fl, void *ctx,
515 const uint8_t *buf, unsigned int len)
516{
517 const uint8_t *cur = buf;
518 int rc;
519
520 while (cur < buf + len) {
521 struct osmo_cbsp_fail_ent *ent = talloc_zero(ctx, struct osmo_cbsp_fail_ent);
522 unsigned int len_remain = len - (cur - buf);
523 OSMO_ASSERT(ent);
524 ent->id_discr = cur[0];
525 rc = gsm0808_decode_cell_id_u(&ent->cell_id, ent->id_discr, cur+1, len_remain-1);
526 if (rc < 0)
527 return rc;
528 cur += rc;
529 ent->cause = *cur++;
530 llist_add_tail(&ent->list, fl);
531 }
532 return 0;
533}
534
535/* 8.2.12 Radio Resource Loading List */
536static int cbsp_decode_loading_list(struct osmo_cbsp_loading_list *ll, void *ctx,
537 const uint8_t *buf, unsigned int len)
538{
539 const uint8_t *cur = buf;
540 int rc;
541
542 ll->id_discr = *cur++;
543 while (cur < buf + len) {
544 struct osmo_cbsp_loading_ent *ent = talloc_zero(ctx, struct osmo_cbsp_loading_ent);
545 unsigned int len_remain = len - (cur - buf);
546 OSMO_ASSERT(ent);
547 rc = gsm0808_decode_cell_id_u(&ent->cell_id, ll->id_discr, cur, len_remain);
548 if (rc < 0)
549 return rc;
550 cur += rc;
551 if (cur + 2 > buf + len) {
552 talloc_free(ent);
553 return -EINVAL;
554 }
555 ent->load[0] = *cur++;
556 ent->load[1] = *cur++;
557 llist_add_tail(&ent->list, &ll->list);
558 }
559 return 0;
560}
561
562/* 8.2.10 Completed List */
563static int cbsp_decode_num_compl_list(struct osmo_cbsp_num_compl_list *cl, void *ctx,
564 const uint8_t *buf, unsigned int len)
565{
566 const uint8_t *cur = buf;
567 int rc;
568
569 cl->id_discr = *cur++;
570 while (cur < buf + len) {
571 struct osmo_cbsp_num_compl_ent *ent = talloc_zero(ctx, struct osmo_cbsp_num_compl_ent);
572 unsigned int len_remain = len - (cur - buf);
573 OSMO_ASSERT(ent);
574 rc = gsm0808_decode_cell_id_u(&ent->cell_id, cl->id_discr, cur, len_remain);
575 if (rc < 0)
576 return rc;
577 cur += rc;
578 if (cur + 3 > buf + len) {
579 talloc_free(ent);
580 return -EINVAL;
581 }
582 ent->num_compl = osmo_load16be(cur); cur += 2;
583 ent->num_bcast_info = *cur++;
584 llist_add_tail(&ent->list, &cl->list);
585 }
586 return 0;
587}
588
589/* 8.2.25 */
590static uint32_t decode_wperiod(uint8_t in)
591{
592 if (in == 0x00)
593 return 0xffffffff; /* infinite */
594 if (in <= 10)
595 return in;
596 if (in <= 20)
597 return 10 + (in - 10)*2;
598 if (in <= 38)
599 return 30 + (in - 20)*5;
600 if (in <= 86)
601 return 120 + (in - 38)*10;
602 if (in <= 186)
603 return 600 + (in - 86)*30;
604 else
605 return 0;
606}
607
608
609/***********************************************************************
610 * Message Decoding
611 ***********************************************************************/
612
613/* 8.1.3.1 WRITE REPLACE */
614static int cbsp_dec_write_repl(struct osmo_cbsp_write_replace *out, const struct tlv_parsed *tp,
615 struct msgb *in, void *ctx)
616{
617 unsigned int i;
618
619 /* check for mandatory IEs */
620 if (!TLVP_PRESENT(tp, CBSP_IEI_MSG_ID) ||
621 !TLVP_PRESENT(tp, CBSP_IEI_NEW_SERIAL_NR) ||
622 !TLVP_PRESENT(tp, CBSP_IEI_CELL_LIST))
623 return -EINVAL;
624
625 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
626 out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
627 if (TLVP_PRESENT(tp, CBSP_IEI_OLD_SERIAL_NR)) {
628 out->old_serial_nr = talloc(ctx, uint16_t);
629 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
630 }
631
632 INIT_LLIST_HEAD(&out->cell_list.list);
633 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
634 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
635
636 if (TLVP_PRESENT(tp, CBSP_IEI_CHANNEL_IND)) {
637 uint8_t num_of_pages;
638 INIT_LLIST_HEAD(&out->u.cbs.msg_content);
639 if (TLVP_PRESENT(tp, CBSP_IEI_EMERG_IND))
640 return -EINVAL;
641 if (!TLVP_PRESENT(tp, CBSP_IEI_CATEGORY) ||
642 !TLVP_PRESENT(tp, CBSP_IEI_REP_PERIOD) ||
643 !TLVP_PRESENT(tp, CBSP_IEI_NUM_BCAST_REQ) ||
644 !TLVP_PRESENT(tp, CBSP_IEI_NUM_OF_PAGES) ||
645 !TLVP_PRESENT(tp, CBSP_IEI_DCS))
646 return -EINVAL;
647 out->is_cbs = true;
648 out->u.cbs.channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
649 out->u.cbs.category = *TLVP_VAL(tp, CBSP_IEI_CATEGORY);
650 out->u.cbs.rep_period = tlvp_val16be(tp, CBSP_IEI_REP_PERIOD);
651 out->u.cbs.num_bcast_req = tlvp_val16be(tp, CBSP_IEI_NUM_BCAST_REQ);
652 num_of_pages = *TLVP_VAL(tp, CBSP_IEI_NUM_OF_PAGES);
653 if (num_of_pages < 1)
654 return -EINVAL;
655 /* parse pages */
656 for (i = 0; i < num_of_pages; i++) {
657 const uint8_t *ie = TLVP_VAL(&tp[i], CBSP_IEI_MSG_CONTENT);
658 struct osmo_cbsp_content *page;
659 if (!ie)
660 return -EINVAL;
661 page = talloc_zero(ctx, struct osmo_cbsp_content);
662 OSMO_ASSERT(page);
663 page->user_len = *(ie-1); /* length byte before payload */
664 memcpy(page->data, ie, sizeof(page->data));
665 llist_add_tail(&page->list, &out->u.cbs.msg_content);
666 }
667 } else {
668 if (!TLVP_PRES_LEN(tp, CBSP_IEI_EMERG_IND, 1) ||
669 !TLVP_PRES_LEN(tp, CBSP_IEI_WARN_TYPE, 2) ||
670 !TLVP_PRES_LEN(tp, CBSP_IEI_WARN_SEC_INFO, 50) ||
671 !TLVP_PRES_LEN(tp, CBSP_IEI_WARNING_PERIOD, 1))
672 return -EINVAL;
673 out->u.emergency.indicator = *TLVP_VAL(tp, CBSP_IEI_EMERG_IND);
674 out->u.emergency.warning_type = tlvp_val16be(tp, CBSP_IEI_WARN_TYPE);
675 memcpy(&out->u.emergency.warning_sec_info, TLVP_VAL(tp, CBSP_IEI_WARN_SEC_INFO),
676 sizeof(out->u.emergency.warning_sec_info));
677 out->u.emergency.warning_period = decode_wperiod(*TLVP_VAL(tp, CBSP_IEI_WARNING_PERIOD));
678 }
679 return 0;
680}
681
682/* 8.1.3.2 WRITE REPLACE COMPLETE*/
683static int cbsp_dec_write_repl_compl(struct osmo_cbsp_write_replace_complete *out,
684 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
685{
686 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
687 !TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2))
688 return -EINVAL;
689
690 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
691 out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
692 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
693 out->old_serial_nr = talloc(ctx, uint16_t);
694 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
695 }
696
697 INIT_LLIST_HEAD(&out->num_compl_list.list);
698 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
699 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
700 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
701 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
702 }
703
704 INIT_LLIST_HEAD(&out->cell_list.list);
705 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
706 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
707
708 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
709 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
710 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
711 }
712 return 0;
713}
714
715/* 8.1.3.3 WRITE REPLACE FAILURE */
716static int cbsp_dec_write_repl_fail(struct osmo_cbsp_write_replace_failure *out,
717 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
718{
719 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
720 !TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2) ||
721 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5))
722 return -EINVAL;
723
724 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
725 out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
726 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
727 out->old_serial_nr = talloc(ctx, uint16_t);
728 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
729 }
730
731 INIT_LLIST_HEAD(&out->fail_list);
732 cbsp_decode_fail_list(&out->fail_list, ctx,
733 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
734 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
735
736 INIT_LLIST_HEAD(&out->num_compl_list.list);
737 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
738 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
739 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
740 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
741 }
742
743 INIT_LLIST_HEAD(&out->cell_list.list);
744 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
745 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
746 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
747 }
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.4 KILL */
757static int cbsp_dec_kill(struct osmo_cbsp_kill *out, const struct tlv_parsed *tp,
758 struct msgb *in, void *ctx)
759{
760 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
761 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
762 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1))
763 return -EINVAL;
764 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
765 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
766
767 INIT_LLIST_HEAD(&out->cell_list.list);
768 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
769 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
770
771 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
772 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
773 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
774 }
775 return 0;
776}
777
778/* 8.1.3.5 KILL COMPLETE */
779static int cbsp_dec_kill_compl(struct osmo_cbsp_kill_complete *out, const struct tlv_parsed *tp,
780 struct msgb *in, void *ctx)
781{
782 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
783 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
784 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1))
785 return -EINVAL;
786
787 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
788 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
789
790 INIT_LLIST_HEAD(&out->num_compl_list.list);
791 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
792 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
793 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
794 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
795 }
796
797 INIT_LLIST_HEAD(&out->cell_list.list);
798 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
799 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
800
801 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
802 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
803 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
804 }
805 return 0;
806}
807
808/* 8.1.3.6 KILL FAILURE */
809static int cbsp_dec_kill_fail(struct osmo_cbsp_kill_failure *out, const struct tlv_parsed *tp,
810 struct msgb *in, void *ctx)
811{
812 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
813 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
814 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5))
815 return -EINVAL;
816
817 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
818 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
819
820 INIT_LLIST_HEAD(&out->fail_list);
821 cbsp_decode_fail_list(&out->fail_list, ctx,
822 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
823 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
824
825 INIT_LLIST_HEAD(&out->num_compl_list.list);
826 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
827 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
828 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
829 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
830 }
831
832 INIT_LLIST_HEAD(&out->cell_list.list);
833 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
834 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
835 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
836 }
837
838 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
839 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
840 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
841 }
842 return 0;
843}
844
845/* 8.1.3.7 LOAD QUERY */
846static int cbsp_dec_load_query(struct osmo_cbsp_load_query *out, const struct tlv_parsed *tp,
847 struct msgb *in, void *ctx)
848{
849 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
850 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1))
851 return -EINVAL;
852
853 INIT_LLIST_HEAD(&out->cell_list.list);
854 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
855 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
856
857 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
858 return 0;
859}
860
861/* 8.1.3.8 LOAD QUERY COMPLETE */
862static int cbsp_dec_load_query_compl(struct osmo_cbsp_load_query_complete *out,
863 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
864{
865 if (!TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6) ||
866 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1))
867 return -EINVAL;
868
869 INIT_LLIST_HEAD(&out->loading_list.list);
870 cbsp_decode_loading_list(&out->loading_list, ctx,
871 TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
872 TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
873
874 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
875 return 0;
876}
877
878/* 8.1.3.9 LOAD QUERY FAILURE */
879static int cbsp_dec_load_query_fail(struct osmo_cbsp_load_query_failure *out,
880 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
881{
882 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
883 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1))
884 return -EINVAL;
885
886 INIT_LLIST_HEAD(&out->fail_list);
887 cbsp_decode_fail_list(&out->fail_list, ctx,
888 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
889 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
890
891 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
892
893 INIT_LLIST_HEAD(&out->loading_list.list);
894 if (TLVP_PRES_LEN(tp, CBSP_IEI_RR_LOADING_LIST, 6)) {
895 cbsp_decode_loading_list(&out->loading_list, ctx,
896 TLVP_VAL(tp, CBSP_IEI_RR_LOADING_LIST),
897 TLVP_LEN(tp, CBSP_IEI_RR_LOADING_LIST));
898 }
899 return 0;
900}
901
902/* 8.1.3.10 STATUS QUERY */
903static int cbsp_dec_msg_status_query(struct osmo_cbsp_msg_status_query *out,
904 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
905{
906 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
907 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
908 !TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
909 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1))
910 return -EINVAL;
911 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
912 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
913
914 INIT_LLIST_HEAD(&out->cell_list.list);
915 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
916 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
917
918 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
919 return 0;
920}
921
922/* 8.1.3.11 STATUS QUERY COMPLETE */
923static int cbsp_dec_msg_status_query_compl(struct osmo_cbsp_msg_status_query_complete *out,
924 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
925{
926 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
927 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
928 !TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7) ||
929 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1))
930 return -EINVAL;
931
932 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
933 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
934
935 INIT_LLIST_HEAD(&out->num_compl_list.list);
936 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
937 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
938 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
939 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
940 return 0;
941}
942
943/* 8.1.3.12 STATUS QUERY FAILURE */
944static int cbsp_dec_msg_status_query_fail(struct osmo_cbsp_msg_status_query_failure *out,
945 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
946{
947 if (!TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2) ||
948 !TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2) ||
949 !TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
950 !TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1))
951 return -EINVAL;
952
953 out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
954 out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
955
956 INIT_LLIST_HEAD(&out->fail_list);
957 cbsp_decode_fail_list(&out->fail_list, ctx,
958 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
959 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
960
961 out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
962
963 INIT_LLIST_HEAD(&out->num_compl_list.list);
964 if (TLVP_PRES_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST, 7)) {
965 cbsp_decode_num_compl_list(&out->num_compl_list, ctx,
966 TLVP_VAL(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST),
967 TLVP_LEN(tp, CBSP_IEI_NUM_BCAST_COMPL_LIST));
968 }
969 return 0;
970}
971
972/* 8.1.3.16 RESET */
973static int cbsp_dec_reset(struct osmo_cbsp_reset *out, const struct tlv_parsed *tp,
974 struct msgb *in, void *ctx)
975{
976 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1))
977 return -EINVAL;
978
979 INIT_LLIST_HEAD(&out->cell_list.list);
980 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
981 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
982 return 0;
983}
984
985/* 8.1.3.17 RESET COMPLETE */
986static int cbsp_dec_reset_compl(struct osmo_cbsp_reset_complete *out, const struct tlv_parsed *tp,
987 struct msgb *in, void *ctx)
988{
989 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1))
990 return -EINVAL;
991
992 INIT_LLIST_HEAD(&out->cell_list.list);
993 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
994 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
995 return 0;
996}
997
998/* 8.1.3.18 RESET FAILURE */
999static int cbsp_dec_reset_fail(struct osmo_cbsp_reset_failure *out, const struct tlv_parsed *tp,
1000 struct msgb *in, void *ctx)
1001{
1002 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5))
1003 return -EINVAL;
1004
1005 INIT_LLIST_HEAD(&out->fail_list);
1006 cbsp_decode_fail_list(&out->fail_list, ctx,
1007 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
1008 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
1009
1010 INIT_LLIST_HEAD(&out->cell_list.list);
1011 if (TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1)) {
1012 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1013 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1014 }
1015 return 0;
1016}
1017
1018/* 8.1.3.18a KEEP ALIVE */
1019static int cbsp_dec_keep_alive(struct osmo_cbsp_keep_alive *out, const struct tlv_parsed *tp,
1020 struct msgb *in, void *ctx)
1021{
1022 if (!TLVP_PRES_LEN(tp, CBSP_IEI_KEEP_ALIVE_REP_PERIOD, 1))
1023 return -EINVAL;
1024
1025 out->repetition_period = *TLVP_VAL(tp, CBSP_IEI_KEEP_ALIVE_REP_PERIOD);
1026 return 0;
1027}
1028
1029/* 8.1.3.18b KEEP ALIVE COMPLETE */
1030static int cbsp_dec_keep_alive_compl(struct osmo_cbsp_keep_alive_complete *out,
1031 const struct tlv_parsed *tp, struct msgb *in, void *ctx)
1032{
1033 return 0;
1034}
1035
1036/* 8.1.3.19 RESTART */
1037static int cbsp_dec_restart(struct osmo_cbsp_restart *out, const struct tlv_parsed *tp,
1038 struct msgb *in, void *ctx)
1039{
1040 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CELL_LIST, 1) ||
1041 !TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1) ||
1042 !TLVP_PRES_LEN(tp, CBSP_IEI_RECOVERY_IND, 1))
1043 return -EINVAL;
1044
1045 INIT_LLIST_HEAD(&out->cell_list.list);
1046 cbsp_decode_cell_list(&out->cell_list, ctx, TLVP_VAL(tp, CBSP_IEI_CELL_LIST),
1047 TLVP_LEN(tp, CBSP_IEI_CELL_LIST));
1048
1049 out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE);
1050 out->recovery_ind = *TLVP_VAL(tp, CBSP_IEI_RECOVERY_IND);
1051 return 0;
1052}
1053
1054/* 8.1.3.20 FAILURE */
1055static int cbsp_dec_failure(struct osmo_cbsp_failure *out, const struct tlv_parsed *tp,
1056 struct msgb *in, void *ctx)
1057{
1058 if (!TLVP_PRES_LEN(tp, CBSP_IEI_FAILURE_LIST, 5) ||
1059 !TLVP_PRES_LEN(tp, CBSP_IEI_BCAST_MSG_TYPE, 1))
1060 return -EINVAL;
1061
1062 INIT_LLIST_HEAD(&out->fail_list);
1063 cbsp_decode_fail_list(&out->fail_list, ctx,
1064 TLVP_VAL(tp, CBSP_IEI_FAILURE_LIST),
1065 TLVP_LEN(tp, CBSP_IEI_FAILURE_LIST));
1066
1067 out->bcast_msg_type = *TLVP_VAL(tp, CBSP_IEI_BCAST_MSG_TYPE);
1068 return 0;
1069}
1070
1071/* 8.1.3.21 ERROR INDICATION */
1072static int cbsp_dec_error_ind(struct osmo_cbsp_error_ind *out, const struct tlv_parsed *tp,
1073 struct msgb *in, void *ctx)
1074{
1075 if (!TLVP_PRES_LEN(tp, CBSP_IEI_CAUSE, 1))
1076 return -EINVAL;
1077
1078 out->cause = *TLVP_VAL(tp, CBSP_IEI_CAUSE);
1079 if (TLVP_PRES_LEN(tp, CBSP_IEI_MSG_ID, 2)) {
1080 out->msg_id = talloc(ctx, uint16_t);
1081 *out->msg_id = tlvp_val16be(tp, CBSP_IEI_MSG_ID);
1082 }
1083 if (TLVP_PRES_LEN(tp, CBSP_IEI_NEW_SERIAL_NR, 2)) {
1084 out->new_serial_nr = talloc(ctx, uint16_t);
1085 *out->new_serial_nr = tlvp_val16be(tp, CBSP_IEI_NEW_SERIAL_NR);
1086 }
1087 if (TLVP_PRES_LEN(tp, CBSP_IEI_OLD_SERIAL_NR, 2)) {
1088 out->old_serial_nr = talloc(ctx, uint16_t);
1089 *out->old_serial_nr = tlvp_val16be(tp, CBSP_IEI_OLD_SERIAL_NR);
1090 }
1091 if (TLVP_PRES_LEN(tp, CBSP_IEI_CHANNEL_IND, 1)) {
1092 out->channel_ind = talloc(ctx, enum cbsp_channel_ind);
1093 *out->channel_ind = *TLVP_VAL(tp, CBSP_IEI_CHANNEL_IND);
1094 }
1095 return 0;
1096}
1097
1098/*! Decode a CBSP message from wire formwat to pased structure.
1099 * \param[in] ctx talloc context from which to allocate decoded output.
1100 * \param[in] in message buffer contiaining binary CBSP message.
1101 * \returns callee-allocated decoded representation of CBSP message; NULL on error */
1102struct osmo_cbsp_decoded *osmo_cbsp_decode(void *ctx, struct msgb *in)
1103{
1104 struct osmo_cbsp_decoded *out = talloc_zero(ctx, struct osmo_cbsp_decoded);
1105 const struct cbsp_header *h = msgb_l1(in);
1106 struct tlv_parsed tp[16]; /* max. number of pages in a given CBS message */
1107 unsigned int len;
1108 int rc;
1109
1110 if (!out)
1111 return NULL;
1112
1113 if (msgb_l1len(in) < sizeof(*h)) {
1114 goto out_err;
1115 }
1116 len = h->len[0] << 16 | h->len[1] << 8 | h->len[2];
1117
1118 /* discard messages where indicated length is more than we have */
1119 if (len > msgb_l2len(in)) {
1120 goto out_err;
1121 }
1122
1123 /* trim any messages with extra payload at the end */
1124 if (len < msgb_l2len(in))
1125 msgb_trim(in, (in->l2h - in->data) + msgb_l2len(in));
1126 out->msg_type = h->msg_type;
1127
1128 rc = tlv_parse2(tp, ARRAY_SIZE(tp), &cbsp_att_tlvdef, msgb_l2(in), msgb_l2len(in), 0, 0);
1129 if (rc < 0) {
1130 goto out_err;
1131 }
1132
1133 switch (h->msg_type) {
1134 case CBSP_MSGT_WRITE_REPLACE:
1135 rc = cbsp_dec_write_repl(&out->u.write_replace, tp, in, out);
1136 break;
1137 case CBSP_MSGT_WRITE_REPLACE_COMPL:
1138 rc = cbsp_dec_write_repl_compl(&out->u.write_replace_compl, tp, in, out);
1139 break;
1140 case CBSP_MSGT_WRITE_REPLACE_FAIL:
1141 rc = cbsp_dec_write_repl_fail(&out->u.write_replace_fail, tp, in, out);
1142 break;
1143 case CBSP_MSGT_KILL:
1144 rc = cbsp_dec_kill(&out->u.kill, tp, in, out);
1145 break;
1146 case CBSP_MSGT_KILL_COMPL:
1147 rc = cbsp_dec_kill_compl(&out->u.kill_compl, tp, in, out);
1148 break;
1149 case CBSP_MSGT_KILL_FAIL:
1150 rc = cbsp_dec_kill_fail(&out->u.kill_fail, tp, in, out);
1151 break;
1152 case CBSP_MSGT_LOAD_QUERY:
1153 rc = cbsp_dec_load_query(&out->u.load_query, tp, in, out);
1154 break;
1155 case CBSP_MSGT_LOAD_QUERY_COMPL:
1156 rc = cbsp_dec_load_query_compl(&out->u.load_query_compl, tp, in, out);
1157 break;
1158 case CBSP_MSGT_LOAD_QUERY_FAIL:
1159 rc = cbsp_dec_load_query_fail(&out->u.load_query_fail, tp, in, out);
1160 break;
1161 case CBSP_MSGT_MSG_STATUS_QUERY:
1162 rc = cbsp_dec_msg_status_query(&out->u.msg_status_query, tp, in, out);
1163 break;
1164 case CBSP_MSGT_MSG_STATUS_QUERY_COMPL:
1165 rc = cbsp_dec_msg_status_query_compl(&out->u.msg_status_query_compl, tp, in, out);
1166 break;
1167 case CBSP_MSGT_MSG_STATUS_QUERY_FAIL:
1168 rc = cbsp_dec_msg_status_query_fail(&out->u.msg_status_query_fail, tp, in, out);
1169 break;
1170 case CBSP_MSGT_RESET:
1171 rc = cbsp_dec_reset(&out->u.reset, tp, in, out);
1172 break;
1173 case CBSP_MSGT_RESET_COMPL:
1174 rc = cbsp_dec_reset_compl(&out->u.reset_compl, tp, in, out);
1175 break;
1176 case CBSP_MSGT_RESET_FAIL:
1177 rc = cbsp_dec_reset_fail(&out->u.reset_fail, tp, in, out);
1178 break;
1179 case CBSP_MSGT_RESTART:
1180 rc = cbsp_dec_restart(&out->u.restart, tp, in, out);
1181 break;
1182 case CBSP_MSGT_FAILURE:
1183 rc = cbsp_dec_failure(&out->u.failure, tp, in, out);
1184 break;
1185 case CBSP_MSGT_ERROR_IND:
1186 rc = cbsp_dec_error_ind(&out->u.error_ind, tp, in, out);
1187 break;
1188 case CBSP_MSGT_KEEP_ALIVE:
1189 rc = cbsp_dec_keep_alive(&out->u.keep_alive, tp, in, out);
1190 break;
1191 case CBSP_MSGT_KEEP_ALIVE_COMPL:
1192 rc = cbsp_dec_keep_alive_compl(&out->u.keep_alive_compl, tp, in, out);
1193 break;
1194 case CBSP_MSGT_SET_DRX:
1195 case CBSP_MSGT_SET_DRX_COMPL:
1196 case CBSP_MSGT_SET_DRX_FAIL:
1197 rc = -1;
1198 break;
1199 default:
1200 rc = -1;
1201 break;
1202 }
1203
1204 if (rc < 0) {
1205 goto out_err;
1206 }
1207
1208 return out;
1209
1210out_err:
1211 talloc_free(out);
1212 return NULL;
1213}
1214
1215/* initialization of 'decoded' structure of given message type */
1216void osmo_cbsp_init_struct(struct osmo_cbsp_decoded *cbsp, enum cbsp_msg_type msg_type)
1217{
1218 memset(cbsp, 0, sizeof(*cbsp));
1219 cbsp->msg_type = msg_type;
1220
1221 switch (msg_type) {
1222 case CBSP_MSGT_WRITE_REPLACE:
1223 INIT_LLIST_HEAD(&cbsp->u.write_replace.cell_list.list);
1224 break;
1225 case CBSP_MSGT_WRITE_REPLACE_COMPL:
1226 INIT_LLIST_HEAD(&cbsp->u.write_replace_compl.num_compl_list.list);
1227 INIT_LLIST_HEAD(&cbsp->u.write_replace_compl.cell_list.list);
1228 break;
1229 case CBSP_MSGT_WRITE_REPLACE_FAIL:
1230 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.fail_list);
1231 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.num_compl_list.list);
1232 INIT_LLIST_HEAD(&cbsp->u.write_replace_fail.cell_list.list);
1233 break;
1234 case CBSP_MSGT_KILL:
1235 INIT_LLIST_HEAD(&cbsp->u.kill.cell_list.list);
1236 break;
1237 case CBSP_MSGT_KILL_COMPL:
1238 INIT_LLIST_HEAD(&cbsp->u.kill_compl.num_compl_list.list);
1239 INIT_LLIST_HEAD(&cbsp->u.kill_compl.cell_list.list);
1240 break;
1241 case CBSP_MSGT_KILL_FAIL:
1242 INIT_LLIST_HEAD(&cbsp->u.kill_fail.fail_list);
1243 INIT_LLIST_HEAD(&cbsp->u.kill_fail.num_compl_list.list);
1244 INIT_LLIST_HEAD(&cbsp->u.kill_fail.cell_list.list);
1245 break;
1246 case CBSP_MSGT_LOAD_QUERY:
1247 INIT_LLIST_HEAD(&cbsp->u.load_query.cell_list.list);
1248 break;
1249 case CBSP_MSGT_LOAD_QUERY_COMPL:
1250 INIT_LLIST_HEAD(&cbsp->u.load_query_compl.loading_list.list);
1251 break;
1252 case CBSP_MSGT_LOAD_QUERY_FAIL:
1253 INIT_LLIST_HEAD(&cbsp->u.load_query_fail.fail_list);
1254 break;
1255 case CBSP_MSGT_MSG_STATUS_QUERY:
1256 INIT_LLIST_HEAD(&cbsp->u.msg_status_query.cell_list.list);
1257 break;
1258 case CBSP_MSGT_MSG_STATUS_QUERY_COMPL:
1259 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_compl.num_compl_list.list);
1260 break;
1261 case CBSP_MSGT_MSG_STATUS_QUERY_FAIL:
1262 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_fail.fail_list);
1263 INIT_LLIST_HEAD(&cbsp->u.msg_status_query_fail.num_compl_list.list);
1264 break;
1265 case CBSP_MSGT_RESET:
1266 INIT_LLIST_HEAD(&cbsp->u.reset.cell_list.list);
1267 break;
1268 case CBSP_MSGT_RESET_COMPL:
1269 INIT_LLIST_HEAD(&cbsp->u.reset_compl.cell_list.list);
1270 break;
1271 case CBSP_MSGT_RESET_FAIL:
1272 INIT_LLIST_HEAD(&cbsp->u.reset_fail.fail_list);
1273 INIT_LLIST_HEAD(&cbsp->u.reset_fail.cell_list.list);
1274 break;
1275 case CBSP_MSGT_RESTART:
1276 INIT_LLIST_HEAD(&cbsp->u.restart.cell_list.list);
1277 break;
1278 case CBSP_MSGT_FAILURE:
1279 INIT_LLIST_HEAD(&cbsp->u.failure.fail_list);
1280 break;
1281 default:
1282 break;
1283 }
1284}
1285
1286/*! Dynamically allocate and initialize decoded CBSP structure.
1287 * \param[in] ctx talloc context from which to allocate
1288 * \param[in] msg_type CBSP message type for which to initialize result
1289 * \returns allocated + initialized decoded CBSP structure; NULL on talloc failure */
1290struct osmo_cbsp_decoded *osmo_cbsp_decoded_alloc(void *ctx, enum cbsp_msg_type msg_type)
1291{
1292 struct osmo_cbsp_decoded *cbsp = talloc_zero(ctx, struct osmo_cbsp_decoded);
1293 if (!cbsp)
1294 return NULL;
1295 osmo_cbsp_init_struct(cbsp, msg_type);
1296 return cbsp;
1297}
1298
1299/***********************************************************************
1300 * Message Reception
1301 ***********************************************************************/
1302
1303#ifdef HAVE_SYS_SOCKET_H
1304#include <sys/socket.h>
1305
1306/*! Read one CBSP message from socket fd or store part if still not fully received.
1307 * \param[in] ctx talloc context from which to allocate new msgb.
1308 * \param[in] fd The fd for the socket to read from.
1309 * \param[out] rmsg internally allocated msgb containing a fully received CBSP message.
1310 * \param[inout] tmp_msg internally allocated msgb caching data for not yet fully received message.
1311 *
1312 * Function is designed just like ipa_msg_recv_buffered()
1313 */
1314int osmo_cbsp_recv_buffered(void *ctx, int fd, struct msgb **rmsg, struct msgb **tmp_msg)
1315{
1316 struct msgb *msg = tmp_msg ? *tmp_msg : NULL;
1317 struct cbsp_header *h;
1318 int len, rc;
1319 int needed;
1320
1321 if (!msg) {
1322 msg = osmo_cbsp_msgb_alloc(ctx, __func__);
1323 if (!msg) {
1324 return -ENOMEM;
1325 goto discard_msg;
1326 }
1327 msg->l1h = msg->tail;
1328 }
1329
1330 if (msg->l2h == NULL) {
1331 /* first read the [missing part of the] header */
1332 needed = sizeof(*h) - msg->len;
1333 rc = recv(fd, msg->tail, needed, 0);
1334 if (rc == 0)
1335 goto discard_msg;
1336 else if (rc < 0) {
1337 if (errno == EAGAIN || errno == EINTR)
1338 rc = 0;
1339 else {
1340 rc = -errno;
1341 goto discard_msg;
1342 }
1343 }
1344 msgb_put(msg, rc);
1345 if (rc < needed) {
1346 if (msg->len == 0) {
1347 rc = -EAGAIN;
1348 goto discard_msg;
1349 }
1350
1351 if (!tmp_msg) {
1352 rc = -EIO;
1353 goto discard_msg;
1354 }
1355 *tmp_msg = msg;
1356 return -EAGAIN;
1357 }
1358 msg->l2h = msg->tail;
1359 }
1360
1361 h = (struct cbsp_header *) msg->data;
1362 /* then read the length as specified in the header */
1363 len = h->len[0] << 16 | h->len[1] << 8 | h->len[2];
1364
1365 needed = len - msgb_l2len(msg);
1366 if (needed > 0) {
1367 rc = recv(fd, msg->tail, needed, 0);
1368 if (rc == 0)
1369 goto discard_msg;
1370 else if (rc < 0) {
1371 if (errno == EAGAIN || errno == EINTR)
1372 rc = 0;
1373 else {
1374 rc = -errno;
1375 goto discard_msg;
1376 }
1377 }
1378 msgb_put(msg, rc);
1379 /* still not all of payload received? */
1380 if (rc < needed) {
1381 if (!tmp_msg) {
1382 rc = -EIO;
1383 goto discard_msg;
1384 }
1385 *tmp_msg = msg;
1386 return -EAGAIN;
1387 }
1388 }
1389 /* else: complete message received */
1390 rc = msgb_l2len(msg);
1391 if (rc == 0) {
1392 /* drop empty message */
1393 rc = -EAGAIN;
1394 goto discard_msg;
1395 }
1396 if (tmp_msg)
1397 *tmp_msg = NULL;
1398 *rmsg = msg;
1399 return rc;
1400
1401discard_msg:
1402 printf("discard_msg\n");
1403 if (tmp_msg)
1404 *tmp_msg = NULL;
1405 msgb_free(msg);
1406 return rc;
1407}
1408
1409#endif /* HAVE_SYS_SOCKET_H */