blob: 3a36c7dc55d278e24daa8865b848d0a58c361d49 [file] [log] [blame]
Harald Welte17a892f2020-12-07 21:39:03 +01001/* BSSGP per-BVC Finite State Machine */
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 <string.h>
25#include <stdio.h>
26
27#include <osmocom/core/fsm.h>
28#include <osmocom/core/logging.h>
29#include <osmocom/core/utils.h>
30#include <osmocom/core/tdef.h>
31
32#include <osmocom/gsm/gsm48.h>
33#include <osmocom/gsm/tlv.h>
34
35#include <osmocom/gprs/gprs_msgb.h>
36#include <osmocom/gprs/gprs_bssgp.h>
37#include <osmocom/gprs/gprs_bssgp2.h>
38#include <osmocom/gprs/bssgp_bvc_fsm.h>
39
40#include "common_vty.h"
41
42#define S(x) (1 << (x))
43
44/* TODO: Those are not made cofnigurable via a VTY yet */
45struct osmo_tdef bssgp_bvc_fsm_tdefs[] = {
46 {
47 .T = 1,
48 .default_val = 5,
49 .min_val = 1,
50 .max_val = 30,
51 .unit = OSMO_TDEF_S,
52 .desc = "Guards the BSSGP BVC (un)blocking procedure",
53 }, {
54 .T = 2,
55 .default_val = 10,
56 .min_val = 1,
57 .max_val = 120,
58 .unit = OSMO_TDEF_S,
59 .desc = "Guards the BSSGP BVC reset procedure",
60 }, {
61 .T = 3,
62 .default_val = 500,
63 .min_val = 100,
64 .max_val = 10000,
65 .unit = OSMO_TDEF_MS,
66 .desc = "Guards the BSSGP SUSPEND procedure",
67 }, {
68 .T = 4,
69 .default_val = 500,
70 .min_val = 100,
71 .max_val = 10000,
72 .unit = OSMO_TDEF_MS,
73 .desc = "Guards the BSSGP RESUME procedure",
74 }, {
75 .T = 5,
76 .default_val = 15,
77 .min_val = 1,
78 .max_val = 30,
79 .unit = OSMO_TDEF_S,
80 .desc = "Guards the BSSGP Radio Access Capability Update procedure",
81 },
82 {}
83};
84
85#define T1 1
86#define T2 2
87
88/* We cannot use osmo_tdef_fsm_* as it makes hard-coded assumptions that
89 * each new/target state will always use the same timer and timeout - or
90 * a timeout at all */
91#define T1_SECS osmo_tdef_get(bssgp_bvc_fsm_tdefs, 1, OSMO_TDEF_S, 5)
92#define T2_SECS osmo_tdef_get(bssgp_bvc_fsm_tdefs, 2, OSMO_TDEF_S, 10)
93
94/* forward declaration */
95static struct osmo_fsm bssgp_bvc_fsm;
96
97static const struct value_string ptp_bvc_event_names[] = {
98 { BSSGP_BVCFSM_E_RX_BLOCK, "RX-BVC-BLOCK" },
99 { BSSGP_BVCFSM_E_RX_BLOCK_ACK, "RX-BVC-BLOCK-ACK" },
100 { BSSGP_BVCFSM_E_RX_UNBLOCK, "RX-BVC-UNBLOCK" },
101 { BSSGP_BVCFSM_E_RX_UNBLOCK_ACK, "RX-BVC-UNBLOCK-ACK" },
102 { BSSGP_BVCFSM_E_RX_RESET, "RX-BVC-RESET" },
103 { BSSGP_BVCFSM_E_RX_RESET_ACK, "RX-BVC-RESET-ACK" },
Harald Welte1fcfce82020-12-08 21:15:45 +0100104 { BSSGP_BVCFSM_E_RX_FC_BVC, "RX-FLOW-CONTROL-BVC" },
105 { BSSGP_BVCFSM_E_RX_FC_BVC_ACK, "RX-FLOW-CONTROL-BVC-ACK" },
Harald Welte17a892f2020-12-07 21:39:03 +0100106 { BSSGP_BVCFSM_E_REQ_BLOCK, "REQ-BLOCK" },
107 { BSSGP_BVCFSM_E_REQ_UNBLOCK, "REQ-UNBLOCK" },
108 { BSSGP_BVCFSM_E_REQ_RESET, "REQ-RESET" },
Harald Welte1fcfce82020-12-08 21:15:45 +0100109 { BSSGP_BVCFSM_E_REQ_FC_BVC, "REQ-FLOW-CONTROL-BVC" },
Harald Welte17a892f2020-12-07 21:39:03 +0100110 { 0, NULL }
111};
112
113struct bvc_fsm_priv {
114 /* NS-instance; defining the scope for NSEI below */
115 struct gprs_ns2_inst *nsi;
116
117 /* NSEI of the underlying NS Entity */
118 uint16_t nsei;
Daniel Willmann1ff86f72021-01-25 17:02:25 +0100119 /* Maximum size of the BSSGP PDU */
120 uint16_t max_pdu_len;
Harald Welte17a892f2020-12-07 21:39:03 +0100121
122 /* BVCI of this BVC */
123 uint16_t bvci;
124
125 /* are we the SGSN (true) or the BSS (false) */
126 bool role_sgsn;
127
128 /* BSS side: are we locally marked blocked? */
129 bool locally_blocked;
130 uint8_t block_cause;
131
132 /* cause value of the last outbound BVC-RESET (for re-transmissions) */
133 uint8_t last_reset_cause;
134
135 struct {
136 /* Bit 0..7: Features; Bit 8..15: Extended Features */
137 uint32_t advertised;
138 uint32_t received;
139 uint32_t negotiated;
Harald Welte1fcfce82020-12-08 21:15:45 +0100140 /* only used if BSSGP_XFEAT_GBIT is negotiated */
141 enum bssgp_fc_granularity fc_granularity;
Harald Welte17a892f2020-12-07 21:39:03 +0100142 } features;
143
144 /* Cell Identification used by BSS when
145 * transmitting BVC-RESET / BVC-RESET-ACK, or those received
146 * from BSS in SGSN role */
147 struct gprs_ra_id ra_id;
148 uint16_t cell_id;
149
150 /* call-backs provided by the user */
151 const struct bssgp_bvc_fsm_ops *ops;
152 /* private data pointer passed to each call-back invocation */
153 void *ops_priv;
154};
155
156static int fi_tx_ptp(struct osmo_fsm_inst *fi, struct msgb *msg)
157{
158 struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
159 struct bvc_fsm_priv *bfp = fi->priv;
160
161 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
162
163 LOGPFSM(fi, "Tx BSSGP %s\n", osmo_tlv_prot_msg_name(&osmo_pdef_bssgp, bgph->pdu_type));
164
165 return bssgp2_nsi_tx_ptp(bfp->nsi, bfp->nsei, bfp->bvci, msg, 0);
166}
167
168static int fi_tx_sig(struct osmo_fsm_inst *fi, struct msgb *msg)
169{
170 struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
171 struct bvc_fsm_priv *bfp = fi->priv;
172
173 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
174
175 LOGPFSM(fi, "Tx BSSGP %s\n", osmo_tlv_prot_msg_name(&osmo_pdef_bssgp, bgph->pdu_type));
176
177 return bssgp2_nsi_tx_sig(bfp->nsi, bfp->nsei, msg, 0);
178}
179
180/* helper function to transmit BVC-RESET with right combination of conditional/optional IEs */
181static void _tx_bvc_reset(struct osmo_fsm_inst *fi, uint8_t cause)
182{
183 struct bvc_fsm_priv *bfp = fi->priv;
184 const uint8_t *features = NULL;
185 const uint8_t *features_ext = NULL;
186 uint8_t _features[2] = {
187 (bfp->features.advertised >> 0) & 0xff,
188 (bfp->features.advertised >> 8) & 0xff,
189 };
190 struct msgb *tx;
191
192 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
193
194 /* transmit BVC-RESET to peer; RA-ID only present for PTP from BSS */
195 if (bfp->bvci == 0) {
196 features = &_features[0];
197 features_ext = &_features[1];
198 }
199 tx = bssgp2_enc_bvc_reset(bfp->bvci, cause,
200 bfp->bvci && !bfp->role_sgsn ? &bfp->ra_id : NULL,
201 bfp->cell_id, features, features_ext);
202 fi_tx_sig(fi, tx);
203}
204
205/* helper function to transmit BVC-RESET-ACK with right combination of conditional/optional IEs */
206static void _tx_bvc_reset_ack(struct osmo_fsm_inst *fi)
207{
208 struct bvc_fsm_priv *bfp = fi->priv;
209 const uint8_t *features = NULL;
210 const uint8_t *features_ext = NULL;
211 uint8_t _features[2] = {
212 (bfp->features.advertised >> 0) & 0xff,
213 (bfp->features.advertised >> 8) & 0xff,
214 };
215 struct msgb *tx;
216
217 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
218
219 /* transmit BVC-RESET-ACK to peer; RA-ID only present for PTP from BSS -> SGSN */
220 if (bfp->bvci == 0) {
221 features = &_features[0];
222 features_ext = &_features[1];
223 }
224 tx = bssgp2_enc_bvc_reset_ack(bfp->bvci, bfp->bvci && !bfp->role_sgsn ? &bfp->ra_id : NULL,
225 bfp->cell_id, features, features_ext);
226 fi_tx_sig(fi, tx);
227}
228
229/* helper function to transmit BVC-STATUS with right combination of conditional/optional IEs */
230static void _tx_status(struct osmo_fsm_inst *fi, enum gprs_bssgp_cause cause, const struct msgb *rx)
231{
232 struct bvc_fsm_priv *bfp = fi->priv;
233 struct msgb *tx;
234 uint16_t *bvci = NULL;
235
236 /* GSM 08.18, 10.4.14.1: The BVCI must be included if (and only if) the
237 * cause is either "BVCI blocked" or "BVCI unknown" */
238 if (cause == BSSGP_CAUSE_UNKNOWN_BVCI || cause == BSSGP_CAUSE_BVCI_BLOCKED)
239 bvci = &bfp->bvci;
240
Daniel Willmannfa632b82021-02-12 01:57:52 +0100241 tx = bssgp2_enc_status(cause, bvci, rx, bfp->max_pdu_len);
Harald Welte17a892f2020-12-07 21:39:03 +0100242
243 if (msgb_bvci(rx) == 0)
244 fi_tx_sig(fi, tx);
245 else
246 fi_tx_ptp(fi, tx);
247}
248
249/* Update the features by bit-wise AND of advertised + received features */
250static void update_negotiated_features(struct osmo_fsm_inst *fi, const struct tlv_parsed *tp)
251{
252 struct bvc_fsm_priv *bfp = fi->priv;
253
254 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
255
256 bfp->features.received = 0;
257
258 if (TLVP_PRES_LEN(tp, BSSGP_IE_FEATURE_BITMAP, 1))
259 bfp->features.received |= *TLVP_VAL(tp, BSSGP_IE_FEATURE_BITMAP);
260
261 if (TLVP_PRES_LEN(tp, BSSGP_IE_EXT_FEATURE_BITMAP, 1))
262 bfp->features.received |= (*TLVP_VAL(tp, BSSGP_IE_EXT_FEATURE_BITMAP) << 8);
263
264 bfp->features.negotiated = bfp->features.advertised & bfp->features.received;
265
266 LOGPFSML(fi, LOGL_NOTICE, "Updating features: Advertised 0x%04x, Received 0x%04x, Negotiated 0x%04x\n",
267 bfp->features.advertised, bfp->features.received, bfp->features.negotiated);
268}
269
270/* "tail" of each onenter() handler: Calling the state change notification call-back */
271static void _onenter_tail(struct osmo_fsm_inst *fi, uint32_t prev_state)
272{
273 struct bvc_fsm_priv *bfp = fi->priv;
274
275 if (prev_state == fi->state)
276 return;
277
278 if (bfp->ops && bfp->ops->state_chg_notification)
279 bfp->ops->state_chg_notification(bfp->nsei, bfp->bvci, prev_state, fi->state, bfp->ops_priv);
280}
281
282static void bssgp_bvc_fsm_null(struct osmo_fsm_inst *fi, uint32_t event, void *data)
283{
284 /* we don't really expect anything in this state; all handled via allstate */
285 OSMO_ASSERT(0);
286}
287
288static void bssgp_bvc_fsm_blocked_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
289{
290 struct bvc_fsm_priv *bfp = fi->priv;
291 /* signaling BVC can never be blocked */
292 OSMO_ASSERT(bfp->bvci != 0);
293 _onenter_tail(fi, prev_state);
294}
295
296static void bssgp_bvc_fsm_blocked(struct osmo_fsm_inst *fi, uint32_t event, void *data)
297{
298 struct bvc_fsm_priv *bfp = fi->priv;
299 struct msgb *rx = NULL, *tx;
300 const struct tlv_parsed *tp = NULL;
301 uint8_t cause;
302
303 switch (event) {
304 case BSSGP_BVCFSM_E_RX_BLOCK_ACK:
305 rx = data;
306 tp = (const struct tlv_parsed *) msgb_bcid(rx);
307 /* If a BVC-BLOCK-ACK PDU is received by a BSS for the signalling BVC, the PDU is ignored. */
308 if (bfp->bvci == 0) {
309 LOGPFSML(fi, LOGL_ERROR, "Rx BVC-BLOCK-ACK on BVCI=0 is illegal\n");
310 if (!bfp->role_sgsn)
311 break;
312 _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
313 break;
314 }
315 /* stop T1 timer */
316 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_BLOCKED, 0, 0);
317 break;
318 case BSSGP_BVCFSM_E_RX_BLOCK:
319 rx = data;
320 tp = (const struct tlv_parsed *) msgb_bcid(rx);
321 cause = *TLVP_VAL(tp, BSSGP_IE_CAUSE);
322 LOGPFSML(fi, LOGL_NOTICE, "Rx BVC-BLOCK (cause=%s)\n", bssgp_cause_str(cause));
323 /* If a BVC-BLOCK PDU is received by an SGSN for a blocked BVC, a BVC-BLOCK-ACK
324 * PDU shall be returned. */
325 if (bfp->role_sgsn) {
326 /* If a BVC-BLOCK PDU is received by an SGSN for
327 * the signalling BVC, the PDU is ignored */
328 if (bfp->bvci == 0)
329 break;
330 tx = bssgp2_enc_bvc_block_ack(bfp->bvci);
331 fi_tx_sig(fi, tx);
332 }
333 break;
334 case BSSGP_BVCFSM_E_RX_UNBLOCK:
335 rx = data;
336 tp = (const struct tlv_parsed *) msgb_bcid(rx);
337 LOGPFSML(fi, LOGL_NOTICE, "Rx BVC-UNBLOCK\n");
338 if (bfp->bvci == 0) {
339 LOGPFSML(fi, LOGL_ERROR, "Rx BVC-UNBLOCK on BVCI=0 is illegal\n");
340 /* If BVC-UNBLOCK PDU is received by an SGSN for the signalling BVC, the PDU is ignored.*/
341 if (bfp->role_sgsn)
342 break;
343 _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
344 break;
345 }
346 if (!bfp->role_sgsn) {
347 LOGPFSML(fi, LOGL_ERROR, "Rx BVC-UNBLOCK on BSS is illegal\n");
348 _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
349 break;
350 }
351 tx = bssgp2_enc_bvc_unblock_ack(bfp->bvci);
352 fi_tx_sig(fi, tx);
353 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, T1_SECS, T1);
354 break;
355 case BSSGP_BVCFSM_E_REQ_UNBLOCK:
356 if (bfp->role_sgsn) {
357 LOGPFSML(fi, LOGL_ERROR, "SGSN side cannot initiate BVC unblock\n");
358 break;
359 }
360 if (bfp->bvci == 0) {
361 LOGPFSML(fi, LOGL_ERROR, "BVCI 0 cannot be unblocked\n");
362 break;
363 }
364 bfp->locally_blocked = false;
365 tx = bssgp2_enc_bvc_unblock(bfp->bvci);
366 fi_tx_sig(fi, tx);
367 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, 0, 0);
368 break;
369 }
370}
371
372/* Waiting for RESET-ACK: Receive PDUs but don't transmit */
373static void bssgp_bvc_fsm_wait_reset_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
374{
375 struct bvc_fsm_priv *bfp = fi->priv;
376 const struct tlv_parsed *tp = NULL;
377 struct msgb *rx = NULL, *tx;
Daniel Willmann767bfd82022-03-30 11:34:36 +0200378 uint8_t cause;
Harald Welte17a892f2020-12-07 21:39:03 +0100379
380 switch (event) {
Harald Welte09422682021-02-13 11:01:47 +0100381 case BSSGP_BVCFSM_E_RX_RESET:
382 /* 48.018 Section 8.4.3: If the BSS (or SGSN) has sent a BVC-RESET PDU for a BVCI to
383 * the SGSN (or BSS) and is awaiting a BVC-RESET-ACK PDU in response, but instead
384 * receives a BVC-RESET PDU indicating the same BVCI, then this shall be interpreted
385 * as a BVC-RESET ACK PDU and the T2 timer shall be stopped. */
386 /* fall-through */
Harald Welte17a892f2020-12-07 21:39:03 +0100387 case BSSGP_BVCFSM_E_RX_RESET_ACK:
388 rx = data;
Daniel Willmann767bfd82022-03-30 11:34:36 +0200389 cause = bfp->last_reset_cause;
Harald Welte17a892f2020-12-07 21:39:03 +0100390 tp = (const struct tlv_parsed *) msgb_bcid(rx);
391 if (bfp->bvci == 0)
392 update_negotiated_features(fi, tp);
393 if (bfp->role_sgsn && bfp->bvci != 0)
394 bfp->cell_id = bssgp_parse_cell_id(&bfp->ra_id, TLVP_VAL(tp, BSSGP_IE_CELL_ID));
395 if (!bfp->role_sgsn && bfp->bvci != 0 && bfp->locally_blocked) {
396 /* initiate the blocking procedure */
397 /* transmit BVC-BLOCK, transition to BLOCKED state and start re-transmit timer */
398 tx = bssgp2_enc_bvc_block(bfp->bvci, bfp->block_cause);
399 fi_tx_sig(fi, tx);
400 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_BLOCKED, T1_SECS, T1);
401 } else
402 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, 0, 0);
Daniel Willmann767bfd82022-03-30 11:34:36 +0200403 if (bfp->ops && bfp->ops->reset_ack_notification)
404 bfp->ops->reset_ack_notification(bfp->nsei, bfp->bvci, &bfp->ra_id, bfp->cell_id, cause, bfp->ops_priv);
Harald Welte17a892f2020-12-07 21:39:03 +0100405 break;
406 }
407}
408
409static void bssgp_bvc_fsm_unblocked(struct osmo_fsm_inst *fi, uint32_t event, void *data)
410{
Harald Welte1fcfce82020-12-08 21:15:45 +0100411 struct bssgp2_flow_ctrl rx_fc, *tx_fc;
Harald Welte17a892f2020-12-07 21:39:03 +0100412 struct bvc_fsm_priv *bfp = fi->priv;
413 const struct tlv_parsed *tp = NULL;
414 struct msgb *rx = NULL, *tx;
Harald Welte1fcfce82020-12-08 21:15:45 +0100415 int rc;
Harald Welte17a892f2020-12-07 21:39:03 +0100416
417 switch (event) {
418 case BSSGP_BVCFSM_E_RX_UNBLOCK_ACK:
419 rx = data;
420 tp = (const struct tlv_parsed *) msgb_bcid(rx);
421 /* If BVC-UNBLOCK-ACK PDU is received by an BSS for the signalling BVC, the PDU is ignored. */
Harald Welte17a892f2020-12-07 21:39:03 +0100422 if (bfp->bvci == 0) {
Daniel Willmann43b0cbe2021-11-19 14:21:24 +0100423 LOGPFSML(fi, LOGL_ERROR, "Rx BVC-UNBLOCK-ACK on BVCI=0 is illegal\n");
Harald Welte17a892f2020-12-07 21:39:03 +0100424 if (!bfp->role_sgsn)
425 break;
426 _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
427 break;
428 }
429 /* stop T1 timer */
430 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, 0, 0);
431 break;
432 case BSSGP_BVCFSM_E_RX_UNBLOCK:
433 rx = data;
434 tp = (const struct tlv_parsed *) msgb_bcid(rx);
435 /* If a BVC-UNBLOCK PDU is received by an SGSN for a blocked BVC, a BVC-UNBLOCK-ACK
436 * PDU shall be returned. */
437 if (bfp->role_sgsn) {
438 /* If a BVC-UNBLOCK PDU is received by an SGSN for
439 * the signalling BVC, the PDU is ignored */
440 if (bfp->bvci == 0)
441 break;
442 bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_UNBLOCK_ACK, bfp->nsei, bfp->bvci, 0);
443 }
444 break;
445 case BSSGP_BVCFSM_E_RX_BLOCK:
446 rx = data;
447 tp = (const struct tlv_parsed *) msgb_bcid(rx);
448 LOGPFSML(fi, LOGL_NOTICE, "Rx BVC-BLOCK (cause=%s)\n",
449 bssgp_cause_str(*TLVP_VAL(tp, BSSGP_IE_CAUSE)));
450 /* If a BVC-BLOCK PDU is received by an SGSN for the signalling BVC, the PDU is ignored */
451 if (bfp->bvci == 0) {
452 LOGPFSML(fi, LOGL_ERROR, "Rx BVC-BLOCK on BVCI=0 is illegal\n");
453 if (bfp->role_sgsn)
454 break;
455 _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
456 break;
457 }
458 if (!bfp->role_sgsn) {
459 LOGPFSML(fi, LOGL_ERROR, "Rx BVC-BLOCK on BSS is illegal\n");
460 _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
461 break;
462 }
463 /* transmit BVC-BLOCK-ACK, transition to BLOCKED state */
464 tx = bssgp2_enc_bvc_block_ack(bfp->bvci);
465 fi_tx_sig(fi, tx);
466 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_BLOCKED, 0, 0);
467 break;
468 case BSSGP_BVCFSM_E_REQ_BLOCK:
469 if (bfp->role_sgsn) {
470 LOGPFSML(fi, LOGL_ERROR, "SGSN may not initiate BVC-BLOCK\n");
Daniel Willmann09bea012021-01-07 14:46:28 +0100471 break;
472 }
473 if (bfp->bvci == 0) {
474 LOGPFSML(fi, LOGL_ERROR, "BVCI 0 cannot be blocked\n");
Harald Welte17a892f2020-12-07 21:39:03 +0100475 break;
476 }
477 bfp->locally_blocked = true;
478 bfp->block_cause = *(uint8_t *)data;
479 /* transmit BVC-BLOCK, transition to BLOCKED state and start re-transmit timer */
480 tx = bssgp2_enc_bvc_block(bfp->bvci, bfp->block_cause);
481 fi_tx_sig(fi, tx);
482 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_BLOCKED, T1_SECS, T1);
483 break;
Harald Welte1fcfce82020-12-08 21:15:45 +0100484 case BSSGP_BVCFSM_E_RX_FC_BVC:
485 rx = data;
486 tp = (const struct tlv_parsed *) msgb_bcid(rx);
487 /* we assume osmo_tlv_prot_* has been used before calling here to ensure this */
488 OSMO_ASSERT(bfp->role_sgsn);
489 rc = bssgp2_dec_fc_bvc(&rx_fc, tp);
490 if (rc < 0) {
491 _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
492 break;
493 }
494 if (bfp->ops->rx_fc_bvc)
495 bfp->ops->rx_fc_bvc(bfp->nsei, bfp->bvci, &rx_fc, bfp->ops_priv);
496 tx = bssgp2_enc_fc_bvc_ack(rx_fc.tag);
497 fi_tx_ptp(fi, tx);
498 break;
499 case BSSGP_BVCFSM_E_RX_FC_BVC_ACK:
500 rx = data;
501 tp = (const struct tlv_parsed *) msgb_bcid(rx);
502 /* we assume osmo_tlv_prot_* has been used before calling here to ensure this */
503 OSMO_ASSERT(!bfp->role_sgsn);
504 break;
505 case BSSGP_BVCFSM_E_REQ_FC_BVC:
506 tx_fc = data;
507 tx = bssgp2_enc_fc_bvc(tx_fc, bfp->features.negotiated & (BSSGP_XFEAT_GBIT << 8) ?
508 &bfp->features.fc_granularity : NULL);
509 fi_tx_ptp(fi, tx);
510 break;
Harald Welte17a892f2020-12-07 21:39:03 +0100511 }
512}
513
514static void bssgp_bvc_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
515{
516 struct bvc_fsm_priv *bfp = fi->priv;
517 uint8_t cause;
518 const struct tlv_parsed *tp = NULL;
519 struct msgb *rx = NULL;
520
521 switch (event) {
522 case BSSGP_BVCFSM_E_REQ_RESET:
523 bfp->locally_blocked = false;
524 cause = bfp->last_reset_cause = *(uint8_t *) data;
525 _tx_bvc_reset(fi, cause);
526 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_WAIT_RESET_ACK, T2_SECS, T2);
527#if 0 /* not sure if we really should notify the application if itself has requested the reset? */
528 if (bfp->ops && bfp->ops->reset_notification)
529 bfp->ops->reset_notification(bfp->nsei, bfp->bvci, NULL, 0, cause, bfp->ops_priv);
530#endif
531 break;
532 case BSSGP_BVCFSM_E_RX_RESET:
533 rx = data;
534 tp = (const struct tlv_parsed *) msgb_bcid(rx);
535 cause = *TLVP_VAL(tp, BSSGP_IE_CAUSE);
536 if (bfp->role_sgsn && bfp->bvci != 0)
537 bfp->cell_id = bssgp_parse_cell_id(&bfp->ra_id, TLVP_VAL(tp, BSSGP_IE_CELL_ID));
538 LOGPFSML(fi, LOGL_NOTICE, "Rx BVC-RESET (cause=%s)\n", bssgp_cause_str(cause));
539 if (bfp->bvci == 0)
540 update_negotiated_features(fi, tp);
541 _tx_bvc_reset_ack(fi);
542 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, 0, 0);
543 if (bfp->ops && bfp->ops->reset_notification) {
544 bfp->ops->reset_notification(bfp->nsei, bfp->bvci, &bfp->ra_id, bfp->cell_id,
545 cause, bfp->ops_priv);
546 }
547 break;
548 }
549}
550
551static int bssgp_bvc_fsm_timer_cb(struct osmo_fsm_inst *fi)
552{
553 struct bvc_fsm_priv *bfp = fi->priv;
554 struct msgb *tx;
555
556 switch (fi->T) {
557 case T1:
558 switch (fi->state) {
559 case BSSGP_BVCFSM_S_BLOCKED:
560 /* re-transmit BVC-BLOCK */
561 tx = bssgp2_enc_bvc_block(bfp->bvci, bfp->block_cause);
562 fi_tx_sig(fi, tx);
563 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_BLOCKED, T1_SECS, T1);
564 break;
565 case BSSGP_BVCFSM_S_UNBLOCKED:
566 /* re-transmit BVC-UNBLOCK */
567 tx = bssgp2_enc_bvc_unblock(bfp->bvci);
568 fi_tx_sig(fi, tx);
569 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, T1_SECS, T1);
570 break;
571 }
572 break;
573 case T2:
574 switch (fi->state) {
575 case BSSGP_BVCFSM_S_WAIT_RESET_ACK:
576 /* re-transmit BVC-RESET */
577 _tx_bvc_reset(fi, bfp->last_reset_cause);
578 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_WAIT_RESET_ACK, T2_SECS, T2);
579 break;
580 case BSSGP_BVCFSM_S_UNBLOCKED:
581 /* re-transmit BVC-RESET-ACK */
582 _tx_bvc_reset_ack(fi);
583 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, T2_SECS, T2);
584 break;
585 }
586 break;
587 default:
588 OSMO_ASSERT(0);
589 break;
590 }
591 return 0;
592}
593
594
595
596static const struct osmo_fsm_state bssgp_bvc_fsm_states[] = {
597 [BSSGP_BVCFSM_S_NULL] = {
598 /* initial state from which we must do a RESET */
599 .name = "NULL",
600 .in_event_mask = 0,
601 .out_state_mask = S(BSSGP_BVCFSM_S_WAIT_RESET_ACK) |
602 S(BSSGP_BVCFSM_S_UNBLOCKED),
603 .action = bssgp_bvc_fsm_null,
604 },
605 [BSSGP_BVCFSM_S_BLOCKED] = {
606 .name = "BLOCKED",
607 .in_event_mask = S(BSSGP_BVCFSM_E_RX_UNBLOCK) |
608 S(BSSGP_BVCFSM_E_RX_BLOCK) |
609 S(BSSGP_BVCFSM_E_RX_BLOCK_ACK) |
610 S(BSSGP_BVCFSM_E_REQ_UNBLOCK),
611 .out_state_mask = S(BSSGP_BVCFSM_S_WAIT_RESET_ACK) |
612 S(BSSGP_BVCFSM_S_UNBLOCKED) |
613 S(BSSGP_BVCFSM_S_BLOCKED),
614 .action = bssgp_bvc_fsm_blocked,
615 .onenter = bssgp_bvc_fsm_blocked_onenter,
616 },
617 [BSSGP_BVCFSM_S_WAIT_RESET_ACK]= {
618 .name = "WAIT_RESET_ACK",
Harald Welte09422682021-02-13 11:01:47 +0100619 .in_event_mask = S(BSSGP_BVCFSM_E_RX_RESET_ACK) |
620 S(BSSGP_BVCFSM_E_RX_RESET),
Harald Welte17a892f2020-12-07 21:39:03 +0100621 .out_state_mask = S(BSSGP_BVCFSM_S_UNBLOCKED) |
622 S(BSSGP_BVCFSM_S_BLOCKED) |
623 S(BSSGP_BVCFSM_S_WAIT_RESET_ACK),
624 .action = bssgp_bvc_fsm_wait_reset_ack,
625 .onenter = _onenter_tail,
626 },
627
628 [BSSGP_BVCFSM_S_UNBLOCKED] = {
629 .name = "UNBLOCKED",
630 .in_event_mask = S(BSSGP_BVCFSM_E_RX_BLOCK) |
631 S(BSSGP_BVCFSM_E_RX_UNBLOCK) |
632 S(BSSGP_BVCFSM_E_RX_UNBLOCK_ACK) |
Harald Welte1fcfce82020-12-08 21:15:45 +0100633 S(BSSGP_BVCFSM_E_REQ_BLOCK) |
634 S(BSSGP_BVCFSM_E_RX_FC_BVC) |
635 S(BSSGP_BVCFSM_E_RX_FC_BVC_ACK) |
636 S(BSSGP_BVCFSM_E_REQ_FC_BVC),
Harald Welte17a892f2020-12-07 21:39:03 +0100637 .out_state_mask = S(BSSGP_BVCFSM_S_BLOCKED) |
638 S(BSSGP_BVCFSM_S_WAIT_RESET_ACK) |
639 S(BSSGP_BVCFSM_S_UNBLOCKED),
640 .action = bssgp_bvc_fsm_unblocked,
641 .onenter = _onenter_tail,
642 },
643};
644
645static struct osmo_fsm bssgp_bvc_fsm = {
646 .name = "BSSGP-BVC",
647 .states = bssgp_bvc_fsm_states,
648 .num_states = ARRAY_SIZE(bssgp_bvc_fsm_states),
649 .allstate_event_mask = S(BSSGP_BVCFSM_E_REQ_RESET) |
650 S(BSSGP_BVCFSM_E_RX_RESET),
651 .allstate_action = bssgp_bvc_fsm_allstate,
652 .timer_cb = bssgp_bvc_fsm_timer_cb,
653 .log_subsys = DLBSSGP,
654 .event_names = ptp_bvc_event_names,
655};
656
657static struct osmo_fsm_inst *
658_bvc_fsm_alloc(void *ctx, struct gprs_ns2_inst *nsi, bool role_sgsn, uint16_t nsei, uint16_t bvci)
659{
660 struct osmo_fsm_inst *fi;
661 struct bvc_fsm_priv *bfp;
662 char idbuf[64];
663
664 /* TODO: encode our role in the id string? */
665 snprintf(idbuf, sizeof(idbuf), "NSE%05u-BVC%05u", nsei, bvci);
666
667 fi = osmo_fsm_inst_alloc(&bssgp_bvc_fsm, ctx, NULL, LOGL_INFO, idbuf);
668 if (!fi)
669 return NULL;
670
671 bfp = talloc_zero(fi, struct bvc_fsm_priv);
672 if (!bfp) {
673 osmo_fsm_inst_free(fi);
674 return NULL;
675 }
676 fi->priv = bfp;
677
678 bfp->nsi = nsi;
679 bfp->role_sgsn = role_sgsn;
680 bfp->nsei = nsei;
681 bfp->bvci = bvci;
Daniel Willmann1ff86f72021-01-25 17:02:25 +0100682 bfp->max_pdu_len = UINT16_MAX;
Harald Welte17a892f2020-12-07 21:39:03 +0100683
684 return fi;
685}
686
687/*! Allocate a SIGNALING-BVC FSM for the BSS role (facing a remote SGSN).
688 * \param[in] ctx talloc context from which to allocate
689 * \param[in] nsi NS Instance on which this BVC operates
690 * \param[in] nsei NS Entity Identifier on which this BVC operates
691 * \param[in] features Feature [byte 0] and Extended Feature [byte 1] bitmap
692 * \returns newly-allocated FSM Instance; NULL in case of error */
693struct osmo_fsm_inst *
694bssgp_bvc_fsm_alloc_sig_bss(void *ctx, struct gprs_ns2_inst *nsi, uint16_t nsei, uint32_t features)
695{
696 struct osmo_fsm_inst *fi = _bvc_fsm_alloc(ctx, nsi, false, nsei, 0);
697 struct bvc_fsm_priv *bfp;
698
699 if (!fi)
700 return NULL;
701
702 bfp = fi->priv;
703 bfp->features.advertised = features;
704
705 return fi;
706}
707
708/*! Allocate a PTP-BVC FSM for the BSS role (facing a remote SGSN).
709 * \param[in] ctx talloc context from which to allocate
710 * \param[in] nsi NS Instance on which this BVC operates
711 * \param[in] nsei NS Entity Identifier on which this BVC operates
712 * \param[in] bvci BVCI of this FSM
713 * \param[in] ra_id Routing Area Identity of the cell (reported to SGSN)
714 * \param[in] cell_id Cell Identifier of the cell (reported to SGSN)
715 * \returns newly-allocated FSM Instance; NULL in case of error */
716struct osmo_fsm_inst *
717bssgp_bvc_fsm_alloc_ptp_bss(void *ctx, struct gprs_ns2_inst *nsi, uint16_t nsei,
718 uint16_t bvci, const struct gprs_ra_id *ra_id, uint16_t cell_id)
719{
720 struct osmo_fsm_inst *fi;
721 struct bvc_fsm_priv *bfp;
722
723 OSMO_ASSERT(bvci >= 2);
724 OSMO_ASSERT(ra_id);
725
726 fi = _bvc_fsm_alloc(ctx, nsi, false, nsei, bvci);
727 if (!fi)
728 return NULL;
729
730 bfp = fi->priv;
731 bfp->ra_id = *ra_id;
732 bfp->cell_id = cell_id;
733
734 return fi;
735}
736
737/*! Allocate a SIGNALING-BVC FSM for the SGSN role (facing a remote BSS).
738 * \param[in] ctx talloc context from which to allocate
739 * \param[in] nsi NS Instance on which this BVC operates
740 * \param[in] nsei NS Entity Identifier on which this BVC operates
741 * \param[in] features Feature [byte 0] and Extended Feature [byte 1] bitmap
742 * \returns newly-allocated FSM Instance; NULL in case of error */
743struct osmo_fsm_inst *
744bssgp_bvc_fsm_alloc_sig_sgsn(void *ctx, struct gprs_ns2_inst *nsi, uint16_t nsei, uint32_t features)
745{
746 struct osmo_fsm_inst *fi = _bvc_fsm_alloc(ctx, nsi, true, nsei, 0);
747 struct bvc_fsm_priv *bfp;
748
749 if (!fi)
750 return NULL;
751
752 bfp = fi->priv;
753 bfp->features.advertised = features;
754
755 return fi;
756}
757
758/*! Allocate a PTP-BVC FSM for the SGSN role (facing a remote BSS).
759 * \param[in] ctx talloc context from which to allocate
760 * \param[in] nsi NS Instance on which this BVC operates
761 * \param[in] nsei NS Entity Identifier on which this BVC operates
762 * \param[in] bvci BVCI of this FSM
763 * \returns newly-allocated FSM Instance; NULL in case of error */
764struct osmo_fsm_inst *
765bssgp_bvc_fsm_alloc_ptp_sgsn(void *ctx, struct gprs_ns2_inst *nsi, uint16_t nsei, uint16_t bvci)
766{
767 struct osmo_fsm_inst *fi;
768
769 OSMO_ASSERT(bvci >= 2);
770
771 fi = _bvc_fsm_alloc(ctx, nsi, true, nsei, bvci);
772 if (!fi)
773 return NULL;
774
775 return fi;
776}
777
778/*! Set the 'operations' callbacks + private data.
779 * \param[in] fi FSM instance for which the data shall be set
780 * \param[in] ops BSSGP BVC FSM operations (call-back functions) to register
781 * \param[in] ops_priv opaque/private data pointer passed through to call-backs */
782void bssgp_bvc_fsm_set_ops(struct osmo_fsm_inst *fi, const struct bssgp_bvc_fsm_ops *ops, void *ops_priv)
783{
784 struct bvc_fsm_priv *bfp = fi->priv;
785
786 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
787
788 bfp->ops = ops;
789 bfp->ops_priv = ops_priv;
790}
791
792/*! Return if the given BVC FSM is in UNBLOCKED state. */
793bool bssgp_bvc_fsm_is_unblocked(struct osmo_fsm_inst *fi)
794{
795 return fi->state == BSSGP_BVCFSM_S_UNBLOCKED;
796}
797
798/*! Determine the cause value why given BVC FSM is blocked. */
799uint8_t bssgp_bvc_fsm_get_block_cause(struct osmo_fsm_inst *fi)
800{
801 struct bvc_fsm_priv *bfp = fi->priv;
802
803 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
804 return bfp->block_cause;
805}
806
807/*! Return the advertised features / extended features. */
Daniel Willmann4e5cdad2021-02-12 22:27:56 +0100808uint32_t bssgp_bvc_fsm_get_features_advertised(struct osmo_fsm_inst *fi)
Harald Welte17a892f2020-12-07 21:39:03 +0100809{
810 struct bvc_fsm_priv *bfp = fi->priv;
811
812 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
813 return bfp->features.advertised;
814}
815
816/*! Return the received features / extended features. */
Daniel Willmann4e5cdad2021-02-12 22:27:56 +0100817uint32_t bssgp_bvc_fsm_get_features_received(struct osmo_fsm_inst *fi)
Harald Welte17a892f2020-12-07 21:39:03 +0100818{
819 struct bvc_fsm_priv *bfp = fi->priv;
820
821 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
822 return bfp->features.received;
823}
824
825/*! Return the negotiated features / extended features. */
Daniel Willmann4e5cdad2021-02-12 22:27:56 +0100826uint32_t bssgp_bvc_fsm_get_features_negotiated(struct osmo_fsm_inst *fi)
Harald Welte17a892f2020-12-07 21:39:03 +0100827{
828 struct bvc_fsm_priv *bfp = fi->priv;
829
830 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
831 return bfp->features.negotiated;
832}
833
Daniel Willmann1ff86f72021-01-25 17:02:25 +0100834/*! Set the maximum size of a BSSGP PDU.
835 *! On the NS layer this corresponds to the size of an NS SDU in NS-UNITDATA (3GPP TS 48.016 Ch. 9.2.10) */
836void bssgp_bvc_fsm_set_max_pdu_len(struct osmo_fsm_inst *fi, uint16_t max_pdu_len) {
837 struct bvc_fsm_priv *bfp = fi->priv;
838
839 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
840 bfp->max_pdu_len = max_pdu_len;
841}
842
843/*! Return the maximum size of a BSSGP PDU
844 *! On the NS layer this corresponds to the size of an NS SDU in NS-UNITDATA (3GPP TS 48.016 Ch. 9.2.10) */
845uint16_t bssgp_bvc_fsm_get_max_pdu_len(const struct osmo_fsm_inst *fi)
846{
847 const struct bvc_fsm_priv *bfp = fi->priv;
848
849 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
850 return bfp->max_pdu_len;
851}
852
853
Harald Welte17a892f2020-12-07 21:39:03 +0100854static __attribute__((constructor)) void on_dso_load_bvc_fsm(void)
855{
Vadim Yanitskiye3d32d52021-02-05 14:37:41 +0100856 OSMO_ASSERT(osmo_fsm_register(&bssgp_bvc_fsm) == 0);
Harald Welte17a892f2020-12-07 21:39:03 +0100857}