blob: 25f94066f88f92b344e6702669c4cffd838a7596 [file] [log] [blame]
Philipp Maier9828d282021-01-06 20:40:23 +01001/*! \file gprs_bssgp.c
2 * GPRS BSSGP RIM protocol implementation as per 3GPP TS 48.018. */
3/*
4 * (C) 2020-2021 by sysmocom - s.f.m.c. GmbH
5 * Author: Philipp Maier <pmaier@sysmocom.de>
6 *
7 * All Rights Reserved
8 *
9 * SPDX-License-Identifier: GPL-2.0+
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 *
24 */
25
26#include <errno.h>
27#include <osmocom/gprs/gprs_bssgp.h>
28#include <osmocom/gprs/gprs_bssgp_rim.h>
29#include <osmocom/gsm/gsm0808_utils.h>
30
31/* TVLV IEs use a variable length field. To be sure we will do all buffer
32 * length checks with the maximum possible header length, which is
33 * 1 octet tag + 2 octets length = 3 */
34#define TVLV_HDR_MAXLEN 3
35
36/* Usually RIM application containers and their surrounding RIM containers
37 * are not likely to exceed 128 octets, so the usual header length will be 2 */
38#define TVLV_HDR_LEN 2
39
40/* The reporting cell identifier is encoded as a cell identifier IE
41 * (3GPP TS 48.018, sub-clause 11.3.9) but without IE and length octets. */
42#define REP_CELL_ID_LEN 8
43
Philipp Maiercf114112021-01-22 18:24:12 +010044const struct value_string bssgp_rim_routing_info_discr_strs[] = {
Philipp Maier0a7e85a2021-01-25 22:03:57 +010045 { BSSGP_RIM_ROUTING_INFO_GERAN, "GERAN-cell" },
46 { BSSGP_RIM_ROUTING_INFO_UTRAN, "UTRAN-RNC" },
47 { BSSGP_RIM_ROUTING_INFO_EUTRAN, "E-UTRAN-eNodeB/HeNB" },
Philipp Maiercf114112021-01-22 18:24:12 +010048 { 0, NULL }
49};
50
Philipp Maier7450f772021-01-06 20:56:43 +010051/*! Parse a RIM Routing information IE (3GPP TS 48.018, chapter 11.3.70).
52 * \param[out] ri user provided memory to store the parsed results.
53 * \param[in] buf input buffer of the value part of the IE.
54 * \returns length of parsed octets, -EINVAL on error. */
55int bssgp_parse_rim_ri(struct bssgp_rim_routing_info *ri, const uint8_t *buf,
56 unsigned int len)
57{
58 struct gprs_ra_id raid_temp;
59
60 memset(ri, 0, sizeof(*ri));
61 if (len < 2)
62 return -EINVAL;
63
64 ri->discr = buf[0] & 0x0f;
65 buf++;
66
67 switch (ri->discr) {
68 case BSSGP_RIM_ROUTING_INFO_GERAN:
69 if (len < 9)
70 return -EINVAL;
71 ri->geran.cid = bssgp_parse_cell_id(&ri->geran.raid, buf);
72 return 9;
73 case BSSGP_RIM_ROUTING_INFO_UTRAN:
74 if (len < 9)
75 return -EINVAL;
76 gsm48_parse_ra(&ri->utran.raid, buf);
77 ri->utran.rncid = osmo_load16be(buf + 6);
78 return 9;
79 case BSSGP_RIM_ROUTING_INFO_EUTRAN:
80 if (len < 7 || len > 14)
81 return -EINVAL;
82 /* Note: 3GPP TS 24.301 Figure 9.9.3.32.1 and 3GPP TS 24.008
83 * Figure 10.5.130 specify MCC/MNC encoding in the same way,
84 * so we can re-use gsm48_parse_ra() for that. */
85 gsm48_parse_ra(&raid_temp, buf);
86 ri->eutran.tai.mcc = raid_temp.mcc;
87 ri->eutran.tai.mnc = raid_temp.mnc;
88 ri->eutran.tai.mnc_3_digits = raid_temp.mnc_3_digits;
89 ri->eutran.tai.tac = osmo_load16be(buf + 3);
90 memcpy(ri->eutran.global_enb_id, buf + 5, len - 6);
91 ri->eutran.global_enb_id_len = len - 6;
92 return len;
93 default:
94 return -EINVAL;
95 }
96}
97
98/*! Encode a RIM Routing information IE (3GPP TS 48.018, chapter 11.3.70).
99 * \param[out] buf user provided memory (at least 14 byte) for the generated value part of the IE.
100 * \param[in] ri user provided input data struct.
101 * \returns length of encoded octets, -EINVAL on error. */
102int bssgp_create_rim_ri(uint8_t *buf, const struct bssgp_rim_routing_info *ri)
103{
104 int rc;
105 struct gprs_ra_id raid_temp;
Philipp Maier7741bc32021-01-07 21:55:48 +0100106 int len;
Philipp Maier7450f772021-01-06 20:56:43 +0100107
108 buf[0] = ri->discr & 0x0f;
109 buf++;
110
111 switch (ri->discr) {
112 case BSSGP_RIM_ROUTING_INFO_GERAN:
113 rc = bssgp_create_cell_id(buf, &ri->geran.raid, ri->geran.cid);
114 if (rc < 0)
115 return -EINVAL;
Philipp Maier7741bc32021-01-07 21:55:48 +0100116 len = rc + 1;
117 break;
Philipp Maier7450f772021-01-06 20:56:43 +0100118 case BSSGP_RIM_ROUTING_INFO_UTRAN:
119 gsm48_encode_ra((struct gsm48_ra_id *)buf, &ri->utran.raid);
120 osmo_store16be(ri->utran.rncid, buf + 6);
Philipp Maier7741bc32021-01-07 21:55:48 +0100121 len = 9;
122 break;
Philipp Maier7450f772021-01-06 20:56:43 +0100123 case BSSGP_RIM_ROUTING_INFO_EUTRAN:
124 /* Note: 3GPP TS 24.301 Figure 9.9.3.32.1 and 3GPP TS 24.008
125 * Figure 10.5.130 specify MCC/MNC encoding in the same way,
126 * so we can re-use gsm48_encode_ra() for that. */
127 raid_temp = (struct gprs_ra_id) {
128 .mcc = ri->eutran.tai.mcc,
129 .mnc = ri->eutran.tai.mnc,
130 .mnc_3_digits = ri->eutran.tai.mnc_3_digits,
131 };
132
133 gsm48_encode_ra((struct gsm48_ra_id *)buf, &raid_temp);
134 osmo_store16be(ri->eutran.tai.tac, buf + 3);
135 OSMO_ASSERT(ri->eutran.global_enb_id_len <=
136 sizeof(ri->eutran.global_enb_id));
137 memcpy(buf + 5, ri->eutran.global_enb_id,
138 ri->eutran.global_enb_id_len);
Philipp Maier7741bc32021-01-07 21:55:48 +0100139 len = ri->eutran.global_enb_id_len + 6;
140 break;
Philipp Maier7450f772021-01-06 20:56:43 +0100141 default:
142 return -EINVAL;
143 }
Philipp Maier7741bc32021-01-07 21:55:48 +0100144
145 OSMO_ASSERT(len <= BSSGP_RIM_ROUTING_INFO_MAXLEN);
146 return len;
Philipp Maier7450f772021-01-06 20:56:43 +0100147}
148
Philipp Maierc08a3fd2021-01-25 22:00:01 +0100149/*! Encode a RIM Routing information into a human readable string.
150 * \param[buf] user provided string buffer to store the resulting string.
151 * \param[buf_len] maximum length of string buffer.
152 * \param[in] ri user provided input data struct.
153 * \returns pointer to the beginning of the resulting string stored in string buffer. */
154char *bssgp_rim_ri_name_buf(char *buf, size_t buf_len, const struct bssgp_rim_routing_info *ri)
155{
156 char plmn_str[16];
157 char enb_id_str[16];
158 char g_id_ps_str[32];
159 struct osmo_plmn_id plmn;
160 struct osmo_cell_global_id_ps g_id_ps;
161
162 if (!ri)
163 return NULL;
164
165 switch (ri->discr) {
166 case BSSGP_RIM_ROUTING_INFO_GERAN:
167 g_id_ps.rai.rac = ri->geran.raid.rac;
168 g_id_ps.rai.lac.lac = ri->geran.raid.lac;
169 g_id_ps.rai.lac.plmn.mcc = ri->geran.raid.mcc;
170 g_id_ps.rai.lac.plmn.mnc_3_digits = ri->geran.raid.mnc_3_digits;
171 g_id_ps.rai.lac.plmn.mnc = ri->geran.raid.mnc;
172 g_id_ps.cell_identity = ri->geran.cid;
173 snprintf(buf, buf_len, "%s-%s", bssgp_rim_routing_info_discr_str(ri->discr),
174 osmo_cgi_ps_name_buf(g_id_ps_str, sizeof(g_id_ps_str), &g_id_ps));
175 break;
176 case BSSGP_RIM_ROUTING_INFO_UTRAN:
177 g_id_ps.rai.rac = ri->utran.raid.rac;
178 g_id_ps.rai.lac.lac = ri->utran.raid.lac;
179 g_id_ps.rai.lac.plmn.mcc = ri->utran.raid.mcc;
180 g_id_ps.rai.lac.plmn.mnc_3_digits = ri->utran.raid.mnc_3_digits;
181 g_id_ps.rai.lac.plmn.mnc = ri->utran.raid.mnc;
182 g_id_ps.cell_identity = ri->utran.rncid;
183 snprintf(buf, buf_len, "%s-%s", bssgp_rim_routing_info_discr_str(ri->discr),
184 osmo_cgi_ps_name_buf(g_id_ps_str, sizeof(g_id_ps_str), &g_id_ps));
185 break;
186 case BSSGP_RIM_ROUTING_INFO_EUTRAN:
187 plmn.mcc = ri->eutran.tai.mcc;
188 plmn.mnc = ri->eutran.tai.mnc;
189 plmn.mnc_3_digits = ri->eutran.tai.mnc_3_digits;
190 snprintf(buf, buf_len, "%s-%s-%u-%s", bssgp_rim_routing_info_discr_str(ri->discr),
191 osmo_plmn_name_buf(plmn_str, sizeof(plmn_str), &plmn), ri->eutran.tai.tac,
192 osmo_hexdump_buf(enb_id_str, sizeof(enb_id_str), ri->eutran.global_enb_id,
193 ri->eutran.global_enb_id_len, "", false));
194 break;
195 default:
196 snprintf(buf, buf_len, "invalid");
197 }
198
199 return buf;
200}
201
202/*! Encode a RIM Routing information into a human readable string.
203 * \param[in] ri user provided input data struct.
204 * \returns pointer to the resulting string. */
205const char *bssgp_rim_ri_name(const struct bssgp_rim_routing_info *ri)
206{
207 static __thread char rim_ri_buf[64];
208 return bssgp_rim_ri_name_buf(rim_ri_buf, sizeof(rim_ri_buf), ri);
209}
210
Philipp Maier9828d282021-01-06 20:40:23 +0100211/*! Decode a RAN Information Request Application Container for NACC (3GPP TS 48.018, section 11.3.63.1.1).
212 * \param[out] user provided memory for decoded data struct.
213 * \param[in] buf user provided memory with the encoded value data of the IE.
214 * \returns 0 on success, -EINVAL on error. */
215int bssgp_dec_ran_inf_req_app_cont_nacc(struct bssgp_ran_inf_req_app_cont_nacc *cont, const uint8_t *buf, size_t len)
216{
217 int rc;
218
219 if (len < REP_CELL_ID_LEN)
220 return -EINVAL;
221
222 rc = gsm0808_decode_cell_id_u((union gsm0808_cell_id_u*)&cont->reprt_cell,
223 CELL_IDENT_WHOLE_GLOBAL_PS, buf, len);
224 if (rc < 0)
225 return -EINVAL;
226
227 return 0;
228}
229
230/*! Encode a RAN Information Request Application Container for NACC (3GPP TS 48.018, section 11.3.63.1.1).
231 * \param[out] buf user provided memory for the generated value part of the IE.
232 * \param[in] cont user provided input data struct.
233 * \returns length of encoded octets, -EINVAL on error. */
234int bssgp_enc_ran_inf_req_app_cont_nacc(uint8_t *buf, size_t len, const struct bssgp_ran_inf_req_app_cont_nacc *cont)
235{
236 int rc;
237 struct gprs_ra_id *raid;
238
239 if (len < REP_CELL_ID_LEN)
240 return -EINVAL;
241
242 raid = (struct gprs_ra_id *)&cont->reprt_cell.rai;
243 rc = bssgp_create_cell_id(buf, raid, cont->reprt_cell.cell_identity);
244 if (rc < 0)
245 return -EINVAL;
246 return rc;
247}
248
249/*! Decode a RAN Information Application Container (3GPP TS 48.018, section 11.3.63.2.1).
250 * \param[out] user provided memory for decoded data struct.
251 * \param[in] buf user provided memory with the encoded value data of the IE.
252 * \returns 0 on success, -EINVAL on error. */
253int bssgp_dec_ran_inf_app_cont_nacc(struct bssgp_ran_inf_app_cont_nacc *cont, const uint8_t *buf, size_t len)
254{
255 unsigned int i;
256 int remaining_buf_len;
257 int rc;
258
259 /* The given buffer must at least contain a reporting cell identifer
260 * plus one octet that defines number/type of attached sysinfo messages. */
261 if (len < REP_CELL_ID_LEN + 1)
262 return -EINVAL;
263
264 rc = gsm0808_decode_cell_id_u((union gsm0808_cell_id_u*)&cont->reprt_cell,
265 CELL_IDENT_WHOLE_GLOBAL_PS, buf, len);
266 if (rc < 0)
267 return -EINVAL;
268
269 buf += REP_CELL_ID_LEN;
270
271 cont->type_psi = buf[0] & 1;
272 cont->num_si = buf[0] >> 1;
273 buf++;
274
275 /* The number of sysinfo messages may be zero */
276 if (cont->num_si == 0)
277 return 0;
278
279 /* Check if the prospected system information messages fit in the
280 * remaining buffer space */
281 remaining_buf_len = len - REP_CELL_ID_LEN - 1;
282 if (remaining_buf_len <= 0)
283 return -EINVAL;
284 if (cont->type_psi && remaining_buf_len / BSSGP_RIM_PSI_LEN < cont->num_si)
285 return -EINVAL;
286 else if (remaining_buf_len / BSSGP_RIM_SI_LEN < cont->num_si)
287 return -EINVAL;
288
289 for (i = 0; i < cont->num_si; i++) {
290 cont->si[i] = buf;
291 if (cont->type_psi)
292 buf += BSSGP_RIM_PSI_LEN;
293 else
294 buf += BSSGP_RIM_SI_LEN;
295 }
296
297 return 0;
298}
299
300/*! Encode a RAN Information Application Container (3GPP TS 48.018, section 11.3.63.2.1).
301 * \param[out] buf user provided memory for the generated value part of the IE.
302 * \param[in] cont user provided input data struct.
303 * \returns length of encoded octets, -EINVAL on error. */
304int bssgp_enc_ran_inf_app_cont_nacc(uint8_t *buf, size_t len, const struct bssgp_ran_inf_app_cont_nacc *cont)
305{
306 uint8_t *buf_ptr = buf;
307 int rc;
308 unsigned int silen;
309 unsigned int i;
310 struct gprs_ra_id *raid;
311
312 if (cont->type_psi)
313 silen = BSSGP_RIM_PSI_LEN;
314 else
315 silen = BSSGP_RIM_SI_LEN;
316
317 /* The buffer must accept the reporting cell id, plus 1 byte to define
318 * the type and number of sysinfo messages. */
319 if (len < REP_CELL_ID_LEN + 1 + silen * cont->num_si)
320 return -EINVAL;
321
322 raid = (struct gprs_ra_id *)&cont->reprt_cell.rai;
323 rc = bssgp_create_cell_id(buf_ptr, raid, cont->reprt_cell.cell_identity);
324 if (rc < 0)
325 return -EINVAL;
326 buf_ptr += rc;
327
328 buf_ptr[0] = 0x00;
329 if (cont->type_psi)
330 buf_ptr[0] |= 0x01;
331 buf_ptr[0] |= (cont->num_si << 1);
332 buf_ptr++;
333
334 for (i = 0; i < cont->num_si; i++) {
335 memcpy(buf_ptr, cont->si[i], silen);
336 buf_ptr += silen;
337 }
338
339 return (int)(buf_ptr - buf);
340}
341
Philipp Maier139c4ae2021-01-22 17:19:05 +0100342/* 3GPP TS 48.018, table 11.3.64.1.b, NACC Cause coding */
343const struct value_string bssgp_nacc_cause_strs[] = {
344 { BSSGP_NACC_CAUSE_UNSPEC, "unspecified error" },
345 { BSSGP_NACC_CAUSE_SYNTAX_ERR, "syntax error in app container" },
346 { BSSGP_NACC_CAUSE_RPRT_CELL_MISSMTCH, "reporting cell id mismatch" },
347 { BSSGP_NACC_CAUSE_SIPSI_TYPE_ERR, "SI/PSI type error" },
348 { BSSGP_NACC_CAUSE_SIPSI_LEN_ERR, "SI/PSI inconsistent length" },
349 { BSSGP_NACC_CAUSE_SIPSI_SET_ERR, "inconsistent set of msg" },
350 { 0, NULL }
351};
352
Philipp Maier9828d282021-01-06 20:40:23 +0100353/*! Decode a Application Error Container for NACC (3GPP TS 48.018, section 11.3.64.1).
354 * \param[out] user provided memory for decoded data struct.
355 * \param[in] buf user provided memory with the encoded value data of the IE.
356 * \returns 0 on success, -EINVAL on error. */
357int bssgp_dec_app_err_cont_nacc(struct bssgp_app_err_cont_nacc *cont, const uint8_t *buf, size_t len)
358{
359 /* The buffer must at least contain the NACC cause code, it should also
360 * contain the application container, but we won't error if it is missing. */
361 if (len < 1)
362 return -EINVAL;
363
364 cont->nacc_cause = buf[0];
365
366 if (len > 1) {
367 cont->err_app_cont = buf + 1;
368 cont->err_app_cont_len = len - 1;
369 } else {
370 cont->err_app_cont = NULL;
371 cont->err_app_cont_len = 0;
372 }
373
374 return 0;
375}
376
377/*! Encode Application Error Container for NACC (3GPP TS 48.018, section 11.3.64.1).
378 * \param[out] buf user provided memory for the generated value part of the IE.
379 * \param[in] cont user provided input data struct.
380 * \returns length of encoded octets, -EINVAL on error. */
381int bssgp_enc_app_err_cont_nacc(uint8_t *buf, size_t len, const struct bssgp_app_err_cont_nacc *cont)
382{
383 uint8_t *buf_ptr = buf;
384
385 /* The buffer must accept the length of the application container and the NACC
386 * cause code, which is one octet in length. */
387 if (len < cont->err_app_cont_len + 1)
388 return -EINVAL;
389
390 buf_ptr[0] = cont->nacc_cause;
391 buf_ptr++;
392
393 memcpy(buf_ptr, cont->err_app_cont, cont->err_app_cont_len);
394 buf_ptr += cont->err_app_cont_len;
395
396 return (int)(buf_ptr - buf);
397}
398
399/* The structs bssgp_ran_inf_req_rim_cont, bssgp_ran_inf_rim_cont and bssgp_ran_inf_app_err_rim_cont *cont
400 * share four common fields at the beginning, we use the following struct as parameter type for the common
401 * encoder/decoder functions. (See also 3GPP TS 48.018 table 11.3.62a.1.b, table 11.3.62a.2.b, and
402 * table 11.3.62a.5.b) */
403struct bssgp_ran_inf_x_cont {
404 enum bssgp_ran_inf_app_id app_id;
405 uint32_t seq_num;
406 struct bssgp_rim_pdu_ind pdu_ind;
407 uint8_t prot_ver;
408};
409
410static int dec_rim_cont_common(struct bssgp_ran_inf_x_cont *cont, struct tlv_parsed *tp)
411{
412 if (TLVP_PRES_LEN(tp, BSSGP_IE_RIM_APP_IDENTITY, sizeof(uint8_t)))
413 cont->app_id = TLVP_VAL(tp, BSSGP_IE_RIM_APP_IDENTITY)[0];
414 else
415 return -EINVAL;
416
417 if (TLVP_PRES_LEN(tp, BSSGP_IE_RIM_SEQ_NR, sizeof(cont->seq_num)))
418 cont->seq_num = tlvp_val32be(tp, BSSGP_IE_RIM_SEQ_NR);
419 else
420 return -EINVAL;
421
422 if (TLVP_PRES_LEN(tp, BSSGP_IE_RIM_PDU_INDICATIONS, sizeof(cont->pdu_ind)))
423 memcpy(&cont->pdu_ind, TLVP_VAL(tp, BSSGP_IE_RIM_PDU_INDICATIONS), sizeof(cont->pdu_ind));
424 else
425 return -EINVAL;
426
427 if (TLVP_PRES_LEN(tp, BSSGP_IE_RIM_PROTOCOL_VERSION, sizeof(cont->prot_ver)))
428 cont->prot_ver = TLVP_VAL(tp, BSSGP_IE_RIM_PROTOCOL_VERSION)[0];
429 else
430 cont->prot_ver = 1;
431
432 return 0;
433}
434
435static uint8_t *enc_rim_cont_common(uint8_t *buf, size_t len, const struct bssgp_ran_inf_x_cont *cont)
436{
437
438 uint32_t seq_num = osmo_htonl(cont->seq_num);
439 uint8_t app_id_temp;
440 uint8_t *buf_ptr = buf;
441
442 if (len <
443 TVLV_HDR_MAXLEN * 4 + sizeof(app_id_temp) + sizeof(seq_num) + sizeof(cont->pdu_ind) +
444 sizeof(cont->prot_ver))
445 return NULL;
446
447 app_id_temp = cont->app_id;
448 buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_RIM_APP_IDENTITY, sizeof(app_id_temp), &app_id_temp);
449 buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_RIM_SEQ_NR, sizeof(seq_num), (uint8_t *) & seq_num);
450 buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_RIM_PDU_INDICATIONS, sizeof(cont->pdu_ind), (uint8_t *) & cont->pdu_ind);
451 if (cont->prot_ver > 0)
452 buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_RIM_PROTOCOL_VERSION, sizeof(cont->prot_ver), &cont->prot_ver);
453
454 return buf_ptr;
455}
456
Philipp Maier8b19d062021-01-22 18:05:35 +0100457/* 3GPP TS 48.018, table 11.3.61.b: RIM Application Identity coding */
458const struct value_string bssgp_ran_inf_app_id_strs[] = {
459 { BSSGP_RAN_INF_APP_ID_NACC, "Network Assisted Cell Change (NACC)" },
460 { BSSGP_RAN_INF_APP_ID_SI3, "System Information 3 (SI3)" },
461 { BSSGP_RAN_INF_APP_ID_MBMS, "MBMS data channel" },
462 { BSSGP_RAN_INF_APP_ID_SON, "SON Transfer" },
463 { BSSGP_RAN_INF_APP_ID_UTRA_SI, "UTRA System Information (UTRA SI)" },
464 { 0, NULL }
465};
466
Philipp Maier9828d282021-01-06 20:40:23 +0100467/*! Decode a RAN Information Request RIM Container (3GPP TS 48.018, table 11.3.62a.1.b).
468 * \param[out] user provided memory for decoded data struct.
469 * \param[in] buf user provided memory with the encoded value data of the IE.
470 * \returns 0 on success, -EINVAL on error. */
471int bssgp_dec_ran_inf_req_rim_cont(struct bssgp_ran_inf_req_rim_cont *cont, const uint8_t *buf, size_t len)
472{
473 int rc;
474 struct tlv_parsed tp;
475
476 memset(cont, 0, sizeof(*cont));
477
478 rc = tlv_parse(&tp, &tvlv_att_def, buf, len, 0, 0);
479 if (rc < 0)
480 return -EINVAL;
481
482 rc = dec_rim_cont_common((struct bssgp_ran_inf_x_cont *)cont, &tp);
483 if (rc < 0)
484 return -EINVAL;
485
486 if (TLVP_PRESENT(&tp, BSSGP_IE_RIM_REQ_APP_CONTAINER)) {
487 switch (cont->app_id) {
488 case BSSGP_RAN_INF_APP_ID_NACC:
489 rc = bssgp_dec_ran_inf_req_app_cont_nacc(&cont->u.app_cont_nacc,
490 TLVP_VAL(&tp, BSSGP_IE_RIM_REQ_APP_CONTAINER),
491 TLVP_LEN(&tp, BSSGP_IE_RIM_REQ_APP_CONTAINER));
492 break;
493 case BSSGP_RAN_INF_APP_ID_SI3:
494 case BSSGP_RAN_INF_APP_ID_MBMS:
495 case BSSGP_RAN_INF_APP_ID_SON:
496 case BSSGP_RAN_INF_APP_ID_UTRA_SI:
497 /* TODO: add parsers for Si3, MBMS, SON, UTRA-SI app containers */
Philipp Maier836c6da2021-01-19 20:03:50 +0100498 return -EOPNOTSUPP;
Philipp Maier9828d282021-01-06 20:40:23 +0100499 default:
500 return -EINVAL;
501 }
502
503 if (rc < 0)
504 return rc;
505 }
506
507 if (TLVP_PRES_LEN(&tp, BSSGP_IE_SON_TRANSFER_APP_ID, 1)) {
508 cont->son_trans_app_id = TLVP_VAL(&tp, BSSGP_IE_SON_TRANSFER_APP_ID);
509 cont->son_trans_app_id_len = TLVP_LEN(&tp, BSSGP_IE_SON_TRANSFER_APP_ID);
510 }
511
512 return 0;
513}
514
515/* Dub a TLVP header into a given buffer. The value part of the IE must start
516 * at the 2nd octet. Should the length field make a 3 octet TLVP header
517 * necessary (unlikely, but possible) the value part is moved ahead by one
518 * octet. The function returns a pointer to the end of value part. */
519static uint8_t *dub_tlvp_header(uint8_t *buf, uint8_t iei, uint16_t len)
520{
521 uint8_t *buf_ptr = buf;
522
523 buf_ptr[0] = iei;
524 if (len <= TVLV_MAX_ONEBYTE) {
525 buf_ptr[1] = (uint8_t) len;
526 buf_ptr[1] |= 0x80;
527 buf_ptr += TVLV_HDR_LEN;
528 } else {
529 memmove(buf_ptr + 1, buf_ptr, len);
530 buf_ptr[1] = len >> 8;
Philipp Maier2b11fa92021-01-20 15:55:40 +0100531 buf_ptr[2] = len & 0xff;
Philipp Maier9828d282021-01-06 20:40:23 +0100532 buf_ptr += TVLV_HDR_MAXLEN;
533 }
534 buf_ptr += len;
535
536 return buf_ptr;
537}
538
539/*! Encode a RAN Information Request RIM Container (3GPP TS 48.018, table 11.3.62a.1.b).
540 * \param[out] buf user provided memory for the generated value part of the IE.
541 * \param[in] cont user provided input data struct.
542 * \returns length of encoded octets, -EINVAL on error. */
543int bssgp_enc_ran_inf_req_rim_cont(uint8_t *buf, size_t len, const struct bssgp_ran_inf_req_rim_cont *cont)
544{
545 uint8_t *buf_ptr = buf;
546 int app_cont_len = 0;
547 int remaining_buf_len;
548
549 buf_ptr = enc_rim_cont_common(buf_ptr, len, (struct bssgp_ran_inf_x_cont *)cont);
550 if (!buf_ptr)
551 return -EINVAL;
552
553 remaining_buf_len = len - (int)(buf_ptr - buf);
554 if (remaining_buf_len <= 0)
555 return -EINVAL;
556
557 switch (cont->app_id) {
558 case BSSGP_RAN_INF_APP_ID_NACC:
559 app_cont_len =
560 bssgp_enc_ran_inf_req_app_cont_nacc(buf_ptr + TVLV_HDR_LEN, remaining_buf_len - TVLV_HDR_MAXLEN,
561 &cont->u.app_cont_nacc);
562 break;
563 case BSSGP_RAN_INF_APP_ID_SI3:
564 case BSSGP_RAN_INF_APP_ID_MBMS:
565 case BSSGP_RAN_INF_APP_ID_SON:
566 case BSSGP_RAN_INF_APP_ID_UTRA_SI:
567 /* TODO: add encoders for Si3, MBMS, SON, UTRA-SI app containers */
Philipp Maier836c6da2021-01-19 20:03:50 +0100568 return -EOPNOTSUPP;
Philipp Maier9828d282021-01-06 20:40:23 +0100569 default:
570 return -EINVAL;
571 }
572
573 if (app_cont_len < 0)
574 return -EINVAL;
575 buf_ptr = dub_tlvp_header(buf_ptr, BSSGP_IE_RIM_REQ_APP_CONTAINER, app_cont_len);
576
577 remaining_buf_len = len - (int)(buf_ptr - buf);
578 if (remaining_buf_len < 0)
579 return -EINVAL;
580
581 if (cont->son_trans_app_id && cont->son_trans_app_id_len > 0) {
582 if (remaining_buf_len < cont->son_trans_app_id_len + TVLV_HDR_MAXLEN)
583 return -EINVAL;
584 buf_ptr =
585 tvlv_put(buf_ptr, BSSGP_IE_SON_TRANSFER_APP_ID, cont->son_trans_app_id_len, cont->son_trans_app_id);
586 }
587 return (int)(buf_ptr - buf);
588}
589
590/*! Decode a RAN Information RIM Container (3GPP TS 48.018, table 11.3.62a.2.b).
591 * \param[out] user provided memory for decoded data struct.
592 * \param[in] buf user provided memory with the encoded value data of the IE.
593 * \returns 0 on success, -EINVAL on error. */
594int bssgp_dec_ran_inf_rim_cont(struct bssgp_ran_inf_rim_cont *cont, const uint8_t *buf, size_t len)
595{
596 int rc;
597 struct tlv_parsed tp;
598
599 memset(cont, 0, sizeof(*cont));
600
601 rc = tlv_parse(&tp, &tvlv_att_def, buf, len, 0, 0);
602 if (rc < 0)
603 return -EINVAL;
604
605 rc = dec_rim_cont_common((struct bssgp_ran_inf_x_cont *)cont, &tp);
606 if (rc < 0)
607 return -EINVAL;
608
609 if (TLVP_PRESENT(&tp, BSSGP_IE_RAN_INFO_APP_CONTAINER)) {
610 switch (cont->app_id) {
611 case BSSGP_RAN_INF_APP_ID_NACC:
612 rc = bssgp_dec_ran_inf_app_cont_nacc(&cont->u.app_cont_nacc,
613 TLVP_VAL(&tp, BSSGP_IE_RAN_INFO_APP_CONTAINER),
614 TLVP_LEN(&tp, BSSGP_IE_RAN_INFO_APP_CONTAINER));
615 break;
616 case BSSGP_RAN_INF_APP_ID_SI3:
617 case BSSGP_RAN_INF_APP_ID_MBMS:
618 case BSSGP_RAN_INF_APP_ID_SON:
619 case BSSGP_RAN_INF_APP_ID_UTRA_SI:
620 /* TODO: add parsers for Si3, MBMS, SON, UTRA-SI app containers */
Philipp Maier836c6da2021-01-19 20:03:50 +0100621 return -EOPNOTSUPP;
Philipp Maier9828d282021-01-06 20:40:23 +0100622 default:
623 return -EINVAL;
624 }
625
626 if (rc < 0)
627 return rc;
628 } else if (TLVP_PRESENT(&tp, BSSGP_IE_APP_ERROR_CONTAINER)) {
629 switch (cont->app_id) {
630 case BSSGP_RAN_INF_APP_ID_NACC:
631 rc = bssgp_dec_app_err_cont_nacc(&cont->u.app_err_cont_nacc,
632 TLVP_VAL(&tp, BSSGP_IE_APP_ERROR_CONTAINER), TLVP_LEN(&tp,
633 BSSGP_IE_APP_ERROR_CONTAINER));
634 break;
635 case BSSGP_RAN_INF_APP_ID_SI3:
636 case BSSGP_RAN_INF_APP_ID_MBMS:
637 case BSSGP_RAN_INF_APP_ID_SON:
638 case BSSGP_RAN_INF_APP_ID_UTRA_SI:
639 /* TODO: add parsers for Si3, MBMS, SON, UTRA-SI app containers */
Philipp Maier836c6da2021-01-19 20:03:50 +0100640 return -EOPNOTSUPP;
Philipp Maier9828d282021-01-06 20:40:23 +0100641 default:
642 return -EINVAL;
643 }
644 if (rc < 0)
645 return rc;
646 cont->app_err = true;
647 }
648
649 if (TLVP_PRES_LEN(&tp, BSSGP_IE_SON_TRANSFER_APP_ID, 1)) {
650 cont->son_trans_app_id = TLVP_VAL(&tp, BSSGP_IE_SON_TRANSFER_APP_ID);
651 cont->son_trans_app_id_len = TLVP_LEN(&tp, BSSGP_IE_SON_TRANSFER_APP_ID);
652 }
653
654 return 0;
655}
656
657/*! Encode a RAN Information RIM Container (3GPP TS 48.018, table 11.3.62a.2.b).
658 * \param[out] buf user provided memory for the generated value part of the IE.
659 * \param[in] cont user provided input data struct.
660 * \returns length of encoded octets, -EINVAL on error. */
661int bssgp_enc_ran_inf_rim_cont(uint8_t *buf, size_t len, const struct bssgp_ran_inf_rim_cont *cont)
662{
663 uint8_t *buf_ptr = buf;
664 int app_cont_len = 0;
665 int remaining_buf_len;
666
667 buf_ptr = enc_rim_cont_common(buf_ptr, len, (struct bssgp_ran_inf_x_cont *)cont);
668 if (!buf_ptr)
669 return -EINVAL;
670
671 remaining_buf_len = len - (int)(buf_ptr - buf);
672 if (remaining_buf_len <= 0)
673 return -EINVAL;
674
675 if (cont->app_err) {
676 switch (cont->app_id) {
677 case BSSGP_RAN_INF_APP_ID_NACC:
678 app_cont_len =
679 bssgp_enc_app_err_cont_nacc(buf_ptr + TVLV_HDR_LEN, remaining_buf_len - TVLV_HDR_MAXLEN,
680 &cont->u.app_err_cont_nacc);
681 break;
682 case BSSGP_RAN_INF_APP_ID_SI3:
683 case BSSGP_RAN_INF_APP_ID_MBMS:
684 case BSSGP_RAN_INF_APP_ID_SON:
685 case BSSGP_RAN_INF_APP_ID_UTRA_SI:
686 /* TODO: add encoders for Si3, MBMS, SON, UTRA-SI app containers */
Philipp Maier836c6da2021-01-19 20:03:50 +0100687 return -EOPNOTSUPP;
Philipp Maier9828d282021-01-06 20:40:23 +0100688 default:
689 return -EINVAL;
690 }
691 if (app_cont_len < 0)
692 return -EINVAL;
693 buf_ptr = dub_tlvp_header(buf_ptr, BSSGP_IE_APP_ERROR_CONTAINER, app_cont_len);
694 } else {
695 switch (cont->app_id) {
696 case BSSGP_RAN_INF_APP_ID_NACC:
697 app_cont_len =
698 bssgp_enc_ran_inf_app_cont_nacc(buf_ptr + TVLV_HDR_LEN, remaining_buf_len - TVLV_HDR_MAXLEN,
699 &cont->u.app_cont_nacc);
700 break;
701 case BSSGP_RAN_INF_APP_ID_SI3:
702 case BSSGP_RAN_INF_APP_ID_MBMS:
703 case BSSGP_RAN_INF_APP_ID_SON:
704 case BSSGP_RAN_INF_APP_ID_UTRA_SI:
705 /* TODO: add encoders for Si3, MBMS, SON, UTRA-SI app containers */
Philipp Maier836c6da2021-01-19 20:03:50 +0100706 return -EOPNOTSUPP;
Philipp Maier9828d282021-01-06 20:40:23 +0100707 default:
708 return -EINVAL;
709 }
710 if (app_cont_len < 0)
711 return -EINVAL;
712 buf_ptr = dub_tlvp_header(buf_ptr, BSSGP_IE_RAN_INFO_APP_CONTAINER, app_cont_len);
713 }
714
715 remaining_buf_len = len - (int)(buf_ptr - buf);
716 if (remaining_buf_len < 0)
717 return -EINVAL;
718
719 if (cont->son_trans_app_id && cont->son_trans_app_id_len > 0) {
720 if (remaining_buf_len < cont->son_trans_app_id_len + TVLV_HDR_MAXLEN)
721 return -EINVAL;
722 buf_ptr =
723 tvlv_put(buf_ptr, BSSGP_IE_SON_TRANSFER_APP_ID, cont->son_trans_app_id_len, cont->son_trans_app_id);
724 }
725 return (int)(buf_ptr - buf);
726}
727
728/*! Decode a RAN Information ACK RIM Container (3GPP TS 48.018, table 11.3.62a.3.b).
729 * \param[out] user provided memory for decoded data struct.
730 * \param[in] buf user provided memory with the encoded value data of the IE.
731 * \returns 0 on success, -EINVAL on error. */
732int bssgp_dec_ran_inf_ack_rim_cont(struct bssgp_ran_inf_ack_rim_cont *cont, const uint8_t *buf, size_t len)
733{
734 int rc;
735 struct tlv_parsed tp;
736
737 memset(cont, 0, sizeof(*cont));
738
739 rc = tlv_parse(&tp, &tvlv_att_def, buf, len, 0, 0);
740 if (rc < 0)
741 return -EINVAL;
742
743 if (TLVP_PRES_LEN(&tp, BSSGP_IE_RIM_APP_IDENTITY, sizeof(uint8_t)))
744 cont->app_id = TLVP_VAL(&tp, BSSGP_IE_RIM_APP_IDENTITY)[0];
745 else
746 return -EINVAL;
747
748 if (TLVP_PRES_LEN(&tp, BSSGP_IE_RIM_SEQ_NR, sizeof(cont->seq_num)))
749 cont->seq_num = tlvp_val32be(&tp, BSSGP_IE_RIM_SEQ_NR);
750 else
751 return -EINVAL;
752
753 if (TLVP_PRES_LEN(&tp, BSSGP_IE_RIM_PROTOCOL_VERSION, sizeof(cont->prot_ver)))
754 cont->prot_ver = TLVP_VAL(&tp, BSSGP_IE_RIM_PROTOCOL_VERSION)[0];
755 else
756 cont->prot_ver = 1;
757
758 if (TLVP_PRES_LEN(&tp, BSSGP_IE_SON_TRANSFER_APP_ID, 1)) {
759 cont->son_trans_app_id = TLVP_VAL(&tp, BSSGP_IE_SON_TRANSFER_APP_ID);
760 cont->son_trans_app_id_len = TLVP_LEN(&tp, BSSGP_IE_SON_TRANSFER_APP_ID);
761 }
762
763 return 0;
764}
765
766/*! Encode a RAN Information ACK RIM Container (3GPP TS 48.018, table 11.3.62a.3.b).
767 * \param[out] buf user provided memory for the generated value part of the IE.
768 * \param[in] cont user provided input data struct.
769 * \returns length of encoded octets, -EINVAL on error. */
770int bssgp_enc_ran_inf_ack_rim_cont(uint8_t *buf, size_t len, const struct bssgp_ran_inf_ack_rim_cont *cont)
771{
772 uint8_t *buf_ptr = buf;
773 uint32_t seq_num = osmo_htonl(cont->seq_num);
774 uint8_t app_id_temp;
775
776 if (len <
777 4 * TVLV_HDR_MAXLEN + sizeof(app_id_temp) + sizeof(seq_num) + sizeof(cont->prot_ver) +
778 cont->son_trans_app_id_len)
779 return -EINVAL;
780
781 app_id_temp = cont->app_id;
782 buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_RIM_APP_IDENTITY, sizeof(app_id_temp), &app_id_temp);
783 buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_RIM_SEQ_NR, sizeof(seq_num), (uint8_t *) & seq_num);
784
785 if (cont->prot_ver > 0)
786 buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_RIM_PROTOCOL_VERSION, sizeof(cont->prot_ver), &cont->prot_ver);
787
788 if (cont->son_trans_app_id && cont->son_trans_app_id_len > 0)
789 buf_ptr =
790 tvlv_put(buf_ptr, BSSGP_IE_SON_TRANSFER_APP_ID, cont->son_trans_app_id_len, cont->son_trans_app_id);
791
792 return (int)(buf_ptr - buf);
793}
794
795/*! Decode a RAN Information Error RIM Container (3GPP TS 48.018, table 11.3.62a.4.b).
796 * \param[out] user provided memory for decoded data struct.
797 * \param[in] buf user provided memory with the encoded value data of the IE.
798 * \returns 0 on success, -EINVAL on error. */
799int bssgp_dec_ran_inf_err_rim_cont(struct bssgp_ran_inf_err_rim_cont *cont, const uint8_t *buf, size_t len)
800{
801 int rc;
802 struct tlv_parsed tp;
803
804 memset(cont, 0, sizeof(*cont));
805
806 rc = tlv_parse(&tp, &tvlv_att_def, buf, len, 0, 0);
807 if (rc < 0)
808 return -EINVAL;
809
810 if (TLVP_PRES_LEN(&tp, BSSGP_IE_RIM_APP_IDENTITY, sizeof(uint8_t)))
811 cont->app_id = TLVP_VAL(&tp, BSSGP_IE_RIM_APP_IDENTITY)[0];
812 else
813 return -EINVAL;
814
815 if (TLVP_PRES_LEN(&tp, BSSGP_IE_CAUSE, sizeof(cont->cause)))
816 cont->cause = TLVP_VAL(&tp, BSSGP_IE_CAUSE)[0];
817 else
818 return -EINVAL;
819
820 if (TLVP_PRES_LEN(&tp, BSSGP_IE_RIM_PROTOCOL_VERSION, sizeof(cont->prot_ver)))
821 cont->prot_ver = TLVP_VAL(&tp, BSSGP_IE_RIM_PROTOCOL_VERSION)[0];
822 else
823 cont->prot_ver = 1;
824
825 if (TLVP_PRESENT(&tp, BSSGP_IE_PDU_IN_ERROR)) {
826 cont->err_pdu = TLVP_VAL(&tp, BSSGP_IE_PDU_IN_ERROR);
827 cont->err_pdu_len = TLVP_LEN(&tp, BSSGP_IE_PDU_IN_ERROR);
828 } else {
829 return -EINVAL;
830 }
831
832 if (TLVP_PRES_LEN(&tp, BSSGP_IE_SON_TRANSFER_APP_ID, 1)) {
833 cont->son_trans_app_id = TLVP_VAL(&tp, BSSGP_IE_SON_TRANSFER_APP_ID);
834 cont->son_trans_app_id_len = TLVP_LEN(&tp, BSSGP_IE_SON_TRANSFER_APP_ID);
835 }
836
837 return 0;
838}
839
840/*! Encode a RAN Information Error RIM Container (3GPP TS 48.018, table 11.3.62a.4.b).
841 * \param[out] buf user provided memory for the generated value part of the IE.
842 * \param[in] cont user provided input data struct.
843 * \returns length of encoded octets, -EINVAL on error. */
844int bssgp_enc_ran_inf_err_rim_cont(uint8_t *buf, size_t len, const struct bssgp_ran_inf_err_rim_cont *cont)
845{
846 uint8_t *buf_ptr = buf;
847 uint8_t app_id_temp;
848
849 if (len <
850 TVLV_HDR_MAXLEN * 5 + sizeof(app_id_temp) + sizeof(cont->cause) + sizeof(cont->prot_ver) +
851 cont->err_pdu_len + cont->son_trans_app_id_len)
852 return -EINVAL;
853
854 app_id_temp = cont->app_id;
855 buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_RIM_APP_IDENTITY, sizeof(app_id_temp), &app_id_temp);
856 buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_CAUSE, sizeof(cont->cause), &cont->cause);
857
858 if (cont->prot_ver > 0)
859 buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_RIM_PROTOCOL_VERSION, sizeof(cont->prot_ver), &cont->prot_ver);
860
861 if (cont->err_pdu && cont->err_pdu_len > 0)
862 buf_ptr = tvlv_put(buf_ptr, BSSGP_IE_PDU_IN_ERROR, cont->err_pdu_len, cont->err_pdu);
863 else
864 return -EINVAL;
865
866 if (cont->son_trans_app_id && cont->son_trans_app_id_len > 0)
867 buf_ptr =
868 tvlv_put(buf_ptr, BSSGP_IE_SON_TRANSFER_APP_ID, cont->son_trans_app_id_len, cont->son_trans_app_id);
869
870 return (int)(buf_ptr - buf);
871}
872
873/*! Decode a RAN Information Application Error RIM Container (3GPP TS 48.018, table 11.3.62a.5.b).
874 * \param[out] user provided memory for decoded data struct.
875 * \param[in] buf user provided memory with the encoded value data of the IE.
876 * \returns 0 on success, -EINVAL on error. */
877int bssgp_dec_ran_inf_app_err_rim_cont(struct bssgp_ran_inf_app_err_rim_cont *cont, const uint8_t *buf, size_t len)
878{
879 int rc;
880 struct tlv_parsed tp;
881
882 memset(cont, 0, sizeof(*cont));
883
884 rc = tlv_parse(&tp, &tvlv_att_def, buf, len, 0, 0);
885 if (rc < 0)
886 return -EINVAL;
887
888 rc = dec_rim_cont_common((struct bssgp_ran_inf_x_cont *)cont, &tp);
889 if (rc < 0)
890 return -EINVAL;
891
892 switch (cont->app_id) {
893 case BSSGP_RAN_INF_APP_ID_NACC:
894 rc = bssgp_dec_app_err_cont_nacc(&cont->u.app_err_cont_nacc,
895 TLVP_VAL(&tp, BSSGP_IE_APP_ERROR_CONTAINER), TLVP_LEN(&tp,
896 BSSGP_IE_APP_ERROR_CONTAINER));
897 break;
898 case BSSGP_RAN_INF_APP_ID_SI3:
899 case BSSGP_RAN_INF_APP_ID_MBMS:
900 case BSSGP_RAN_INF_APP_ID_SON:
901 case BSSGP_RAN_INF_APP_ID_UTRA_SI:
902 /* TODO: add parsers for Si3, MBMS, SON, UTRA-SI app containers */
Philipp Maier836c6da2021-01-19 20:03:50 +0100903 return -EOPNOTSUPP;
Philipp Maier9828d282021-01-06 20:40:23 +0100904 default:
905 return -EINVAL;
906 }
907 if (rc < 0)
908 return rc;
909
910 return 0;
911}
912
913/*! Encode a RAN Information Application Error RIM Container (3GPP TS 48.018, table 11.3.62a.5.b).
914 * \param[out] buf user provided memory for the generated value part of the IE.
915 * \param[in] cont user provided input data struct.
916 * \returns length of encoded octets, -EINVAL on error. */
917int bssgp_enc_ran_inf_app_err_rim_cont(uint8_t *buf, size_t len, const struct bssgp_ran_inf_app_err_rim_cont *cont)
918{
919 uint8_t *buf_ptr = buf;
920 int app_cont_len = 0;
921 int remaining_buf_len;
922
923 buf_ptr = enc_rim_cont_common(buf_ptr, len, (struct bssgp_ran_inf_x_cont *)cont);
924 if (!buf_ptr)
925 return -EINVAL;
926
927 remaining_buf_len = len - (int)(buf_ptr - buf);
928 if (remaining_buf_len <= 0)
929 return -EINVAL;
930
931 switch (cont->app_id) {
932 case BSSGP_RAN_INF_APP_ID_NACC:
933 app_cont_len =
934 bssgp_enc_app_err_cont_nacc(buf_ptr + TVLV_HDR_LEN, remaining_buf_len - TVLV_HDR_MAXLEN,
935 &cont->u.app_err_cont_nacc);
936 break;
937 case BSSGP_RAN_INF_APP_ID_SI3:
938 case BSSGP_RAN_INF_APP_ID_MBMS:
939 case BSSGP_RAN_INF_APP_ID_SON:
940 case BSSGP_RAN_INF_APP_ID_UTRA_SI:
941 /* TODO: add encoders for Si3, MBMS, SON, UTRA-SI app containers */
Philipp Maier836c6da2021-01-19 20:03:50 +0100942 return -EOPNOTSUPP;
Philipp Maier9828d282021-01-06 20:40:23 +0100943 default:
944 return -EINVAL;
945 }
946 if (app_cont_len < 0)
947 return -EINVAL;
948 buf_ptr = dub_tlvp_header(buf_ptr, BSSGP_IE_APP_ERROR_CONTAINER, app_cont_len);
949
950 return (int)(buf_ptr - buf);
951}