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