blob: 124e46bfad00240e806c739950cff77adae2aceb [file] [log] [blame]
Harald Welteb8b85a12016-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 Welte2483f1b2016-06-19 18:06:02 +020026#include <osmocom/core/signal.h>
Harald Welteb8b85a12016-06-17 00:06:42 +020027
Neels Hofmeyr90843962017-09-04 15:04:35 +020028#include <osmocom/msc/osmo_msc.h>
29#include <osmocom/msc/vlr.h>
30#include <osmocom/msc/debug.h>
31#include <osmocom/msc/transaction.h>
32#include <osmocom/msc/signal.h>
33#include <osmocom/msc/a_iface.h>
Philipp Maierfbf66102017-04-09 12:32:51 +020034
Harald Welte2483f1b2016-06-19 18:06:02 +020035#define SUBSCR_CONN_TIMEOUT 5 /* seconds */
Harald Welteb8b85a12016-06-17 00:06:42 +020036
37static const struct value_string subscr_conn_fsm_event_names[] = {
38 OSMO_VALUE_STRING(SUBSCR_CONN_E_INVALID),
Harald Welte2483f1b2016-06-19 18:06:02 +020039 OSMO_VALUE_STRING(SUBSCR_CONN_E_START),
Harald Welteb8b85a12016-06-17 00:06:42 +020040 OSMO_VALUE_STRING(SUBSCR_CONN_E_ACCEPTED),
Harald Welte2483f1b2016-06-19 18:06:02 +020041 OSMO_VALUE_STRING(SUBSCR_CONN_E_COMMUNICATING),
Harald Welteb8b85a12016-06-17 00:06:42 +020042 OSMO_VALUE_STRING(SUBSCR_CONN_E_BUMP),
43 OSMO_VALUE_STRING(SUBSCR_CONN_E_MO_CLOSE),
44 OSMO_VALUE_STRING(SUBSCR_CONN_E_CN_CLOSE),
Harald Welteb8b85a12016-06-17 00:06:42 +020045 { 0, NULL }
46};
47
48const struct value_string subscr_conn_from_names[] = {
49 OSMO_VALUE_STRING(SUBSCR_CONN_FROM_INVALID),
50 OSMO_VALUE_STRING(SUBSCR_CONN_FROM_LU),
51 OSMO_VALUE_STRING(SUBSCR_CONN_FROM_CM_SERVICE_REQ),
52 OSMO_VALUE_STRING(SUBSCR_CONN_FROM_PAGING_RESP),
53 { 0, NULL }
54};
55
Neels Hofmeyr84da6b12016-05-20 21:59:55 +020056static void paging_event(struct gsm_subscriber_connection *conn,
57 enum gsm_paging_event pe)
Harald Welteb8b85a12016-06-17 00:06:42 +020058{
Harald Welte2483f1b2016-06-19 18:06:02 +020059 subscr_paging_dispatch(GSM_HOOK_RR_PAGING, pe, NULL, conn, conn->vsub);
60}
61
62void subscr_conn_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
63{
64 OSMO_ASSERT(event == SUBSCR_CONN_E_START);
65 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_NEW,
66 SUBSCR_CONN_TIMEOUT, 0);
Harald Welteb8b85a12016-06-17 00:06:42 +020067}
68
69void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data)
70{
71 struct gsm_subscriber_connection *conn = fi->priv;
72 enum subscr_conn_from from = SUBSCR_CONN_FROM_INVALID;
Harald Welte2483f1b2016-06-19 18:06:02 +020073 bool success;
Harald Welteb8b85a12016-06-17 00:06:42 +020074
75 if (data) {
76 from = *(enum subscr_conn_from*)data;
77 LOGPFSM(fi, "%s\n", subscr_conn_from_name(from));
78 }
79
80 /* If accepted, transition the state, all other cases mean failure. */
81 switch (event) {
82 case SUBSCR_CONN_E_ACCEPTED:
Harald Welte2483f1b2016-06-19 18:06:02 +020083 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_ACCEPTED,
84 SUBSCR_CONN_TIMEOUT, 0);
Harald Welteb8b85a12016-06-17 00:06:42 +020085 break;
86
87 case SUBSCR_CONN_E_MO_CLOSE:
88 case SUBSCR_CONN_E_CN_CLOSE:
Neels Hofmeyr84da6b12016-05-20 21:59:55 +020089 if (data)
90 LOGPFSM(fi, "Close event, cause %u\n",
91 *(uint32_t*)data);
92 /* will release further below, see
93 * 'if (fi->state != SUBSCR_CONN_S_ACCEPTED)' */
Harald Welteb8b85a12016-06-17 00:06:42 +020094 break;
95
96 default:
Neels Hofmeyr84da6b12016-05-20 21:59:55 +020097 LOGPFSML(fi, LOGL_ERROR,
98 "Unexpected event: %d %s\n", event,
99 osmo_fsm_event_name(fi->fsm, event));
Harald Welteb8b85a12016-06-17 00:06:42 +0200100 break;
101 }
102
Harald Welte2483f1b2016-06-19 18:06:02 +0200103 success = (fi->state == SUBSCR_CONN_S_ACCEPTED);
104
105 if (from == SUBSCR_CONN_FROM_LU)
106 rate_ctr_inc(&conn->network->msc_ctrs->ctr[
107 success ? MSC_CTR_LOC_UPDATE_COMPLETED
108 : MSC_CTR_LOC_UPDATE_FAILED]);
109
110 /* signal paging success or failure in case this was a paging */
111 if (from == SUBSCR_CONN_FROM_PAGING_RESP)
Neels Hofmeyr84da6b12016-05-20 21:59:55 +0200112 paging_event(conn,
113 success ? GSM_PAGING_SUCCEEDED
114 : GSM_PAGING_EXPIRED);
115
116 /* FIXME rate counters */
117 /*rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_COMPLETED]);*/
Harald Welteb8b85a12016-06-17 00:06:42 +0200118
119 /* On failure, discard the conn */
Harald Welte2483f1b2016-06-19 18:06:02 +0200120 if (!success) {
Harald Welteb8b85a12016-06-17 00:06:42 +0200121 /* TODO: on MO_CLOSE or CN_CLOSE, first go to RELEASING and
Neels Hofmeyr84da6b12016-05-20 21:59:55 +0200122 * await BSC/RNC confirmation? */
Harald Welteb8b85a12016-06-17 00:06:42 +0200123 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
124 return;
125 }
126
Harald Welteb8b85a12016-06-17 00:06:42 +0200127 if (from == SUBSCR_CONN_FROM_CM_SERVICE_REQ) {
128 conn->received_cm_service_request = true;
Neels Hofmeyr84da6b12016-05-20 21:59:55 +0200129 LOGPFSML(fi, LOGL_DEBUG, "received_cm_service_request = true\n");
Harald Welteb8b85a12016-06-17 00:06:42 +0200130 }
131
132 osmo_fsm_inst_dispatch(fi, SUBSCR_CONN_E_BUMP, data);
133}
134
Harald Welteb8b85a12016-06-17 00:06:42 +0200135static void subscr_conn_fsm_bump(struct osmo_fsm_inst *fi, uint32_t event, void *data)
136{
137 struct gsm_subscriber_connection *conn = fi->priv;
Neels Hofmeyr84da6b12016-05-20 21:59:55 +0200138 struct gsm_trans *trans;
Harald Welteb8b85a12016-06-17 00:06:42 +0200139
Neels Hofmeyr84da6b12016-05-20 21:59:55 +0200140 if (conn->silent_call) {
141 LOGPFSML(fi, LOGL_DEBUG, "bump: silent call still active\n");
Harald Welteb8b85a12016-06-17 00:06:42 +0200142 return;
Neels Hofmeyr84da6b12016-05-20 21:59:55 +0200143 }
Harald Welteb8b85a12016-06-17 00:06:42 +0200144
Neels Hofmeyr84da6b12016-05-20 21:59:55 +0200145 if (conn->received_cm_service_request) {
146 LOGPFSML(fi, LOGL_DEBUG, "bump: still awaiting first request after a CM Service Request\n");
Harald Welteb8b85a12016-06-17 00:06:42 +0200147 return;
Neels Hofmeyr84da6b12016-05-20 21:59:55 +0200148 }
Harald Welteb8b85a12016-06-17 00:06:42 +0200149
Neels Hofmeyr84da6b12016-05-20 21:59:55 +0200150 if (conn->vsub && !llist_empty(&conn->vsub->cs.requests)) {
151 struct subscr_request *sr;
152 if (!log_check_level(fi->fsm->log_subsys, LOGL_DEBUG)) {
153 llist_for_each_entry(sr, &conn->vsub->cs.requests, entry) {
154 LOGPFSML(fi, LOGL_DEBUG, "bump: still active: %s\n",
155 sr->label);
156 }
157 }
Harald Welteb8b85a12016-06-17 00:06:42 +0200158 return;
Neels Hofmeyr84da6b12016-05-20 21:59:55 +0200159 }
Harald Welteb8b85a12016-06-17 00:06:42 +0200160
Neels Hofmeyr84da6b12016-05-20 21:59:55 +0200161 if ((trans = trans_has_conn(conn))) {
162 LOGPFSML(fi, LOGL_DEBUG,
163 "bump: connection still has active transaction: %s\n",
164 gsm48_pdisc_name(trans->protocol));
Harald Welteb8b85a12016-06-17 00:06:42 +0200165 return;
Neels Hofmeyr84da6b12016-05-20 21:59:55 +0200166 }
Harald Welteb8b85a12016-06-17 00:06:42 +0200167
Neels Hofmeyr84da6b12016-05-20 21:59:55 +0200168 LOGPFSML(fi, LOGL_DEBUG, "bump: releasing conn\n");
Harald Welteb8b85a12016-06-17 00:06:42 +0200169 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
170}
171
Harald Welte2483f1b2016-06-19 18:06:02 +0200172static void subscr_conn_fsm_accepted_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
173{
174 struct gsm_subscriber_connection *conn = fi->priv;
175 osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_ATTACHED, conn->vsub);
176}
177
Harald Welteb8b85a12016-06-17 00:06:42 +0200178static void subscr_conn_fsm_accepted(struct osmo_fsm_inst *fi, uint32_t event, void *data)
179{
180 switch (event) {
Harald Welte2483f1b2016-06-19 18:06:02 +0200181 case SUBSCR_CONN_E_COMMUNICATING:
182 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_COMMUNICATING, 0, 0);
183 return;
184
Harald Welteb8b85a12016-06-17 00:06:42 +0200185 case SUBSCR_CONN_E_BUMP:
186 subscr_conn_fsm_bump(fi, event, data);
187 return;
188
189 default:
190 break;
191 }
192 /* Whatever unexpected happens in the accepted state, it means release.
193 * Even if an unexpected event is passed, the safest thing to do is
194 * discard the conn. We don't expect another SUBSCR_CONN_E_ACCEPTED. */
195 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
196}
197
Harald Welte2483f1b2016-06-19 18:06:02 +0200198static void subscr_conn_fsm_communicating(struct osmo_fsm_inst *fi, uint32_t event, void *data)
199{
200 switch (event) {
201 case SUBSCR_CONN_E_COMMUNICATING:
202 /* no-op */
203 return;
204
205 case SUBSCR_CONN_E_BUMP:
206 subscr_conn_fsm_bump(fi, event, data);
207 return;
208
209 default:
210 break;
211 }
212 /* Whatever unexpected happens in the accepted state, it means release.
213 * Even if an unexpected event is passed, the safest thing to do is
214 * discard the conn. We don't expect another SUBSCR_CONN_E_ACCEPTED. */
215 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
216}
217
218static void subscr_conn_fsm_cleanup(struct osmo_fsm_inst *fi,
219 enum osmo_fsm_term_cause cause)
Harald Welteb8b85a12016-06-17 00:06:42 +0200220{
221 struct gsm_subscriber_connection *conn = fi->priv;
Harald Welte2483f1b2016-06-19 18:06:02 +0200222 fi->priv = NULL;
223
Harald Welteb8b85a12016-06-17 00:06:42 +0200224 if (!conn)
225 return;
Harald Welte2483f1b2016-06-19 18:06:02 +0200226 conn->conn_fsm = NULL;
227 msc_subscr_conn_close(conn, cause);
Neels Hofmeyr6166f292017-11-22 14:33:12 +0100228 msc_subscr_conn_put(conn, MSC_CONN_USE_FSM);
Harald Welte2483f1b2016-06-19 18:06:02 +0200229}
Harald Welteb8b85a12016-06-17 00:06:42 +0200230
Harald Welte2483f1b2016-06-19 18:06:02 +0200231int subscr_conn_fsm_timeout(struct osmo_fsm_inst *fi)
232{
233 struct gsm_subscriber_connection *conn = fi->priv;
234 if (conn)
235 vlr_subscr_conn_timeout(conn->vsub);
Neels Hofmeyr3ddd7422017-11-18 23:25:41 +0100236 else
237 osmo_fsm_inst_dispatch(fi, SUBSCR_CONN_E_CN_CLOSE, NULL);
Harald Welte2483f1b2016-06-19 18:06:02 +0200238 return 0;
239}
240
241static void subscr_conn_fsm_release(struct osmo_fsm_inst *fi, uint32_t prev_state)
242{
243 osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
Harald Welteb8b85a12016-06-17 00:06:42 +0200244}
245
246#define S(x) (1 << (x))
247
248static const struct osmo_fsm_state subscr_conn_fsm_states[] = {
Harald Welte2483f1b2016-06-19 18:06:02 +0200249 [SUBSCR_CONN_S_INIT] = {
250 .name = OSMO_STRINGIFY(SUBSCR_CONN_S_INIT),
251 .in_event_mask = S(SUBSCR_CONN_E_START),
252 .out_state_mask = S(SUBSCR_CONN_S_NEW),
253 .action = subscr_conn_fsm_init,
254 },
Harald Welteb8b85a12016-06-17 00:06:42 +0200255 [SUBSCR_CONN_S_NEW] = {
256 .name = OSMO_STRINGIFY(SUBSCR_CONN_S_NEW),
257 .in_event_mask = S(SUBSCR_CONN_E_ACCEPTED) |
258 S(SUBSCR_CONN_E_MO_CLOSE) |
Harald Welte2483f1b2016-06-19 18:06:02 +0200259 S(SUBSCR_CONN_E_CN_CLOSE),
Harald Welteb8b85a12016-06-17 00:06:42 +0200260 .out_state_mask = S(SUBSCR_CONN_S_ACCEPTED) |
261 S(SUBSCR_CONN_S_RELEASED),
262 .action = subscr_conn_fsm_new,
263 },
264 [SUBSCR_CONN_S_ACCEPTED] = {
265 .name = OSMO_STRINGIFY(SUBSCR_CONN_S_ACCEPTED),
266 /* allow everything to release for any odd behavior */
Harald Welte2483f1b2016-06-19 18:06:02 +0200267 .in_event_mask = S(SUBSCR_CONN_E_COMMUNICATING) |
268 S(SUBSCR_CONN_E_BUMP) |
269 S(SUBSCR_CONN_E_ACCEPTED) |
Harald Welteb8b85a12016-06-17 00:06:42 +0200270 S(SUBSCR_CONN_E_MO_CLOSE) |
Harald Welte2483f1b2016-06-19 18:06:02 +0200271 S(SUBSCR_CONN_E_CN_CLOSE),
272 .out_state_mask = S(SUBSCR_CONN_S_RELEASED) |
273 S(SUBSCR_CONN_S_COMMUNICATING),
274 .onenter = subscr_conn_fsm_accepted_enter,
Harald Welteb8b85a12016-06-17 00:06:42 +0200275 .action = subscr_conn_fsm_accepted,
276 },
Harald Welte2483f1b2016-06-19 18:06:02 +0200277 [SUBSCR_CONN_S_COMMUNICATING] = {
278 .name = OSMO_STRINGIFY(SUBSCR_CONN_S_COMMUNICATING),
279 /* allow everything to release for any odd behavior */
280 .in_event_mask = S(SUBSCR_CONN_E_BUMP) |
281 S(SUBSCR_CONN_E_ACCEPTED) |
282 S(SUBSCR_CONN_E_COMMUNICATING) |
283 S(SUBSCR_CONN_E_MO_CLOSE) |
284 S(SUBSCR_CONN_E_CN_CLOSE),
285 .out_state_mask = S(SUBSCR_CONN_S_RELEASED),
286 .action = subscr_conn_fsm_communicating,
287 },
Harald Welteb8b85a12016-06-17 00:06:42 +0200288 [SUBSCR_CONN_S_RELEASED] = {
289 .name = OSMO_STRINGIFY(SUBSCR_CONN_S_RELEASED),
290 .onenter = subscr_conn_fsm_release,
291 },
292};
293
294static struct osmo_fsm subscr_conn_fsm = {
295 .name = "Subscr_Conn",
296 .states = subscr_conn_fsm_states,
297 .num_states = ARRAY_SIZE(subscr_conn_fsm_states),
298 .allstate_event_mask = 0,
299 .allstate_action = NULL,
Neels Hofmeyr84da6b12016-05-20 21:59:55 +0200300 .log_subsys = DMM,
Harald Welteb8b85a12016-06-17 00:06:42 +0200301 .event_names = subscr_conn_fsm_event_names,
Harald Welte2483f1b2016-06-19 18:06:02 +0200302 .cleanup = subscr_conn_fsm_cleanup,
303 .timer_cb = subscr_conn_fsm_timeout,
Harald Welteb8b85a12016-06-17 00:06:42 +0200304};
305
306int msc_create_conn_fsm(struct gsm_subscriber_connection *conn, const char *id)
307{
308 struct osmo_fsm_inst *fi;
309 OSMO_ASSERT(conn);
310
311 if (conn->conn_fsm) {
312 LOGP(DMM, LOGL_ERROR,
313 "%s: Error: connection already in use\n", id);
314 return -EINVAL;
315 }
316
Harald Welte2483f1b2016-06-19 18:06:02 +0200317 /* Allocate the FSM not with the subscr_conn. Semantically it would
318 * make sense, but in subscr_conn_fsm_cleanup(), we want to discard the
319 * subscriber connection. If the FSM is freed along with the subscriber
320 * connection, then in _osmo_fsm_inst_term() the osmo_fsm_inst_free()
321 * that follows the cleanup() call would run into a double free. */
322 fi = osmo_fsm_inst_alloc(&subscr_conn_fsm, conn->network,
Neels Hofmeyr6166f292017-11-22 14:33:12 +0100323 msc_subscr_conn_get(conn, MSC_CONN_USE_FSM),
Harald Welte2483f1b2016-06-19 18:06:02 +0200324 LOGL_DEBUG, id);
Harald Welteb8b85a12016-06-17 00:06:42 +0200325
326 if (!fi) {
327 LOGP(DMM, LOGL_ERROR,
328 "%s: Failed to allocate subscr conn master FSM\n", id);
329 return -ENOMEM;
330 }
331 conn->conn_fsm = fi;
Harald Welte2483f1b2016-06-19 18:06:02 +0200332 osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_START, NULL);
Harald Welteb8b85a12016-06-17 00:06:42 +0200333 return 0;
334}
335
Maxd83b17b2018-02-06 16:51:31 +0100336bool msc_subscr_conn_is_accepted(const struct gsm_subscriber_connection *conn)
Harald Welteb8b85a12016-06-17 00:06:42 +0200337{
338 if (!conn)
339 return false;
Harald Welte2483f1b2016-06-19 18:06:02 +0200340 if (!conn->vsub)
Harald Welteb8b85a12016-06-17 00:06:42 +0200341 return false;
342 if (!conn->conn_fsm)
343 return false;
Harald Welte2483f1b2016-06-19 18:06:02 +0200344 if (!(conn->conn_fsm->state == SUBSCR_CONN_S_ACCEPTED
345 || conn->conn_fsm->state == SUBSCR_CONN_S_COMMUNICATING))
Harald Welteb8b85a12016-06-17 00:06:42 +0200346 return false;
347 return true;
348}
349
Harald Welte2483f1b2016-06-19 18:06:02 +0200350void msc_subscr_conn_communicating(struct gsm_subscriber_connection *conn)
351{
352 OSMO_ASSERT(conn);
353 osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_COMMUNICATING,
354 NULL);
355}
356
Harald Welteb8b85a12016-06-17 00:06:42 +0200357void msc_subscr_conn_init(void)
358{
359 osmo_fsm_register(&subscr_conn_fsm);
360}