blob: 0f519adb8fd45e7d6a14f07c311d79dab34fc3ab [file] [log] [blame]
Harald Welte17a892f2020-12-07 21:39:03 +01001/* BSSGP2 - second generation of BSSGP library */
2
3/* (C) 2020 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
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
24#include <osmocom/core/utils.h>
25#include <osmocom/core/byteswap.h>
26#include <osmocom/core/msgb.h>
27
28#include <osmocom/gsm/gsm48.h>
29#include <osmocom/gsm/tlv.h>
30
31#include <osmocom/gprs/gprs_ns2.h>
32#include <osmocom/gprs/gprs_bssgp.h>
33#include <osmocom/gprs/gprs_bssgp2.h>
34
35
36/*! transmit BSSGP PDU over NS (PTP BVC)
37 * \param[in] nsi NS Instance through which to transmit
38 * \param[in] nsei NSEI of NSE through which to transmit
39 * \param[in] bvci BVCI through which to transmit
40 * \param[in] msg BSSGP PDU to transmit
41 * \returns 0 on success; negative on error */
42int bssgp2_nsi_tx_ptp(struct gprs_ns2_inst *nsi, uint16_t nsei, uint16_t bvci,
43 struct msgb *msg, uint32_t lsp)
44{
45 struct osmo_gprs_ns2_prim nsp = {};
46 int rc;
47
48 if (!msg)
49 return 0;
50
51 nsp.bvci = bvci;
52 nsp.nsei = nsei;
53 nsp.u.unitdata.link_selector = lsp;
54
Alexander Couzens138b96f2021-01-25 16:23:29 +010055 osmo_prim_init(&nsp.oph, SAP_NS, GPRS_NS2_PRIM_UNIT_DATA, PRIM_OP_REQUEST, msg);
Harald Welte17a892f2020-12-07 21:39:03 +010056 rc = gprs_ns2_recv_prim(nsi, &nsp.oph);
57
58 return rc;
59}
60
61/*! transmit BSSGP PDU over NS (SIGNALING BVC)
62 * \param[in] nsi NS Instance through which to transmit
63 * \param[in] nsei NSEI of NSE through which to transmit
64 * \param[in] msg BSSGP PDU to transmit
65 * \returns 0 on success; negative on error */
66int bssgp2_nsi_tx_sig(struct gprs_ns2_inst *nsi, uint16_t nsei, struct msgb *msg, uint32_t lsp)
67{
68 return bssgp2_nsi_tx_ptp(nsi, nsei, 0, msg, lsp);
69}
70
71/*! Encode BSSGP BVC-BLOCK PDU as per TS 48.018 Section 10.4.8. */
72struct msgb *bssgp2_enc_bvc_block(uint16_t bvci, enum gprs_bssgp_cause cause)
73{
74 struct msgb *msg = bssgp_msgb_alloc();
75 struct bssgp_normal_hdr *bgph;
76 uint16_t _bvci = osmo_htons(bvci);
77
78 if (!msg)
79 return NULL;
80
81 bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
82 bgph->pdu_type = BSSGP_PDUT_BVC_BLOCK;
83
84 msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
85 msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, (uint8_t *) &cause);
86
87 return msg;
88}
89
90/*! Encode BSSGP BVC-BLOCK-ACK PDU as per TS 48.018 Section 10.4.9. */
91struct msgb *bssgp2_enc_bvc_block_ack(uint16_t bvci)
92{
93 struct msgb *msg = bssgp_msgb_alloc();
94 struct bssgp_normal_hdr *bgph;
95 uint16_t _bvci = osmo_htons(bvci);
96
97 if (!msg)
98 return NULL;
99
100 bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
101 bgph->pdu_type = BSSGP_PDUT_BVC_BLOCK_ACK;
102
103 msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
104
105 return msg;
106}
107
108/*! Encode BSSGP BVC-UNBLOCK PDU as per TS 48.018 Section 10.4.10. */
109struct msgb *bssgp2_enc_bvc_unblock(uint16_t bvci)
110{
111 struct msgb *msg = bssgp_msgb_alloc();
112 struct bssgp_normal_hdr *bgph;
113 uint16_t _bvci = osmo_htons(bvci);
114
115 if (!msg)
116 return NULL;
117
118 bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
119 bgph->pdu_type = BSSGP_PDUT_BVC_UNBLOCK;
120
121 msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
122
123 return msg;
124}
125
126/*! Encode BSSGP BVC-UNBLOCK-ACK PDU as per TS 48.018 Section 10.4.11. */
127struct msgb *bssgp2_enc_bvc_unblock_ack(uint16_t bvci)
128{
129 struct msgb *msg = bssgp_msgb_alloc();
130 struct bssgp_normal_hdr *bgph;
131 uint16_t _bvci = osmo_htons(bvci);
132
133 if (!msg)
134 return NULL;
135
136 bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
137 bgph->pdu_type = BSSGP_PDUT_BVC_UNBLOCK_ACK;
138
139 msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
140
141 return msg;
142}
143
144/*! Encode BSSGP BVC-RESET PDU as per TS 48.018 Section 10.4.12.
145 * \param[in] bvci PTP BVCI to encode into the BVCI IE
146 * \param[in] cause BSSGP Cause value (reason for reset)
147 * \param[in] ra_id Routing Area ID to be encoded to CELL_ID IE (optional)
148 * \param[in] cell_id Cell ID to be encoded to CELL_ID IE (only if ra_id is non-NULL)
149 * \param[in] feat_bm Feature Bitmap (optional)
150 * \param[in] ext_feat_bm Extended Feature Bitmap (optional) */
151struct msgb *bssgp2_enc_bvc_reset(uint16_t bvci, enum gprs_bssgp_cause cause,
152 const struct gprs_ra_id *ra_id, uint16_t cell_id,
153 const uint8_t *feat_bm, const uint8_t *ext_feat_bm)
154{
155 struct msgb *msg = bssgp_msgb_alloc();
156 struct bssgp_normal_hdr *bgph;
157 uint16_t _bvci = osmo_htons(bvci);
158
159 if (!msg)
160 return NULL;
161
162 bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
163 bgph->pdu_type = BSSGP_PDUT_BVC_RESET;
164
165 msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
166 msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, (uint8_t *) &cause);
167 if (ra_id) {
168 uint8_t bssgp_cid[8];
169 bssgp_create_cell_id(bssgp_cid, ra_id, cell_id);
170 msgb_tvlv_put(msg, BSSGP_IE_CELL_ID, sizeof(bssgp_cid), bssgp_cid);
171 }
172
173 if (feat_bm)
174 msgb_tvlv_put(msg, BSSGP_IE_FEATURE_BITMAP, 1, feat_bm);
175
176 if (ext_feat_bm)
177 msgb_tvlv_put(msg, BSSGP_IE_EXT_FEATURE_BITMAP, 1, feat_bm);
178
179 return msg;
180}
181
182/*! Encode BSSGP BVC-RESET-ACK PDU as per TS 48.018 Section 10.4.13.
183 * \param[in] bvci PTP BVCI to encode into the BVCI IE
184 * \param[in] ra_id Routing Area ID to be encoded to CELL_ID IE (optional)
185 * \param[in] cell_id Cell ID to be encoded to CELL_ID IE (only if ra_id is non-NULL)
186 * \param[in] feat_bm Feature Bitmap (optional)
187 * \param[in] ext_feat_bm Extended Feature Bitmap (optional) */
188struct msgb *bssgp2_enc_bvc_reset_ack(uint16_t bvci, const struct gprs_ra_id *ra_id, uint16_t cell_id,
189 const uint8_t *feat_bm, const uint8_t *ext_feat_bm)
190{
191 struct msgb *msg = bssgp_msgb_alloc();
192 struct bssgp_normal_hdr *bgph;
193 uint16_t _bvci = osmo_htons(bvci);
194
195 if (!msg)
196 return NULL;
197
198 bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
199 bgph->pdu_type = BSSGP_PDUT_BVC_RESET_ACK;
200
201 msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
202 if (ra_id) {
203 uint8_t bssgp_cid[8];
204 bssgp_create_cell_id(bssgp_cid, ra_id, cell_id);
205 msgb_tvlv_put(msg, BSSGP_IE_CELL_ID, sizeof(bssgp_cid), bssgp_cid);
206 }
207
208 if (feat_bm)
209 msgb_tvlv_put(msg, BSSGP_IE_FEATURE_BITMAP, 1, feat_bm);
210
211 if (ext_feat_bm)
212 msgb_tvlv_put(msg, BSSGP_IE_EXT_FEATURE_BITMAP, 1, feat_bm);
213
214 return msg;
215}
216
217/*! Encode BSSGP STATUS PDU as per TS 48.018 Section 10.4.14.
218 * \param[in] cause BSSGP Cause value
219 * \param[in] bvci optional BVCI - only encoded if non-NULL
220 * \param[in] msg optional message buffer containing PDU in error - only encoded if non-NULL */
221struct msgb *bssgp2_enc_status(uint8_t cause, const uint16_t *bvci, const struct msgb *orig_msg)
222{
223 struct msgb *msg = bssgp_msgb_alloc();
224 struct bssgp_normal_hdr *bgph;
225
226 if (!msg)
227 return NULL;
228
229 bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
230 bgph->pdu_type = BSSGP_PDUT_STATUS;
231 msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause);
232 if (bvci) {
233 uint16_t _bvci = osmo_htons(*bvci);
234 msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci);
235 }
236 if (orig_msg)
237 msgb_tvlv_put(msg, BSSGP_IE_PDU_IN_ERROR, msgb_bssgp_len(orig_msg), msgb_bssgph(orig_msg));
238
239 return msg;
240}
Harald Welte4394bb92020-12-08 20:40:44 +0100241
242static const unsigned int bssgp_fc_gran_tbl[] = {
243 [BSSGP_FC_GRAN_100] = 100,
244 [BSSGP_FC_GRAN_1000] = 1000,
245 [BSSGP_FC_GRAN_10000] = 10000,
246 [BSSGP_FC_GRAN_100000] = 100000,
247};
248
249/*! Decode a FLOW-CONTROL-BVC PDU as per TS 48.018 Section 10.4.4.
250 * \param[out] fc caller-allocated memory for parsed output
251 * \param[in] tp pre-parsed TLVs; caller must ensure mandatory IE presence/length
252 * \returns 0 on success; negative in case of error */
253int bssgp2_dec_fc_bvc(struct bssgp2_flow_ctrl *fc, const struct tlv_parsed *tp)
254{
255 unsigned int granularity = 100;
256
257 /* optional "Flow Control Granularity IE" (11.3.102); applies to
258 * bucket_size_max, bucket_leak_rate and PFC FC params IE */
259 if (TLVP_PRESENT(tp, BSSGP_IE_FLOW_CTRL_GRANULARITY)) {
260 uint8_t gran = *TLVP_VAL(tp, BSSGP_IE_FLOW_CTRL_GRANULARITY);
261 granularity = bssgp_fc_gran_tbl[gran & 3];
262 }
263
264 /* mandatory IEs */
265 fc->tag = *TLVP_VAL(tp, BSSGP_IE_TAG);
266 fc->bucket_size_max = granularity * tlvp_val16be(tp, BSSGP_IE_BVC_BUCKET_SIZE);
267 fc->bucket_leak_rate = (granularity * tlvp_val16be(tp, BSSGP_IE_BUCKET_LEAK_RATE)) / 8;
268 fc->u.bvc.bmax_default_ms = granularity * tlvp_val16be(tp, BSSGP_IE_BMAX_DEFAULT_MS);
269 fc->u.bvc.r_default_ms = (granularity * tlvp_val16be(tp, BSSGP_IE_R_DEFAULT_MS)) / 8;
270
271 /* optional / conditional */
272 if (TLVP_PRESENT(tp, BSSGP_IE_BUCKET_FULL_RATIO)) {
273 fc->bucket_full_ratio_present = true;
274 fc->bucket_full_ratio = *TLVP_VAL(tp, BSSGP_IE_BUCKET_FULL_RATIO);
275 } else {
276 fc->bucket_full_ratio_present = false;
277 }
278
279 if (TLVP_PRESENT(tp, BSSGP_IE_BVC_MEASUREMENT)) {
280 uint16_t val = tlvp_val16be(tp, BSSGP_IE_BVC_MEASUREMENT);
281 fc->u.bvc.measurement_present = true;
282 /* convert from centi-seconds to milli-seconds */
283 if (val == 0xffff)
284 fc->u.bvc.measurement = 0xffffffff;
285 else
286 fc->u.bvc.measurement = val * 10;
287 } else {
288 fc->u.bvc.measurement_present = false;
289 }
290
291 return 0;
292
293}
294
295/*! Encode a FLOW-CONTROL-BVC PDU as per TS 48.018 Section 10.4.4.
296 * \param[in] fc structure describing to-be-encoded FC parameters
297 * \param[in] gran if non-NULL: Encode using specified unit granularity
298 * \returns encoded PDU or NULL in case of error */
299struct msgb *bssgp2_enc_fc_bvc(const struct bssgp2_flow_ctrl *fc, enum bssgp_fc_granularity *gran)
300{
301 struct msgb *msg = bssgp_msgb_alloc();
302 struct bssgp_normal_hdr *bgph;
303 unsigned int granularity = 100;
304
305 if (gran)
306 granularity = bssgp_fc_gran_tbl[*gran & 3];
307
308 if (!msg)
309 return NULL;
310
311 bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
312 bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_BVC;
313
314 msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &fc->tag);
315 msgb_tvlv_put_16be(msg, BSSGP_IE_BVC_BUCKET_SIZE, fc->bucket_size_max / granularity);
316 msgb_tvlv_put_16be(msg, BSSGP_IE_BUCKET_LEAK_RATE, fc->bucket_leak_rate * 8 / granularity);
317 msgb_tvlv_put_16be(msg, BSSGP_IE_BMAX_DEFAULT_MS, fc->u.bvc.bmax_default_ms / granularity);
318 msgb_tvlv_put_16be(msg, BSSGP_IE_R_DEFAULT_MS, fc->u.bvc.r_default_ms * 8 / granularity);
319
320 if (fc->bucket_full_ratio_present)
321 msgb_tvlv_put(msg, BSSGP_IE_BUCKET_FULL_RATIO, 1, &fc->bucket_full_ratio);
322
323 if (fc->u.bvc.measurement_present) {
324 uint16_t val;
325 /* convert from ms to cs */
326 if (fc->u.bvc.measurement == 0xffffffff)
327 val = 0xffff;
328 else
329 val = fc->u.bvc.measurement / 10;
330 msgb_tvlv_put_16be(msg, BSSGP_IE_BVC_MEASUREMENT, val);
331 }
332
333 if (gran) {
334 uint8_t val = *gran & 3;
335 msgb_tvlv_put(msg, BSSGP_IE_FLOW_CTRL_GRANULARITY, 1, &val);
336 }
337
338 return msg;
339}
340
341/*! Encode a FLOW-CONTROL-BVC-ACK PDU as per TS 48.018 Section 10.4.4.
342 * \param[in] tag the tag IE value to encode
343 * \returns encoded PDU or NULL in case of error */
344struct msgb *bssgp2_enc_fc_bvc_ack(uint8_t tag)
345{
346 struct msgb *msg = bssgp_msgb_alloc();
347 struct bssgp_normal_hdr *bgph;
348
349 if (!msg)
350 return NULL;
351
352 bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
353 bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_BVC_ACK;
354
355 msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &tag);
356
357 return msg;
358}
359
360/*! Decode a FLOW-CONTROL-MS PDU as per TS 48.018 Section 10.4.6.
361 * \param[out] fc caller-allocated memory for parsed output
362 * \param[in] tp pre-parsed TLVs; caller must ensure mandatory IE presence/length
363 * \returns 0 on success; negative in case of error */
364int bssgp2_dec_fc_ms(struct bssgp2_flow_ctrl *fc, struct tlv_parsed *tp)
365{
366 unsigned int granularity = 100;
367
368 /* optional "Flow Control Granularity IE" (11.3.102); applies to
369 * bucket_size_max, bucket_leak_rate and PFC FC params IE */
370 if (TLVP_PRESENT(tp, BSSGP_IE_FLOW_CTRL_GRANULARITY)) {
371 uint8_t gran = *TLVP_VAL(tp, BSSGP_IE_FLOW_CTRL_GRANULARITY);
372 granularity = bssgp_fc_gran_tbl[gran & 3];
373 }
374
375 /* mandatory IEs */
376 fc->u.ms.tlli = tlvp_val32be(tp, BSSGP_IE_TLLI);
377 fc->tag = *TLVP_VAL(tp, BSSGP_IE_TAG);
378 fc->bucket_size_max = granularity * tlvp_val16be(tp, BSSGP_IE_MS_BUCKET_SIZE);
379 fc->bucket_leak_rate = (granularity * tlvp_val16be(tp, BSSGP_IE_BUCKET_LEAK_RATE)) / 8;
380
381 /* optional / conditional */
382 if (TLVP_PRESENT(tp, BSSGP_IE_BUCKET_FULL_RATIO)) {
383 fc->bucket_full_ratio_present = true;
384 fc->bucket_full_ratio = *TLVP_VAL(tp, BSSGP_IE_BUCKET_FULL_RATIO);
385 } else {
386 fc->bucket_full_ratio_present = false;
387 }
388
389 return 0;
390}
391
392/*! Encode a FLOW-CONTROL-MS PDU as per TS 48.018 Section 10.4.6.
393 * \param[in] fc structure describing to-be-encoded FC parameters
394 * \param[in] gran if non-NULL: Encode using specified unit granularity
395 * \returns encoded PDU or NULL in case of error */
396struct msgb *bssgp2_enc_fc_ms(const struct bssgp2_flow_ctrl *fc, enum bssgp_fc_granularity *gran)
397{
398 struct msgb *msg = bssgp_msgb_alloc();
399 struct bssgp_normal_hdr *bgph;
400 unsigned int granularity = 100;
401
402 if (gran)
403 granularity = bssgp_fc_gran_tbl[*gran & 3];
404
405 if (!msg)
406 return NULL;
407
408 bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
409 bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_MS;
410
411 msgb_tvlv_put_32be(msg, BSSGP_IE_TLLI, fc->u.ms.tlli);
412 msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &fc->tag);
413 msgb_tvlv_put_16be(msg, BSSGP_IE_MS_BUCKET_SIZE, fc->bucket_size_max / granularity);
414 msgb_tvlv_put_16be(msg, BSSGP_IE_BUCKET_LEAK_RATE, fc->bucket_leak_rate * 8 / granularity);
415
416 if (fc->bucket_full_ratio_present)
417 msgb_tvlv_put(msg, BSSGP_IE_BUCKET_FULL_RATIO, 1, &fc->bucket_full_ratio);
418
419 if (gran) {
420 uint8_t val = *gran & 3;
421 msgb_tvlv_put(msg, BSSGP_IE_FLOW_CTRL_GRANULARITY, 1, &val);
422 }
423
424 return msg;
425}
426
427/*! Encode a FLOW-CONTROL-BVC-ACK PDU as per TS 48.018 Section 10.4.7.
428 * \param[in] tlli the TLLI IE value to encode
429 * \param[in] tag the tag IE value to encode
430 * \returns encoded PDU or NULL in case of error */
431struct msgb *bssgp2_enc_fc_ms_ack(uint32_t tlli, uint8_t tag)
432{
433 struct msgb *msg = bssgp_msgb_alloc();
434 struct bssgp_normal_hdr *bgph;
435
436 if (!msg)
437 return NULL;
438
439 bgph = (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph));
Harald Welte1fcfce82020-12-08 21:15:45 +0100440 bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_MS_ACK;
Harald Welte4394bb92020-12-08 20:40:44 +0100441
442 msgb_tvlv_put_32be(msg, BSSGP_IE_TLLI, tlli);
443 msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &tag);
444
445 return msg;
446}