blob: 6b8bd142e7b9acc80d7c4f71735626d4502cdbbb [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;
119
120 /* BVCI of this BVC */
121 uint16_t bvci;
122
123 /* are we the SGSN (true) or the BSS (false) */
124 bool role_sgsn;
125
126 /* BSS side: are we locally marked blocked? */
127 bool locally_blocked;
128 uint8_t block_cause;
129
130 /* cause value of the last outbound BVC-RESET (for re-transmissions) */
131 uint8_t last_reset_cause;
132
133 struct {
134 /* Bit 0..7: Features; Bit 8..15: Extended Features */
135 uint32_t advertised;
136 uint32_t received;
137 uint32_t negotiated;
Harald Welte1fcfce82020-12-08 21:15:45 +0100138 /* only used if BSSGP_XFEAT_GBIT is negotiated */
139 enum bssgp_fc_granularity fc_granularity;
Harald Welte17a892f2020-12-07 21:39:03 +0100140 } features;
141
142 /* Cell Identification used by BSS when
143 * transmitting BVC-RESET / BVC-RESET-ACK, or those received
144 * from BSS in SGSN role */
145 struct gprs_ra_id ra_id;
146 uint16_t cell_id;
147
148 /* call-backs provided by the user */
149 const struct bssgp_bvc_fsm_ops *ops;
150 /* private data pointer passed to each call-back invocation */
151 void *ops_priv;
152};
153
154static int fi_tx_ptp(struct osmo_fsm_inst *fi, struct msgb *msg)
155{
156 struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
157 struct bvc_fsm_priv *bfp = fi->priv;
158
159 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
160
161 LOGPFSM(fi, "Tx BSSGP %s\n", osmo_tlv_prot_msg_name(&osmo_pdef_bssgp, bgph->pdu_type));
162
163 return bssgp2_nsi_tx_ptp(bfp->nsi, bfp->nsei, bfp->bvci, msg, 0);
164}
165
166static int fi_tx_sig(struct osmo_fsm_inst *fi, struct msgb *msg)
167{
168 struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg);
169 struct bvc_fsm_priv *bfp = fi->priv;
170
171 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
172
173 LOGPFSM(fi, "Tx BSSGP %s\n", osmo_tlv_prot_msg_name(&osmo_pdef_bssgp, bgph->pdu_type));
174
175 return bssgp2_nsi_tx_sig(bfp->nsi, bfp->nsei, msg, 0);
176}
177
178/* helper function to transmit BVC-RESET with right combination of conditional/optional IEs */
179static void _tx_bvc_reset(struct osmo_fsm_inst *fi, uint8_t cause)
180{
181 struct bvc_fsm_priv *bfp = fi->priv;
182 const uint8_t *features = NULL;
183 const uint8_t *features_ext = NULL;
184 uint8_t _features[2] = {
185 (bfp->features.advertised >> 0) & 0xff,
186 (bfp->features.advertised >> 8) & 0xff,
187 };
188 struct msgb *tx;
189
190 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
191
192 /* transmit BVC-RESET to peer; RA-ID only present for PTP from BSS */
193 if (bfp->bvci == 0) {
194 features = &_features[0];
195 features_ext = &_features[1];
196 }
197 tx = bssgp2_enc_bvc_reset(bfp->bvci, cause,
198 bfp->bvci && !bfp->role_sgsn ? &bfp->ra_id : NULL,
199 bfp->cell_id, features, features_ext);
200 fi_tx_sig(fi, tx);
201}
202
203/* helper function to transmit BVC-RESET-ACK with right combination of conditional/optional IEs */
204static void _tx_bvc_reset_ack(struct osmo_fsm_inst *fi)
205{
206 struct bvc_fsm_priv *bfp = fi->priv;
207 const uint8_t *features = NULL;
208 const uint8_t *features_ext = NULL;
209 uint8_t _features[2] = {
210 (bfp->features.advertised >> 0) & 0xff,
211 (bfp->features.advertised >> 8) & 0xff,
212 };
213 struct msgb *tx;
214
215 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
216
217 /* transmit BVC-RESET-ACK to peer; RA-ID only present for PTP from BSS -> SGSN */
218 if (bfp->bvci == 0) {
219 features = &_features[0];
220 features_ext = &_features[1];
221 }
222 tx = bssgp2_enc_bvc_reset_ack(bfp->bvci, bfp->bvci && !bfp->role_sgsn ? &bfp->ra_id : NULL,
223 bfp->cell_id, features, features_ext);
224 fi_tx_sig(fi, tx);
225}
226
227/* helper function to transmit BVC-STATUS with right combination of conditional/optional IEs */
228static void _tx_status(struct osmo_fsm_inst *fi, enum gprs_bssgp_cause cause, const struct msgb *rx)
229{
230 struct bvc_fsm_priv *bfp = fi->priv;
231 struct msgb *tx;
232 uint16_t *bvci = NULL;
233
234 /* GSM 08.18, 10.4.14.1: The BVCI must be included if (and only if) the
235 * cause is either "BVCI blocked" or "BVCI unknown" */
236 if (cause == BSSGP_CAUSE_UNKNOWN_BVCI || cause == BSSGP_CAUSE_BVCI_BLOCKED)
237 bvci = &bfp->bvci;
238
239 tx = bssgp2_enc_status(cause, bvci, rx);
240
241 if (msgb_bvci(rx) == 0)
242 fi_tx_sig(fi, tx);
243 else
244 fi_tx_ptp(fi, tx);
245}
246
247/* Update the features by bit-wise AND of advertised + received features */
248static void update_negotiated_features(struct osmo_fsm_inst *fi, const struct tlv_parsed *tp)
249{
250 struct bvc_fsm_priv *bfp = fi->priv;
251
252 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
253
254 bfp->features.received = 0;
255
256 if (TLVP_PRES_LEN(tp, BSSGP_IE_FEATURE_BITMAP, 1))
257 bfp->features.received |= *TLVP_VAL(tp, BSSGP_IE_FEATURE_BITMAP);
258
259 if (TLVP_PRES_LEN(tp, BSSGP_IE_EXT_FEATURE_BITMAP, 1))
260 bfp->features.received |= (*TLVP_VAL(tp, BSSGP_IE_EXT_FEATURE_BITMAP) << 8);
261
262 bfp->features.negotiated = bfp->features.advertised & bfp->features.received;
263
264 LOGPFSML(fi, LOGL_NOTICE, "Updating features: Advertised 0x%04x, Received 0x%04x, Negotiated 0x%04x\n",
265 bfp->features.advertised, bfp->features.received, bfp->features.negotiated);
266}
267
268/* "tail" of each onenter() handler: Calling the state change notification call-back */
269static void _onenter_tail(struct osmo_fsm_inst *fi, uint32_t prev_state)
270{
271 struct bvc_fsm_priv *bfp = fi->priv;
272
273 if (prev_state == fi->state)
274 return;
275
276 if (bfp->ops && bfp->ops->state_chg_notification)
277 bfp->ops->state_chg_notification(bfp->nsei, bfp->bvci, prev_state, fi->state, bfp->ops_priv);
278}
279
280static void bssgp_bvc_fsm_null(struct osmo_fsm_inst *fi, uint32_t event, void *data)
281{
282 /* we don't really expect anything in this state; all handled via allstate */
283 OSMO_ASSERT(0);
284}
285
286static void bssgp_bvc_fsm_blocked_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
287{
288 struct bvc_fsm_priv *bfp = fi->priv;
289 /* signaling BVC can never be blocked */
290 OSMO_ASSERT(bfp->bvci != 0);
291 _onenter_tail(fi, prev_state);
292}
293
294static void bssgp_bvc_fsm_blocked(struct osmo_fsm_inst *fi, uint32_t event, void *data)
295{
296 struct bvc_fsm_priv *bfp = fi->priv;
297 struct msgb *rx = NULL, *tx;
298 const struct tlv_parsed *tp = NULL;
299 uint8_t cause;
300
301 switch (event) {
302 case BSSGP_BVCFSM_E_RX_BLOCK_ACK:
303 rx = data;
304 tp = (const struct tlv_parsed *) msgb_bcid(rx);
305 /* If a BVC-BLOCK-ACK PDU is received by a BSS for the signalling BVC, the PDU is ignored. */
306 if (bfp->bvci == 0) {
307 LOGPFSML(fi, LOGL_ERROR, "Rx BVC-BLOCK-ACK on BVCI=0 is illegal\n");
308 if (!bfp->role_sgsn)
309 break;
310 _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
311 break;
312 }
313 /* stop T1 timer */
314 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_BLOCKED, 0, 0);
315 break;
316 case BSSGP_BVCFSM_E_RX_BLOCK:
317 rx = data;
318 tp = (const struct tlv_parsed *) msgb_bcid(rx);
319 cause = *TLVP_VAL(tp, BSSGP_IE_CAUSE);
320 LOGPFSML(fi, LOGL_NOTICE, "Rx BVC-BLOCK (cause=%s)\n", bssgp_cause_str(cause));
321 /* If a BVC-BLOCK PDU is received by an SGSN for a blocked BVC, a BVC-BLOCK-ACK
322 * PDU shall be returned. */
323 if (bfp->role_sgsn) {
324 /* If a BVC-BLOCK PDU is received by an SGSN for
325 * the signalling BVC, the PDU is ignored */
326 if (bfp->bvci == 0)
327 break;
328 tx = bssgp2_enc_bvc_block_ack(bfp->bvci);
329 fi_tx_sig(fi, tx);
330 }
331 break;
332 case BSSGP_BVCFSM_E_RX_UNBLOCK:
333 rx = data;
334 tp = (const struct tlv_parsed *) msgb_bcid(rx);
335 LOGPFSML(fi, LOGL_NOTICE, "Rx BVC-UNBLOCK\n");
336 if (bfp->bvci == 0) {
337 LOGPFSML(fi, LOGL_ERROR, "Rx BVC-UNBLOCK on BVCI=0 is illegal\n");
338 /* If BVC-UNBLOCK PDU is received by an SGSN for the signalling BVC, the PDU is ignored.*/
339 if (bfp->role_sgsn)
340 break;
341 _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
342 break;
343 }
344 if (!bfp->role_sgsn) {
345 LOGPFSML(fi, LOGL_ERROR, "Rx BVC-UNBLOCK on BSS is illegal\n");
346 _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
347 break;
348 }
349 tx = bssgp2_enc_bvc_unblock_ack(bfp->bvci);
350 fi_tx_sig(fi, tx);
351 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, T1_SECS, T1);
352 break;
353 case BSSGP_BVCFSM_E_REQ_UNBLOCK:
354 if (bfp->role_sgsn) {
355 LOGPFSML(fi, LOGL_ERROR, "SGSN side cannot initiate BVC unblock\n");
356 break;
357 }
358 if (bfp->bvci == 0) {
359 LOGPFSML(fi, LOGL_ERROR, "BVCI 0 cannot be unblocked\n");
360 break;
361 }
362 bfp->locally_blocked = false;
363 tx = bssgp2_enc_bvc_unblock(bfp->bvci);
364 fi_tx_sig(fi, tx);
365 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, 0, 0);
366 break;
367 }
368}
369
370/* Waiting for RESET-ACK: Receive PDUs but don't transmit */
371static void bssgp_bvc_fsm_wait_reset_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
372{
373 struct bvc_fsm_priv *bfp = fi->priv;
374 const struct tlv_parsed *tp = NULL;
375 struct msgb *rx = NULL, *tx;
376
377 switch (event) {
378 case BSSGP_BVCFSM_E_RX_RESET_ACK:
379 rx = data;
380 tp = (const struct tlv_parsed *) msgb_bcid(rx);
381 if (bfp->bvci == 0)
382 update_negotiated_features(fi, tp);
383 if (bfp->role_sgsn && bfp->bvci != 0)
384 bfp->cell_id = bssgp_parse_cell_id(&bfp->ra_id, TLVP_VAL(tp, BSSGP_IE_CELL_ID));
385 if (!bfp->role_sgsn && bfp->bvci != 0 && bfp->locally_blocked) {
386 /* initiate the blocking procedure */
387 /* transmit BVC-BLOCK, transition to BLOCKED state and start re-transmit timer */
388 tx = bssgp2_enc_bvc_block(bfp->bvci, bfp->block_cause);
389 fi_tx_sig(fi, tx);
390 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_BLOCKED, T1_SECS, T1);
391 } else
392 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, 0, 0);
393 break;
394 }
395}
396
397static void bssgp_bvc_fsm_unblocked(struct osmo_fsm_inst *fi, uint32_t event, void *data)
398{
Harald Welte1fcfce82020-12-08 21:15:45 +0100399 struct bssgp2_flow_ctrl rx_fc, *tx_fc;
Harald Welte17a892f2020-12-07 21:39:03 +0100400 struct bvc_fsm_priv *bfp = fi->priv;
401 const struct tlv_parsed *tp = NULL;
402 struct msgb *rx = NULL, *tx;
Harald Welte1fcfce82020-12-08 21:15:45 +0100403 int rc;
Harald Welte17a892f2020-12-07 21:39:03 +0100404
405 switch (event) {
406 case BSSGP_BVCFSM_E_RX_UNBLOCK_ACK:
407 rx = data;
408 tp = (const struct tlv_parsed *) msgb_bcid(rx);
409 /* If BVC-UNBLOCK-ACK PDU is received by an BSS for the signalling BVC, the PDU is ignored. */
410 LOGPFSML(fi, LOGL_ERROR, "Rx BVC-UNBLOCK-ACK on BVCI=0 is illegal\n");
411 if (bfp->bvci == 0) {
412 if (!bfp->role_sgsn)
413 break;
414 _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
415 break;
416 }
417 /* stop T1 timer */
418 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, 0, 0);
419 break;
420 case BSSGP_BVCFSM_E_RX_UNBLOCK:
421 rx = data;
422 tp = (const struct tlv_parsed *) msgb_bcid(rx);
423 /* If a BVC-UNBLOCK PDU is received by an SGSN for a blocked BVC, a BVC-UNBLOCK-ACK
424 * PDU shall be returned. */
425 if (bfp->role_sgsn) {
426 /* If a BVC-UNBLOCK PDU is received by an SGSN for
427 * the signalling BVC, the PDU is ignored */
428 if (bfp->bvci == 0)
429 break;
430 bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_UNBLOCK_ACK, bfp->nsei, bfp->bvci, 0);
431 }
432 break;
433 case BSSGP_BVCFSM_E_RX_BLOCK:
434 rx = data;
435 tp = (const struct tlv_parsed *) msgb_bcid(rx);
436 LOGPFSML(fi, LOGL_NOTICE, "Rx BVC-BLOCK (cause=%s)\n",
437 bssgp_cause_str(*TLVP_VAL(tp, BSSGP_IE_CAUSE)));
438 /* If a BVC-BLOCK PDU is received by an SGSN for the signalling BVC, the PDU is ignored */
439 if (bfp->bvci == 0) {
440 LOGPFSML(fi, LOGL_ERROR, "Rx BVC-BLOCK on BVCI=0 is illegal\n");
441 if (bfp->role_sgsn)
442 break;
443 _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
444 break;
445 }
446 if (!bfp->role_sgsn) {
447 LOGPFSML(fi, LOGL_ERROR, "Rx BVC-BLOCK on BSS is illegal\n");
448 _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
449 break;
450 }
451 /* transmit BVC-BLOCK-ACK, transition to BLOCKED state */
452 tx = bssgp2_enc_bvc_block_ack(bfp->bvci);
453 fi_tx_sig(fi, tx);
454 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_BLOCKED, 0, 0);
455 break;
456 case BSSGP_BVCFSM_E_REQ_BLOCK:
457 if (bfp->role_sgsn) {
458 LOGPFSML(fi, LOGL_ERROR, "SGSN may not initiate BVC-BLOCK\n");
Daniel Willmann09bea012021-01-07 14:46:28 +0100459 break;
460 }
461 if (bfp->bvci == 0) {
462 LOGPFSML(fi, LOGL_ERROR, "BVCI 0 cannot be blocked\n");
Harald Welte17a892f2020-12-07 21:39:03 +0100463 break;
464 }
465 bfp->locally_blocked = true;
466 bfp->block_cause = *(uint8_t *)data;
467 /* transmit BVC-BLOCK, transition to BLOCKED state and start re-transmit timer */
468 tx = bssgp2_enc_bvc_block(bfp->bvci, bfp->block_cause);
469 fi_tx_sig(fi, tx);
470 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_BLOCKED, T1_SECS, T1);
471 break;
Harald Welte1fcfce82020-12-08 21:15:45 +0100472 case BSSGP_BVCFSM_E_RX_FC_BVC:
473 rx = data;
474 tp = (const struct tlv_parsed *) msgb_bcid(rx);
475 /* we assume osmo_tlv_prot_* has been used before calling here to ensure this */
476 OSMO_ASSERT(bfp->role_sgsn);
477 rc = bssgp2_dec_fc_bvc(&rx_fc, tp);
478 if (rc < 0) {
479 _tx_status(fi, BSSGP_CAUSE_SEM_INCORR_PDU, rx);
480 break;
481 }
482 if (bfp->ops->rx_fc_bvc)
483 bfp->ops->rx_fc_bvc(bfp->nsei, bfp->bvci, &rx_fc, bfp->ops_priv);
484 tx = bssgp2_enc_fc_bvc_ack(rx_fc.tag);
485 fi_tx_ptp(fi, tx);
486 break;
487 case BSSGP_BVCFSM_E_RX_FC_BVC_ACK:
488 rx = data;
489 tp = (const struct tlv_parsed *) msgb_bcid(rx);
490 /* we assume osmo_tlv_prot_* has been used before calling here to ensure this */
491 OSMO_ASSERT(!bfp->role_sgsn);
492 break;
493 case BSSGP_BVCFSM_E_REQ_FC_BVC:
494 tx_fc = data;
495 tx = bssgp2_enc_fc_bvc(tx_fc, bfp->features.negotiated & (BSSGP_XFEAT_GBIT << 8) ?
496 &bfp->features.fc_granularity : NULL);
497 fi_tx_ptp(fi, tx);
498 break;
Harald Welte17a892f2020-12-07 21:39:03 +0100499 }
500}
501
502static void bssgp_bvc_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
503{
504 struct bvc_fsm_priv *bfp = fi->priv;
505 uint8_t cause;
506 const struct tlv_parsed *tp = NULL;
507 struct msgb *rx = NULL;
508
509 switch (event) {
510 case BSSGP_BVCFSM_E_REQ_RESET:
511 bfp->locally_blocked = false;
512 cause = bfp->last_reset_cause = *(uint8_t *) data;
513 _tx_bvc_reset(fi, cause);
514 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_WAIT_RESET_ACK, T2_SECS, T2);
515#if 0 /* not sure if we really should notify the application if itself has requested the reset? */
516 if (bfp->ops && bfp->ops->reset_notification)
517 bfp->ops->reset_notification(bfp->nsei, bfp->bvci, NULL, 0, cause, bfp->ops_priv);
518#endif
519 break;
520 case BSSGP_BVCFSM_E_RX_RESET:
521 rx = data;
522 tp = (const struct tlv_parsed *) msgb_bcid(rx);
523 cause = *TLVP_VAL(tp, BSSGP_IE_CAUSE);
524 if (bfp->role_sgsn && bfp->bvci != 0)
525 bfp->cell_id = bssgp_parse_cell_id(&bfp->ra_id, TLVP_VAL(tp, BSSGP_IE_CELL_ID));
526 LOGPFSML(fi, LOGL_NOTICE, "Rx BVC-RESET (cause=%s)\n", bssgp_cause_str(cause));
527 if (bfp->bvci == 0)
528 update_negotiated_features(fi, tp);
529 _tx_bvc_reset_ack(fi);
530 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, 0, 0);
531 if (bfp->ops && bfp->ops->reset_notification) {
532 bfp->ops->reset_notification(bfp->nsei, bfp->bvci, &bfp->ra_id, bfp->cell_id,
533 cause, bfp->ops_priv);
534 }
535 break;
536 }
537}
538
539static int bssgp_bvc_fsm_timer_cb(struct osmo_fsm_inst *fi)
540{
541 struct bvc_fsm_priv *bfp = fi->priv;
542 struct msgb *tx;
543
544 switch (fi->T) {
545 case T1:
546 switch (fi->state) {
547 case BSSGP_BVCFSM_S_BLOCKED:
548 /* re-transmit BVC-BLOCK */
549 tx = bssgp2_enc_bvc_block(bfp->bvci, bfp->block_cause);
550 fi_tx_sig(fi, tx);
551 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_BLOCKED, T1_SECS, T1);
552 break;
553 case BSSGP_BVCFSM_S_UNBLOCKED:
554 /* re-transmit BVC-UNBLOCK */
555 tx = bssgp2_enc_bvc_unblock(bfp->bvci);
556 fi_tx_sig(fi, tx);
557 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, T1_SECS, T1);
558 break;
559 }
560 break;
561 case T2:
562 switch (fi->state) {
563 case BSSGP_BVCFSM_S_WAIT_RESET_ACK:
564 /* re-transmit BVC-RESET */
565 _tx_bvc_reset(fi, bfp->last_reset_cause);
566 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_WAIT_RESET_ACK, T2_SECS, T2);
567 break;
568 case BSSGP_BVCFSM_S_UNBLOCKED:
569 /* re-transmit BVC-RESET-ACK */
570 _tx_bvc_reset_ack(fi);
571 osmo_fsm_inst_state_chg(fi, BSSGP_BVCFSM_S_UNBLOCKED, T2_SECS, T2);
572 break;
573 }
574 break;
575 default:
576 OSMO_ASSERT(0);
577 break;
578 }
579 return 0;
580}
581
582
583
584static const struct osmo_fsm_state bssgp_bvc_fsm_states[] = {
585 [BSSGP_BVCFSM_S_NULL] = {
586 /* initial state from which we must do a RESET */
587 .name = "NULL",
588 .in_event_mask = 0,
589 .out_state_mask = S(BSSGP_BVCFSM_S_WAIT_RESET_ACK) |
590 S(BSSGP_BVCFSM_S_UNBLOCKED),
591 .action = bssgp_bvc_fsm_null,
592 },
593 [BSSGP_BVCFSM_S_BLOCKED] = {
594 .name = "BLOCKED",
595 .in_event_mask = S(BSSGP_BVCFSM_E_RX_UNBLOCK) |
596 S(BSSGP_BVCFSM_E_RX_BLOCK) |
597 S(BSSGP_BVCFSM_E_RX_BLOCK_ACK) |
598 S(BSSGP_BVCFSM_E_REQ_UNBLOCK),
599 .out_state_mask = S(BSSGP_BVCFSM_S_WAIT_RESET_ACK) |
600 S(BSSGP_BVCFSM_S_UNBLOCKED) |
601 S(BSSGP_BVCFSM_S_BLOCKED),
602 .action = bssgp_bvc_fsm_blocked,
603 .onenter = bssgp_bvc_fsm_blocked_onenter,
604 },
605 [BSSGP_BVCFSM_S_WAIT_RESET_ACK]= {
606 .name = "WAIT_RESET_ACK",
607 .in_event_mask = S(BSSGP_BVCFSM_E_RX_RESET_ACK),
608 .out_state_mask = S(BSSGP_BVCFSM_S_UNBLOCKED) |
609 S(BSSGP_BVCFSM_S_BLOCKED) |
610 S(BSSGP_BVCFSM_S_WAIT_RESET_ACK),
611 .action = bssgp_bvc_fsm_wait_reset_ack,
612 .onenter = _onenter_tail,
613 },
614
615 [BSSGP_BVCFSM_S_UNBLOCKED] = {
616 .name = "UNBLOCKED",
617 .in_event_mask = S(BSSGP_BVCFSM_E_RX_BLOCK) |
618 S(BSSGP_BVCFSM_E_RX_UNBLOCK) |
619 S(BSSGP_BVCFSM_E_RX_UNBLOCK_ACK) |
Harald Welte1fcfce82020-12-08 21:15:45 +0100620 S(BSSGP_BVCFSM_E_REQ_BLOCK) |
621 S(BSSGP_BVCFSM_E_RX_FC_BVC) |
622 S(BSSGP_BVCFSM_E_RX_FC_BVC_ACK) |
623 S(BSSGP_BVCFSM_E_REQ_FC_BVC),
Harald Welte17a892f2020-12-07 21:39:03 +0100624 .out_state_mask = S(BSSGP_BVCFSM_S_BLOCKED) |
625 S(BSSGP_BVCFSM_S_WAIT_RESET_ACK) |
626 S(BSSGP_BVCFSM_S_UNBLOCKED),
627 .action = bssgp_bvc_fsm_unblocked,
628 .onenter = _onenter_tail,
629 },
630};
631
632static struct osmo_fsm bssgp_bvc_fsm = {
633 .name = "BSSGP-BVC",
634 .states = bssgp_bvc_fsm_states,
635 .num_states = ARRAY_SIZE(bssgp_bvc_fsm_states),
636 .allstate_event_mask = S(BSSGP_BVCFSM_E_REQ_RESET) |
637 S(BSSGP_BVCFSM_E_RX_RESET),
638 .allstate_action = bssgp_bvc_fsm_allstate,
639 .timer_cb = bssgp_bvc_fsm_timer_cb,
640 .log_subsys = DLBSSGP,
641 .event_names = ptp_bvc_event_names,
642};
643
644static struct osmo_fsm_inst *
645_bvc_fsm_alloc(void *ctx, struct gprs_ns2_inst *nsi, bool role_sgsn, uint16_t nsei, uint16_t bvci)
646{
647 struct osmo_fsm_inst *fi;
648 struct bvc_fsm_priv *bfp;
649 char idbuf[64];
650
651 /* TODO: encode our role in the id string? */
652 snprintf(idbuf, sizeof(idbuf), "NSE%05u-BVC%05u", nsei, bvci);
653
654 fi = osmo_fsm_inst_alloc(&bssgp_bvc_fsm, ctx, NULL, LOGL_INFO, idbuf);
655 if (!fi)
656 return NULL;
657
658 bfp = talloc_zero(fi, struct bvc_fsm_priv);
659 if (!bfp) {
660 osmo_fsm_inst_free(fi);
661 return NULL;
662 }
663 fi->priv = bfp;
664
665 bfp->nsi = nsi;
666 bfp->role_sgsn = role_sgsn;
667 bfp->nsei = nsei;
668 bfp->bvci = bvci;
669
670 return fi;
671}
672
673/*! Allocate a SIGNALING-BVC FSM for the BSS role (facing a remote SGSN).
674 * \param[in] ctx talloc context from which to allocate
675 * \param[in] nsi NS Instance on which this BVC operates
676 * \param[in] nsei NS Entity Identifier on which this BVC operates
677 * \param[in] features Feature [byte 0] and Extended Feature [byte 1] bitmap
678 * \returns newly-allocated FSM Instance; NULL in case of error */
679struct osmo_fsm_inst *
680bssgp_bvc_fsm_alloc_sig_bss(void *ctx, struct gprs_ns2_inst *nsi, uint16_t nsei, uint32_t features)
681{
682 struct osmo_fsm_inst *fi = _bvc_fsm_alloc(ctx, nsi, false, nsei, 0);
683 struct bvc_fsm_priv *bfp;
684
685 if (!fi)
686 return NULL;
687
688 bfp = fi->priv;
689 bfp->features.advertised = features;
690
691 return fi;
692}
693
694/*! Allocate a PTP-BVC FSM for the BSS role (facing a remote SGSN).
695 * \param[in] ctx talloc context from which to allocate
696 * \param[in] nsi NS Instance on which this BVC operates
697 * \param[in] nsei NS Entity Identifier on which this BVC operates
698 * \param[in] bvci BVCI of this FSM
699 * \param[in] ra_id Routing Area Identity of the cell (reported to SGSN)
700 * \param[in] cell_id Cell Identifier of the cell (reported to SGSN)
701 * \returns newly-allocated FSM Instance; NULL in case of error */
702struct osmo_fsm_inst *
703bssgp_bvc_fsm_alloc_ptp_bss(void *ctx, struct gprs_ns2_inst *nsi, uint16_t nsei,
704 uint16_t bvci, const struct gprs_ra_id *ra_id, uint16_t cell_id)
705{
706 struct osmo_fsm_inst *fi;
707 struct bvc_fsm_priv *bfp;
708
709 OSMO_ASSERT(bvci >= 2);
710 OSMO_ASSERT(ra_id);
711
712 fi = _bvc_fsm_alloc(ctx, nsi, false, nsei, bvci);
713 if (!fi)
714 return NULL;
715
716 bfp = fi->priv;
717 bfp->ra_id = *ra_id;
718 bfp->cell_id = cell_id;
719
720 return fi;
721}
722
723/*! Allocate a SIGNALING-BVC FSM for the SGSN role (facing a remote BSS).
724 * \param[in] ctx talloc context from which to allocate
725 * \param[in] nsi NS Instance on which this BVC operates
726 * \param[in] nsei NS Entity Identifier on which this BVC operates
727 * \param[in] features Feature [byte 0] and Extended Feature [byte 1] bitmap
728 * \returns newly-allocated FSM Instance; NULL in case of error */
729struct osmo_fsm_inst *
730bssgp_bvc_fsm_alloc_sig_sgsn(void *ctx, struct gprs_ns2_inst *nsi, uint16_t nsei, uint32_t features)
731{
732 struct osmo_fsm_inst *fi = _bvc_fsm_alloc(ctx, nsi, true, nsei, 0);
733 struct bvc_fsm_priv *bfp;
734
735 if (!fi)
736 return NULL;
737
738 bfp = fi->priv;
739 bfp->features.advertised = features;
740
741 return fi;
742}
743
744/*! Allocate a PTP-BVC FSM for the SGSN role (facing a remote BSS).
745 * \param[in] ctx talloc context from which to allocate
746 * \param[in] nsi NS Instance on which this BVC operates
747 * \param[in] nsei NS Entity Identifier on which this BVC operates
748 * \param[in] bvci BVCI of this FSM
749 * \returns newly-allocated FSM Instance; NULL in case of error */
750struct osmo_fsm_inst *
751bssgp_bvc_fsm_alloc_ptp_sgsn(void *ctx, struct gprs_ns2_inst *nsi, uint16_t nsei, uint16_t bvci)
752{
753 struct osmo_fsm_inst *fi;
754
755 OSMO_ASSERT(bvci >= 2);
756
757 fi = _bvc_fsm_alloc(ctx, nsi, true, nsei, bvci);
758 if (!fi)
759 return NULL;
760
761 return fi;
762}
763
764/*! Set the 'operations' callbacks + private data.
765 * \param[in] fi FSM instance for which the data shall be set
766 * \param[in] ops BSSGP BVC FSM operations (call-back functions) to register
767 * \param[in] ops_priv opaque/private data pointer passed through to call-backs */
768void bssgp_bvc_fsm_set_ops(struct osmo_fsm_inst *fi, const struct bssgp_bvc_fsm_ops *ops, void *ops_priv)
769{
770 struct bvc_fsm_priv *bfp = fi->priv;
771
772 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
773
774 bfp->ops = ops;
775 bfp->ops_priv = ops_priv;
776}
777
778/*! Return if the given BVC FSM is in UNBLOCKED state. */
779bool bssgp_bvc_fsm_is_unblocked(struct osmo_fsm_inst *fi)
780{
781 return fi->state == BSSGP_BVCFSM_S_UNBLOCKED;
782}
783
784/*! Determine the cause value why given BVC FSM is blocked. */
785uint8_t bssgp_bvc_fsm_get_block_cause(struct osmo_fsm_inst *fi)
786{
787 struct bvc_fsm_priv *bfp = fi->priv;
788
789 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
790 return bfp->block_cause;
791}
792
793/*! Return the advertised features / extended features. */
794uint32_t bssgp_bvc_get_features_advertised(struct osmo_fsm_inst *fi)
795{
796 struct bvc_fsm_priv *bfp = fi->priv;
797
798 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
799 return bfp->features.advertised;
800}
801
802/*! Return the received features / extended features. */
803uint32_t bssgp_bvc_get_features_received(struct osmo_fsm_inst *fi)
804{
805 struct bvc_fsm_priv *bfp = fi->priv;
806
807 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
808 return bfp->features.received;
809}
810
811/*! Return the negotiated features / extended features. */
812uint32_t bssgp_bvc_get_features_negotiated(struct osmo_fsm_inst *fi)
813{
814 struct bvc_fsm_priv *bfp = fi->priv;
815
816 OSMO_ASSERT(fi->fsm == &bssgp_bvc_fsm);
817 return bfp->features.negotiated;
818}
819
820static __attribute__((constructor)) void on_dso_load_bvc_fsm(void)
821{
Vadim Yanitskiye3d32d52021-02-05 14:37:41 +0100822 OSMO_ASSERT(osmo_fsm_register(&bssgp_bvc_fsm) == 0);
Harald Welte17a892f2020-12-07 21:39:03 +0100823}