blob: cdeeae9036ddf3a85ba62a17f2abdc59feac0ff7 [file] [log] [blame]
Harald Weltea183a6e2016-06-17 00:06:42 +02001/* MSC subscriber connection implementation */
2
3/*
4 * (C) 2016 by sysmocom s.m.f.c. <info@sysmocom.de>
5 * All Rights Reserved
6 *
7 * Author: Neels Hofmeyr
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
18 *
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
24#include <osmocom/core/logging.h>
25#include <osmocom/core/fsm.h>
Harald Welte7b423ed2016-06-19 18:06:02 +020026#include <osmocom/core/signal.h>
Harald Weltea183a6e2016-06-17 00:06:42 +020027
28#include <openbsc/osmo_msc.h>
29#include <openbsc/vlr.h>
30#include <openbsc/debug.h>
31#include <openbsc/transaction.h>
Harald Welte7b423ed2016-06-19 18:06:02 +020032#include <openbsc/signal.h>
Neels Hofmeyra1756f32016-05-20 21:59:55 +020033#include <openbsc/iu.h>
Philipp Maier4b60d072017-04-09 12:32:51 +020034#include <openbsc/a_iface.h>
35
Harald Welte7b423ed2016-06-19 18:06:02 +020036
37#define SUBSCR_CONN_TIMEOUT 5 /* seconds */
Harald Weltea183a6e2016-06-17 00:06:42 +020038
39static const struct value_string subscr_conn_fsm_event_names[] = {
40 OSMO_VALUE_STRING(SUBSCR_CONN_E_INVALID),
Harald Welte7b423ed2016-06-19 18:06:02 +020041 OSMO_VALUE_STRING(SUBSCR_CONN_E_START),
Harald Weltea183a6e2016-06-17 00:06:42 +020042 OSMO_VALUE_STRING(SUBSCR_CONN_E_ACCEPTED),
Harald Welte7b423ed2016-06-19 18:06:02 +020043 OSMO_VALUE_STRING(SUBSCR_CONN_E_COMMUNICATING),
Harald Weltea183a6e2016-06-17 00:06:42 +020044 OSMO_VALUE_STRING(SUBSCR_CONN_E_BUMP),
45 OSMO_VALUE_STRING(SUBSCR_CONN_E_MO_CLOSE),
46 OSMO_VALUE_STRING(SUBSCR_CONN_E_CN_CLOSE),
Harald Weltea183a6e2016-06-17 00:06:42 +020047 { 0, NULL }
48};
49
50const struct value_string subscr_conn_from_names[] = {
51 OSMO_VALUE_STRING(SUBSCR_CONN_FROM_INVALID),
52 OSMO_VALUE_STRING(SUBSCR_CONN_FROM_LU),
53 OSMO_VALUE_STRING(SUBSCR_CONN_FROM_CM_SERVICE_REQ),
54 OSMO_VALUE_STRING(SUBSCR_CONN_FROM_PAGING_RESP),
55 { 0, NULL }
56};
57
Neels Hofmeyra1756f32016-05-20 21:59:55 +020058static void paging_event(struct gsm_subscriber_connection *conn,
59 enum gsm_paging_event pe)
Harald Weltea183a6e2016-06-17 00:06:42 +020060{
Harald Welte7b423ed2016-06-19 18:06:02 +020061 subscr_paging_dispatch(GSM_HOOK_RR_PAGING, pe, NULL, conn, conn->vsub);
62}
63
64void subscr_conn_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
65{
66 OSMO_ASSERT(event == SUBSCR_CONN_E_START);
67 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_NEW,
68 SUBSCR_CONN_TIMEOUT, 0);
Harald Weltea183a6e2016-06-17 00:06:42 +020069}
70
71void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data)
72{
73 struct gsm_subscriber_connection *conn = fi->priv;
74 enum subscr_conn_from from = SUBSCR_CONN_FROM_INVALID;
Harald Welte7b423ed2016-06-19 18:06:02 +020075 bool success;
Harald Weltea183a6e2016-06-17 00:06:42 +020076
77 if (data) {
78 from = *(enum subscr_conn_from*)data;
79 LOGPFSM(fi, "%s\n", subscr_conn_from_name(from));
80 }
81
82 /* If accepted, transition the state, all other cases mean failure. */
83 switch (event) {
84 case SUBSCR_CONN_E_ACCEPTED:
Harald Welte7b423ed2016-06-19 18:06:02 +020085 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_ACCEPTED,
86 SUBSCR_CONN_TIMEOUT, 0);
Harald Weltea183a6e2016-06-17 00:06:42 +020087 break;
88
89 case SUBSCR_CONN_E_MO_CLOSE:
90 case SUBSCR_CONN_E_CN_CLOSE:
Neels Hofmeyra1756f32016-05-20 21:59:55 +020091 if (data)
92 LOGPFSM(fi, "Close event, cause %u\n",
93 *(uint32_t*)data);
94 /* will release further below, see
95 * 'if (fi->state != SUBSCR_CONN_S_ACCEPTED)' */
Harald Weltea183a6e2016-06-17 00:06:42 +020096 break;
97
98 default:
Neels Hofmeyra1756f32016-05-20 21:59:55 +020099 LOGPFSML(fi, LOGL_ERROR,
100 "Unexpected event: %d %s\n", event,
101 osmo_fsm_event_name(fi->fsm, event));
Harald Weltea183a6e2016-06-17 00:06:42 +0200102 break;
103 }
104
Harald Welte7b423ed2016-06-19 18:06:02 +0200105 success = (fi->state == SUBSCR_CONN_S_ACCEPTED);
106
107 if (from == SUBSCR_CONN_FROM_LU)
108 rate_ctr_inc(&conn->network->msc_ctrs->ctr[
109 success ? MSC_CTR_LOC_UPDATE_COMPLETED
110 : MSC_CTR_LOC_UPDATE_FAILED]);
111
112 /* signal paging success or failure in case this was a paging */
113 if (from == SUBSCR_CONN_FROM_PAGING_RESP)
Neels Hofmeyra1756f32016-05-20 21:59:55 +0200114 paging_event(conn,
115 success ? GSM_PAGING_SUCCEEDED
116 : GSM_PAGING_EXPIRED);
117
118 /* FIXME rate counters */
119 /*rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_COMPLETED]);*/
Harald Weltea183a6e2016-06-17 00:06:42 +0200120
121 /* On failure, discard the conn */
Harald Welte7b423ed2016-06-19 18:06:02 +0200122 if (!success) {
Harald Weltea183a6e2016-06-17 00:06:42 +0200123 /* TODO: on MO_CLOSE or CN_CLOSE, first go to RELEASING and
Neels Hofmeyra1756f32016-05-20 21:59:55 +0200124 * await BSC/RNC confirmation? */
Harald Weltea183a6e2016-06-17 00:06:42 +0200125 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
126 return;
127 }
128
Harald Weltea183a6e2016-06-17 00:06:42 +0200129 if (from == SUBSCR_CONN_FROM_CM_SERVICE_REQ) {
130 conn->received_cm_service_request = true;
Neels Hofmeyra1756f32016-05-20 21:59:55 +0200131 LOGPFSML(fi, LOGL_DEBUG, "received_cm_service_request = true\n");
Harald Weltea183a6e2016-06-17 00:06:42 +0200132 }
133
134 osmo_fsm_inst_dispatch(fi, SUBSCR_CONN_E_BUMP, data);
135}
136
Harald Weltea183a6e2016-06-17 00:06:42 +0200137static void subscr_conn_fsm_bump(struct osmo_fsm_inst *fi, uint32_t event, void *data)
138{
139 struct gsm_subscriber_connection *conn = fi->priv;
Neels Hofmeyra1756f32016-05-20 21:59:55 +0200140 struct gsm_trans *trans;
Harald Weltea183a6e2016-06-17 00:06:42 +0200141
Neels Hofmeyra1756f32016-05-20 21:59:55 +0200142 if (conn->silent_call) {
143 LOGPFSML(fi, LOGL_DEBUG, "bump: silent call still active\n");
Harald Weltea183a6e2016-06-17 00:06:42 +0200144 return;
Neels Hofmeyra1756f32016-05-20 21:59:55 +0200145 }
Harald Weltea183a6e2016-06-17 00:06:42 +0200146
Neels Hofmeyra1756f32016-05-20 21:59:55 +0200147 if (conn->received_cm_service_request) {
148 LOGPFSML(fi, LOGL_DEBUG, "bump: still awaiting first request after a CM Service Request\n");
Harald Weltea183a6e2016-06-17 00:06:42 +0200149 return;
Neels Hofmeyra1756f32016-05-20 21:59:55 +0200150 }
Harald Weltea183a6e2016-06-17 00:06:42 +0200151
Neels Hofmeyra1756f32016-05-20 21:59:55 +0200152 if (conn->vsub && !llist_empty(&conn->vsub->cs.requests)) {
153 struct subscr_request *sr;
154 if (!log_check_level(fi->fsm->log_subsys, LOGL_DEBUG)) {
155 llist_for_each_entry(sr, &conn->vsub->cs.requests, entry) {
156 LOGPFSML(fi, LOGL_DEBUG, "bump: still active: %s\n",
157 sr->label);
158 }
159 }
Harald Weltea183a6e2016-06-17 00:06:42 +0200160 return;
Neels Hofmeyra1756f32016-05-20 21:59:55 +0200161 }
Harald Weltea183a6e2016-06-17 00:06:42 +0200162
Neels Hofmeyra1756f32016-05-20 21:59:55 +0200163 if ((trans = trans_has_conn(conn))) {
164 LOGPFSML(fi, LOGL_DEBUG,
165 "bump: connection still has active transaction: %s\n",
166 gsm48_pdisc_name(trans->protocol));
Harald Weltea183a6e2016-06-17 00:06:42 +0200167 return;
Neels Hofmeyra1756f32016-05-20 21:59:55 +0200168 }
Harald Weltea183a6e2016-06-17 00:06:42 +0200169
Neels Hofmeyra1756f32016-05-20 21:59:55 +0200170 LOGPFSML(fi, LOGL_DEBUG, "bump: releasing conn\n");
Harald Weltea183a6e2016-06-17 00:06:42 +0200171 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
172}
173
Harald Welte7b423ed2016-06-19 18:06:02 +0200174static void subscr_conn_fsm_accepted_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
175{
176 struct gsm_subscriber_connection *conn = fi->priv;
177 osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_ATTACHED, conn->vsub);
178}
179
Harald Weltea183a6e2016-06-17 00:06:42 +0200180static void subscr_conn_fsm_accepted(struct osmo_fsm_inst *fi, uint32_t event, void *data)
181{
182 switch (event) {
Harald Welte7b423ed2016-06-19 18:06:02 +0200183 case SUBSCR_CONN_E_COMMUNICATING:
184 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_COMMUNICATING, 0, 0);
185 return;
186
Harald Weltea183a6e2016-06-17 00:06:42 +0200187 case SUBSCR_CONN_E_BUMP:
188 subscr_conn_fsm_bump(fi, event, data);
189 return;
190
191 default:
192 break;
193 }
194 /* Whatever unexpected happens in the accepted state, it means release.
195 * Even if an unexpected event is passed, the safest thing to do is
196 * discard the conn. We don't expect another SUBSCR_CONN_E_ACCEPTED. */
197 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
198}
199
Harald Welte7b423ed2016-06-19 18:06:02 +0200200static void subscr_conn_fsm_communicating(struct osmo_fsm_inst *fi, uint32_t event, void *data)
201{
202 switch (event) {
203 case SUBSCR_CONN_E_COMMUNICATING:
204 /* no-op */
205 return;
206
207 case SUBSCR_CONN_E_BUMP:
208 subscr_conn_fsm_bump(fi, event, data);
209 return;
210
211 default:
212 break;
213 }
214 /* Whatever unexpected happens in the accepted state, it means release.
215 * Even if an unexpected event is passed, the safest thing to do is
216 * discard the conn. We don't expect another SUBSCR_CONN_E_ACCEPTED. */
217 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
218}
219
220static void subscr_conn_fsm_cleanup(struct osmo_fsm_inst *fi,
221 enum osmo_fsm_term_cause cause)
Harald Weltea183a6e2016-06-17 00:06:42 +0200222{
223 struct gsm_subscriber_connection *conn = fi->priv;
Harald Welte7b423ed2016-06-19 18:06:02 +0200224 fi->priv = NULL;
225
Harald Weltea183a6e2016-06-17 00:06:42 +0200226 if (!conn)
227 return;
Harald Welte7b423ed2016-06-19 18:06:02 +0200228 conn->conn_fsm = NULL;
229 msc_subscr_conn_close(conn, cause);
230 msc_subscr_conn_put(conn);
231}
Harald Weltea183a6e2016-06-17 00:06:42 +0200232
Harald Welte7b423ed2016-06-19 18:06:02 +0200233int subscr_conn_fsm_timeout(struct osmo_fsm_inst *fi)
234{
235 struct gsm_subscriber_connection *conn = fi->priv;
236 if (conn)
237 vlr_subscr_conn_timeout(conn->vsub);
238 osmo_fsm_inst_dispatch(fi, SUBSCR_CONN_E_CN_CLOSE, NULL);
239 return 0;
240}
241
242static void subscr_conn_fsm_release(struct osmo_fsm_inst *fi, uint32_t prev_state)
243{
244 osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
Harald Weltea183a6e2016-06-17 00:06:42 +0200245}
246
247#define S(x) (1 << (x))
248
249static const struct osmo_fsm_state subscr_conn_fsm_states[] = {
Harald Welte7b423ed2016-06-19 18:06:02 +0200250 [SUBSCR_CONN_S_INIT] = {
251 .name = OSMO_STRINGIFY(SUBSCR_CONN_S_INIT),
252 .in_event_mask = S(SUBSCR_CONN_E_START),
253 .out_state_mask = S(SUBSCR_CONN_S_NEW),
254 .action = subscr_conn_fsm_init,
255 },
Harald Weltea183a6e2016-06-17 00:06:42 +0200256 [SUBSCR_CONN_S_NEW] = {
257 .name = OSMO_STRINGIFY(SUBSCR_CONN_S_NEW),
258 .in_event_mask = S(SUBSCR_CONN_E_ACCEPTED) |
259 S(SUBSCR_CONN_E_MO_CLOSE) |
Harald Welte7b423ed2016-06-19 18:06:02 +0200260 S(SUBSCR_CONN_E_CN_CLOSE),
Harald Weltea183a6e2016-06-17 00:06:42 +0200261 .out_state_mask = S(SUBSCR_CONN_S_ACCEPTED) |
262 S(SUBSCR_CONN_S_RELEASED),
263 .action = subscr_conn_fsm_new,
264 },
265 [SUBSCR_CONN_S_ACCEPTED] = {
266 .name = OSMO_STRINGIFY(SUBSCR_CONN_S_ACCEPTED),
267 /* allow everything to release for any odd behavior */
Harald Welte7b423ed2016-06-19 18:06:02 +0200268 .in_event_mask = S(SUBSCR_CONN_E_COMMUNICATING) |
269 S(SUBSCR_CONN_E_BUMP) |
270 S(SUBSCR_CONN_E_ACCEPTED) |
Harald Weltea183a6e2016-06-17 00:06:42 +0200271 S(SUBSCR_CONN_E_MO_CLOSE) |
Harald Welte7b423ed2016-06-19 18:06:02 +0200272 S(SUBSCR_CONN_E_CN_CLOSE),
273 .out_state_mask = S(SUBSCR_CONN_S_RELEASED) |
274 S(SUBSCR_CONN_S_COMMUNICATING),
275 .onenter = subscr_conn_fsm_accepted_enter,
Harald Weltea183a6e2016-06-17 00:06:42 +0200276 .action = subscr_conn_fsm_accepted,
277 },
Harald Welte7b423ed2016-06-19 18:06:02 +0200278 [SUBSCR_CONN_S_COMMUNICATING] = {
279 .name = OSMO_STRINGIFY(SUBSCR_CONN_S_COMMUNICATING),
280 /* allow everything to release for any odd behavior */
281 .in_event_mask = S(SUBSCR_CONN_E_BUMP) |
282 S(SUBSCR_CONN_E_ACCEPTED) |
283 S(SUBSCR_CONN_E_COMMUNICATING) |
284 S(SUBSCR_CONN_E_MO_CLOSE) |
285 S(SUBSCR_CONN_E_CN_CLOSE),
286 .out_state_mask = S(SUBSCR_CONN_S_RELEASED),
287 .action = subscr_conn_fsm_communicating,
288 },
Harald Weltea183a6e2016-06-17 00:06:42 +0200289 [SUBSCR_CONN_S_RELEASED] = {
290 .name = OSMO_STRINGIFY(SUBSCR_CONN_S_RELEASED),
291 .onenter = subscr_conn_fsm_release,
292 },
293};
294
295static struct osmo_fsm subscr_conn_fsm = {
296 .name = "Subscr_Conn",
297 .states = subscr_conn_fsm_states,
298 .num_states = ARRAY_SIZE(subscr_conn_fsm_states),
299 .allstate_event_mask = 0,
300 .allstate_action = NULL,
Neels Hofmeyra1756f32016-05-20 21:59:55 +0200301 .log_subsys = DMM,
Harald Weltea183a6e2016-06-17 00:06:42 +0200302 .event_names = subscr_conn_fsm_event_names,
Harald Welte7b423ed2016-06-19 18:06:02 +0200303 .cleanup = subscr_conn_fsm_cleanup,
304 .timer_cb = subscr_conn_fsm_timeout,
Harald Weltea183a6e2016-06-17 00:06:42 +0200305};
306
307int msc_create_conn_fsm(struct gsm_subscriber_connection *conn, const char *id)
308{
309 struct osmo_fsm_inst *fi;
310 OSMO_ASSERT(conn);
311
312 if (conn->conn_fsm) {
313 LOGP(DMM, LOGL_ERROR,
314 "%s: Error: connection already in use\n", id);
315 return -EINVAL;
316 }
317
Harald Welte7b423ed2016-06-19 18:06:02 +0200318 /* Allocate the FSM not with the subscr_conn. Semantically it would
319 * make sense, but in subscr_conn_fsm_cleanup(), we want to discard the
320 * subscriber connection. If the FSM is freed along with the subscriber
321 * connection, then in _osmo_fsm_inst_term() the osmo_fsm_inst_free()
322 * that follows the cleanup() call would run into a double free. */
323 fi = osmo_fsm_inst_alloc(&subscr_conn_fsm, conn->network,
324 msc_subscr_conn_get(conn),
325 LOGL_DEBUG, id);
Harald Weltea183a6e2016-06-17 00:06:42 +0200326
327 if (!fi) {
328 LOGP(DMM, LOGL_ERROR,
329 "%s: Failed to allocate subscr conn master FSM\n", id);
330 return -ENOMEM;
331 }
332 conn->conn_fsm = fi;
Harald Welte7b423ed2016-06-19 18:06:02 +0200333 osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_START, NULL);
Harald Weltea183a6e2016-06-17 00:06:42 +0200334 return 0;
335}
336
337bool msc_subscr_conn_is_accepted(struct gsm_subscriber_connection *conn)
338{
339 if (!conn)
340 return false;
Harald Welte7b423ed2016-06-19 18:06:02 +0200341 if (!conn->vsub)
Harald Weltea183a6e2016-06-17 00:06:42 +0200342 return false;
343 if (!conn->conn_fsm)
344 return false;
Harald Welte7b423ed2016-06-19 18:06:02 +0200345 if (!(conn->conn_fsm->state == SUBSCR_CONN_S_ACCEPTED
346 || conn->conn_fsm->state == SUBSCR_CONN_S_COMMUNICATING))
Harald Weltea183a6e2016-06-17 00:06:42 +0200347 return false;
348 return true;
349}
350
Harald Welte7b423ed2016-06-19 18:06:02 +0200351void msc_subscr_conn_communicating(struct gsm_subscriber_connection *conn)
352{
353 OSMO_ASSERT(conn);
354 osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_COMMUNICATING,
355 NULL);
356}
357
Harald Weltea183a6e2016-06-17 00:06:42 +0200358void msc_subscr_conn_init(void)
359{
360 osmo_fsm_register(&subscr_conn_fsm);
361}